How do I force a native application to use an older C runtime

女生的网名这么多〃 提交于 2019-12-05 16:08:29
Kevin Smyth

The answer comes from http://blog.kalmbachnet.de/?postid=80

The trick is to remove from the application manifest the publicKey attribute on the assemblyIdentity so WinSxS is not used.

GME.exe.manifest:

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

Embed the manifest into GME.exe (substitute 1 for 2 for modifying dlls):
mt -manifest GME.exe.manifest -outputresource:GME.exe;1

Then copy the necessary dlls:
cp -a windows/winsxs/x86_microsoft.vc90.{atl,crt,mfc}*30729.4148*/*dll path-to-app/

Then create manifests for each assembly that SxS isn't being used for and place them next to the application. The manifests are based on e.g. C:\Windows\WinSxS\Manifests\x86_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.30729.4148_none_5090ab56bcba71c2.manifest:
Microsoft.VC90.CRT.Manifest:

<?xml version="1.0"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity type="win32" name="Microsoft.VC90.CRT" version="9.0.30729.4148" processorArchitecture="x86"></assemblyIdentity>
    <file name="msvcr90.dll"></file>
    <file name="msvcp90.dll"></file>
    <file name="msvcm90.dll"></file>
</assembly>

It is not possible to remove the assembly references from the application manifest, as the CRT complains that it is not being loaded via SxS.

Unfortunately it seems one must modify the manifest for every dependent dll in the application, including the dlls copied from WinSxS, or multiple versions may be loaded.

Here's a bash script that worked for me, where ~/Documents/sxs-hack/ contains the CRT dlls and modified manifests:

rm -rf bin
mkdir bin
cp -a ~/Documents/sxs-hack/* bin/
find -iname \*.dll -or -iname \*.ocx -or -iname \*.exe | while read -r file; do
  cp -a "$file" bin/"$(basename $file)"
  export file=bin/"$(basename $file)"
  export res=$file\;2
  if [ ${file:${#file}-3} = "exe" ]; then export res=$file\;1; fi
  echo $file
  mt.exe -nologo -inputresource:"$res" -out:extracted.manifest &&
  perl -pli -e 's/(Microsoft.VC90.[^>]*)version="[^"]*"([^>]*)publicKeyToken="[^"]*"/$1 $2 version="9.0.30729.4148"/g;' extracted.manifest &&
  mt -nologo -manifest extracted.manifest -outputresource:"$res"
  regsvr32 /s "$file" || true
done

Here's the trick to get the Application Config to work with Win2003 and later:

http://www.tech-archive.net/Archive/VC/microsoft.public.vc.ide_general/2008-01/msg00033.html

Essentially, one needs to add the app to the compatibility database with "EnableAppConfig"

This is documented here:

http://msdn.microsoft.com/en-us/library/ee710783%28VS.85%29.aspx

Working GME.exe.Config:

<?xml version="1.0"?>
<configuration>
  <windows>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity type="win32" name="Microsoft.VC90.CRT" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
        <publisherPolicy apply="no"/>
        <bindingRedirect oldVersion="9.0.21022.0-9.0.21022.4974" newVersion="9.0.30729.1" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity type="win32" name="Microsoft.VC90.MFC" publicKeyToken="1fc8b3b9a1e18e3b" processorArchitecture="x86"/>
        <publisherPolicy apply="no"/>
        <bindingRedirect oldVersion="9.0.21022.0-9.0.21022.4974" newVersion="9.0.30729.1" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity type="win32" name="Microsoft.VC90.ATL" publicKeyToken="1fc8b3b9a1e18e3b" processorArchitecture="x86"/>
        <publisherPolicy apply="no"/>
        <bindingRedirect oldVersion="9.0.21022.0-9.0.21022.4974" newVersion="9.0.30729.1" />
      </dependentAssembly>

    </assemblyBinding>
  </windows>
</configuration>

It seems one needs to do this for loaded .dlls too.

If you've got the source you could always statically link the c-runtime library that you want to use... Not always the greatest idea but if you've inherited a monster library that will only run in debug mode and can't redistribute the debug CRT it'll do the trick...

Kevin Smyth

Here's how to disable the publisher policy on Vista or 7:

Navigate to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\SideBySide\Winners\x86_policy.9.0.microsoft.vc90.crt_1fc8b3b9a1e18e3b_none_02d0010672fd8219\9.0

Set the Default key to the version you want, e.g. 9.0.30729.4148. Set the version you don't want to 0, e.g. "9.0.30729.4974"=00.

You must do this for crt, atl, mfc, etc.

WinSxS seems to cache the policy. This worked for me: touch(1) the application, then set HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\SideBySide\PublisherPolicyChangeTime to something low, e.g. 10.

This will disable the newer runtime for the entire system.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!