所以我有一个使用 CMake 构建的 DLL,需要嵌入特定的清单文件。在 Visual Studio 设置中,我只需在清单工具/输入和输出/附加清单文件下添加清单文件名,它就可以正常工作。看起来这应该是 CMake 可以做到的事情,但我一直无法弄清楚。
关于如何使用 CMake 完成此任务有什么想法吗?
cmake-3.4 现在已经学会了如何处理列为源文件的 *.manifest 文件。
在CMake中无法生成
Additional Manifest Files
字段(我检查了源代码)。所以我们必须偷偷摸摸。
Visual 生成自己的清单 ( yourapp.exe.manifest.intermediate ) 并将其与您的清单混合。 因此,我们必须生成此清单一次,禁用生成,然后使用生成的清单。
生成清单:
如果您知道如何自己编写完整的清单,则此步骤是可选的。如果你和世界其他人一样:
Additional Manifest Files
)禁用生成:
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)
我刚刚发现您可以使用 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>
我刚刚自己完成了这个练习,这就是我来到这个页面的原因。 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。
这非常有帮助。这是我最终为需要 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."
)
我想分享我用来执行此操作的 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。