[Windows翻译]Windows SDK中的cppwinrt.exe。

654 阅读8分钟

原文地址:modernwindows.wordpress.com/2017/11/15/…

原文作者:modernwindows.wordpress.com/

发布时间:2017年11月15日

11月初,C++/WinRT首次在Windows SDK(17025)中发布。该SDK包含了C++/WinRT头文件,所以你不必先从GitHub上获取它们。取而代之的是,你可以简单地在任何C++项目中包含相应的头文件,并开始使用Windows API编写现代C++代码。

我很高兴地宣布,今天更新的Windows SDK (17035)现在也包含了C++/WinRT编译器(cppwinrt.exe)。虽然在大多数情况下,你不需要自己运行编译器,但如果你需要使用第三方WinRT组件,它可以派上用场,并且需要用C++/WinRT编写自己的WinRT组件。你也可以用它为自己的一组.winmd文件制作自定义投影。

Scott Jones在今年早些时候的CppCon上首次演示了这一点。你可以在这里观看我们的整个CppCon 2017演讲。

www.youtube.com/watch?v=7Td…

最终,我们将把C++/WinRT完全集成到Visual Studio中,但在那之前,你必须做一点工作来让它顺利集成。上次我向你展示了如何从与Visual C++ 2017项目开始使用C++/WinRT头文件。我还准备了一套示例项目,你可以将其作为一个起点。你可以在这里找到它们。

github.com/kennykerr/c…

这些示例与今天更新的Windows SDK一起工作,并且有各种示例可以让你开始使用桌面以及商店应用程序。有的样本说明了各种互操作的场景,甚至还有一个完整的编写WinRT组件的例子。

不过,我知道很多开发者还是急于尝试C++/WinRT编译器本身。让我告诉你如何开始,但请记住,这仍然被认为是实验性的,可能会发生变化。不过,我们非常乐意在GitHub问题页面上回答你的任何问题。如果你想跟随我们,请打开一个开发者命令提示符。我通常使用VS 2017的x86 Native Tools命令提示符。假设你已经安装了Windows SDK,你应该在路径中找到cppwinrt.exe。你可以通过使用where命令进行双重检查。它应该看起来像这样。

> where cppwinrt
 
C:\Program Files (x86)\Windows Kits\10\bin\10.0.17035.0\x86\cppwinrt.exe

你也可以运行cppwinrt命令本身来探索一些可用的功能和选项。

> cppwinrt
 
C++/WinRT v1.0.170906.1
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
 
  cppwinrt.exe [options...]
 
Options:
 
  -in     <spec>      Winmd files to include in projection
  -ref    <spec>      Winmd files to reference from projection
  -out    <path>      Location of generated projection files
  -component          Generate component implementation source files
  -filter <prefix>    One or more prefixes to include in component
  -name   <name>      Assume this component name to shorten file names
  -natvis             Generate a natvis script for debug visualization
  -verbose            Show detailed progress information
  -overwrite          Overwrite existing component projection files
  -help               Show detailed help with examples
  @<path>             Response file containing command line options
 
Where <spec> is one or more of:
 
  path                Path to winmd file or recursively scanned folder
  local               Local %WinDir%\System32\WinMetadata folder
  10.0.15063.0        Specific version of Windows SDK

正如你从看选项中可以想象的那样,我经常把cppwinrt称为编译器,因为它读取一些输入文件集,并写入相应的输出文件集。让我们从一个为使用Windows Insider构建的开发者量身定做的具体例子开始。让我们想象一下,你得到了一个新的构建,并立即想尝试一些新的功能,但与之匹配的Windows SDK构建却莫名其妙地延迟了。我知道这种情况从来没有发生过,但是玩玩看。没问题,你可以使用本地选项,并为你碰巧运行的Windows版本制作一个投影。

> cppwinrt -in local

你应该会发现刚刚创建了一个名为winrt的本地文件夹,其中包括不少头文件。你可以通过使用 verbose 选项来获得更多的可见性。

