【Win10 UWP】微信SDK基本使用方法和基本原理


上回講到,作為一個長期散播溫暖,散播希望的小清新無公害WP開發者,繼QQ SDK之后,又把UWP微信SDK這茬了結了,僅供學習交流。

1.安裝微信SDK for UWP

微信官方此前明確說明短時間內暫不提供RT版和UWP版的微信SDK,但眼見UWP開發之勢愈烈,微信分享也必然是許多應用的標配功能,那怎么辦呢,自己移植成UWP版吧。拙者提供的微信SDK是基於官方silverlight版sdk反編譯后重新打包封裝成UWP版本的,對部分內容稍加修改,命名空間、使用方法基本與官方文檔無異。

通過nuget下載並安裝微信SDK,鏈接https://www.nuget.org/packages/WeChatSDK/

或者在控制台輸入PM> Install-Package WeChatSDK

注意項目會引用依賴Google.ProtocolBuffersLite,如果安裝完成之后該依賴未自動安裝,建議您Unload后Reload一下項目,或者手動安裝該依賴

PM> Install-Package Google.ProtocolBuffersLite

另外,本項目代碼已放在github上,請高手幫忙改進https://github.com/zhxilin/WeChatSDK

2.申請微信開放平台應用Id

到微信開放平台官網https://open.weixin.qq.com/,創建應用獲得AppId和AppSecret。

3.配置Package.appxmanifest文件

與QQ SDK不同,微信SDK僅使用文件關聯協議進行應用間通信,為此需要對配置文件進行配置。打開Package.appxmanifest文件,在<Extensions>節點下添加windows.fileTypeAssociation的聲明:

 <Extensions>
    <uap:Extension Category="windows.fileTypeAssociation">
        <uap:FileTypeAssociation Name="wechat">
            <uap:SupportedFileTypes>
                <uap:FileType>.[YOUR APP ID]</uap:FileType>
            </uap:SupportedFileTypes>
        </uap:FileTypeAssociation>
    </uap:Extension>
</Extensions>

注意不要漏了appid前面的“.”

4.調用微信SDK進行分享

微信消息的基類是WXBaseMessage類,包含屬性Title、Description和ThumbData,其中Title的最大長度為512B,Description最大長度為1K,ThumbData最大長度為32K,否則會引發WXException錯誤。根據不同場合消息又分為8種類型,均集成自WXBaseMessage類:

WXTextMessage,文本消息,Type = WXBaseMessage.TYPE_TEXT

WXImageMessage,圖片消息,Type = WXBaseMessage.TYPE_IMAGE

WXMusicMessage,音樂消息,Type =  WXBaseMessage.TYPE_MUSIC

WXVideoMessage,視頻消息,Type = WXBaseMessage.TYPE_VIDEO

WXWebpageMessage,網頁消息,Type = WXWebpageMessage.TYPE_URL

WXFileMessage,文件消息,Type = WXWebpageMessage.TYPE_FILE

WXAppExtendMessage,App擴展消息,Type = WXWebpageMessage.TYPE_APPDATA

WXEmojiMessage,表情消息,Type = WXEmojiMessage.TYPE_EMOJI

根據常用性,這里列舉文本、圖片和網頁三種消息的分享方法,直接列舉代碼說明。以下是消息分享均通過打包消息數據后,發送SendMessageToWX.Req類型的請求來呼起微信客戶端。

(1)分享文本消息

 1 try
 2 {
 3         var scene = SendMessageToWX.Req.WXSceneTimeline;
 4         var message = new WXTextMessage
 5         {        
 6                 Title = "Sharing a text title!",
 7                 Text = "This is text content",
 8                 Description = "This is a text message.這是一個文本消息。",
 9                 ThumbData = null
10         };
11         SendMessageToWX.Req req = new SendMessageToWX.Req(message, scene);
12         IWXAPI api = WXAPIFactory.CreateWXAPI("[YOUR APP ID]");
13         var isValid = await api.SendReq(req);
14 }
15 catch (WXException ex)
16 {
17         Debug.WriteLine(ex.Message);
18 }

其中scene有3種類型可以選擇:

SendMessageToWX.Req.WXSceneChooseByUser // 用戶自行選擇
SendMessageToWX.Req.WXSceneSession // 分享給好友
SendMessageToWX.Req.WXSceneTimeline // 分享到朋友圈

其中Text屬性是Text類型消息的專有屬性,最大長度640K。

