How to embed manifest inside C/C++ binary using makefiles
Here is an unofficial preview of sample that is going to be available soon on MSDN. As always, standard disclosure that this post is provided "AS IS" with no warranties, and confer no rights and use of this sample is subject to the terms specified at https://www.microsoft.com/info/cpyright.htm
It is recommended for C/C++ applications to have manifest embedded inside the final binary because this ensures proper runtime behavior in most of scenarios. Visual Studio by default tries to embed manifest when building project from source files, see my post on how manifest is generated in visual studio for more details. However if an application is built using nmake build utility, some changes to existing makefile are necessary. In this section it is demonstrated how to change existing makefiles to automatically embed manifest inside final binary.
First let’s take a look on MyApplication.exe, which is a simple application built from one file using a nmake script as the following:
!if "$(DEBUG)" == "1"
CPPFLAGS=$(CPPFLAGS) /MDd
LFLAGS=$(LFLAGS) /INCREMENTAL
!else
CPPFLAGS=$(CPPFLAGS) /MD
!endif
MyApplication.exe : MyApplication.obj
link $** /out:$@ $(LFLAGS)
MyApplication.obj : MyApplication.cpp
clean :
del MyApplication.obj MyApplication.exe
If this script is run unchanged with Visual C++ 2005, it successfully creates MyApplication.exe and external manifest MyApplication.exe.manifest that is used by operation system to load dependent assemblies at runtime.
However it may be required to embed manifest inside MyApplication.exe for some reasons. There are several ways to achieve this. A simple change to make is to embed external manifest generated by linker as a resources inside the final binary. This can be achieved by making changes to nmake script file. First
# Step 1. Adding _VC_MANIFEST_INC to specify which build is incremental (1 - incremental)
# Step 2. Adding _VC_MANIFEST_BASENAME used to specify name of a temporary resource file
!if "$(DEBUG)" == "1"
CPPFLAGS=$(CPPFLAGS) /MDd
LFLAGS=$(LFLAGS) /INCREMENTAL
_VC_MANIFEST_INC=1
_VC_MANIFEST_BASENAME=__VC80.Debug
!else
CPPFLAGS=$(CPPFLAGS) /MD
_VC_MANIFEST_INC=0
_VC_MANIFEST_BASENAME=__VC80
!endif
#Step 3. Specifying name of temporary resource file used only in incremental builds
!if "$(_VC_MANIFEST_INC)" == "1"
_VC_MANIFEST_AUTO_RES=$(_VC_MANIFEST_BASENAME).auto.res
!else
_VC_MANIFEST_AUTO_RES=
!endif
#Step 4. Adding _VC_MANIFEST_EMBED_EXE - command to embedd manifest to EXE
!if "$(_VC_MANIFEST_INC)" == "1"
#MT_SPECIAL_RETURN=1090650113
#MT_SPECIAL_SWITCH=-notify_resource_update
MT_SPECIAL_RETURN=0
MT_SPECIAL_SWITCH=
_VC_MANIFEST_EMBED_EXE= \
if exist $@.manifest mt.exe -manifest $@.manifest -out:$(_VC_MANIFEST_BASENAME).auto.manifest $(MT_SPECIAL_SWITCH) & \
if "%ERRORLEVEL%" == "$(MT_SPECIAL_RETURN)" \
rc /r $(_VC_MANIFEST_BASENAME).auto.rc & \
link $** /out:$@ $(LFLAGS)
!else
_VC_MANIFEST_EMBED_EXE= \
if exist $@.manifest mt.exe -manifest $@.manifest -outputresource:$@;1
!endif
# Step 5. Changing command used to build EXE
MyApplication.exe : MyApplication.obj $(_VC_MANIFEST_AUTO_RES)
link $** /out:$@ $(LFLAGS)
$(_VC_MANIFEST_EMBED_EXE)
MyApplication.obj : MyApplication.cpp
# Step 6. Adding commands to generate initial empty manifest file, RC file
# that references it and for generating the .res file
$(_VC_MANIFEST_BASENAME).auto.res : $(_VC_MANIFEST_BASENAME).auto.rc
$(_VC_MANIFEST_BASENAME).auto.rc : $(_VC_MANIFEST_BASENAME).auto.manifest
type <<$@
#include <winuser.h>
1 RT_MANIFEST "$(_VC_MANIFEST_BASENAME).auto.manifest"
<< KEEP
$(_VC_MANIFEST_BASENAME).auto.manifest :
type <<$@
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
</assembly>
<< KEEP
# Step 7. Adding _VC_MANIFEST_CLEAN - command to clean generate files
# for temporary resources
!if "$(_VC_MANIFEST_INC)" == "1"
_VC_MANIFEST_CLEAN=-del $(_VC_MANIFEST_BASENAME).auto.res \
$(_VC_MANIFEST_BASENAME).auto.rc \
$(_VC_MANIFEST_BASENAME).auto.manifest
!else
_VC_MANIFEST_CLEAN=
!endif
clean :
del MyApplication.obj MyApplication.exe MyApplication.exe.manifest
$(_VC_MANIFEST_CLEAN)
Similarly changes to nmake scripts required in case of an library MyLibrary.dll. If it was built using the following script:
!if "$(DEBUG)" == "1"
CPPFLAGS=$(CPPFLAGS) /MDd
LFLAGS=$(LFLAGS) /DLL /INCREMENTAL
!else
CPPFLAGS=$(CPPFLAGS) /MD
LFLAGS=$(LFLAGS) /DLL
!endif
MyLibrary.dll : MyLibrary.obj
link $** /out:$@ $(LFLAGS)
MyLibrary.obj : MyLibrary.cpp
clean :
del MyLibrary.obj MyLibrary.dll
In this case, build script has to change in a similar way to EXE with only one exception. Manifest has to embedded as resource with ID equal to 2, instead of 1 as for EXE.
# Step 1. Adding _VC_MANIFEST_INC to specify which build is incremental (1 - incremental)
# Step 2. Adding _VC_MANIFEST_BASENAME used to specify name of a temporary resource file
!if "$(DEBUG)" == "1"
CPPFLAGS=$(CPPFLAGS) /MDd
LFLAGS=$(LFLAGS) /DLL /INCREMENTAL
_VC_MANIFEST_INC=1
_VC_MANIFEST_BASENAME=__VC80.Debug
!else
CPPFLAGS=$(CPPFLAGS) /MD
LFLAGS=$(LFLAGS) /DLL
_VC_MANIFEST_INC=0
_VC_MANIFEST_BASENAME=__VC80
!endif
#Step 3. Specifying name of temporary resource file used only in incremental builds
!if "$(_VC_MANIFEST_INC)" == "1"
_VC_MANIFEST_AUTO_RES=$(_VC_MANIFEST_BASENAME).auto.res
!else
_VC_MANIFEST_AUTO_RES=
!endif
#Step 4. Adding _VC_MANIFEST_EMBED_DLL - command to embedd manifest into DLL
!if "$(_VC_MANIFEST_INC)" == "1"
MT_SPECIAL_RETURN=1090650113
MT_SPECIAL_SWITCH=-notify_update
_VC_MANIFEST_EMBED_DLL= \
if exist $@.manifest mt.exe -manifest $@.manifest -out:$(_VC_MANIFEST_BASENAME).auto.manifest $(MT_SPECIAL_SWITCH) & \
if "%ERRORLEVEL%" == "$(MT_SPECIAL_RETURN)" \
rc /r $(_VC_MANIFEST_BASENAME).auto.rc & \
link $** /out:$@ $(LFLAGS)
!else
_VC_MANIFEST_EMBED_DLL= \
if exist $@.manifest mt.exe -manifest $@.manifest -outputresource:$@;2
!endif
# Step 5. Changing command used to build DLL
MyLibrary.dll : MyLibrary.obj $(_VC_MANIFEST_AUTO_RES)
link $** /out:$@ $(LFLAGS)
$(_VC_MANIFEST_EMBED_DLL)
MyLibrary.obj : MyLibrary.cpp
# Step 6. Adding commands to generate initial empty manifest file, RC file that references it
# and for generating the .res file
# Note: RT_MANIFEST has ID 2, because this is DLL.
$(_VC_MANIFEST_BASENAME).auto.res : $(_VC_MANIFEST_BASENAME).auto.rc
$(_VC_MANIFEST_BASENAME).auto.rc : $(_VC_MANIFEST_BASENAME).auto.manifest
type <<$@
#include <winuser.h>
2 RT_MANIFEST "$(_VC_MANIFEST_BASENAME).auto.manifest"
<< KEEP
$(_VC_MANIFEST_BASENAME).auto.manifest :
type <<$@
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
</assembly>
<< KEEP
#Step 7. Adding _VC_MANIFEST_CLEAN - command to clean generate files for temporary resources
!if "$(_VC_MANIFEST_INC)" == "1"
_VC_MANIFEST_CLEAN=-del $(_VC_MANIFEST_BASENAME).auto.res \
$(_VC_MANIFEST_BASENAME).auto.rc \
$(_VC_MANIFEST_BASENAME).auto.manifest
!else
_VC_MANIFEST_CLEAN=
!endif
clean :
del MyLibrary.obj MyLibrary.dll MyLibrary.dll.manifest
$(_VC_MANIFEST_CLEAN)
Try your new makefile. This should work for you. If it does not, please let me know ASAP. I will try fixing it for you.
Have fun,
Nikola
Comments
- Anonymous
June 19, 2006
PingBack from http://blogates.com/twinsant/2006/06/20/yesterday-readings-158/