开发者

How can I embed a specific manifest file in a Windows DLL with a CMake build?

开发者 https://www.devze.com 2023-03-12 21:54 出处:网络
So I have a开发者_JAVA技巧 DLL that is being built with CMake that requires a specific manifest file to be embedded. In Visual Studio settings I can just add the manifest filename under Manifest Tool/

So I have a开发者_JAVA技巧 DLL that is being built with CMake that requires a specific manifest file to be embedded. In Visual Studio settings I can just add the manifest filename under Manifest Tool/Input and Ouput/Additional Manifest Files, and it works correctly. It seems like this is something that should be doable with CMake, but I have been unable to figure it out.

Any ideas on how I can accomplish this with CMake?


cmake-3.4 has now learned how to handle *.manifest files listed as source files.

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


It's not possible to generate the Additional Manifest Files field in CMake (I checked the source code). So we have to be sneakier.

Visual generates a manifest of its own ( yourapp.exe.manifest.intermediate ) and mixes it with yours. So we have to generate this manifest once, disable the generation, and use the generated manifest afterwards.

Generating the manifest :

This step is optional if you know how to write a complete manifest by yourself. If you're like the rest of the world :

  • Create your own manifest as usual
  • Add it in the interface (Additional Manifest Files)
  • Recompile, relink
  • Locate yourapp.exe.manifest (next to your .exe). Copy it in your sources directory and version it. Don't hesitate to rename it, like yourapp.final.manifest, if it's clearer for you

Disabling the generation :

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

Using the generated manifest afterwards :

This is done by a manual call to mt.exe (the manifest tool which is normally called after the linker... unless it's disabled) in a post-build step :

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

(You'll probably need to change $(TargetDir) to $(OutDir) depending on how you wrote your CMake; Use Visual's Macros button to see their values. And remember : #1 for executables, #2 for dlls)


I just found out that you can merge multiple manifest files (or embedded manifests inside executables) into an existing manifest file (or executable) with mt.exe. This way, you don't have to disable automatic manifest generation of visual studio. You can just add new manifest data with mt.exe as a postbuild step. Example:

program.exe has embedded manifest:

<?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 contains:

<?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>

Run command:

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

Now program.exe contains embedded manifest:

<?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>


I just went through this exercise myself, which is what brought me to this page. Calvin1602's answer pretty much lays out the solution, but I had to finagle the syntax a bit to make it work for me. Here are the exact commands that finally worked:

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..." 
                  )

Note that you should use #1 in the mt.exe command when the target is an application, and #2 when it's a DLL (at least, as far as I understand it--it didn't work for me until I changed the 1 to a 2).

Also, you can use mt.exe to extract the original manifest from the DLL if you want/need to. The command looks like this:

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

It's not too hard to hand-edit the output if you have a manifest file for the dependency you want to merge in. But I sorta like Calvin1602's trick of having Visual Studio do it for you if you're using Visual Studio solution files rather than nmake.


This was very helpful. Here's what I ended up doing for a DLL that needed an MSVCR90 manifest, your mileage may vary:

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

精彩评论

暂无评论...
验证码 换一张
取 消