(2)分享圖片消息

 1 try
 2 {
 3     var scene = SendMessageToWX.Req.WXSceneTimeline;
 4     var file = await Package.Current.InstalledLocation.GetFileAsync("1.png");
 5     using (var stream = await file.OpenReadAsync())
 6     {
 7         var pic = new byte[stream.Size];
 8         await stream.AsStream().ReadAsync(pic, 0, pic.Length);
 9         var message = new WXImageMessage
10         {
11             Title = "Sharing a picture!",
12             Description = "This is a image message.這是一個圖片消息",
13             ThumbData = pic,
14             ImageUrl = "http://tp3.sinaimg.cn/1882347990/180/5725518284/1"
15         };
16         SendMessageToWX.Req req = new SendMessageToWX.Req(message, scene);
17         IWXAPI api = WXAPIFactory.CreateWXAPI("[YOUR APP ID]");
18         var isValid = await api.SendReq(req);
19     }
20 }
21 catch (WXException ex)
22 {
23     Debug.WriteLine(ex.Message);
24 }

其中ImageUrl和ImageData只能設置一個,ImageData不能超過10M。

(3)分享網頁消息

 1 try
 2 {
 3         var scene = SendMessageToWX.Req.WXSceneTimeline;
 4         var file = await Package.Current.InstalledLocation.GetFileAsync("1.png");
 5         using (var stream = await file.OpenReadAsync())
 6         {
 7                 var pic = new byte[stream.Size];
 8                 await stream.AsStream().ReadAsync(pic, 0, pic.Length);
 9                 var message = new WXWebpageMessage
10                 {
11                         WebpageUrl = "http://www.baidu.com",
12                         Title = "Sharing a link!",
13                         Description = "This is a link message.這是一個鏈接消息",
14                         ThumbData = pic
15                 };
16                 SendMessageToWX.Req req = new SendMessageToWX.Req(message, scene);
17                 IWXAPI api = WXAPIFactory.CreateWXAPI("[YOUR APP ID]");
18                 var isValid = await api.SendReq(req);
19         }
20 }
21 catch (WXException ex)
22 {
23         Debug.WriteLine(ex.Message);
24 }

其中WebpageUrl不能超過10K。

(4)登錄授權

登錄授權向微信客戶端發出的請求類型是SendAuth.Req

 1 try
 2 {
 3         SendAuth.Req req = new SendAuth.Req("[YOUR SCOPE]", "test");
 4         IWXAPI api = WXAPIFactory.CreateWXAPI("[YOUR APP ID]");
 5         var isValid = await api.SendReq(req);
 6 }
 7 catch (WXException ex)
 8 {
 9         Debug.WriteLine(ex.Message);
10 }

其中[YOUR SCOPE]指的是在開放平台注冊應用,開通登錄授權后的權限范圍。

(5)微信支付

SDK也提供了微信支付的請求類型SendPay.Req,由於支付權限申請比較困難,暫時沒有測試,以后有實際場景再試。

5.回調結果處理

微信SDK提供了WXEntryBasePage類,繼承自Page類,用來響應微信的回調。

如果你的頁面基類仍然是Page,可以替換為WXEntryBasePage作為你的Page基類,直接重載各種Response方法即可得到回調結果;如果你已有自定義的Page基類,則通過另外的途徑獲取回調結果。

WXEntryBasePage類提供public void Handle(FileActivatedEventArgs e)方法來處理回調,我們僅需在App.xaml.cs中重載OnFileActivated方法,將參數傳入Hadle方法即可進行結果解析,並將不同類型的消息結果通過不同的響應方法進行通知。

WXEntryBasePage類包含以下Response方法:

 1 public virtual void OnSendAuthResponse(SendAuth.Resp response)
 2 {
 3 }
 4 
 5 public virtual void OnSendMessageToWXResponse(SendMessageToWX.Resp response)
 6 {
 7 }
 8 
 9 public virtual void OnSendPayResponse(SendPay.Resp response)
10 {
11 }

以上三種Response方法分別對應登錄授權、消息分享以及支付等操作的結果。

  • 如果你的Page基類已改成WXEntryBasePage,那么直接重載對應的這三個方法即可,如:
1 public override async void OnSendMessageToWXResponse(SendMessageToWX.Resp response)
2 {
3         base.OnSendMessageToWXResponse(response);
4         var dialog = new MessageDialog(response.ErrCode == 0 ? "分享成功": "分享失敗");
5         await dialog.ShowAsync();
6 }
  • 如果你的Page基類沒有改成WXEntryBasePage,那么你需要自己實現一個Callback類,繼承自WXEntryBasePage:
1 public class WeChatCallback : WXEntryBasePage
2 {
3 }

並在App.xaml.cs的OnFileActivated方法中,創建該對象,並調用其Handle方法:

 1  protected override void OnFileActivated(FileActivatedEventArgs args)
 2 {
 3         base.OnFileActivated(args);
 4         try
 5         {
 6             var wechat = new WeChatCallback();
 7             wechat.Handle(args);
 8         }
 9         catch (Exception)
10         {
11                 // ignored
12         }
13 }        

 

對於消息分享的Response,參數類型是SendMessageToWX.Resp,ErrCode == 0時表示分享成功,否則分享失敗;

