http://blogs.msdn.com/b/michkap/archive/2005/10/07/478235.aspx
How to build the 8.0 MFC and CRT DLLs with MSLU
Updated for RTM of Visual Studio 2005
Updated for SP1 of Visual Studio 2005
How to build the 8.0 MFC and the CRT DLLs with MSLU
The MSDN documentation generally recommends that you use the static versions of libraries like the C Runtime (CRT) or the Microsoft Foundation Classes (MFC). The reason for this is that the DLL versions have not been built for MSLU and thus have no knowledge of the need to use the Microsoft Layer for Unicode for Unicode APIs. However, many complex applications really need to use the DLL versions of these libraries. If you are the developer for one of these applications, you will need to rebuild them so they link with Unicows.lib. The following is a small guide on how to perform this task.
This document is divided into 3 parts
- How to build the C Runtime Library 8.0 with MSLU
- How to build MFC 8.0 with MSLU
- Switching between non MSLU builds and MSLU builds
The fine print
For more info on rebuilding MFC extension DLLs, see TN033: DLL Version of MFC , specifically the section entitled "Building the MFC DLL" towards the bottom of the article. Our steps here seem a lot nicer. :-)
All of these steps were used to build DLLs that were subsequently tested on Win98 SE. They are expected to work on all platforms.
Special thanks are owed to Ted W. for taking the time to do what we all knew was theoretically possible and making it technically possible for everyone. This document is mostly due to his efforts. Thanks, Ted!
In all instructions below, the assumption is a default install path and an en-US copy of Windows; if either is not the case, make sure you replace paths such as C:/Program Files/Microsoft Visual Studio 8 with the appropriate install location.
Also, special thanks to Tim Dowty of Music Match for the great text of step #4 under the MFC build!
Before you start:
- Install Visual Studio 2005 including all necessary files.
The first thing you need to do is make sure that when you install Visual Studio 2005 that you make sure both the Unicode MFC version and the CRT source code are installed. This is now done by default so unless you made changes, you should have both.
- Identify the folders and files you will be modifying.
If you installed to the default locations, all of the files we need to change are contained in the tree /Program Files/Microsoft Visual Studio 8/VC. Find the ATLMFC/SRC folder and the CRT/SRC folder.
- Install the Platform SDK and copy the latest unicows.lib file to your VC/PlatformSDK/LIB folder. Since VC8 comes with a unicows.lib, this step is optional, although it is good to be on the latest unicows.lib from the most recent Platform SDK.
How to build the CRT 8.0 with MSLU
The first thing we want to do is make a backup of our VC/Lib folder. We will be replacing files in it, so if we need to go back (or switch between MSLU and non-MSLU version of the CRT) we can always do that.
Secondly, let's copy the VC/CRT/SRC folder to a comfortable place so we can change it and build from it. For example, we'll copy it to the root of C: so we have a folder called C:/SRC, available for quick access from the command line.
Special note for Visual Studio 2005 Service Pack 1 users:The Visual Studio 2005 Service Pack 1 C Runtime Library source code shipped with some minor problems that prevent it from being built successfully. If you've installed Service Pack 1, please follow these steps before proceeding with the rest of the CRT rebuild instructions.
1) In the SRC folder, two files need minor changes. Open up the following two files in notepad and make these changes:a) in the MAKEFILE
On lines 302, 303, 304, 307, 308 and 309, remove the -wx option
b) In the MAKEFILE.SUB
On line 103 remove the -wx option2) A file named unhandld.obj was inadvertently left out of the CRT source distribution. To recover this file, we will extract it from the eh.lib libraryFrom a Visual Studio 2005 Command Prompt, do the following:C:
CD /SRC
for %i in (dll mt xdll xmt) do pushd intel/%i_lib && lib /extract:../build/intel/%i_obj/unhandld.obj eh.lib && popdWhen building the CRT we are actually building three release DLLs: MSVCR80.DLL, MSVCP80.DLL MSVCM80.DLL. Since we are building both debug and release builds it makes a total of six DLLs we need to build (the corresponding debug versions are MSVCR80D.DLL MSVCP80D.DLL MSVCM80D.DLL
In the SRC folder, there is a provided batch file bldnt.cmd that will build the all CRT DLLs and an associated makefile .
Now we will open up the makefile in notepad. At the top of makefile there is a section that controls the naming of the DLLs to build. For this purpose we will use the name MSLU as a prefix to all of the DLLs instead of the standard name MSVC. So the six names of the DLLs we will create are:
MSLUR80.DLL MSLUR80D.DLL MSLUP80.DLL MSLUP80D.DLL MSLUM80.DLL MSLUM80D.DLLWarning: Since most people who follow these steps will probably use the exact names given here, please be sure to keep these versions of the DLLs in your own private directory when you use them.
For the MSLUM80.DLL and MSLUM80D.DLL (new DLLs used for managed .NET programming) we actually have two different pairs of libraries, one pair for compiling using the command line compiler option /clr , and one for /clr:pure . There are also two extra pairs of libraries used for partial trust programming. The names of all the libraries for this purpose are:
MSVCMRT.LIB MSVCMRTD.LIB MSVCURT.LIB MSVCURTD.LIB PTRUSTM.LIB PTRUSTMD.LIB PTRUSTU.LIB PTRUSTUD.LIBcopy _SAMPLE_.RC MSLUR80.RC
copy SAMPLE_P.RC MSLUP80.RC
copy SAMPLE_M.RC MSLUM80.RC
copy SAMPLE_P.DEF MSLUP80.DEF
copy SAMPLD_P.DEF MSLUP80D.DEF
copy SAMPLE_M.DEF MSLUM80.DEF
copy SAMPLD_M.DEF MSLUM80D.DEF
copy SAMPLE_U.DEF MSLUU80.DEF
copy SAMPLD_U.DEF MSLUU80D.DEF
copy Intel/_SAMPLE_.DEF Intel/MSLUR80.DEF
copy Intel/_SAMPLD_.DEF Intel/MSLUR80D.DEF
The provided makefile needs some minor changes to get it to work properly and link with Unicows.lib.
- Change the top block of defines to the following:
RETAIL_DLL_NAME=MSLUR80
RETAIL_LIB_NAME=MSLUR80
RETAIL_DLLCPP_NAME=MSLUP80
RETAIL_LIBCPP_NAME=MSLUP80
RETAIL_DLLMIXED_NAME=MSLUM80
RETAIL_LIBMIXED_NAME=MSLUM80
RETAIL_LIBPURE_NAME=MSLUU80
RETAIL_PT_LIBMIXED_NAME=MSLUPTM
RETAIL_PT_LIBPURE_NAME=MSLUPTU
DEBUG_DLL_NAME=MSLUR80D
DEBUG_LIB_NAME=MSLUR80D
DEBUG_DLLCPP_NAME=MSLUP80D
DEBUG_LIBCPP_NAME=MSLUP80D
DEBUG_DLLMIXED_NAME=MSLUM80D
DEBUG_LIBMIXED_NAME=MSLUM80D
DEBUG_LIBPURE_NAME=MSLUU80D
DEBUG_PT_LIBMIXED_NAME=MSLUPTMD
DEBUG_PT_LIBPURE_NAME=MSLUPTUD
RC_NAME=MSLUR80
RCCPP_NAME=MSLUP80
RCMIXED_NAME=MSLUM80- The VCTOOLS path should be changed to point to the path where you installed Visual Studio 2005, e.g.
- We want to link to unicows.lib before any other lib files.
On lines 1779, 1841, 1905, 1940, 2037, 2105, 2139, 2230 change kernel32.lib to:
unicows.lib kernel32.lib set VCTOOLS=C:/Program Files/Microsoft Visual Studio 8/VC
BLDNT- Now we've got 12 libs (6 debug, 6 release) we can link to. Let's copy those new libs back to the original names of the libs, e.g.
copy MSLUPTM.LIB PTRUSTM.LIB
copy MSLUPTMD.LIB PTRUSTMD.LIB
copy MSLUPTU.LIB PTRUSTU.LIB
copy MSLUPTUD.LIB PTRUSTUD.LIB
copy MSLUR80.LIB MSVCRT.LIB
copy MSLUR80D.LIB MSVCRTD.LIB
copy MSLUP80.LIB MSVCPRT.LIB
copy MSLUP80D.LIB MSVCPRTD.LIB
copy MSLUM80.LIB MSVCMRT.LIB
copy MSLUM80D.LIB MSVCMRTD.LIB
copy MSLUU80.LIB MSVCURT.LIB
copy MSLUU80D.LIB MSVCURTD.LIB- Now copy the 12 MSVC libs to the VC/Lib folder (overwriting the existing ones)
- Repeat steps 4 through 6. This step is necessary to rebuild MSLUP80(D).DLL and static libraries again so they link to our newly created MSVCRT(D).LIB (which points to our new MSLUR80(D).DLL).
Building MFC 8.0 Unicode version with MSLU
The Unicode version of MFC is 4 different DLLs:
- MFC80U.DLL (Unicode Release)
- MFC80UD.DLL (Unicode Debug)
- MFCM80U.DLL (Mixed mode/Managed Unicode Release)
- MFCM80UD.DLL (Mixed mode/Managed Unicode Debug)
There is also a static component to even a DLL build of MFC, named as follows:
- MFCS80U.LIB (Unicode Release – static library – deprecated classes)
- MFCS80UD.LIB (Unicode Debug – static library – deprecated classes)
To build MFC, there is one master Makefile in the VC/ATLMFC folder named:
There is one Makefile in the VC/ATLMFC/SRC/MFC folder named
Finally, there is one Makefile in the VC/ATLMFC/SRC/MFCM folder named
- First, we will change the MFCDLL.MAK and MFCMDLL.MAK files to link to Unicows.lib. In both files, after the lines that state:
link @<<insert the following line:
/nod:kernel32.lib /nod:advapi32.lib /nod:user32.lib /nod:gdi32.lib /nod:shell32.lib /nod:comdlg32.lib /nod:version.lib /nod:mpr.lib /nod:rasapi32.lib /nod:winmm.lib /nod:winspool.lib /nod:vfw32.lib /nod:secur32.lib /nod:oleacc.lib /nod:oledlg.lib /nod:sensapi.lib
Also, on line 180 of MFCDLL.MAK, change the line:
LIBS=$(CRTDLL) kernel32.lib gdi32.lib msimg32.lib user32.lib uuid.lib daouuid.lib htmlhelp.lib shlwapi.lib $(PROFLIB)
to
LIBS=$(CRTDLL) unicows.lib kernel32.lib gdi32.lib msimg32.lib user32.lib uuid.lib daouuid.lib htmlhelp.lib shlwapi.lib $(PROFLIB)
And finally, on line 215 in MFCMDLL.MAK
change
LIBS=$(CRTDLL) $(CRTMDLL) mscoree.lib kernel32.lib gdi32.lib msimg32.lib user32.lib /
to
LIBS=$(CRTDLL) $(CRTMDLL) mscoree.lib unicows.lib kernel32.lib gdi32.lib msimg32.lib user32.lib /
here ).
- Now, we will decide what to name our new DLL. We do not want to use the standard name(s) for the same reasons we did not use the standard names for the CRT. So we will come up with a simple naming convention: we'll add an "L" to the name. So the new names will be:
- MFC80LU.DLL
- MFC80LUD.DLL
- MFCM80LU.DLL
- MFCM80LUD.DLL
- In the MFCM/Intel folder, modify the mfcm80u.def and mfcm80ud.def files to change the LIBRARY line to match the name you gave your DLLs in step 2
- Three subparts to this part:
- In the file:
/Program Files/Microsoft Visual Studio 8/VC/atlmfc/src/mfcm/wfrmcmd.cpp
comment out the following line:
#error Following information required to build private version
and change the line:
[assembly: AssemblyTitle("")];
to
[assembly: AssemblyTitle("mfcm80L")];
- And in the file:
/Program Files/Microsoft Visual Studio 8/VC/atlmfc/src/mfcm/interfaces/AssemblyInfo.cpp
comment out the following line
#error Following information required to build private version
and change the line:
[assembly: AssemblyTitle("")];
to
[assembly: AssemblyTitle("mfcm80Lifaces")];
- MFC 8.0 has a new wrapper class named CCommDlgWrapper that is responsible for dealing with isolation aware APIs. Unfortunately it uses GetProcAddress for every function in comdlg32.dll and ends up bypassing the versions in unicows.dll.
To fix this problem, in the file/Program Files/Microsoft Visual Studio 8/VC/atlmfc/include/afxcomctl32.hchange the line:
: CDllIsolationWrapperBase(_T("comdlg32.dll"))to:: CDllIsolationWrapperBase(
#ifdef _UNICODE
(GetVersion() & 0x80000000) ? _T("unicows.dll") :
#endif
_T("comdlg32.dll"))
- Now we're ready to build the versions of MFC:
From a Visual Studio 2005 Command Prompt, create a new batch file called buildmfc.bat in the ATLMFC/SRC folder with the following content:
nmake -f atlmfc.mak MFC libname=MFC80LThis will build all MFC libraries, not just the Unicode DLLs, but it will save us the effort of figuring out how to use the other makefiles.
Run the batch file. If you need to rebuild any time in the future you now have a convenient batch file to do so. The DLL and PDB files will be created in the VC/ATLMFC/SRC/MFC(M)/INTEL folder. The LIB files will be created in the ATLMFC/LIB/INTEL folder.
There is one crucial step missing from the supplied MFC makefiles. If you take a look inside …VC/mfc/makefile, you’ll see that one of the options passed to the compiler is /Zc:wchar_t, which causes wchar_t to become an implicit type. This may be what you want, but if the application you’re linking the lib to wasn’t compiled with this same option (and -- therefore -- has wchar_t #defined to unsigned short), you will get unresolved externals when you link. Your program is looking for function signatures with unsigned shorts in them, but the lib only exports wchar_t in the function signatures.
You could remove the /Zc:wchar_t from the makefile, but this solution isn’t universal; it would still prevent linking with programs compiled with the /Zc:wchar_t switch.
A better solution is to do what Microsoft did in the original mfc80 libraries: include alias records in the library so that you can link both implicit wchar_t and unsigned short programs. Alias records allow a library to export multiple function signatures that resolve down to the same object code.
So how do you add alias records to your newly-built MFC libraries?
For both the debug and release MFC library libraries you need to do the following:
a) Extract all of the alias records from the corresponding retail MFC library
b) Create a new library comprising only these alias records
c) Merge your new Unicows-compliant MFC library with the associated alias-record library
Step A)
This one requires a small detour because lib.exe only allows you to extract one object at a time. We want to automate this step by creating a batch file to do all of the extractions.
First, get a command prompt and make …/VC/atlmfc/lib your current directory. Next, create a list of all of the alias records in both debug and release MFC libs using the following two command lines:
lib /LIST mfc80ud.lib > mfc80ud.lib.lst
lib /LIST mfc80u.lib > mfc80u.lib.lst
lib /LIST mfcm80ud.lib > mfcm80ud.lib.lst
lib /LIST mfcm80u.lib > mfcm80u.lib.lst
You should now have the 4 .lib.lst files that each contain a list of library objects, one per line.
Now, we will create a perl script to build 2 pair of batch files from the .lib.lst files (if you don’t already have perl, it’s freely available from several sources. You can find Perl
Start up a text editor and enter the following text:
#!/usr/bin/perl
# builds a batch file to extract all alias records
# in the input file (input file created with lib.exe /LIST)
$targetLib = "mfc80ud.lib"; $outDir = "_aliasRecordsD";
print "md .//$outDir/n";
while (<>) {
# find alias record name
if (/_alias[0-9]+/.obj/) { chop;
print "LIB /EXTRACT:$_ /OUT:.//$outDir//$_ $targetLib/n"; } }Save the text as BuildAliasExtractBatchD.pl.
Now edit the text so that the $targetLib variable is changed as follows:
$targetLib = "mfc80u.lib";also change $outDir as shown:
$outDir = “_aliasRecords";Save the edited text as BuildAliasExtractBatch.pl.
Repeat the above for both MFCM libraries, i.e.
targetLib = "mfcm80ud.lib";
also change $outDir as shown:
$outDir = “_aliasRecordsMD";Save the edited text as BuildAliasExtractBatchMD.pl.
targetLib = "mfcm80u.lib";also change $outDir as shown:
$outDir = “_aliasRecordsM";Save the edited text as BuildAliasExtractBatchM.pl.
Now run the 4 perl scripts as follows from the command prompt:
perl BuildAliasExtractBatchD.pl mfc80ud.lib.lst > BuildAliasExtractD.bat
perl BuildAliasExtractBatch.pl mfc80u.lib.lst > BuildAliasExtract.bat
perl BuildAliasExtractBatchMD.pl mfcm80ud.lib.lst > BuildAliasExtractMD.bat
perl BuildAliasExtractBatchM.pl mfcm80u.lib.lst > BuildAliasExtractM.batAt this point you have four batch files, two of which will extract the alias records from the debug library and two that will extract from the release library.
To complete step a) all that’s left is to run the batch files. Note that there are about 2000 alias records in each MFC library, and extracting them one by one is a slow process; each library extraction took about 4 hours on a fast PC. The MFCM libraries only have a few alias records so those library extractions will go quickly.
At the completion of this step, you will have four new directories under …/VC/atlmfc/lib each of which contains extracted alias records. Each extracted alias record is a file with a name of the form _alias*.obj where * is one to four decimal digits.
Step B)
For Step b), we want to create a new library from the extracted records. Fortunately, this can be done in two simple steps; in contrast to Step a) we can use a response file with lib.exe to simplify our operation.
First, we create a pair of perl scripts that will build the response files.
Use your favorite text editor to enter the following text:
#!/usr/bin/perl
# builds a response file for lib.exe to build a library of
# alias records. (input file created with lib.exe /LIST)
$outLib = "mfc80udAlias.lib";
$aliasDir = "_aliasRecordsD";
print "/OUT:$outLib";
while (<>) {
# find alias record name
if (/_alias[0-9]+/.obj/) { chop;
print " .//$aliasDir//$_"; } }Save the file as CreateAliasLibD.pl.
Now edit the variable declarations so they read:
$outLib = "mfc80uAlias.lib";
$aliasDir = "_aliasRecords";and save the file as CreateAliasLib.pl
Repeat the above for the MFCM libraires, i.e.
$outLib = "mfcm80uAlias.lib";
$aliasDir = "_aliasRecordsM";and save the file as CreateAliasLibM.pl
and again
$outLib = "mfcm80udAlias.lib";
$aliasDir = "_aliasRecordsMD";and save the file as CreateAliasLibMD.pl
Run the perl scripts from the command line. Note that we reuse the .lib.lst files we created in Step A) as input here:
perl CreateAliasLibD.pl mfc80ud.lib.lst > mfc80udAlias.rsp
perl CreateAliasLib.pl mfc80u.lib.lst > mfc80uAlias.rsp
perl CreateAliasLibMD.pl mfcm80ud.lib.lst > mfcm80udAlias.rsp
perl CreateAliasLibM.pl mfcm80u.lib.lst > mfcm80uAlias.rspWith the response files made, we now use them with lib.exe to create the alias libraries:
lib @mfc80udAlias.rsp
lib @mfc80uAlias.rsp
lib @mfcm80udAlias.rsp
lib @mfcm80uAlias.rspAt the completion of this step you will have 4 new libraries: mfc80udAlias.lib, mfc80uAlias.lib, mfcm80udAlias.lib, and mfcm80uAlias.lib. They will each contain their respective alias records. We'll copy these 4 libraries to the VC/ATLMFC/LIB folder.
Step C)
In this step, we simply merge our custom-built MFC libraries with the alias libraries we just made. While we’re at it, we’ll also rename the libraries so they’ll replace the original libraries. Note that we get our custom-built libraries directly from their output locations. In VC/ATLMFC/LIB:
lib /OUT:mfc80ud.lib ./Intel/MFC80LUD.lib mfc80udAlias.lib
lib /OUT:mfc80u.lib ./Intel/MFC80LU.lib mfc80uAlias.lib
lib /OUT:mfcm80ud.lib ./Intel/MFCM80LUD.lib mfcm80udAlias.lib
lib /OUT:mfcm80u.lib ./Intel/MFCM80LU.lib mfcm80uAlias.lib
- After the building is done, we need to copy the rest of the created LIBs in the VC/ATLMFC/LIB/INTEL folder back to their original names in VC/ATLMFC/LIB (overwriting what's there) so that any of our apps that we link will use the new DLLs. i.e.
copy ./Intel/MFC80LSU.LIB MFCS80U.LIB
copy ./Intel/MFC80LSUD.LIB MFCS80UD.LIBThe reason we do this is the same as for the CRT: we don't have to worry about changing any linker options in our projects to link to the new version of MFC. We should also copy the PDB files back from the LIB/INTEL folder back to the LIB folder.
Now, Repeat steps 5, 6(c), and 7 above so any MFC DLLs that need to be linked to each other will pick up the new LIB references.
Now we're ready to do a test build of an application. Create a new SDI MFC application using the AppWizard, choose dynamic MFC, create a Unicode Debug and Release build, change the settings to link to unicows.lib, copy the newly created CRT and MFC DLLs to your DEBUG or RELEASE build folder(s) and then run the application. It should all work. Special note: to run the application on Windows XP and higher, you may have to remove the generated manifest file from your app because it refers to the standard built-in CRT and MFC libraries in the WinSxS folder instead of your rebuilt ones. In these instructions, I have not addressed how to install your own custom built DLLs into the WinSxS folder.
Use dependency walker to make sure that everything is getting linked properly and the proper DLLs are being loaded (run a profile in dependency walker). No references to the old names of the DLLs for both the CRT or MFC should be there. However, you may still see a dependency on MSVCRT.DLL. Unlike previous versions of the CRT, the 8.0 version of the CRT has a hard dependency on MSVCRT.DLL. Notice that there is one dependency to the function "getdrives" in MSVCRT.DLL. The reason for the hard dependency is to allow future operating system versions of MSVCRT.DLL to share some "critical state information" with MSVCR80(D).DLL. So do not be alarmed if you still see this dependency after rebuilding the CRT.
Switching between non MSLU builds and MSLU builds
Because we have done all of the above, any Unicode build on your machine will now link to MSLU. We may not want this necessarily, or we may want to link back to the original CRT and MFC DLLs. This is what we made the backups for. To restore the system, simply restore your VC/LIB and VC/ATLMFC/LIB folders. You could even make a simple batch file that copies older or newer versions of the LIBs back to the LIB folders depending on what you want to build. As we made changes to the makefiles and MFC source code, you may want to restore that as well.