Проблемы с midl.exe и cppwinrt.exe из CMake

Это своего рода продолжение Как использовать 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 с несколькими проблемами:

    • It #includes <windows.h>, which ultimately #defines preprocessor macros for GetClassName and GetCurrentTime that cause compiler errors in WinRT functions with those names.
    • Я потратил несколько часов на отслеживание этого и научился компилировать с #define COM_NO_WINDOWS_H, чтобы предотвратить это.
    • It #includes non-existent *.h files from WinRT References Contracts directories instead of the Include directories:
      • #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 создает module.g.cpp, который # включает 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

Я упустил какую-то простую вещь, которая решила бы все эти проблемы с отсутствующими, неполными или некорректно сгенерированными файлами?


person Theodore Hall    schedule 22.08.2020    source источник


Ответы (1)


Неразрешенные ошибки внешнего символа указывают на то, что отсутствует библиотека импорта. В этом случае вы захотите установить ссылку на зонтичную библиотеку WindowsApp.lib, которая экспортирует необходимые символы.

Обратите внимание, что имена символов, которые вы наблюдаете, являются артефактом требований C ++ / WinRT к сборке с заголовками Windows SDK, а также без них. Он решает эту проблему, объявляя импорт (с префиксом WINRT_ для предотвращения конфликтов с объявлениями заголовка SDK), а затем сопоставляет переименованные символы с помощью / ALTERNATENAME переключатель компоновщика.

Я не уверен, что это решит все ваши проблемы, но вы определенно захотите добавить ${MYDIR}\\GeneratedFiles в дополнительные подключаемые каталоги. Это должно позаботиться о невозможности включить сгенерированные заголовки из подкаталога winrt (base.h, а также спроектированные заголовки типа среды выполнения Windows).

cppwinrt также записывает реализации заглушек для ваших собственных типов в ${MYDIR}\\GeneratedFiles\\sources, когда он обрабатывает файл .winmd, ранее созданный из ваших .idl (ов). К сожалению, здесь есть ручной шаг: вам нужно скопировать сгенерированные файлы .h и .cpp в ваше исходное дерево и реализовать реализации скелета. Это требуется всякий раз, когда вы изменяете одно из определений интерфейса.

Отметим, что файлы module.g.cpp, созданные для моих проектов, не содержат заголовков моих настраиваемых типов. Возможно, вы используете старую версию C ++ / WinRT (я использую v2.0.200203.5). Я считаю, что это было изменено с введением фабрики со стиранием типов в C ++ / WinRT версии 2.0. Если вы этого еще не сделали, вам следует использовать cppwinrt из Пакет Microsoft.Windows.CppWinRT NuGet в отличие от двоичного файла, который (раньше) поставлялся с Windows SDK.

person IInspectable    schedule 22.08.2020
comment
Спасибо за быстрый ответ, но есть проблемы, которые я, очевидно, должен решить в отдельных комментариях из-за ограничения длины: - person Theodore Hall; 24.08.2020
comment
ссылка на зонтичную библиотеку WindowsApp.lib - это разрешило внешние символы, спасибо. Я использовал это для UWP, но не для рабочего стола. Еще нужно проверить выполнение. + Мне все еще нужно быть осторожным с порядком #includes: как я упоминал ранее, cppwinrt генерирует заголовок, содержащий: `` `` #ifndef COM_NO_WINDOWS_H #include ‹windows.h› #include ‹ole2.h› #endif / * COM_NO_WINDOWS_H * / `` Мне нужно сначала включить это, а затем #undef GetClassName и #undef GetCurrentTime; иначе эти макросы мешают определениям или вызовам функций WinRT с этими именами. - person Theodore Hall; 24.08.2020
comment
Если я компилирую с COM_NO_WINDOWS_H, это создает больше проблем, чем решает, потому что другие необходимые вещи опускаются. - person Theodore Hall; 24.08.2020
comment
вам следует использовать cppwinrt из пакета NuGet Microsoft.Windows.CppWinRT, а не двоичный файл, который (раньше) поставлялся с Windows SDK. - Согласно cppwinrt /?, Я использую версию 1.0.190111.3, которая, как мне кажется, идет с SDK. Я не могу получить версию NuGet из проекта CMake - см. Ниже: - person Theodore Hall; 24.08.2020
comment
PM ›Install-Package Microsoft.Windows.CppWinRT -Version 2.0.200729.8 Install-Package: Project 'Default' не найден. В строке: 1 символ: 1 + Install-Package Microsoft.Windows.CppWinRT -Version 2.0.200729.8 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo: ObjectNotFound: (по умолчанию: String) [Install-Package ], ItemNotFoundException + FullyQualifiedErrorId: NuGetProjectNotFound, NuGet.PackageManagement.PowerShellCmdlets.InstallPackageCommand - person Theodore Hall; 24.08.2020
comment
Я получил пакет NuGet CppWinRT в типичном решении Visual Studio (с файлами решения и проекта), но когда я открываю командную строку и пробую cppwinrt /? он по-прежнему показывает v1.0.190111.3. Таким образом, даже несмотря на то, что внутренняя логика сборки VS может использовать версию 2.0.200729.8, она недоступна для командной строки или CMake без некоторых изысков. Я вижу, что cppwinrt.exe v2.0.200729.8 установлен в решении VS. Что произойдет, если я скопирую и выполню это из некоторого места, доступного для проекта CMake? Думаю, я проведу эксперимент, но он кажется грязным. - person Theodore Hall; 24.08.2020
comment
@ Настройка C ++ / WinRT v2.0 - это, вероятно, то, к чему вы хотели бы обратиться в первую очередь. Ошибка предполагает, что Install-Package не может найти проект для установки. Если вы используете CMake 3.15 или более поздней версии, вы можете увидеть, настроили ли VS_PACKAGE_REFERENCES работает для вас (см. этот ответ). - person IInspectable; 25.08.2020
comment
Тем не менее, C ++ / WinRT v2.0 должен компилироваться независимо от того, #include <Windows.h> вы ли и когда, хотя GetCurrentTime - это известная коллизия. Запись в документации Изоляция заголовочных файлов Windows SDK содержит дополнительную информацию. - person IInspectable; 25.08.2020
comment
Я добавил VS_PACKAGE_REFERENCES, но это не дало никакого видимого эффекта, кроме установки свойства. Я не вижу, чтобы v2 cppwinrt.exe был установлен где-либо. CMake по-прежнему вызывает v1, который поставляется с SDK. Согласно этой теме, Хм, очевидно, проекты C ++ не поддерживаются: stackoverflow.com/questions/61909735/ (я использую vs2019, но и там он, похоже, не работает.) Пока что мы вообще не используем файлы Visual Studio SLN или VCXPROJ, но мне, возможно, придется отказаться от борьбы и прибегнуть к этому для работы с графическим интерфейсом. - person Theodore Hall; 25.08.2020
comment
Это настоящий облом. Другой вариант - запустить восстановление nuget с присутствующим файлом package.config ( не забудьте установить targetFramework="native"). Пакет устанавливается в каталог, переданный с помощью флага -OutputDirectory. CMake необходимо запустить cppwinrt.exe оттуда. - person IInspectable; 25.08.2020
comment
GetCurrentTime - известная коллизия. Добавьте на эту страницу: winrt / Windows.UI.Xaml.Automation.Peers.h объявляет метод с именем GetClassName, а windows.h (через winuser.h) определяет макрос с именем GetClassName. - person Theodore Hall; 25.08.2020