views:

144

answers:

4

Visual Studio 2010 installs version ...4974 of the VC9 runtime whose .pdbs are unavailable. How can I force my GME.exe to use an older VC9 runtime?

I've tried putting this into GME.exe.config:

<?xml version="1.0"?>
<configuration>
  <windows>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <assemblyIdentity type="win32" name="GME" processorArchitecture="x86" version="1.0.0.1"/>
      <dependentAssembly>
        <assemblyIdentity type="win32" name="Microsoft.VC90.CRT" publicKeyToken="1fc8b3b9a1e18e3b" processorArchitecture="x86" />
        <bindingRedirect oldVersion="9.0.21022.8-9.0.21022.4974" newVersion="9.0.30729.4148" />
        <bindingRedirect oldVersion="9.0.30729.0-9.0.30729.4974" newVersion="9.0.30729.4148" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity type="win32" name="Microsoft.VC90.MFC" publicKeyToken="1fc8b3b9a1e18e3b" processorArchitecture="x86" />
        <bindingRedirect oldVersion="9.0.21022.8-9.0.21022.4974" newVersion="9.0.30729.4148" />
        <bindingRedirect oldVersion="9.0.30729.0-9.0.30729.4974" newVersion="9.0.30729.4148" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity type="win32" name="Microsoft.VC90.ATL" publicKeyToken="1fc8b3b9a1e18e3b" processorArchitecture="x86" />
        <bindingRedirect oldVersion="9.0.21022.8-9.0.21022.4974" newVersion="9.0.30729.4148" />
        <bindingRedirect oldVersion="9.0.30729.0-9.0.30729.4974" newVersion="9.0.30729.4148" />
      </dependentAssembly>
    </assemblyBinding>
  </windows>
</configuration>

However, sxstrace reports:

INFO: Resolving reference Microsoft.VC90.CRT,processorArchitecture="x86",publicKeyToken="1fc8b3b9a1e18e3b",type="win32",version="9.0.21022.8"
....
INFO: Publisher Policy redirected assembly version.

Adding <publisherPolicy apply="no"/> under <dependentAssembly> results in ERROR: Activation Context generation failed. with no other helpful information on Windows 7.

Note this is only for debugging my local copy, not redistribution, so I'm not worried about security updates or other benefits of the publisher policy.

+2  A: 

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
Kevin Smyth
A: 

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

Arnshea
Note that according to http://msdn.microsoft.com/en-us/library/aa985618%28VS.90%29.aspx "Debug versions of an application are not redistributable" (but I don't plan to redistribute this version anyway)
Kevin Smyth
A: 

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.

Kevin Smyth
A: 

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.

Kevin Smyth