在WPF桌面应用中调用Windows 10 UWP API遇到的问题求助
嘿,我之前也碰到过类似的坑,核心原因就是手动引用Windows.winmd这种老方式容易触发兼容性问题,现在微软有更靠谱的官方方案,咱们一步步来解决:
先搞懂你遇到的两个问题根源
- 特定版本winmd的语音命令API报错:你引用
10.0.17134.0版本的winmd时,Geolocator能正常工作是因为这个API对无包身份的桌面应用做了兼容,但Package.Current.InstalledLocation依赖应用的包身份——非打包的桌面应用根本没有这个身份,所以必然抛出0x80073D54错误。 - 通用winmd的循环类型转发错误:根目录的
Windows.winmd是通用版本,包含大量类型转发逻辑,但和桌面应用的.NET运行时环境适配不佳,就出现了CS0731这种循环依赖的报错。
正确姿势:用官方NuGet包替代手动引用winmd
微软现在明确推荐非打包桌面应用(WPF/WinForms这类)通过Microsoft.Windows.SDK.Contracts NuGet包来调用WinRT API,它会自动适配你的Windows版本,完全避开手动引用winmd的各种坑。
第一步:安装NuGet包
打开Visual Studio,右键你的项目→管理NuGet程序包,搜索Microsoft.Windows.SDK.Contracts,选择和你目标Windows版本匹配的包安装(比如你之前用的是17134,就找对应版本的包即可)。
第二步:调整代码适配非打包场景
关于Geolocator
安装完包后,直接用Windows.Devices.Geolocation.Geolocator就行,和你之前的写法完全一致,不需要额外配置:
using Windows.Devices.Geolocation; // 示例调用代码 var locator = new Geolocator(); var position = await locator.GetGeopositionAsync();
关于语音命令的文件读取
既然是非打包应用,就别用Package.Current了——它是给UWP或打包桌面应用设计的。换用桌面应用的方式获取文件路径:
如果你的CustomVoiceCommandDefinitions.xml和exe放在同目录,用AppDomain.CurrentDomain.BaseDirectory拼接路径,再转成StorageFile即可:
using Windows.Storage; using Windows.ApplicationModel.VoiceCommands; using System.IO; static async void LoadVoiceCommands() { try { // 获取exe所在目录的文件绝对路径 string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "CustomVoiceCommandDefinitions.xml"); // 把本地路径转换成WinRT的StorageFile StorageFile storageFile = await StorageFile.GetFileFromPathAsync(filePath); await VoiceCommandDefinitionManager.InstallCommandDefinitionsFromStorageFileAsync(storageFile); } catch (Exception ex) { MessageBox.Show(ex.Message); } }
额外要注意的细节
- 位置权限配置:非打包桌面应用要使用Geolocator,得声明位置权限。如果是WPF项目,添加一个
app.manifest文件,然后加入这些内容:
或者你也可以让用户手动在Windows设置里给你的应用授予位置权限。<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <application> <!-- 声明支持的Windows 10版本 --> <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" /> </application> </compatibility> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> <security> <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3"> <requestedExecutionLevel level="asInvoker" uiAccess="false" /> </requestedPrivileges> <!-- 声明位置权限 --> <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3"> <privilege name="SeLocationPrivilege" /> </requestedPrivileges> </security> </trustInfo> - 语音命令的兼容性:确保你的Windows版本支持语音命令API,而且要把应用正确注册为语音命令的处理程序,不然安装命令定义可能会失败。
为啥不推荐手动引用Windows.winmd?
手动引用winmd本质上是旧的兼容方式,不同版本的SDK winmd包含的API实现不一样,和.NET运行时的类型转发逻辑很容易冲突(就是你碰到的CS0731错误)。用官方NuGet包是微软维护的方案,自动处理版本适配和依赖问题,省心多了。
内容的提问来源于stack exchange,提问作者sanjar14

