存档

2026年9月 的存档

UWP中为 WinRT 组件创建 NuGet 包

2026年9月6日 没有评论

这篇文章可以说是迟到了 N 多年的分享了,一直放在草稿箱里但是没有发布,今天赶紧地整理。

先搞清楚:WinRT 组件里到底有什么

假设我们有一个 WinRT 组件:Demo.winmd、Demo.dll。这两个文件虽然经常一起出现,但职责完全不同。.winmdWindows Metadata 文件,它描述的是这个 WinRT 组件对外暴露的类型信息,比如:

namespace
class
interface
method
property
enum

WinRT 的语言投影也依赖这些 metadata。比如 C++/WinRT 会根据 WinRT metadata 生成对应的 C++ 投影代码。所以可以简单理解成:Demo.winmd 告诉编译器“这个组件长什么样”,它本身并不是业务代码的实现。.dll 才是实际的 native implementation。也就是说:Demo.dll 真正执行代码。对于一个典型的 WinRT Component,这两个文件是配套的。

Visual Studio 在构建 Windows Runtime Component 时,会生成 WinRT metadata;对于 C++/WinRT 项目,工具链也是先处理 IDL 生成 .winmd,再基于 metadata 生成相应的投影代码。

不要把 .winmd 理解成普通 .NET 程序集,也不要把它和 DLL 当成两个可以随便替换的文件。它们共同描述并实现了一个 WinRT 组件。

NuGet 包里应该放什么?

对于这个例子,一个最基本的 NuGet 包可以长这样:

com.xx.demo.nupkg
│
├─ lib
│  └─ uap10.0
│     └─ Demo.winmd
├─ runtimes
│  ├─ win10-x86
│  │  └─ native
│  │     └─ Demo.dll
│  └─ win10-x64
│     └─ native
│        └─ Demo.dll
└─ build
   └─ native
      └─ com.xx.demo.targets

这里其实就是整个事情最关键的部分,.nuspec 本身反而没什么好讲的——如果你平时用 NuGet,知道怎么写一个基本的 .nuspec 就够了。

为什么 .winmd.dll 要放在不同目录?

这是很多第一次做 native NuGet 包的人容易疑惑的地方。.winmd 是给编译阶段使用的,所以我们把它放到 lib/uap10.0/,而 DLL 属于 native runtime binary,因此放到:runtimes/win10-x64/native/、runtimes/win10-x86/native/。

这样做的一个直接好处就是:同一个 NuGet 包可以同时支持多个 CPU 架构。微软的 UWP NuGet 示例也是把 WinMD 放到 lib\uap10.0,native DLL 放到 runtimes 对应的架构目录。 Microsoft Learn+1

C++ 项目为什么还需要 .targets

NuGet 对 native C++ 包的处理方式,本身就和 .NET 程序集不太一样。微软的 native NuGet 文档也明确提到,native 包通常通过 buildcontenttools 等目录以及 MSBuild 的 .props/.targets 来完成项目集成,而不是依赖 lib 直接添加 C++ Reference。 所以我们通常需要提供一个:com.xx.demo.targets。它的作用可以简单理解成:告诉 MSBuild:这个 NuGet 包里的 WinMD 和 DLL 怎么添加到当前项目。

一个简化后的思路大概是:

<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'UAP'">
    <Reference Include="
        $(MSBuildThisFileDirectory)
        ..\..\lib\uap10.0\Demo.winmd">

        <Implementation>Demo.dll</Implementation>
    </Reference>
    <ReferenceCopyLocalPaths Include="
        $(MSBuildThisFileDirectory)
        ..\..\runtimes\win10-$(Platform)\native\Demo.dll" />
</ItemGroup>

.nuspec 不需要写得很复杂

如果已经熟悉 NuGet,可以把它理解成一个文件映射:

<files>
    <file
        src=".\Demo.winmd"
        target="lib\uap10.0" />
    <file
        src=".\Demo.dll"
        target="runtimes\win10-x64\native" />
    <file
        src=".\com.xx.demo.targets"
        target="build\native" />
</files>

如果还要支持 x86,就再放一个对应的 DLL:runtimes/win10-x86/native/Demo.dll。

这里最重要的不是 XML 本身,而是记住:

WinMD
    → lib/uap10.0
Native DLL
    → runtimes/<RID>/native
MSBuild 集成
    → build/native

XML 文档要不要一起打包?

如果组件是给别人使用的,比较建议加上。里面保存 C++/C# 的 API 文档注释,这样 IDE 可以显示对应的 API 注释。这个东西不影响组件运行,但对 SDK 类组件来说体验差别还是挺大的。

分类: 日常 标签: ,