by Michael S. Kaplan, published on 2004/12/07 00:09 -05:00, original URI: http://blogs.msdn.com/b/michkap/archive/2004/12/06/276123.aspx
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 7.0 with MSLU
- How to build MFC 7.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 .NET with the appropriate install location.
Also, special thanks to Tim Dowty of Music Match for the great text of step #5 under the MFC build!
Before you start:
- Install Visual Studio .NET including all necessary files.
The first thing you need to do is make sure that when you install Visual Studio .NET that you make sure both the Unicode MFC version and the CRT source code are installed.
- 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 .NET\VC7. Find the ATLMFC\SRC folder and the CRT\SRC folder.
- Install the Platform SDK and copy the latest unicows.lib file to your VC7\PlatformSDK\LIB folder. Since VC7 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 7.0 with MSLU
The first thing we want to do is make a backup of our VC7\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 VC7\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.
When building the CRT we are actually building three DLLs: MSVCR70.DLL, MSVCP70.DLL, and MSVCI70.DLL. Since we are building both debug and release builds it makes a total of six DLLs we need to build.
In the SRC folder, there is a provided batch file bldwin9x.bat 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 three 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:
MSLUR70.DLL MSLUR70D.DLL MSLUP70.DLL MSLUP70D.DLL MSLUI70.DLL MSLUI70D.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.
The default names provided in the makefile are _SAMPLE_, SAMPLE_I, and SAMPLE_P. There are associated RC and DEF files for each of these names, so we need to copy them to the new names, i.e.
copy _SAMPLE_.RC MSLUR70.RC
copy SAMPLE_I.RC MSLUI70.RC
copy SAMPLE_P.RC MSLUP70.RC
copy SAMPLE_I.DEF MSLUI70.DEF
copy SAMPLD_I.DEF MSLUI70D.DEF
copy SAMPLE_P.DEF MSLUP70.DEF
copy SAMPLD_P.DEF MSLUP70D.DEF
copy Intel\_SAMPLE_.DEF Intel\MSLUR70.DEF
copy Intel\_SAMPLD_.DEF Intel\MSLUR70D.DEFNext we need to change the LIBRARY name in each of the above DEF files to match the name of the DEF file. Open up each file in notepad to make the change.
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=MSLUR70
RETAIL_LIB_NAME=MSLUR70
RETAIL_DLLCPP_NAME=MSLUP70
RETAIL_LIBCPP_NAME=MSLUP70
RETAIL_DLLIOS_NAME=MSLUI70
RETAIL_LIBIOS_NAME=MSLUI70
DEBUG_DLL_NAME=MSLUR70D
DEBUG_LIB_NAME=MSLUR70D
DEBUG_DLLCPP_NAME=MSLUP70D
DEBUG_LIBCPP_NAME=MSLUP70D
DEBUG_DLLIOS_NAME=MSLUI70D
DEBUG_LIBIOS_NAME=MSLUI70D- The VCTOOLS path should be changed to point to the path where you installed Visual Studio .NET, e.g.
VCTOOLS=C:\Program Files\Microsoft Visual Studio .NET\VC7
- We want to link to unicows.lib before any other lib files.
line 1527, 1580, 1630, 1678, 1728, 1775 change kernel32.lib to:
unicows.lib kernel32.lib advapi32.lib user32.lib gdi32.lib shell32.lib comdlg32.lib version.lib mpr.lib rasapi32.lib winmm.lib winspool.lib vfw32.lib oleacc.lib oledlg.libOnce we make these changes, we are ready to build the DLLs. It's simple – launch a Visual Studio .NET command prompt (start menu-programs-Visual Studio .NET – Visual Studio .net tools – Visual Studio command prompt) and then go to the C:\SRC folder and type:
set VCTOOLS=C:\Program Files\Microsoft Visual Studio .NET\VC7
BLDWIN9XOnce the DLLs finish building they will be in a subfolder called BUILD\INTEL. The Libs, PDBs, and Maps are also in that folder.
- Now we've got 6 libs (3 debug, 3 release) we can link to. Let's copy those new libs back to the original names of the libs, e.g.
copy MSLUR70.LIB MSVCRT.LIB
copy MSLUR70D.LIB MSVCRTD.LIB
copy MSLUP70.LIB MSVCPRT.LIB
copy MSLUP70D.LIB MSVCPRTD.LIB
copy MSLUI70.LIB MSVCIRT.LIB
copy MSLUI70D.LIB MSVCIRTD.LIBThe reason we do this is so we can link our existing apps (and build MFC) without having to change the libraries that they link to. The Libs still point to the newly named DLLs, even though they don't share the same names as the new ones anymore.
- Now copy the 6 MSVC libs to the VC7\Lib folder (overwriting the existing ones)
The CRT build is now done.
Before proceeding any further we need to close the command prompt that we used to build the CRT because it created certain environment variables that will cause compile errors in the next step, building the Unicode version of MFC.
Building MFC 7.0 Unicode version with MSLU
First we will make a backup of the following folders (and all subfolders of): VC7\ATLMFC\LIB, and VC7\ATLMFC\SRC so we can restore them later if necessary.
Building the Unicode version of MFC is slightly easier than building the CRT. The Unicode version of MFC is 2 different DLLs (unlike the 5 different DLLs that we had to worry about when building MFC 6.0):
- MFC70U.DLL (Unicode Release)
- MFC70UD.DLL (Unicode Debug)
There is also a static component to even a DLL build of MFC, named as follows:
- MFCS70U.LIB (Unicode Release – static library – deprecated classes)
- MFCS70UD.LIB (Unicode Debug – static library – deprecated classes)
To build MFC, there is one master Makefile in the VC7\ATLMFC folder named:
- ATLMFC.MAK
And there is one Makefile in the VC7\ATLMFC\SRC\MFC folder named
- MFCDLL.MAK
- First, we will change the MFCDLL.MAK file to link to Unicows.lib. In each file, after the line that states:
link @<<insert the following lines:
/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
unicows.lib kernel32.lib advapi32.lib user32.lib gdi32.lib shell32.lib comdlg32.lib version.lib mpr.lib rasapi32.lib winmm.lib winspool.lib vfw32.lib oleacc.lib oledlg.libThey must go in that position, if we don't do this then a library reference will be included causing unicows.lib to be linked after kernel32.lib (which will then cause the unicows.dll load to fail). Other DLLs in the wrong order will simply cause APIs in those specific DLLs to not be called.
The line number to insert the above two lines after is line 287.
- 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:
- MFC70LU.DLL
- MFC70LUD.DLL
- Now we need to change the following three files to match our naming. MFC does a LoadLibrary and has hard-coded each of the above DLL names to our new names in the following files: DLLDB.CPP, DLLNET.CPP, and DLLOLE.CPP.
In DLLDB.CPP, change lines 34, and 42, i.e.
#define MFC70_DLL "MFC70LUD.DLL"
#define MFC70_DLL "MFC70LU.DLL"In DLLNET.CPP, change lines 33 and 39, i.e.
#define MFC70_DLL "MFC70LUD.DLL"
#define MFC70_DLL "MFC70LU.DLL"In DLLOLE.CPP, change lines 34 and 40, i.e.
#define MFC70_DLL "MFC70LUD.DLL"
#define MFC70_DLL "MFC70LU.DLL"- Now we're ready to build the versions of MFC:
From a Visual Studio .NET 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=MFC70LThis will build all MFC libraries, not just the Unicode DLLs, but it will save us the effort of figuring out how to use the MFCDLL.MAK makefile.
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 VC7\ATLMFC\SRC\MFC\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 at line 425 of …vc7\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 mfc70 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 …\Vc7\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 mfc70ud.lib > mfc70ud.lib.lst
lib /LIST mfc70u.lib > mfc70u.lib.lstYou should now have the two .lib.lst files that each contain a list of library objects, one per line.
Now, we will create a perl script to build a 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 here).
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 = "mfc70ud.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 = "mfc70u.lib";also change $outDir as shown:
$outDir = “_aliasRecords";Save the edited text as BuildAliasExtractBatch.pl.
Now run the two perl scripts as follows from the command prompt:
perl BuildAliasExtractBatchD.pl mfc70ud.lib.lst > BuildAliasExtractD.bat
perl BuildAliasExtractBatch.pl mfc70u.lib.lst > BuildAliasExtract.batAt this point you have two batch files, one of which will extract the alias records from the debug library and one 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.
At the completion of this step, you will have two new directories under …\vc7\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 = "mfc70udAlias.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 = "mfc70uAlias.lib";
$aliasDir = "_aliasRecords";and save the file as CreateAliasLib.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 mfc70ud.lib.lst > mfc70udAlias.rsp
perl CreateAliasLib.pl mfc70u.lib.lst > mfc70uAlias.rspWith the response files made, we now use them with lib.exe to create the alias libraries:
lib @mfc70udAlias.rsp
lib @mfc70uAlias.rspAt the completion of this step you will have two new libraries: mfc70udAlias.lib and mfc70uAlias.lib. They will each contain their respective alias records.
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.
lib /OUT:mfc70ud.lib .\Intel\MFC70LUD.lib mfc70udAlias.lib
lib /OUT:mfc70u.lib .\Intel\MFC70LU.lib mfc70uAlias.lib- After the building is done, we need to copy the rest of the created LIBs in the VC7\ATLMFC\LIB\INTEL folder back to their original names in VC7\ATLMFC\LIB (overwriting what's there) so that any of our apps that we link will use the new DLLs. i.e.
copy MFC70LSU.LIB MFCS70U.LIB
copy MFC70LSUD.LIB MFCS70UD.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 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.
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.
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 VC7\LIB and VC7\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.
referenced by
2007/12/24 The 9.0 instructions for building MFC and the CRT to use MSLU?
2006/04/15 MFC/CRT build instructions tweaklets
2006/03/26 Non-default paths and instructions....
2004/12/07 Rebuilding the CRT and MFC for Unicode on Win9x....