You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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

火山引擎 最新活动