1

这在某种程度上是如何使用 midlrt.exe 将 .idl 编译为 .winmd 的后续内容?

我的 CMakeLists.txt 中有这个。我的问题不是关于 CMake 逻辑,而是关于 midl 和 cppwinrt 命令的输出,以及随后的编译和链接错误。我怀疑我可能缺少一些命令行选项。

# Pathnames for WinRT References
set (WINSDKREFDIR "$ENV{WindowsSdkDir}References\\$ENV{WindowsSDKVersion}")

# Remove trailing \ from $ENV{WindowsSDKVersion}
string (REGEX MATCH "[^\\]*" WINSDKVER $ENV{WindowsSDKVersion})

# COMMAND lines wrapped in this post for readability, not wrapped in the actual CMakeLists.txt
add_custom_target (MYLIB_PREBUILD ALL
  COMMAND midl /winrt /ns_prefix /x64 /nomidl
    /metadata_dir
      "${WINSDKREFDIR}windows.foundation.foundationcontract\\3.0.0.0"
    /reference 
      "${WINSDKREFDIR}windows.foundation.foundationcontract\\3.0.0.0\\Windows.Foundation.FoundationContract.winmd"
    /reference
      "${WINSDKREFDIR}Windows.Foundation.UniversalApiContract\\8.0.0.0\\Windows.Foundation.UniversalApiContract.winmd"
    /out "${MYDIR}\\GeneratedFiles" "${MYDIR}\\MyClass.idl"
  COMMAND cppwinrt
    -in "${MYDIR}\\GeneratedFiles\\MyClass.winmd"
    -ref ${WINSDKVER} -component -pch "pch.h" -out "${MYDIR}\\GeneratedFiles"
)

add_dependencies (MYLIB MYLIB_PREBUILD)

在 cppwinrt 命令中,我尝试了不同形式的 -ref [spec] 和 -pch 选项,但无论如何似乎都得到了相同的结果。这些是我遇到的问题:

  • MIDLRT 生成的头文件“MyClass.h”有几个问题:

    • 它#includes <windows.h>,最终#defines 为GetClassName 和GetCurrentTime 定义预处理器宏,这些宏会在具有这些名称的WinRT 函数中导致编译器错误。
    • 我花了几个小时来跟踪它并学习使用#define COM_NO_WINDOWS_H 进行编译以防止这种情况发生。
    • 它#includes WinRT References Contracts 目录而不是 Include 目录中不存在的 *.h 文件:
      • #include "C:\Program Files (x86)\Windows Kits\10\References\10.0.18362.0\Windows.Foundation.FoundationContract\3.0.0.0\Windows.Foundation.FoundationContract.h"
      • #include "C:\Program Files (x86)\Windows Kits\10\References\10.0.18362.0\Windows.Foundation.UniversalApiContract\8.0.0.0\Windows.Foundation.UniversalApiContract.h"
    • 所以我复制了这个文件并用 #include <winrt/Windows.Foundation.h> 替换了那些
  • CPPWINRT 生成#includes "MyNamespace.MyClass.h" 的"module.g.cpp",但不生成那个.h 文件。它确实会生成“MyNamespace/MyClass.h”(注意“/”而不是“.”),所以我创建了前一个 .h 并从中简单地#include 后一个 .h。

  • CPPWINRT 不会生成我在 Microsoft 示例中看到的所有基本标头。它只生成与 MyClass 直接相关的头文件——例如,定义模板基类 winrt::MyNamespace::implementation::MyClassT<>、包装器 winrt::MyNamespace::MyClass 等。

  • winrt::MyNamespace::factory_implementation::MyClass 未定义。MyClassT<> 在那里定义,但不是 MyClass。我从 Microsoft 示例中找到了一个范例并将其粘贴到:

    // Missing from the generated stuff -- derived from a Microsoft example:
    namespace winrt::MyNamespace::factory_implementation
    {
        struct MyClass : MyClassT<MyClass, implementation::MyClass>
        {
        };
    }
  • 我收到了关于 CHECK_NS_PREFIX_STATE 定义不一致的编译器警告:在某些地方它是“总是”,而在其他地方它是“从不”。所以现在我#define MIDL_NS_PREFIX 和#define CHECK_NS_PREFIX_STATE="always"

现在构建通过编译器,但我在链接器中有未解析的外部符号。我认为这些东西应该在“winrt/base.h”中内联定义,但是 cppwinrt 没有导出这样的文件(正如我在 Microsoft 示例中看到的那样),系统目录中的等效文件只包含原型,而不包含身体:

WINRT_GetRestrictedErrorInfo
WINRT_RoInitialize
WINRT_RoOriginateLanguageException
WINRT_SetRestrictedErrorInfo
WINRT_WindowsCreateString
WINRT_WindowsCreateStringReference
WINRT_WindowsDeleteString
WINRT_WindowsPreallocateStringBuffer
WINRT_WindowsDeleteStringBuffer
WINRT_WindowsPromoteStringBuffer
WINRT_WindowsGetStringRawBuffer
WINRT_RoGetActivationFactory
WINRT_WindowsDuplicateString

我是否遗漏了一些简单的东西来解决所有这些与丢失、不完整和不正确的生成文件有关的问题?

4

1 回答 1

0

未解决的外部符号错误表明您缺少导入库。在这种情况下,您需要链接到导出所需符号的WindowsApp.lib伞库。

请注意,您观察到的符号名称是 C++/WinRT 要求使用和不使用 Windows SDK 标头进行构建的产物。它通过声明导入来解决这个问题(带有WINRT_前缀以防止与 SDK 标头声明冲突),然后使用/ALTERNATENAME链接器开关映射重命名的符号。

我不确定这是否会解决您的所有问题,但您肯定希望添加${MYDIR}\\GeneratedFiles到您的其他包含目录中。这应该注意无法包含winrt子目录中生成的标头(base.h 以及预计的 Windows 运行时类型标头)。

当cppwinrt${MYDIR}\\GeneratedFiles\\sources处理之前从您的 .idl(s) 编译的 .winmd 文件时,它还会将您自己类型的存根实现写入. 不幸的是,这里涉及到一个手动步骤:您需要将生成的 .h 和 .cpp 文件复制到源代码树中,并实现框架实现。每当您修改其中一个接口定义时,都需要这样做。

请注意,为我的项目生成的module.g.cpp文件不包含我的任何自定义类型标头。也许您使用的是旧版本的 C++/WinRT(我使用的是 v2.0.200203.5)。我相信随着C++/WinRT 2.0 版中类型擦除工厂的引入,这种情况发生了变化。除非您已经这样做了,否则您应该使用Microsoft.Windows.CppWinRT NuGet 包中的cppwinrt,而不是(过去)随 Windows SDK 提供的二进制文件。

于 2020-08-22T12:50:00.987 回答