views:

1940

answers:

5

We have a very large project mostly written in C# that has some small, but important, components written in C++. We target the RTM of .NET 2.0 as the minimum required version. So far, in order to meet this requirement we've made sure to have only the RTM of .NET 2.0 on our build box so that the C++ pieces link against that version.

Update: The C++ assembly that is causing the issue is a mixed-mode C++ assembly being loaded into a managed process.

Unfortunately when the confiker was set to do something on April 1st, our corporate IT made a huge push to get everything patched and up to date, and as a result everything up through 3.5 SP1 got installed on the build box. We've tried uninstalling everything, which has happened before, but now we are unable to meet our minimum requirements as anything built on that particular box requires .NET 2.0 SP1.

Since the box seems to be hosed in that we can't just uninstall the offending versions, is there any way to build the assemblies and explicitly tell them to use the RTM of .NET 2.0 (which is v2.0.50727.42)? I've seen pages that refer to using a manifest, but I can't figure out how to actually implement a proper manifest and get it into the assemblies. My expertise is in the managed world, so I'm at a bit of a loss on this.

Can anyone explain how I can make these assemblies target the .NET 2.0 RTM SxS assemblies?

Thanks!

+3  A: 

Yes. In your project properties, there is a page that indicates the runtime. There is a drop down that lists all of the runtimes available. Choose the one that is appropriate for you. (For VS 2008: Right click on the project -> properties, Compile tab, Advanced Compiler Settings button -> Target framework)

We do this right now. We would like to move to VS 2008, but we are doing it incrementally. So right now we have a VS 2008 solution, but all the projects still target .Net 2.0. Thus, when we compile and deploy, we don't need the .Net 3.5 stuff installed on our test boxes.

UPDATE:

To force a native program to link to specific versions of .dlls, you probably want to use something like this:

#pragma message ("Explicit link to generate a manifest entry for MFC.")

#if defined (_DEBUG)

#pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.VC80.DebugMFC' version='8.0.50608.0' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b'\"")

#else

#pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.VC80.MFC' version='8.0.50608.0' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b'\"")

#endif

Except, that instead of MFC, you should find the correct values for the .Net .DLLs.

It is reasonable to believe that you cannot have .Net 2.0 SP1 and .Net 2.0 on the same box. So getting this to work on that box is likely going to be really, really painful. It is probably better to spin up a new build VM that you can install the old, unpatched .Net framework on (if you can even get a hold of it anymore.)

Otherwise you will need to copy all the build-time files over to your current box, and then make adjustments to the include and library paths based on your build type. Probably this is a much bigger headache than it's worth.

Christopher
I believe that only works for .Net projects. I don't think that works for a unmanaged C++ project. I've always found Visual Studio to target the latest version of the runtime when compiling C++ code.
Soo Wei Tan
Correct...that only works for managed projects. I wish it were that easy for unmanaged. :)
John Clayton
Sorry, I thought you wanted managed C++. You can do similar things for native C++ by choosing which SDK you are targetting. This basically involves setting the correct compiler paths. For VS2005 there is a batch file called vcvars.bat Are you using makefiles to build, or devenv?
Christopher
We are using MSBuild, which under the hood is calling vcbuild for the VC projects. This certainly looks more like what I think is needed.Can you clarify where I'd put the code above (I'm not so hot in C++ land)? If I'm understanding your update correctly, does that bit of code actually generate/embed the manifest at build time?
John Clayton
The code goes in a header file included by all the translation units (C++ code implementation files) that need it. In reality, it only needs to be in one translation unit per .dll, but I prefer to just put it in the appropriate header. That snippet is a preprocessor directive that tells the compiler to generate a manifest at build time.
Christopher
A: 

Methinks you want CorBindToRuntime. This will allow you to specify the version of the CLR your C++ loads.

Logan Capaldo
Unless I'm missing something I don't think that's what we're looking for. The CorBindToRuntime appears to be an unmanaged API to bring managed code into an unmanaged process. The executable, and most of application, is managed C#. The assembly we're trying to load into the managed process is a mixed-mode C++ assembly.Is there something I'm missing?
John Clayton
Ah. Most of the interop I've dealt with has been going the other way. You're right, this probably won't help unless you rearchitect the app to have an unmanaged executable that loads the managed code.
Logan Capaldo
+3  A: 

While I'm pretty sure that Christopher's answer and code sample (thank you, Christopher!) is part of a more elegant solution, we were under the gun to get this out the door and found a very similar, but different, solution.

The first step is to create a manifest for the assembly:

<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type='win32' name='Microsoft.VC80.DebugCRT' version='8.0.50608.0' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />
    </dependentAssembly>
  </dependency>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type='win32' name='Microsoft.VC80.CRT' version='8.0.50608.0' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />
    </dependentAssembly>
  </dependency>
</assembly>

Next you have to set the 'Generate Manifest' option to 'No' under Configuration Properties -> Linker -> Manifest File, and set the 'Embed Manifest' option to 'No' under Configuration Properties -> Manifest Tool -> Input and Output.

Finally, to get your new manifest into the assembly add the following command to the project's post-build step:

mt.exe /manifest "$(ProjectDir)cppassembly.dll.manifest" /outputresource:"$(TargetDir)\cppassembly.dll";#2 -out:"$(TargetDir)\cppassembly.dll.manifest"

Once built we can open the dll in Visual Studio to view the manifest under RT_MANIFEST and confirm that it has our manifest!

When I put Christopher's code in the stdafx.h it ended up adding it as an additional dependency...the manifest was still looking for v8.0.50727.762. The manifest it generated looked like this:

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.VC80.DebugCRT" version="8.0.50608.0" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
    </dependentAssembly>
  </dependency>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.VC80.DebugCRT" version="8.0.50727.762" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
    </dependentAssembly>
  </dependency>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.VC80.CRT" version="8.0.50727.762" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
    </dependentAssembly>
  </dependency>
</assembly>

I could not track down another switch that would remove or clear existing dependencies. I like Christopher's approach better than a post-build step, but for now this works. If anyone has any additional input on how to clear out any existing dependencies that'd be great.

Thanks!

John Clayton
+1  A: 

Hi. I know this is a hack, but i've resorted to generating the manifest externally, and modifying it with notepad. In a pinch, it's done the trick for me. In my case, i was looking to have a VC++ 2005 app point to a .762 CRT.

Good Luck! Terry

+1  A: 

John's answer works for us. In Visual C++ 2005, the compiler generates manifests that include both 762 and 4053 versions of MFC and CRT. We deleted the 4053 version from the manifest, and went to the manual step described above. (Internally code will actually grab 4053 as it's a recognized security fix over 762, but the spec is necessary or linking will just fail.)

Ted's blog (tedwvc.wordpress.com) posting gave us the hint, but his solution didn't work out for us. This approach here works.