對於登錄授權的Response,參數類型是SendAuth.Resp,其中Code即OAuth授權第一步所需的code,通過這個code,調用微信的Open API換取AccessToken,之后才能得到用戶的基本資料。微信SDK並不實現Open API,所以需要自行實現。我還寫了一個WeChatSDK.Extensions類庫,提供WeChatSns類,封裝了部分Open API,直接調用封裝好的接口即可完成整個OAuth:

 1 public override async void OnSendAuthResponse(SendAuth.Resp response)
 2 {
 3         base.OnSendAuthResponse(response);
 4         if (response.ErrCode == 0)
 5         {
 6                 if (!string.IsNullOrEmpty(response.Code))
 7                 {
 8                         var token = await WeChatSns.GetAccessTokenAsync(response.Code);
 9                         if (token != null)
10                         {
11                                 var user = await WeChatSns.GetUserInfoAsync(token.AccessToken, token.OpenId);
12                                 var dialog = new MessageDialog($"name:{user.Nickname}\r\nopenid:{user.OpenId}","授權成功");
13                                 await dialog.ShowAsync();
14                         }
15                 }
16         }
17         else
18         {
19                 var dialog = new MessageDialog("授權失敗");
20                 await dialog.ShowAsync();
21         }
22 }

WeChatSDK.Extensions類庫源碼已貼在Github上,歡迎補充https://github.com/zhxilin/WeChatSDK/tree/master/UWP/WeChatSDK.Extensions

更多微信Open API請參考官方文檔:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&lang=zh_CN

6.剖析微信SDK的原理

說完微信SDK的用法,下面談談它的實現原理。

要理解微信SDK的實現原理,首先要解決一個問題,理解什么是Protocol Buffers。可能很多人都注意到了微信SDK需要引用Google.ProtocolBuffersLite類庫。

Protocol Buffers 是一種輕便高效的結構化數據存儲格式,可以用於結構化數據串行化,很適合做數據存儲或 RPC 數據交換格式。它可用於通訊協議、數據存儲等領域的語言無關、平台無關、可擴展的序列化結構數據格式。目前支持C++、Java、Python、C#等語言。

...

是Google 公司內部的混合語言數據標准

使用Protocol Buffers比XML的好處在於更簡單、更快、更小,對於微信這種頻繁數據序列化和反序列化的場景來說非常實用。微信的數據交換在設計之初必須考慮實現消息的跨平台傳輸,那么對於平台無關、可擴展性超高的Protocol Buffers技術來說,就有了很大的用武之地。

對Protocol Buffers有興趣的朋友可以多查閱一下相關資料

http://www.ibm.com/developerworks/cn/linux/l-cn-gpb/index.html

http://kb.cnblogs.com/page/77621/

http://www.cnblogs.com/wu-jian/archive/2011/02/22/1961104.html

 

簡單來說,對於服務端,只需要傳輸定義好結構的序列化文件 .proto文件到不同平台的客戶端,通過Google提供的工具,如protoc、ProtoGen等工具即可反序列化為對應平台的具體對象。可以通過一張圖來理解:

.proto文件會轉成二進制序列protobuf進行傳輸,客戶端接收到二進制序列后,按照指定的格式讀取到對應平台的結構類型中就可以了。整個解析過程需要 Protocol Buffers框架和Protobuf 編譯器生成的代碼共同完成。比如protoc將.proto文件轉成二進制文件,再通過protogen讀取二進制文件轉成C#代碼。

 

了解了以上知識之后,我們來看看微信SDK是怎么做的。

比如分享消息,SDK將每個消息的結構整理后填入TransactionData中,該類包含ToProto方法和FromProto方法,分別將數據結構序列化成.proto對象和將.proto對象反序列化成數據結構。ToProto方法調用了Goolge.ProtocolBuffersLite類庫提供的GeneratedBuilderLite<TMessage, TBuilder>.Build()方法完成序列化;FromProto方法則調用了BuildParsed()方法,將二進制序列反序列化成數據結構。

1 internal TransactDataP ToProto()
2 {
3         TransactDataP.Builder builder = TransactDataP.CreateBuilder();
4         // ... 將TransactData中的屬性填入builder后Build
5         return builder.Build();
6 }
public static SendAuthResp ParseFrom(byte[] data)
{
        return CreateBuilder().MergeFrom(data).BuildParsed();
}

我們的應用將數據打包放入一個后綴為.wechat的文件中,通過Launcher啟動這個文件,自然只有微信客戶端能識別這個文件,所以微信被呼叫起來並通過protobuf機制處理這個文件,完成我們的請求。

至於文件關聯協議,之前講過,在Package.appxmanifest中使用AppId(如wx0123456789abcdef)填寫的聲明,就是為了保證微信在處理完分享或者完成登錄授權后,生成數據並寫入文件.wx0123456789abcdef中,通過FileTypeAssociation協議,這種后綴的文件,也只能由我們的應用來打開了,這樣就保證了互調關系的唯一性。OnFileActivated接收到文件之后,把文件內的數據通過protobuf機制反序列出來就能得到結果了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM