Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Creating Static-linked Executables using VC++ Standard Edition

0.00/5 (No votes)
15 May 2001 1  
VC++ Standard Edition only has support for dynamically linked exes. This article shows you how to by-pass this restriction.
  • Download demo project - 14 Kb
  • Being cheap and having no money, I bought the cheapest VC++ (the "Standard" version) and found out later that I could only build new applications dependent on shared libraries. i.e. no statically linked executables. Which means if I used MFC I would have to distribute the MFC*.dll with my app. Those of you with the more expensive versions might take statically linked executables for granted. As I learned VC++, compiling the examples, reading books, I noticed that the MFCIE example has configurations for static builds, so I compiled it statically and, lo and behold - no MFC dependencies!! Wow, I thought, it IS possible after all. So I put it in the back of my mind for a year or so, and went on learning how to write code. And so one day I decided that I wanted a static build, and would be nice to know how to do such a thing, by-passing Microsoft's version of crippleware.

    First thing to do is grab a project, or start a new project, compile it, and export the makefile (and write the dependencies).

    Next thing I did was to examine the .mak files, comparing the MFCIE and my projects .mak files. I also examined the .clw,.dsp, .dep and .dsw files. Hmm, interesting, I thought. I noticed that the .dsp, and .dep files were most interesting. So I grabbed one of the many favorite editors I use (deciding between emacs, ultra edit, text edit, joe, or Visual Studio - I chose Ultra Edit), closed the myProject in VC++, and opened up mfcie.dsp, mfcie.dep, and myProject.dsp, myProject.dep, in the editor.

    I copied the !MESSAGE lines from the file mfcie.dsp to the file myProject.dsp that contained the word "static" and changed the name to "myProject" like this: (the first two are the original lines)

    !MESSAGE "myProject - Win32 Release" (based on "Win32 (x86) Application")
    !MESSAGE "myProject - Win32 Debug" (based on "Win32 (x86) Application")
    !MESSAGE "myProject - Win32 Static Release" (based on "Win32 (x86) Application")
    !MESSAGE "myProject - Win32 Static Debug" (based on "Win32 (x86) Application")
    
    I Changed
    # PROP AllowPerConfigDependencies 0
    to
    # PROP AllowPerConfigDependencies 1

    I then copied the

    !ELSEIF  "$(CFG)" == "mfcie - Win32 Static Release"
    and
    !ELSEIF  "$(CFG)" == "mfcie - Win32 Static Debug"
    sections to myProject.dsp changing "mfcie" to "myProject", and the Output_Dir and Intermediate_Dir to something reasonable. Which look like this:
    !ELSEIF  "$(CFG)" == "myProject - Win32 Static Release"
    
    # PROP BASE Use_MFC 6
    # PROP BASE Use_Debug_Libraries 0
    # PROP BASE Output_Dir "StaticRelease"
    # PROP BASE Intermediate_Dir "StaticRelease"
    # PROP BASE Target_Dir ""
    # PROP Use_MFC 6
    # PROP Use_Debug_Libraries 0
    # PROP Output_Dir "StaticRelease"
    # PROP Intermediate_Dir "StaticRelease"
    # PROP Ignore_Export_Lib 0
    # PROP Target_Dir ""
    # ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_AFXDLL" /Yu"stdafx.h" /FD /c
    # ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /Yu"stdafx.h" /FD /c
    # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32
    # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32
    # ADD BASE RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL"
    # ADD RSC /l 0x409 /d "NDEBUG"
    BSC32=bscmake.exe
    # ADD BASE BSC32 /nologo
    # ADD BSC32 /nologo
    LINK32=link.exe
    # ADD BASE LINK32 /nologo /subsystem:windows /machine:I386
    # ADD LINK32 /nologo /subsystem:windows /machine:I386
    
    !ELSEIF  "$(CFG)" == "myProject - Win32 Static Debug"
    
    # PROP BASE Use_MFC 6
    # PROP BASE Use_Debug_Libraries 0
    # PROP BASE Output_Dir "StaticDebug"
    # PROP BASE Intermediate_Dir "StaticDebug"
    # PROP BASE Target_Dir ""
    # PROP Use_MFC 6
    # PROP Use_Debug_Libraries 0
    # PROP Output_Dir "StaticDebug"
    # PROP Intermediate_Dir "StaticDebug"
    # PROP Target_Dir ""
    # ADD BASE CPP /nologo /MD /W3 /Gm /GX /Zi /O2 /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "NDEBUG" /Yu"stdafx.h" /FD /c
    # ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /Yu"stdafx.h" /FD /c
    # ADD BASE MTL /nologo /D "_DEBUG" /D "NDEBUG" /mktyplib203 /o "NUL" /win32 /win32
    # ADD MTL /nologo /D "_DEBUG" /D "NDEBUG" /mktyplib203 /o "NUL" /win32 /win32
    # ADD BASE RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" /d "NDEBUG"
    # ADD RSC /l 0x409 /d "_DEBUG" /d "NDEBUG"
    BSC32=bscmake.exe
    # ADD BASE BSC32 /nologo
    # ADD BSC32 /nologo
    LINK32=link.exe
    # ADD BASE LINK32 /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
    # ADD LINK32 /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
    
    between the last entry of the Debug section, and the !ENDIF statement. I also added my names in the # Begin Target section:
    # Name "myProject - Win32 Release"
    # Name "myProject - Win32 Debug"
    # Name "myProject - Win32 Static Release"
    # Name "myProject - Win32 Static Debug"
    
    I also added the lines
    !IF  "$(CFG)" == "myProject - Win32 Release"
    
    !ELSEIF  "$(CFG)" == "myProject - Win32 Debug"
    
    !ELSEIF  "$(CFG)" == "myProject - Win32 Static Release"
    
    !ELSEIF  "$(CFG)" == "myProject - Win32 Static Debug"
    
    !ENDIF 
    
    after the line
    SOURCE=.\myProject.rc
    and before
    # End Source File

    I also opened mfcie.dep, and myProject.dep files in the editor. Interesting differences here. Although I am not sure how much it depends on this file. I think most of it is in the .dsp file.

    I took out every line that was

      ".\StdAfx.h"\
    from the files listed there. Moved the line that is
        .\StdAfx.cpp : \
        ".\StdAfx.h"\
    to the last line and changed it to resemble the one in the mfcie.dep file, which is
     ".\StdAfx.h"\
        .\StdAfx.cpp : \
        "..\vc98\include\basetsd.h"\
        ".\StdAfx.h"\
    
    obviously changing where basetsd.h is located, because it was different in this instance.

    Just make sure that the C++ pre-processor settings are correct and that the Output directories are set.

    I opened up myProject.dsp in VC++6, changed the Active configuration, and compiled. I ignored the linker warning

    LINK : warning LNK4089: all references to "SHELL32.dll" discarded by /OPT:REF

    Contrary to what I thought would happen, it compiled and linked successfully. I checked the dependencies using my favorite dependency checker, and no MFC*.dll's were listed, and the executable was 2x as big, so as far as I KNOW, it was statically built.

    After trying this a few times, wizarding up new projects, and editing the files, I have been successful 7/8 of the time, including adding statically linked executable (as far as MFC*.DLL is concerned) to a couple of already matured, projects-in-progress. The time it didn't work, I didn't compile and export the makefile beforehand. A few times, I had to mess around with linker settings and such, but they eventually compiled/linked.

    Addenda

    Since writing this article I have found a new way of doing this:

    1. Move the whole Visual C++ directory structure kit and kabootle somewhere else. (You do not need to move MSDN, its better to leave that, otherwise help & MSDN will not work, unless you reinstall MSDN)
      From D:\Program Files\Microsoft Visual Studio
      To D:\VC
    2. Edit the environmental variables in (Win2k) (Control Panel->System ->Advanced->Environmental Variables) just replace
      D:\Program Files\Microsoft Visual Studio
      with D:\VC

      for all the variables listed below. (I'm not sure where they are in Win9x). Do this for the variables:
       

      • include
      • lib
      • MSDevDir
      • path
         
    3. Edit the link you use to open VC++. Open VC++6. It will inform you that it can't find your macros and add-ins. Don't worry, we will fix this, lickity-split.
       
    4. And from VC++6 itself (Tools->Options->Directories), replace
      D:\Program Files\Microsoft Visual Studio
      with D:\VC

      for all the variables listed below.

      • include files
      • lib files
      • executables
      • source files
         
    5. Go to Add-ins and macros paths (Tools->Customize->Add-ins and Macro Files). Reselect the ones you use. This will automatically re-register the dll's
       
    6. You might also want to edit and relink all the VC++ tools in the Start Menu.
       
    7. Now, try it out. Go to File->New->MFC AppWizard. On step 3, you should now see
           How would you like to use the MFC library?
           o As a shared library
           o As a statically linked library
       

      Pick statically linked library. Now build it. Start up Depends to see if it is actually not dependant on MFC.dll. I am quite sure it is perfectly legal to move files around your computer.

    Alternatively, I have written a macro that will use the method in my previous article, on the same subject to add statically linked configurations to your current project. This method is less time consuming.

    Just cut-n-paste this to a macro file, and run it on your project.

    ==============================================================
    Sub StaticLink()
    'DESCRIPTION: Makes a statically linked project
       Dim projectName
       projectName = ActiveProject.Name
       Msganswer  = MsgBox ("Do you really want to edit the Developer Studio Project file? " &_
                            "It will be backed up to "+projectName+".dsp.bak",260, "llornkcor VC++ Macro")
       if Msganswer = vbNo then  
          Exit Sub
       end if
    
       Dim fso, filespec
       Set fso = CreateObject("Scripting.FileSystemObject")
       filespec = ActiveProject.Name+".dsp.bak"
       If (fso.FileExists( filespec)) Then
          Msganswer  = MsgBox ("It looks like it has already been edited. " &_
                               Do you really want to do this?",260, "llornkcor VC++ Macro")
          if Msganswer = vbNo then 
             Exit Sub
          end if
       End If
       Stat()
    End Sub
    
    
    Sub Stat()
    'DESCRIPTION: Worker for statically linked project function
    
    Dim fso
    Dim dspFileName
    Dim projectName
    Dim dspFile
    Dim appDev
    Set appDev = GetObject(,"MSDev.Application")
    
    dspFileName = ActiveProject.Name + ".dsp"
    projectName = ActiveProject.Name
    
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set dspFile = fso.GetFile( dspFileName)
    
    dspFile.Copy( dspFileName+".bak")
    
    Documents.Open ( dspFileName), "Text"
    ActiveDocument.Selection.FindText "Win32 Debug"" (based on ""Win32 (x86) Application"")", dsMatchForward + dsMatchFromStart + dsMatchCase  
        ActiveDocument.Selection.EndOfLine
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "!MESSAGE """+projectName+" - Win32 Static Debug"" (based on ""Win32 (x86) Application"")"
    ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "!MESSAGE """+projectName+" - Win32 Static Release"" (based on ""Win32 (x86) Application"")"
    
    ActiveDocument.Selection.FindText "# PROP AllowPerConfigDependencies"
        ActiveDocument.Selection.EndOfLine
        ActiveDocument.Selection.WordLeft 1
        Dim strVersion4
        strVersion4 = ActiveDocument.Selection.Text 
        ActiveDocument.Selection.ReplaceText strVersion4 , "1"
    
        ActiveDocument.Selection.FindText "!ENDIF"
        ActiveDocument.Selection.StartOfLine
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "!ELSEIF  ""$(CFG)"" == """+projectName+" - Win32 Static Debug"""
        ActiveDocument.Selection.NewLine
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# PROP BASE Use_MFC 6"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# PROP BASE Use_Debug_Libraries 0"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# PROP BASE Output_Dir ""StaticDebug"""
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# PROP BASE Intermediate_Dir ""StaticDebug"""
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# PROP BASE Target_Dir """""
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# PROP Use_MFC 6"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# PROP Use_Debug_Libraries 0"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# PROP Output_Dir ""StaticDebug"""
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# PROP Intermediate_Dir ""StaticDebug"""
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# PROP Ignore_Export_Lib 0"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# PROP Target_Dir """""
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# ADD BASE CPP /nologo /MD /W3 /Gm /GX /Zi /O2 /D ""WIN32"" /D ""_DEBUG"" /D ""_WINDOWS"" /D ""_AFXDLL"" /D ""NDEBUG"" /Yu""stdafx.h"" /FD /c"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# ADD CPP /nologo /MT /W3 /GX /O2 /D ""WIN32"" /D ""_DEBUG"" /D ""_WINDOWS"" /Yu""stdafx.h"" /FD /c"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# ADD BASE MTL /nologo /D ""_DEBUG"" /D ""NDEBUG"" /mktyplib203 /o ""NUL"" /win32 /win32"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# ADD MTL /nologo /D ""_DEBUG"" /D ""NDEBUG"" /mktyplib203 /o ""NUL"" /win32 /win32"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# ADD BASE RSC /l 0x409 /d ""_DEBUG"" /d ""_AFXDLL"" /d ""NDEBUG"""
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# ADD RSC /l 0x409 /d ""_DEBUG"" /d ""NDEBUG"""
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "BSC32=bscmake.exe"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# ADD BASE BSC32 /nologo"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# ADD BSC32 /nologo"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "LINK32=link.exe"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# ADD BASE LINK32 /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# ADD LINK32 /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# SUBTRACT LINK32 /nodefaultlib"
        ActiveDocument.Selection.NewLine
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "!ELSEIF  ""$(CFG)"" == """+projectName+" - Win32 Static Release"""
        ActiveDocument.Selection.NewLine
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# PROP BASE Use_MFC 6"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# PROP BASE Use_Debug_Libraries 0"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# PROP BASE Output_Dir ""StaticRelease"""
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# PROP BASE Intermediate_Dir ""StaticRelease"""
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# PROP BASE Target_Dir """""
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# PROP Use_MFC 6"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# PROP Use_Debug_Libraries 0"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# PROP Output_Dir ""StaticRelease"""
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# PROP Intermediate_Dir ""StaticRelease"""
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# PROP Ignore_Export_Lib 0"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# PROP Target_Dir """""
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# ADD BASE CPP /nologo /MD /W3 /GX /O2 /D ""WIN32"" /D ""NDEBUG"" /D ""_WINDOWS"" /D ""_AFXDLL"" /Yu""stdafx.h"" /FD /c"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# ADD CPP /nologo /MT /W3 /GX /O2 /D ""WIN32"" /D ""NDEBUG"" /D ""_WINDOWS"" /Yu""stdafx.h"" /FD /c"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# ADD BASE MTL /nologo /D ""NDEBUG"" /mktyplib203 /o ""NUL"" /win32"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# ADD MTL /nologo /D ""NDEBUG"" /mktyplib203 /o ""NUL"" /win32"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# ADD BASE RSC /l 0x409 /d ""NDEBUG"" /d ""_AFXDLL"""
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# ADD RSC /l 0x409 /d ""NDEBUG"""
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "BSC32=bscmake.exe"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# ADD BASE BSC32 /nologo"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# ADD BSC32 /nologo"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "LINK32=link.exe"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# ADD BASE LINK32 /nologo /subsystem:windows /machine:I386"
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# ADD LINK32 /nologo /subsystem:windows /machine:I386 /out:""StaticRelease/"+projectName+".exe"""
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# SUBTRACT LINK32 /pdb:none"
        ActiveDocument.Selection.NewLine
        ActiveDocument.Selection.NewLine
        ActiveDocument.Selection.FindText "# Name """+projectName+" - Win32 Debug"""
        ActiveDocument.Selection.EndOfLine
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# Name """+projectName+" - Win32 Static Debug"""
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "# Name """+projectName+" - Win32 Static Release"""
    
        ActiveDocument.Selection.FindText"SOURCE=.\"+projectName+".rc"
        ActiveDocument.Selection.EndOfLine
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "!IF  ""$(CFG)"" == """+projectName+" - Win32 Release"""
        ActiveDocument.Selection.NewLine
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "!ELSEIF  ""$(CFG)"" == """+projectName+" - Win32 Debug"""
        ActiveDocument.Selection.NewLine
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "!ELSEIF  ""$(CFG)"" == """+projectName+" - Win32 Static Debug"""
        ActiveDocument.Selection.NewLine
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "!ELSEIF  ""$(CFG)"" == """+projectName+" - Win32 Static Release"""
        ActiveDocument.Selection.NewLine
        ActiveDocument.Selection.NewLine
    ActiveDocument.Selection.Text = "!ENDIF"
        ActiveDocument.Selection.NewLine
        
    ActiveDocument.Save (ActiveProject.Name + ".dsp")
    ActiveDocument.Close() 
    'App.WorkspaceClose()
    End Sub
    

    License

    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here