> cppwinrt -in local -verbose
 
 in:   C:\WINDOWS\System32\WinMetadata\Windows.ApplicationModel.winmd
 in:   C:\WINDOWS\System32\WinMetadata\Windows.Data.winmd
 in:   C:\WINDOWS\System32\WinMetadata\Windows.Devices.winmd
 in:   C:\WINDOWS\System32\WinMetadata\Windows.Foundation.winmd
 in:   C:\WINDOWS\System32\WinMetadata\Windows.Gaming.winmd
 in:   C:\WINDOWS\System32\WinMetadata\Windows.Globalization.winmd
 in:   C:\WINDOWS\System32\WinMetadata\Windows.Graphics.winmd
 in:   C:\WINDOWS\System32\WinMetadata\Windows.Management.winmd
 in:   C:\WINDOWS\System32\WinMetadata\Windows.Media.winmd
 in:   C:\WINDOWS\System32\WinMetadata\Windows.Networking.winmd
 in:   C:\WINDOWS\System32\WinMetadata\Windows.Perception.winmd
 in:   C:\WINDOWS\System32\WinMetadata\Windows.Security.winmd
 in:   C:\WINDOWS\System32\WinMetadata\Windows.Services.winmd
 in:   C:\WINDOWS\System32\WinMetadata\Windows.Storage.winmd
 in:   C:\WINDOWS\System32\WinMetadata\Windows.System.winmd
 in:   C:\WINDOWS\System32\WinMetadata\Windows.UI.Xaml.winmd
 in:   C:\WINDOWS\System32\WinMetadata\Windows.UI.winmd
 in:   C:\WINDOWS\System32\WinMetadata\Windows.Web.winmd
 out:  C:\sample
 time: 2890ms

现在你可以清楚地看到本地输入代表什么,以及输出在哪里被写入,在本例中是写入本地文件夹。不幸的是,SDK中的cppwinrt.exe版本比我们的内部构建晚了几个星期。更多的编译器的最新版本在三分之一的时间内就完成了编译(由于一个coroutine优化器的bug)。这也意味着在这个版本的编译器中有一些已知的bug,但如果你遇到任何困难,请告诉我们。

现在你已经创建了一个投影,你可以像使用Windows SDK中的投影一样使用它。当然,请记住,你现在可以访问各种API,这些API可能会发生变化。不过,你还是可以用一个简单的控制台应用程序快速测试它的工作情况。

> type sample.cpp
 
#pragma comment(lib, "windowsapp")
#include "winrt/Windows.Foundation.Metadata.h"
 
using namespace winrt;
using namespace Windows::Foundation::Metadata;
 
int main()
{
    init_apartment();
 
    bool const rs4 = ApiInformation::IsApiContractPresent(L"Windows.Foundation.UniversalApiContract", 6);
 
    printf("Am I running on Redstone 4? %s\n", rs4 ? "Yes!" : "No. :(");
}

然后简单地使用支持异常处理和C++17的C++编译器。

C:\sdk>cl /EHsc /std:c++17 sample.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.11.25547 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.
 
sample.cpp
Microsoft (R) Incremental Linker Version 14.11.25547.0
Copyright (C) Microsoft Corporation.  All rights reserved.
 
/out:sample.exe
sample.obj

然后,示例应用程序将告诉您是否运行在Windows的Redstone 4版本上。

> sample.exe
 
Am I running on Redstone 4? Yes!

当然,您可以使用 cppwinrt 来瞄准更多的本地机器。更典型的情况是,您需要为 Windows SDK 的特定版本创建投影。例如,以下是您如何为Fall Creators Update (Redstone 3)创建投影。

> cppwinrt -in 10.0.16299.0 -verbose

当然,前提是你已经安装了16299版本的Windows SDK。我把输出结果给你留着,但从啰嗦的输出中应该可以看出它是用哪一组.winmd文件来创建投影的。

