Windows平台Golang应用集成Azure WNS推送服务技术问询
Windows平台Golang应用集成Azure WNS推送服务技术问询
嗨,我来帮你梳理下在Windows平台用Golang集成WNS+Azure推送的相关问题,都是实际开发中踩过坑的经验分享:
1. 可行性:完全可以实现
WNS本质是基于HTTP/HTTPS的标准化推送协议,Azure通知中心又提供了通用的REST API和Golang SDK支持,所以不管是发送还是接收推送,Golang都能搞定。核心逻辑是:Windows系统负责和WNS服务器交互获取ChannelURI,你的Golang应用只需要调用Windows的系统API拿到这个URI,再对接Azure通知中心的接口就行。
2. 如何获取ChannelURI
ChannelURI是Windows设备/应用从WNS服务器获取的唯一标识,必须通过Windows Runtime(WinRT)API来获取。Golang可以借助go-ole库(一个封装Win32 COM/WinRT的第三方库)来调用这些API,步骤如下:
步骤1:安装依赖库
go get github.com/go-ole/go-ole
步骤2:代码示例获取ChannelURI
package main import ( "fmt" "github.com/go-ole/go-ole" "github.com/go-ole/go-ole/oleutil" "time" ) func main() { // 初始化COM环境,必须调用且要和后续的释放配对 err := ole.CoInitializeEx(0, ole.COINIT_APARTMENTTHREADED|ole.COINIT_DISABLE_OLE1DDE) if err != nil { fmt.Printf("COM初始化失败: %v\n", err) return } defer ole.CoUninitialize() // 创建PushNotificationChannelManager实例 manager, err := oleutil.CreateObject("Windows.Networking.PushNotifications.PushNotificationChannelManager") if err != nil { fmt.Printf("创建ChannelManager失败: %v\n", err) return } defer manager.Release() managerDispatch, err := manager.QueryInterface(ole.IID_IDispatch) if err != nil { fmt.Printf("获取Dispatch接口失败: %v\n", err) return } defer managerDispatch.Release() // 调用异步方法获取Channel asyncResult, err := oleutil.CallMethod(managerDispatch, "CreatePushNotificationChannelForApplicationAsync") if err != nil { fmt.Printf("调用异步方法失败: %v\n", err) return } defer asyncResult.Clear() asyncDispatch := asyncResult.ToIDispatch() defer asyncDispatch.Release() // 等待异步操作完成(实际项目建议用事件监听,这里用轮询做简化示例) for { statusProp, err := oleutil.GetProperty(asyncDispatch, "Status") if err != nil { fmt.Printf("获取异步状态失败: %v\n", err) break } status := statusProp.Value().(int32) statusProp.Clear() switch status { case ole.StatusCompleted: // 拿到最终的Channel对象 channel, err := oleutil.CallMethod(asyncDispatch, "GetResults") if err != nil { fmt.Printf("获取Channel失败: %v\n", err) break } defer channel.Clear() // 提取ChannelURI uriProp, err := oleutil.GetProperty(channel.ToIDispatch(), "Uri") if err != nil { fmt.Printf("获取URI失败: %v\n", err) break } channelURI := uriProp.Value().(string) uriProp.Clear() fmt.Printf("成功获取ChannelURI: %s\n", channelURI) return case ole.StatusError: errProp, err := oleutil.GetProperty(asyncDispatch, "Error") if err != nil { fmt.Printf("获取错误信息失败: %v\n", err) break } fmt.Printf("异步操作出错: %v\n", errProp.Value()) errProp.Clear() return default: // 等待100ms再轮询 time.Sleep(100 * time.Millisecond) } } }
注意:获取到ChannelURI后,你需要把它上传到自己的后端或者直接注册到Azure通知中心,后续推送就靠这个URI定位设备。
3. Golang应用接收WNS推送的处理方案
Windows系统会自动接收WNS推送,然后把通知转发给你的应用。Golang应用要处理这些通知,还是得通过WinRT API监听推送事件,同样用go-ole实现:
代码示例:监听PushNotificationReceived事件
// 承接上面获取到的channel对象,注册事件回调 eventHandler, err := oleutil.AddEventHandler(channel.ToIDispatch(), "PushNotificationReceived", func(eventUnknown *ole.IUnknown) { eventDispatch, err := eventUnknown.QueryInterface(ole.IID_IDispatch) if err != nil { fmt.Printf("获取事件接口失败: %v\n", err) return } defer eventDispatch.Release() // 获取推送通知对象 notificationProp, err := oleutil.GetProperty(eventDispatch, "Notification") if err != nil { fmt.Printf("获取Notification失败: %v\n", err) return } defer notificationProp.Clear() notificationDispatch := notificationProp.ToIDispatch() // 判断通知类型(Toast/ Tile/ Badge等) typeProp, err := oleutil.GetProperty(notificationDispatch, "Type") if err != nil { fmt.Printf("获取通知类型失败: %v\n", err) return } defer typeProp.Clear() notificationType := typeProp.Value().(int32) switch notificationType { case 0: // ToastNotification类型 contentProp, err := oleutil.GetProperty(notificationDispatch, "Content") if err != nil { fmt.Printf("获取Toast内容失败: %v\n", err) return } defer contentProp.Clear() fmt.Printf("收到Toast推送: %s\n", contentProp.Value().(string)) case 1: // TileNotification类型 fmt.Println("收到Tile推送") // 可以在这里更新应用磁贴 default: fmt.Printf("收到未知类型推送: %d\n", notificationType) } }) if err != nil { fmt.Printf("注册推送事件失败: %v\n", err) return } defer eventHandler.Dispose() // 保持程序运行,监听事件 fmt.Println("等待推送通知...") select {}
额外补充:对接Azure通知中心
拿到ChannelURI后,你可以用Azure官方的Golang SDK aznotificationhubs来发送推送:
go get github.com/Azure/azure-sdk-for-go/sdk/notificationhubs/aznotificationhubs
示例代码(发送Toast推送):
package main import ( "context" "fmt" "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" "github.com/Azure/azure-sdk-for-go/sdk/notificationhubs/aznotificationhubs" ) func main() { // 初始化Azure通知中心客户端 client, err := aznotificationhubs.NewClientFromConnectionString("<你的Azure连接字符串>", "<通知中心名称>", nil) if err != nil { fmt.Printf("创建Azure客户端失败: %v\n", err) return } // 构造Toast推送内容(符合WNS格式) toastPayload := `<?xml version="1.0" encoding="utf-8"?> <toast> <visual> <binding template="ToastText01"> <text id="1">Hello from Azure + Golang!</text> </binding> </visual> </toast>` // 发送推送 resp, err := client.Send(context.TODO(), &aznotificationhubs.SendOptions{ Message: &aznotificationhubs.WindowsMessage{ Payload: to.Ptr(toastPayload), Headers: map[string]string{ "X-WNS-Type": "wns/toast", }, }, Target: aznotificationhubs.TargetPlatformWindows, }) if err != nil { fmt.Printf("发送推送失败: %v\n", err) return } fmt.Printf("推送发送成功,跟踪ID: %s\n", *resp.TrackingID) }
关键注意事项
- 你的应用必须在Windows开发者中心注册,获取Package SID和安全密钥,配置到Azure通知中心的WNS设置里。
- COM初始化的线程模型要正确(上面用的
COINIT_APARTMENTTHREADED是单线程公寓模型,适合UI类应用)。 - 异步操作尽量用事件回调,轮询只是简化示例,生产环境容易出问题。
备注:内容来源于stack exchange,提问作者beta-programmer9696




