Friday, July 19, 2013

Visual Studio Runtime

When you build a project with Microsoft Visual Studio, you introduce a dependancy on a certain verson of Microsoft's C++ Runtime (CRT). If you migrate some projects (say that produce a dll) from Visual Studio 2008 (aka vs9) to Visual Studio 2010 (aka vs10) because you like the new IDE, you want need them to still build with vs9 so that no vs10 dependancy is introduced becaue the exe that uses your dll can't use vs9 and you don't want to introduce a vs9/vs10 conflict.

This may or may not be possible. Here's some details.

http://msdn.microsoft.com/en-us/library/abx4dbyh%28v=vs.80%29.aspx
http://msdn.microsoft.com/en-us/library/abx4dbyh%28v=vs.90%29.aspx
http://msdn.microsoft.com/en-us/library/abx4dbyh%28v=vs.100%29.aspx

Visual Studio 2005
 /MT =  statically link to microsoft's multithreaded CRT using libcmt.lib at link time
 /MD = dynamically link to microsoft's multithreaded CRT using msvcrt.lib at link time and msvcr80.dll at runtime

Visual Studio 2008 (aka vs9)
 /MT =  statically link to microsoft's multithreaded CRT using libcmt.lib at link time
 /MD = dynamically link to microsoft's multithreaded CRT using msvcrt.lib at link time and msvcr90.dll at runtime

Visual Studio 2010 (aka vs10)
 /MT =  statically link to microsoft's multithreaded CRT using libcmt.lib at link time
 /MD = dynamically link to microsoft's multithreaded CRT using msvcrt.lib at link time and msvcr100.dll at runtime

same deal for Standard C++ Libarary
 LIBCPMT.LIB
 MSVCPRT.LIB - MSVCP80.dll, MSVCP90.dll, MSVCP100.dll

In Visual C++ 2005, the C and C++ runtime libraries (e.g. msvcm80.dll, msvcm80d.dll) are stored in the global assembly cache.
In Visual C++ 2008, the C and C++ runtime libraries (e.g. msvcm90.dll, msvcm90d.dll) are stored in the global assembly cache.

http://msdn.microsoft.com/en-us/library/6sehtctf%28v=vs.90%29.aspx
Beginning with Visual C++ 2008, Visual C++ does not support targeting Windows 95, Windows 98, Windows ME, or Windows NT.

http://msdn.microsoft.com/en-us/library/aa383745%28v=vs.90%29.aspx
0x0500 = Windows 2000
0x0501 = Windows XP
0x0501 = Windows Server 2003
0x0502 = Windows XPsp2
0x0502 = Windows Server 2003sp1
0x0600 = Windows Vista
0x0600 = Windows Server 2008
0x0601 = Windows 7
0x0602 = Windows 8

It seems that if your project defines WINVER=0x0501
Then is should also define _WIN32_WINNT=0x0501 and NTDDI_VERSION=NTDDI_WINXP

Your project generates a manifest.

Its location is specified by:
 Project > Properties > Configuration Properties > Linker > Manifest File

Its will contain something like this:
 <assemblyIdentity type="win32"
                      name="Microsoft.VC90.CRT"
                   version="9.0.21022.8"
     processorArchitecture="x86"
            publicKeyToken="1fc8b3b9a1e18e3b">
 </assemblyIdentity>

This indicates the specific version 9.0.21022.8
                     of the version VC90
                             of the CRT
 that is required by your project.

If not installed on the runtime system, you'll see an error like:
 The progarm can't start because MSVCR90.dll is missing from your computer. 
 Try reinstalling the program to fix this problem.

So, how might you tell vs10 that you want it to build with vs9 dependancies?

Project > Properties > Configuration Properties > General > Platform Toolset > v90

Which just changes this:

…
  <PropertyGroup Condition…Win32…>
    …
  </PropertyGroup>
…

To this:

…
  <PropertyGroup Condition…Win32…>
    …
    <PlatformToolset>v90</PlatformToolset>
  </PropertyGroup>
…

