【问题标题】:Problems with midl.exe and cppwinrt.exe from CMakeCMake 中 midl.exe 和 cppwinrt.exe 的问题
【发布时间】:2020-08-22 03:13:34
【问题描述】:

这是How to use midlrt.exe to compile .idl to .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 ,最终#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
  • CPPWINRT 生成“module.g.cpp”,其中#includes“MyNamespace.MyClass.h”,但不生成该.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

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

【问题讨论】:

    标签: cmake c++-winrt midl cppwinrt


    【解决方案1】:

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

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

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

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

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

    【讨论】:

    • 感谢您的快速回复,但有问题,由于长度限制,我显然必须在单独的 cmets 中解决:
    • “与 WindowsApp.lib 伞形库的链接”——这确实解决了外部符号,谢谢。我将它用于 UWP,但不是桌面。仍然需要测试执行。 + 我仍然需要注意#includes 的顺序:正如我之前提到的,cppwinrt 生成一个包含:``` #ifndef COM_NO_WINDOWS_H #include #include #endif /* COM_NO_WINDOWS_H*/ ``` 我需要先包含它,然后是 #undef GetClassName 和 #undef GetCurrentTime;否则这些宏会干扰使用这些名称的 WinRT 函数的定义或调用。
    • 如果我用 COM_NO_WINDOWS_H 编译它会产生比它解决的问题更多的问题,因为其他必要的东西被省略了。
    • “您应该使用 Microsoft.Windows.CppWinRT NuGet 包中的 cppwinrt,而不是(过去)随 Windows SDK 提供的二进制文件。” -- 根据 cppwinrt /?,我使用的是 v1.0.190111.3,我猜它是随 SDK 一起提供的。我无法从 CMake 项目中获取 NuGet 版本——见下文:
    • PM> 安装包 Microsoft.Windows.CppWinRT -版本 2.0.200729.8 安装包:未找到项目“默认”。在 line:1 char:1 + Install-Package Microsoft.Windows.CppWinRT -Version 2.0.200729.8 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : ObjectNotFound: (Default:String) [Install-Package ], ItemNotFoundException + FullyQualifiedErrorId : NuGetProjectNotFound,NuGet.PackageManagement.PowerShellCmdlets.InstallPackageCommand
    猜你喜欢
    • 1970-01-01
    • 2021-08-18
    • 1970-01-01
    • 2011-09-01
    • 2019-05-14
    • 2012-05-20
    • 1970-01-01
    • 1970-01-01
    • 2012-10-14
    相关资源
    最近更新 更多