Creating Static-linked Executables using VC++ Standard Edition





5.00/5 (1 vote)
Jan 17, 2000

183172

791
VC++ Standard Edition only has support for dynamically linked exes. This article shows you how to by-pass this restriction.
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:septbetween 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" !ENDIFafter the line
SOURCE=.\myProject.rcand 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:
- 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 - Edit the environmental variables in (Win2k) (Control Panel->System ->Advanced->Environmental Variables)
just replace
D:\Program Files\Microsoft Visual Studio
with D:\VCfor all the variables listed below. (I'm not sure where they are in Win9x). Do this for the variables:
- include
- lib
- MSDevDir
- path
- 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.
- And from VC++6 itself (Tools->Options->Directories), replace
D:\Program Files\Microsoft Visual Studio
with D:\VCfor all the variables listed below.
- include files
- lib files
- executables
- source files
- 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
- You might also want to edit and relink all the VC++ tools in the Start Menu.
- 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