In my case wrote this little Java program to update all 244 effected project files.

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class FindAndReplace
{
    public static void main(String[] args) throws Exception 
    {
        String fileName = "D:/fileNames.txt";
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(fileName)));
        String line = null;
        while ( (line = br.readLine()) != null ) 
        {
            process(line);
        }
        br.close();
    }
    
    public static void process(String fileName) throws Exception
    {
        System.out.println("processing "+fileName);
        File file = new File(fileName);
        File temp = new File(fileName + ".tmp");

        BufferedWriter bw;
        bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(temp)));
        
        String PROPERTY_CONDITION = "<PropertyGroup Condition";
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
        String line = null;
        int pos = 0;
        boolean inGroup = false;
        String linebreak = "";
        while ( (line = br.readLine()) != null ) 
        {        
            if ( line.indexOf("WINVER=0X0400") > -1 )
            {
                line = line.replaceFirst("WINVER=0X0400", "WINVER=0X0501");
                System.out.println(" replaced winver");
            }
            else if ( (pos = line.indexOf(PROPERTY_CONDITION)) > -1 
                   && line.indexOf("Win32", pos+PROPERTY_CONDITION.length()) > -1 )
            {
                inGroup = true;
            }
            else if ( inGroup && line.indexOf("</PropertyGroup>") > -1 )
            {
                bw.write(linebreak+"    <PlatformToolset>v90</PlatformToolset>");
                inGroup = false;
                System.out.println(" injected v90");
            }
            bw.write(linebreak+line);
            linebreak = "\r\n";
        }
        br.close();
        bw.close();
        file.renameTo(new File(fileName + ".org"));
        temp.renameTo(new File(fileName));
    }
}

Sadly, the DLLs that came out of the loadbuild machine still had the MSVCR100.dll dependancy. There is some hope. Microsoft says that to target the v90 platform toolset, you must have Visual Studio 2008 installed.

At this stage, I'm skeptical. First lets produce a mini vs9 exe that uses a vs9 dll then upgrade the vs9 dll to vs10 and observe the MSVCR100.dll error, then see if we can resolve it.

Create a project to create a DLL:

vs9
 File > New > Project
  Visual C++ > Win32 > Win32 Console Appliation
   Name: SimpleDll
   > Ok > Next
    DLL
    Empty Project
    > Finish

[right-click] Header Files > Add > New Item > Header File
 Name: SimpleUtil.h
 > Add

[right-click] Source Files > Add > New Item > C++ File
 Name: SimpleUtil.cpp
 > Add

SimpleUtil.h

namespace SimpleUtil
{
    class MySimpleUtil
    {
    public:
        // returns a + b
        static __declspec(dllexport) int Add(int a, int b);
    };
}

SimpleUtil.cpp

#include "SimpleUtil.h"

#include <stdexcept>

using namespace std;

namespace SimpleUtil
{
    int MySimpleUtil::Add(int a, int b)
    {
        return a + b;
    }
}

Create a project to use the DLL:

vs9
 File > New > Project
  Visual C++ > Win32 > Win32 Console Appliation
   Name: SimpleBin
   > Ok > Next
    Console Application
    Empty Project
    > Finish

[right-click] Source Files > Add > New Item > C++ File
 Name: main.cpp
 > Add

[right-click] SimpleBin > Properties > Configuration Properties
 > C/C++ > General > Additional Include Directories
  ..\..\SimpleDll\SimpleDll
 > Linker
  > General > Additional Library Directories
   ..\..\SimpleDll\Debug
  > Input > Additional Dependancies
   SimpleDll.lib
 > Debugging > Environment
  PATH=..\..\SimpleDll\Debug

main.cpp

#include <iostream>
#include <SimpleUtil.h>
int main()
{
  int a = 2;
  int b = 3;
  int c = SimpleUtil::MySimpleUtil::Add(a,b);
  printf("%i + %i = %i\n",a,b,c);

  char whatever[512];
  printf("Hit Enter To Exit");
  gets(whatever);
  return 0;
}

The created file structure is as follows:

SimpleDll
  Debug
    SimpleDll.dll
    SimpleDll.lib
  SimpleDll
    Debug
      SimpleUtil.obj
    SimpleDll.vcproj
    SimpleUtil.cpp
    SimpleUtil.h
  SimpleDll.sln
  