那第三方组件呢?想象一下,某个开发者给你发了一个DLL以及一个.winmd文件,描述了一个你想利用的组件。我知道你会问,答案是否定的,这在今天的Win2D中是行不通的,因为Windows SDK中附带的cppwinrt.exe版本有一个bug,妨碍了你的使用。我们的内部构建已经支持Win2D有一段时间了,我被告知下一个Windows SDK会有修复,希望在本月晚些时候。现在考虑一个不同的组件。

> dir /b Component.*
 
Component.dll
Component.winmd

这就是该组件的作者所需要的全部内容。现在你可以为这个组件创建一个投影,如下所示。

> cppwinrt -in Component.winmd -ref local -out Component

在前面的例子中,输入的是Windows SDK的某个版本,现在输入的是代表WinRT组件的特定.winmd。现在输入的是代表WinRT组件的特定.winmd。ref选项告诉编译器,哪一组底层的.winmds代表了这个组件所指的平台。例如,它可能有一个Windows自己定义的某种类型的参数,或者可能实现了一个像Windows.Foundation.IStringable这样的通用接口。cppwinrt编译器需要知道如何解析这些类型,这就是ref选项的作用。最后,组件的头文件应该驻留在自己的文件夹中,C++项目可以根据需要简单地包含各种文件夹。

现在我们换个角度,在Visual Studio里面看一个更具体的例子。你可以先在Visual Studio中打开Component解决方案。它包括两个项目,一个是用C++/WinRT编写的商店应用,以及一个同样用C++/WinRT编写的WinRT组件。当然,如果你愿意,你也可以从C#应用程序中消耗组件。如果需要的话,你也可以在Desktop Bridge的帮助下使用一个桌面应用程序。

如果你看看组件的IDL文件,你会注意到一个简单的WinRT运行时类的定义。C++项目调用MIDL编译器来产生上述的.winmd文件。然后,它还调用cppwinrt来创建在C++/WinRT中代表组件的头文件,但它更进一步,使用组件选项也产生任何需要的脚手架,以实现现代C++的运行时类。让我们改变一下IDL来看看这个操作。我将在IButton2接口中添加一个名为Hide的方法。

[version(2.0), uuid(d3235252-4081-4cc8-b0e0-8c7691813845), exclusiveto(Button)]
interface IButton2 : IInspectable
{
    HRESULT Show();
    HRESULT Hide(); // <-- add this!
};

构建项目会使MIDL生成一个新的.winmd文件,这将使cppwinrt更新头文件和实现脚手架。你所要做的就是在Button类中添加Hide方法。在Button.h文件中,你可以添加隐藏方法的声明。

struct Button : ButtonT<Button>
{
    Button() = default;
 
    hstring Text();
    void Show();
    hstring ToString();
 
    void Hide(); // <-- add this!
};

就是那个ButtonT类模板,cppwinrt更新了Hide方法的期望。然后在Button.cpp文件里面可以添加定义。

void Button::Hide()
{
}

而这就是全部。在幕后,C++/WinRT做了大量的工作,以确保这种现代的C++转化为Windows Runtime所需的ABI的有效实现,但确保你可以坚持使用现代或习惯的C++作为你实现的一部分。

C++/WinRT对你的代码和生成的代码进行了明确的区分。例如,Button.h文件可能最初是由cppwinrt生成的,但现在是你要维护和扩展的代码。另一方面,它所包含的Button.g.h文件总是由cppwinrt再生,并且在文件的顶部有警告提醒你。当然,如果你在IDL中定义了一个全新的运行时类,而这个类一开始并不存在.h和.cpp文件,cppwinrt会继续创建这些文件来帮助你入门。

正如我所提到的,我们正忙于改进C++/WinRT,你应该期待在接下来的几个月里定期更新和改进。直到下一次,我希望你喜欢使用C++/WinRT和cppwinrt编译器。


www.deepl.com 翻译