随着 C++/WinRT 的后续版本发布,本主题介绍新增功能和更改内容。
截至 2020 年 3 月的最新改进/新增内容汇总
构建时间最多可缩短 23%
C++/WinRT 和 C++ 编译器团队已展开协作,尽一切可能缩短生成时间。 我们已深入分析编译器分析,以了解 C++/WinRT 的内部结构,以帮助 C++ 编译器消除编译时开销,以及如何改进 C++ 编译器本身来处理 C++/WinRT 库。 C++/WinRT 已针对编译器进行了优化;并且编译器已针对 C++/WinRT 进行了优化。
让我们以这样一种最坏情况为例:构建一个包含所有 C++/WinRT 投影命名空间头文件的预编译头(PCH)。
| 版本 | PCH 大小(字节) | 时间 (s) |
|---|---|---|
| 7 月起支持 Visual C++ 16.3 的 C++/WinRT | 3,004,104,632 | 31 |
| C++/WinRT 2.0.200316.3 版本,配合 Visual C++ 16.5 | 2,393,515,336 | 24 |
大小减少 20%,生成时间减少 23%。
改进了 MSBuild 支持
我们投入了大量工作来改进 MSBuild 对大量不同方案的支持。
更快速的工厂缓存
我们改进了工厂缓存内联,以便更好地内联热路径,从而提升执行速度。
这种改进不会影响代码大小,如 Optimized EH 代码代中所述,如果应用程序使用 C++ 异常处理非常严重,则可以使用/d2FH4选项来收缩二进制文件,该选项在使用 Visual Studio 2019 16.3 和更高版本创建的新项目中默认处于打开状态。
更高效的装箱
在 XAML 应用程序中使用时,winrt::box_value 现在效率更高(请参阅 装箱和拆箱)。 执行大量装箱的应用程序也会发现代码体积有所减小。
支持实现 IInspectable 的 COM 接口
如果需要实现刚好实现 IInspectable 的 COM 接口(非Windows-Runtime),则现在可以使用 C++/WinRT 执行此操作。 请参阅 实现 IInspectable 的 COM 接口。
模块锁定改进
控制模块锁定现在允许自定义托管方案和完全消除模块级锁定。 请参阅 模块锁定改进。
支持非 Windows Runtime 错误信息的处理
某些 API(甚至某些 Windows 运行时 API)在未使用 Windows 运行时 错误源 API 的情况下报告错误。 在这种情况下,C++/WinRT 现在改为使用 COM 错误信息。 请参阅 C++/WinRT 对非 WinRT 错误信息的支持。
启用 C++ 模块支持
C++ 模块支持已恢复,但仅以实验形式提供。 该功能尚未在 C++ 编译器中完成。
更高效的协同例程恢复
C++/WinRT 协程本身已经具备良好的性能,但我们仍在继续寻找进一步提升其性能的方法。 请参阅 改进协同例程恢复的可伸缩性。
新的 when_all 和 when_any 异步辅助函数
when_all辅助函数创建一个IAsyncAction对象,该对象会在所有提供的可等待对象均已完成后完成。 when_any辅助函数会创建一个IAsyncAction,该操作会在所提供的任一可等待对象完成时完成。
请参阅 添加 when_any 异步帮助程序 和 添加 when_all 异步帮助程序。
其他优化和添加
此外,引入了许多 bug 修复和次要优化和新增功能,包括简化调试和优化内部和默认实现的各种改进。 请遵循此链接获取详尽列表: https://github.com/microsoft/xlang/pulls?q=is%3Apr+is%3Aclosed
C++/WinRT 2.0 中的新闻和更改
有关 C++/WinRT Visual Studio 扩展 (VSIX)的详细信息,Microsoft.Windows。CppWinRT NuGet 包以及cppwinrt.exe工具(包括如何获取和安装它们)请参阅对 C++/WinRT、XAML、VSIX 扩展和 NuGet 包的Visual Studio支持。
对版本 2.0 的 C++/WinRT Visual Studio 扩展(VSIX)的更改
- 调试可视化工具现在支持 Visual Studio 2019;并继续支持 Visual Studio 2017。
- 进行了许多 bug 修复。
Microsoft.Windows.CppWinRT NuGet 包 2.0 版的更改
-
cppwinrt.exe工具现已包含在 Microsoft.Windows.CppWinRT NuGet 包中,并且该工具会按需为每个项目生成平台投影头文件。 因此,该工具cppwinrt.exe不再依赖于Windows SDK(尽管该工具仍附带 SDK,但出于兼容性原因)。 -
cppwinrt.exe现在会在每个特定于平台/配置的中间目录($IntDir)下生成投影头文件,以支持并行生成。 - C++/WinRT 构建支持(props/targets)现已提供完整的文档说明,方便你在需要时手动自定义项目文件。 请参阅 Microsoft.Windows.CppWinRT NuGet 包的自述文件。
- 进行了许多 bug 修复。
对版本 2.0 的 C++/WinRT 的更改
开源
cppwinrt.exe工具接受一个 Windows 运行时 元数据(.winmd)文件作为输入,并据此生成一个基于头文件的标准 C++ 库,将元数据中描述的 API 投影出来。 这样一来,你就可以在你的 C++/WinRT 代码中调用这些 API。
此工具现在是一个完全开放源代码的项目,可在GitHub上使用。 访问Microsoft/cppwinrt。
xlang 库
完全可移植的仅标头库(用于分析Windows 运行时使用的 ECMA-335 元数据格式)构成了今后所有Windows 运行时和 xlang 工具的基础。 值得注意的是,我们还使用 xlang 库从头重新改写 cppwinrt.exe 该工具。 这提供了更准确的元数据查询,解决了 C++/WinRT 语言投影的一些长期问题。
依赖项更少
由于 xlang 元数据读取器, cppwinrt.exe 工具本身的依赖项较少。 这使得它更加灵活,并在更多方案中可用,尤其是在受约束的生成环境中。 值得注意的是,它不再依赖 RoMetadata.dll。
这些是 2.0 的 cppwinrt.exe 依赖项。
- ADVAPI32.dll
- KERNEL32.dll
- SHLWAPI.dll
- XmlLite.dll
所有这些 DLL 不仅在Windows 10上可用,而且一直到Windows 7,甚至Windows Vista。 如果需要,运行Windows 7的旧生成服务器现在可以运行cppwinrt.exe,为项目生成 C++ 标头。 稍加处理一下,如果你对此感兴趣,甚至可以在 Windows 7 上运行 C++/WinRT。
将上面的列表与 1.0 具有的这些依赖项 cppwinrt.exe 进行对比。
- ADVAPI32.dll
- SHELL32.dll
- api-ms-win-core-file-l1-1-0.dll
- XmlLite.dll
- api-ms-win-core-libraryloader-l1-2-0.dll
- api-ms-win-core-processenvironment-l1-1-0.dll
- RoMetadata.dll
- SHLWAPI.dll
- KERNEL32.dll
- api-ms-win-core-rtlsupport-l1-1-0.dll
- api-ms-win-core-heap-l1-1-0.dll
- api-ms-win-core-timezone-l1-1-0.dll
- api-ms-win-core-console-l1-1-0.dll
- api-ms-win-core-localization-l1-2-0.dll
- OLEAUT32.dll
- api-ms-win-core-winrt-error-l1-1-0.dll
- api-ms-win-core-winrt-error-l1-1-1.dll
- api-ms-win-core-winrt-l1-1-0.dll
- api-ms-win-core-winrt-string-l1-1-0.dll
- api-ms-win-core-synch-l1-1-0.dll
- api-ms-win-core-threadpool-l1-2-0.dll
- api-ms-win-core-com-l1-1-0.dll
- api-ms-win-core-com-l1-1-1.dll
- api-ms-win-core-synch-l1-2-0.dll
Windows 运行时 noexcept 特性
Windows 运行时有一个新[noexcept]属性,可用于修饰 MIDL 3.0 中的方法和属性。 该属性的存在会向支持该属性的工具表明,你的实现不会引发异常(也不会返回失败的 HRESULT)。 这使语言投影能够通过避免为支持可能失败的应用程序二进制接口(ABI)调用所需的异常处理开销来优化代码生成。
C++/WinRT 通过生成使用代码和创作代码的 C++ noexcept 实现来利用这一点。 如果你有无故障的 API 方法或属性,并且你担心代码大小,则可以调查此属性。
优化的代码生成
C++/WinRT 现在生成更高效的 C++ 源代码(幕后),以便 C++ 编译器可以生成尽可能小且最有效的二进制代码。 许多改进旨在通过避免不必要的展开信息来降低异常处理成本。 使用大量 C++/WinRT 代码的二进制文件将大致减少 4% 代码大小。 由于指令计数减少,代码效率也更高(运行速度更快)。
这些改进也依赖于可供你使用的新互操作功能。 所有属于资源所有者的 C++/WinRT 类型现在都包含用于直接获取所有权的构造函数,从而避免了前面的两步方法。
ABI::Windows::Foundation::IStringable* raw = ...
IStringable projected(raw, take_ownership_from_abi);
printf("%ls\n", projected.ToString().c_str());
优化的异常处理(EH)代码生成
此更改补充了 Microsoft C++ 优化器团队完成的工作,以减少异常处理的成本。 如果在代码中大量使用应用程序二进制接口(如 COM),则会看到许多遵循此模式的代码。
int32_t Function() noexcept
{
try
{
// code here constitutes unique value.
}
catch (...)
{
// code here is always duplicated.
}
}
C++/WinRT 本身为每个实现的 API 生成此模式。 使用数千个 API 函数,此处的任何优化都可能很重要。 过去,优化器无法识别这些 catch 代码块其实都完全相同,因此它会在每个 ABI 周边重复生成大量代码(这也进一步让人们认为,在系统代码中使用异常会产生体积庞大的二进制文件)。 但是,从 Visual Studio 2019 开始,C++ 编译器会合并所有这些 catch funclet,并且只保留其中唯一的那些。 结果是,对于高度依赖这种模式的二进制文件,其代码大小总体上进一步减少了 18%。 EH 代码现在比使用返回代码更有效,而且对较大的二进制文件的关注现在也是过去的事情。
增量构建改进
该工具 cppwinrt.exe 现在将生成的标头/源文件的输出与磁盘上任何现有文件的内容进行比较,并且仅当文件已发生更改时才会写出该文件。 这可以节省大量磁盘 I/O 的时间,并确保 C++ 编译器不会将文件视为“脏”。 结果是,在许多情况下,可以避免或减少重新编译。
泛型接口现已全部生成
由于 xlang 元数据读取器,C++/WinRT 现在从元数据生成所有参数化或泛型接口。
Windows::Foundation::Collections::IVector<T> 等接口现在从元数据生成,而不是手动写入winrt/base.h。 结果是,winrt/base.h 的大小减少了一半,并且优化会直接生成到代码中(而这一点用手写方式实现起来很棘手)。
Important
给定的示例等接口现在出现在各自的命名空间标头中,而不是出现在 winrt/base.h其中。 因此,如果尚未这样做,则必须包含适当的命名空间标头才能使用该接口。
组件优化
此更新新增了对 C++/WinRT 的另外几项需显式启用的优化的支持,具体见下文各节。 由于这些优化属于破坏性变更(你可能需要做一些小的修改才能适配),因此需要显式启用这些优化。 在Visual Studio中,将项目属性 Common Properties>C++/WinRT>Optimized 设置为 Yes。 这样会将 <CppWinRTOptimized>true</CppWinRTOptimized> 添加到你的项目文件中。 在从命令行调用-opt[imize]时,它与添加cppwinrt.exe开关的效果相同。
根据项目模板创建的新项目将默认使用 -opt。
统一的构造方式,以及对实现的直接访问
这两项优化使组件能够直接访问其自身的实现类型,即使它只使用投影类型。 如果只想使用公共 API 图面,则无需使用 make、 make_self,也无需 get_self 。 你的调用会被编译成对实现的直接调用,这些调用甚至可能会被完全内联。
有关更多信息和代码示例,请参阅 选择采用统一构造方式和直接访问实现。
类型擦除工厂
此优化可避免 #include 依赖项 module.g.cpp ,因此每次发生任何单个实现类发生更改时都不需要重新编译它。 其结果是提升了构建性能。
面向包含多个库的大型项目,更智能、更高效module.g.cpp
该文件 module.g.cpp 现在还包含另外两个可组合的帮助程序,名为 winrt_can_unload_now和 winrt_get_activation_factory。 这些设计用于大型项目,其中 DLL 由多个库组成,每个库都有自己的运行时类。 在这种情况下,需要手动将 DLL 的 DllGetActivationFactory 和 DllCanUnloadNow 拼凑在一起。 这些辅助工具通过避免无谓的来源错误,让你更容易做到这一点。
cppwinrt.exe该工具的-lib标志还可用于为每个库指定其各自的前导码(而不是winrt_xxx),这样便可分别命名每个库中的函数,从而无歧义地将它们组合在一起。
协同程序支持
默认包含协程支持。 以前,支持驻留在多个地方,我们认为这种支持太有限了。 然后,在 v2.0 中曾暂时需要一个 winrt/coroutine.h 头文件,但现在已经不再需要了。 由于Windows 运行时异步接口现已生成,而不是手动写入,因此它们现在驻留在winrt/Windows.Foundation.h内。 除了更易于维护和支持之外,还意味着 resume_foreground 等协同例程帮助程序不必再被附加到特定命名空间标头的末尾。 相反,它们可以更自然地包括其依赖项。 这进一步使 resume_foreground 不仅支持在给定的 Windows::UI::Core::CoreDispatcher 上继续执行,还支持在给定的 Windows::System::DispatcherQueue 上继续执行。 此前,只能支持其中一个,而不能同时支持两者,因为该定义只能位于一个命名空间中。
下面是 DispatcherQueue 支持的示例。
...
#include <winrt/Windows.System.h>
using namespace Windows::System;
...
fire_and_forget Async(DispatcherQueueController controller)
{
bool queued = co_await resume_foreground(controller.DispatcherQueue());
assert(queued);
// This is just to simulate queue failure...
co_await controller.ShutdownQueueAsync();
queued = co_await resume_foreground(controller.DispatcherQueue());
assert(!queued);
}
这些协程辅助程序现在也使用 [[nodiscard]] 进行了修饰,从而提高了其可用性。 如果你忘记了(或者没有意识到你必须这样做)co_await 才能使其生效,那么由于 [[nodiscard]],这类错误现在会触发编译器警告。
关于诊断直接(栈)分配的帮助
由于投影类和实现类的名称(默认情况下)相同,并且仅命名空间不同,因此可能会将两者混淆,并意外地在栈上创建实现类实例,而不是使用 make 系列辅助函数。 在某些情况下,这可能很难排查,因为对象可能会在尚未完成的引用操作仍在进行时就被销毁。 现在,在调试版本中,断言会捕捉到这一点。 虽然该断言无法检测协程内部的栈分配,但它仍有助于发现大多数这类错误。
有关详细信息,请参阅 诊断直接分配。
改进了捕获帮助器和可变委托
此更新还通过支持投影类型修复了捕获帮助程序的限制。 在 Windows 运行时 互操作 API 中,当它们返回投影类型时,这种情况时不时会出现。
此更新还增加了在创建可变参数(非 Windows 运行时)委托时对 get_strong 和 get_weak 的支持。
支持延迟销毁以及在销毁过程中安全地进行 QI
在运行时类对象的析构函数中,调用会暂时增加引用计数的方法并不少见。 当引用计数返回到零时,对象将再次析构。 在 XAML 应用程序中,可能需要在析构函数中进行 QueryInterface(QI),以便调用层次结构中上层或下层的某些清理实现。 但对象的引用计数已达到零,因此 QI 也构成引用计数弹跳。
此次更新增加了对引用计数进行防抖处理的支持,确保一旦引用计数降为零,对象就绝不会被重新激活;同时仍允许你在销毁过程中对所需的任何临时对象执行 QI。 此过程在某些 XAML 应用程序/控件中是不可避免的,C++/WinRT 现在具有复原能力。
可以通过在实现类型上提供静态 final_release 函数来延迟销毁。 指向对象的最后一个剩余指针以 std::unique_ptr 的形式传递给 final_release。 然后,可以选择将该指针的所有权移动到其他一些上下文。 你可以安全地对该指针执行 QI,而不会触发重复析构。 但在析构对象时,对引用计数的净更改必须为零。
final_release的返回值可以是void异步操作对象,如 IAsyncAction 或 winrt::fire_and_forget。
struct Sample : implements<Sample, IStringable>
{
hstring ToString()
{
return L"Sample";
}
~Sample()
{
// Called when the unique_ptr below is reset.
}
static void final_release(std::unique_ptr<Sample> self) noexcept
{
// Move 'self' as needed to delay destruction.
}
};
在下面的示例中,在 MainPage 发布(最后一次)后,将调用 final_release 。 该函数在线程池中等待五秒钟,然后在页面的 Dispatcher 上恢复执行(这需要通过 QI/AddRef/Release 才能正常工作)。 然后,它会清理该 UI 线程上的资源。 最后,它会将 unique_ptr 清空,从而真正调用 MainPage 的析构函数。 即使在该析构函数中,也会调用 DataContext ,这需要 IFrameworkElement 的 QI。
无需将 final_release 实现为协程。 但是,这确实起作用,它使得将销毁移动到另一个线程非常简单,这就是此示例中发生的事情。
struct MainPage : PageT<MainPage>
{
MainPage()
{
}
~MainPage()
{
DataContext(nullptr);
}
static IAsyncAction final_release(std::unique_ptr<MainPage> self)
{
co_await 5s;
co_await resume_foreground(self->Dispatcher());
co_await self->resource.CloseAsync();
// The object is destructed normally at the end of final_release,
// when the std::unique_ptr<MyClass> destructs. If you want to destruct
// the object earlier than that, then you can set *self* to `nullptr`.
self = nullptr;
}
};
有关详细信息,请参阅 延迟销毁。
改进了对 COM 样式单接口继承的支持
除了Windows 运行时编程,C++/WinRT 还用于创作和使用仅限 COM 的 API。 通过此更新,可以实现存在接口层次结构的 COM 服务器。 Windows 运行时不需要此操作,但某些 COM 实现需要这样做。
正确处理 out 参数
处理out参数可能很棘手,尤其是 Windows 运行时 数组。 通过此次更新,C++/WinRT 在处理 out params 和数组时变得更加稳健,也更能容忍错误;无论这些参数是通过语言投影传入的,还是来自使用原始 ABI 的 COM 开发人员——而这些开发人员会因未始终一致地初始化变量而出错。 无论哪种情况,C++/WinRT 现在都能在将投影类型传递给 ABI 时做正确的处理(会记得释放相关资源),并且在对通过 ABI 传入的参数进行置零或清空时也能正确处理。
事件现在可靠地处理无效令牌
winrt::event 实现现在正常处理其删除方法使用无效令牌值(数组中不存在的值)调用的情况。
协同例程本地变量现在在协同例程返回之前被销毁
实现协程类型的传统方式可能会导致协程中的局部变量在协程返回/完成后才被销毁(而不是在最终挂起之前)。 为避免此问题并带来其他益处,任何等待者的恢复现均推迟到最终挂起时才进行。
Windows SDK 版本 10.0.17763.0 中的新闻和更改(Windows 10版本 1809)
下表包含 Windows SDK 版本 10.0.17763.0(Windows 10 版本 1809)中 C++/WinRT 的新闻和更改。
| 新增或更改的功能 | 详细信息 |
|---|---|
| 破坏性变更。 为了进行编译,C++/WinRT 不依赖于 Windows SDK 中的标头。 | 请参阅下文的与 Windows SDK 头文件隔离。 |
| Visual Studio项目系统格式已更改。 | 请参阅下面的如何将 C++/WinRT 项目重定向到更高版本的 Windows SDK。 |
| 有新的函数和基类可帮助你将集合对象传递给Windows 运行时函数,或实现自己的集合属性和集合类型。 | 请参阅 使用 C++/WinRT 的集合。 |
| 可以将 {Binding} 标记扩展用于 C++/WinRT 运行时类。 | 有关详细信息和代码示例,请参阅 数据绑定概述。 |
| 对取消协程的支持允许您注册取消回调。 | 有关详细信息和代码示例,请参阅 “取消异步操作”和“取消回调”。 |
| 创建指向成员函数的委托时,可以在注册处理程序时将对当前对象的引用设为强引用或弱引用(而不是使用裸 this 指针)。 | 有关详细信息和代码示例,请参阅“ 如果使用成员函数作为委托 子部分,请参阅”安全地 使用事件处理委托访问 此 指针“部分中的成员函数。 |
| 修复了因 Visual Studio 对 C++ 标准的符合性提高而暴露出的错误。 LLVM 和 Clang 工具链还更好地用于验证 C++/WinRT 的标准一致性。 | 你将不再遇到 为什么我的新项目无法编译?我使用的是 Visual Studio 2017(版本 15.8.0 或更高版本),以及 SDK 版本 17134 中所述的问题 |
其他更改。
-
破坏性变更。
winrt::get_abi(winrt::hstring const>) 现在返回
void*而不是HSTRING。 你可以使用static_cast<HSTRING>(get_abi(my_hstring));来获取 HSTRING。 请参阅 与 ABI 的 HSTRING 进行互操作。 -
破坏性变更。
winrt::put_abi(winrt::hstring&) 现在返回
void**而不是HSTRING*。 你可以使用reinterpret_cast<HSTRING*>(put_abi(my_hstring));获取 HSTRING*。 请参阅 与 ABI 的 HSTRING 进行互操作。 -
破坏性变更。 HRESULT 现在映射为 winrt::hresult。 如果需要 HRESULT(执行类型检查或支持类型特征),则可以
static_cast使用 winrt::hresult。 否则,只要在包含任何 C++/WinRT 标头之前包含unknwn.h,winrt::hresult 就可转换为 HRESULT。 -
破坏性变更。 GUID 现在投影为 winrt::guid。 对于实现的 API,必须为 GUID 参数使用 winrt::guid 。 否则,只要在包含任何 C++/WinRT 标头之前包含
unknwn.h,winrt::guid 就可转换为 GUID。 请参阅 与 ABI 的 GUID 结构进行互操作。 - 破坏性变更。 winrt::handle_type 构造函数通过显式进行强化(现在很难编写不正确的代码)。 如果需要分配原始句柄值,请改为调用 handle_type::attach 函数 。
-
破坏性变更。
WINRT_CanUnloadNow和WINRT_GetActivationFactory的签名已更改。 根本不必须声明这些函数。 相反,应包含
winrt/base.h(如果你包含了任何 C++/WinRT Windows 命名空间头文件,则会自动包含它),以引入这些函数的声明。 - 对于 winrt::clock 结构,from_FILETIME/to_FILETIME 已弃用,建议改用 from_file_time/to_file_time。
- 接受 IBuffer 参数的简化 API。 大多数 API 更喜欢集合或数组。 但我们认为,我们应该更轻松地调用依赖于 IBuffer 的 API。 此更新提供对 IBuffer 实现背后的数据的直接访问。 它使用的数据命名约定与 C++ 标准库容器所使用的命名约定相同。 该约定还避免了与通常以大写字母开头的元数据名称冲突。
- 改进了代码生成:通过各种改进来减小代码大小、改进内联和优化工厂缓存。
- 删除了不必要的递归。 当命令行指向某个文件夹而不是特定的
.winmd时,cppwinrt.exe工具不再递归搜索.winmd文件。cppwinrt.exe工具现在也能够更智能地处理重复项,从而对用户错误和格式不正确的.winmd文件具有更强的容错能力。 - 强化的智能指针。 此前,事件撤销器在通过移动赋值接收新值时,未能执行撤销。 这有助于发现一个问题:智能指针类无法可靠地处理自赋值;其根源在于 winrt::com_ptr struct template。 winrt::com_ptr 已修复,事件撤销器也已修正为能够正确处理移动语义,从而在赋值时执行撤销。
Important
在版本 1.0.181002.2 中以及版本 1.0.190128.4 中对 C++/WinRT Visual Studio 扩展(VSIX)进行了重要更改。 有关这些更改的详细信息,以及这些更改会如何影响您现有的项目,请参阅 Visual Studio 对 C++/WinRT 的支持和 VSIX 扩展的早期版本。
与 Windows SDK 头文件相隔离
这对你的代码来说可能是破坏性变更。
为了进行编译,C++/WinRT 不再依赖于 Windows SDK 中的头文件。 C 运行时库(CRT)和 C++ 标准模板库(STL)中的头文件也不包含任何Windows SDK 标头。 这提高了标准合规性,避免了无意的依赖项,并大大减少了必须防范的宏数量。
这种独立性意味着 C++/WinRT 现在更可移植且符合标准,并进一步使它成为跨编译器和跨平台库的可能性。 这也意味着 C++/WinRT 标头不会对宏产生不利影响。
如果你之前一直由 C++/WinRT 负责在你的项目中包含任何 Windows 头文件,那么现在就需要你自己来包含这些头文件。 无论如何,最佳做法始终是显式包含你所依赖的头文件,而不要依赖其他库替你包含这些头文件。
目前,Windows SDK 头文件隔离的唯一例外是内部函数和数值。 这些最后剩余的依赖项没有已知问题。
如果需要,可以在项目中重新启用与 Windows SDK 头文件的互操作。 例如,你可能想要实现 COM 接口(根于 IUnknown)。 对于该示例,请在包含任何 C++/WinRT 标头之前包含 unknwn.h 。 这样做会导致 C++/WinRT 基库启用各种挂钩来支持经典 COM 接口。 有关代码示例,请参阅 使用 C++/WinRT 创作 COM 组件。 同样,显式包含任何其他Windows SDK 标头,这些标头声明要调用的类型和/或函数。
如何将 C++/WinRT 项目重定向到更高版本的 Windows SDK
对项目重新定向目标的方法中,最有可能引发最少编译器和链接器问题的那一种,同时也是最费力的方法。 该方法涉及创建新项目(面向所选的 Windows SDK 版本),然后从旧项目将文件复制到新项目。 你的旧 .vcxproj 和 .vcxproj.filters 文件中有些部分可以直接复制过来,这样就可以省去在 Visual Studio 中添加文件的麻烦。
不过,在 Visual Studio 中,还有另外两种方法可以重新定向项目目标。
- 转到项目属性“常规>Windows SDK 版本”,然后选择“所有配置”和“所有平台”。 将Windows SDK 版本设置为要面向的版本。
- 在解决方案资源管理器中,右键单击项目节点,单击“重定目标项目”,选择要面向的版本,然后单击“确定”。
如果在使用这两种方法之一后遇到任何编译器或链接器错误,则可以尝试清理解决方案(生成>清理解决方案 和/或手动删除所有临时文件夹和文件),然后再尝试再次生成。
如果 C++ 编译器报出“错误 C2039: 'IUnknown': 不是‘全局命名空间’的成员”,则请将 #include <unknwn.h> 添加到你的 pch.h 文件顶部(在包含任何 C++/WinRT 标头之前)。
你可能还需要在此之后添加 #include <hstring.h> 。
如果 C++ 链接器报告“错误 LNK2019:函数 _VSDesignerCanUnloadNow@0 中引用了无法解析的外部符号 _WINRT_CanUnloadNow@0”,则可以通过将 #define _VSDESIGNER_DONT_LOAD_AS_DLL 添加到 pch.h 文件中来解决此错误。