SimpleBin
  Debug
    SimpleBin.exe
  SimpleBin
    Debug
      main.obj
    SimpleBin.vcproj
    main.cpp
  SimpleBin.sln

The program output is as follows:

2 + 3 = 5
Hit Enter To Exit

Running SimpleBin from Visual Studio shows in the Visual Studio console that we depend on msvcr90d.dll.

Loaded 'C:\Users\mwood\Documents\Visual Studio 2008\Projects\SimpleBin\Debug\SimpleBin.exe'
Loaded 'C:\Windows\SysWOW64\ntdll.dll'
Loaded 'C:\Windows\SysWOW64\kernel32.dll'
Loaded 'C:\Windows\SysWOW64\KernelBase.dll'
Loaded 'C:\Users\mwood\Documents\Visual Studio 2008\Projects\SimpleDll\Debug\SimpleDll.dll'
Loaded 'C:\Windows\winsxs\x86_microsoft.vc90.debugcrt_1fc8b3b9a1e18e3b_9.0.30729.6161_none_2a4f639a55563668\msvcr90d.dll'

Opening SimpleDll.sln with vs10 launches the Conversion Wizard.

> Next > Yes, create a backup before converting > Finish > Close

It looks like this modified SimpleDll.sln and created SimpleDll.vcxproj from SimpleDll.vcproj

Rebuild the DLL with vs10 and the EXE with vs9, then run it.

Loaded 'C:\Users\mwood\Documents\Visual Studio 2008\Projects\SimpleBin\Debug\SimpleBin.exe'
Loaded 'C:\Windows\SysWOW64\ntdll.dll'
Loaded 'C:\Windows\SysWOW64\kernel32.dll'
Loaded 'C:\Windows\SysWOW64\KernelBase.dll'
Loaded 'C:\Users\mwood\Documents\Visual Studio 2008\Projects\SimpleDll\Debug\SimpleDll.dll'
Loaded 'C:\Windows\SysWOW64\msvcr100d.dll'
Loaded 'C:\Windows\winsxs\x86_microsoft.vc90.debugcrt_1fc8b3b9a1e18e3b_9.0.30729.6161_none_2a4f639a55563668\msvcr90d.dll'

Now we depend on both msvcr90d.dll and msvcr100d.dll.

vs10
 [right-click] SimpleDll > Properties > Configuration Properties
 > General > Platform Toolset
  v90

Rebuild both and run again.

Loaded 'C:\Users\mwood\Documents\Visual Studio 2008\Projects\SimpleBin\Debug\SimpleBin.exe'
Loaded 'C:\Windows\SysWOW64\ntdll.dll'
Loaded 'C:\Windows\SysWOW64\kernel32.dll'
Loaded 'C:\Windows\SysWOW64\KernelBase.dll'
Loaded 'C:\Users\mwood\Documents\Visual Studio 2008\Projects\SimpleDll\Debug\SimpleDll.dll'
Loaded 'C:\Windows\winsxs\x86_microsoft.vc90.debugcrt_1fc8b3b9a1e18e3b_9.0.30729.6161_none_2a4f639a55563668\msvcr90d.dll'

Now the msvcr100d.dll is gone, so if your toolset swap didn't fix your problem then either your build machine doesn't have vs9 installed or your didn't update all the vcxproj configs to the vs9 toolset.

{ "loggedin": false, "owner": false, "avatar": "", "render": "nothing", "trackingID": "UA-36983794-1", "description": "", "page": { "blogIds": [ 431 ] }, "domain": "holtstrom.com", "base": "\/michael", "url": "https:\/\/holtstrom.com\/michael\/", "frameworkFiles": "https:\/\/holtstrom.com\/michael\/_framework\/_files.4\/", "commonFiles": "https:\/\/holtstrom.com\/michael\/_common\/_files.3\/", "mediaFiles": "https:\/\/holtstrom.com\/michael\/media\/_files.3\/", "tmdbUrl": "http:\/\/www.themoviedb.org\/", "tmdbPoster": "http:\/\/image.tmdb.org\/t\/p\/w342" }