如何使用 CMake 构建将特定清单文件嵌入到 Windows DLL 中?

问题描述 投票:0回答:6

所以我有一个使用 CMake 构建的 DLL,需要嵌入特定的清单文件。在 Visual Studio 设置中,我只需在清单工具/输入和输出/附加清单文件下添加清单文件名,它就可以正常工作。看起来这应该是 CMake 可以做到的事情,但我一直无法弄清楚。

关于如何使用 CMake 完成此任务有什么想法吗?

windows cmake manifest
6个回答
19
投票

cmake-3.4 现在已经学会了如何处理列为源文件的 *.manifest 文件。

https://cmake.org/cmake/help/v3.4/release/3.4.html#other


9
投票

在CMake中无法生成

Additional Manifest Files
字段(我检查了源代码)。所以我们必须偷偷摸摸。

Visual 生成自己的清单 ( yourapp.exe.manifest.intermediate ) 并将其与您的清单混合。 因此,我们必须生成此清单一次,禁用生成,然后使用生成的清单。

生成清单:

如果您知道如何自己编写完整的清单,则此步骤是可选的。如果你和世界其他人一样:

  • 照常创建您自己的清单
  • 在界面中添加(
    Additional Manifest Files
    )
  • 重新编译,重新链接
  • 找到您的app.exe.manifest(在您的.exe 旁边)。将其复制到源目录中并对其进行版本控制。如果您觉得更清楚,请毫不犹豫地重命名它,例如 yourapp.final.manifest

禁用生成:

IF( WIN32 )
    SET ( CMAKE_SHARED_LINKER_FLAGS /MANIFEST:NO )
ENDIF( WIN32 )

之后使用生成的清单:

这是通过在构建后步骤中手动调用 mt.exe(通常在链接器之后调用的清单工具......除非它被禁用)来完成的:

add_custom_command(
    TARGET YourApp
    POST_BUILD
    COMMAND "mt.exe" -manifest \"$(TargetDir)\\yourapp.final.manifest\" -outputresource:"$(TargetDir)$(TargetFileName)"\;\#1
    COMMENT "Adding manifest..." 
)

(您可能需要将 $(TargetDir) 更改为 $(OutDir),具体取决于您编写 CMake 的方式;使用 Visual 的

Macros
按钮查看它们的值。并记住:#1 表示可执行文件,#2 表示 dll)


7
投票

我刚刚发现您可以使用 mt.exe 将多个清单文件(或可执行文件中的嵌入清单)合并到现有清单文件(或可执行文件)中。这样,您就不必禁用 Visual Studio 的自动清单生成。您只需使用 mt.exe 添加新的清单数据作为构建后步骤。示例:

program.exe 已嵌入清单:

<?xml version="1.0"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="amd64" publicKeyToken="6595b64144ccf1df" language="*"/>
    </dependentAssembly>
  </dependency>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
      </requestedPrivileges>
    </security>
  </trustInfo>
</assembly>

dpiaware.manifest 包含:

<?xml version="1.0"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <ms_windowsSettings:dpiAware xmlns:ms_windowsSettings="http://schemas.microsoft.com/SMI/2005/WindowsSettings" xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</ms_windowsSettings:dpiAware>
    </windowsSettings>
  </application>
</assembly>

运行命令:

mt.exe -manifest dpiaware.manifest "-inputresource:program.exe;#1" -outputresource:program.exe;#1

现在program.exe包含嵌入式清单:

<?xml version="1.0"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="amd64" publicKeyToken="6595b64144ccf1df" language="*"/>
    </dependentAssembly>
  </dependency>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
      </requestedPrivileges>
    </security>
  </trustInfo>
  <application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <ms_windowsSettings:dpiAware xmlns:ms_windowsSettings="http://schemas.microsoft.com/SMI/2005/WindowsSettings" xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</ms_windowsSettings:dpiAware>
    </windowsSettings>
  </application>
</assembly>

3
投票

刚刚自己完成了这个练习,这就是我来到这个页面的原因。 Calvin1602 的答案几乎列出了解决方案,但我必须稍微调整一下语法才能使其对我有用。以下是最终起作用的确切命令:

if (WIN32)
    set(CMAKE_SHARED_LINKER_FLAGS /MANIFEST:NO)
endif()

add_custom_command(TARGET
                     odrmanager
                   POST_BUILD
                   COMMAND
                     "mt.exe" -manifest \"${CMAKE_CURRENT_SOURCE_DIR}\\odrmanager.dll.manifest\" -outputresource:\"${CMAKE_CURRENT_BINARY_DIR}\\odrmanager\\odrmanager.dll\"\;\#2
                   COMMENT
                     "Adding custom manifest containing MSVCRT80 dependency..." 
                  )

请注意,当目标是应用程序时,您应该在

#1
命令中使用
mt.exe
,当目标是 DLL 时,应该使用
#2
(至少,据我了解,它对我不起作用,直到我将
1
更改为
2
)。

此外,如果您愿意/需要,您可以使用

mt.exe
从 DLL 中提取原始清单。该命令如下所示:

mt -inputresource:odrmanager.dll;#2 -out:odrmanager.manifest

如果您有要合并的依赖项的清单文件,那么手动编辑输出并不太难。但我有点喜欢 Calvin1602 的技巧,如果您使用的是 Visual Studio 解决方案文件,则让 Visual Studio 为您完成此操作比nmake。


0
投票

这非常有帮助。这是我最终为需要 MSVCR90 清单的 DLL 所做的事情,您的情况可能会有所不同:

add_custom_command(
  TARGET foo
  POST_BUILD COMMAND
    mt.exe -manifest \"${MYDEPDIR}/msvcr90/Microsoft.VC90.CRT.manifest\" "-inputresource:\"${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/foo.dll\";#2" -outputresource:\"${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/foo.dll\";#2
  COMMENT
    "Appending manifest for MSVCRT90 dependency."
)

0
投票

我想分享我用来执行此操作的 CMake 函数,基于 Calvin1602 的 myavuzselim 的 答案:

# add_manifest(<target> <manifest file>)
#
# Adds a manifest file (https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests)
# to an EXE
function(add_manifest TARGET_NAME MANIFEST_FILE)
  if(NOT TARGET_NAME)
      message(FATAL_ERROR "You must provide a target")
    endif()
    if(NOT MANIFEST_FILE)
      message(FATAL_ERROR "You must provide a manifest file")
    endif()
    add_custom_command(
        TARGET ${TARGET_NAME}
        POST_BUILD
        COMMAND "mt.exe" -manifest \"${MANIFEST_FILE}\" \"-updateresource:$<TARGET_FILE:${TARGET_NAME}>\"
    )
endfunction()

如果默认值 (

;#1
) 不起作用,可以扩展以接受选项 RESOURCE_ID 参数。它还可以扩展为在非 Windows 平台上不执行任何操作。

我这样使用:我在仓库中有一个

utf8.manifest
,其中包含以下内容:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <application>
    <windowsSettings>
      <activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
    </windowsSettings>
  </application>
</assembly>

并定义一个

set(MANIFEST_FILE_UTF8 "${CMAKE_SOURCE_DIR}/cmake/utf8.manifest")
,因此对于大多数 EXE,我只需执行
add_manifest(${BIN} ${MANIFEST_FILE_UTF8})
并且我的所有 win32 -A api 都支持 UTF-8。

© www.soinside.com 2019 - 2024. All rights reserved.