by Michael S. Kaplan, published on 2004/12/07 00:07 -05:00, original URI: http://blogs.msdn.com/b/michkap/archive/2004/12/06/276122.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 4 parts:
- How to build the C Runtime Library with MSLU
- How to build MFC 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.
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 with the appropriate install location.
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!
- I dynamically link to MFC in my Unicode app and want to use MSLU. Why do I need to worry about this?
Answer: The MFC Unicode version was originally meant for NT based operating systems only. With the advent of MSLU, it is now possible to write a single binary that runs on all platforms. If you have an existing Unicode MFC application or simply want to take advantage of the MFC in a Unicode app you will need to rebuild both the CRT and MFC. You need to build the CRT because MFC MUST be linked dynamically to the CRT.
- Doesn't the CRT already work on all platforms? Why do I need to rebuild the runtime library? I thought that the CRT was already Unicode ready - I've been using the string functions, e.g. wcscpy extensively already.
Answer: No, not all CRT functions work on all platforms. The CRT does run on Windows 9x platforms but there are certain wide versions of functions, e.g. wfullpath, that call wide Windows API functions, e.g. GetFullPathNameW, which are only available on NT based systems. So therefore if calling one of these functions, as MFC does, the function will fail. This affects MFC in a major way, the MFC startup code passes in an invalid hInstance, causing the DLL to fail.
- How can I build MFC and the runtime library? Isn't it complicated?
Answer: It's not very complicated. Microsoft in their wisdom included the source code to all of MFC and the runtime library, so it's possible to create new DLLs with minor modifications to the provided makefiles.
- Why doesn't Microsoft provide these rebuilt DLLs? Isn't it a pretty common scenario?
Answer: For one thing, MSLU came out long after MFC did, and the team that created MSLU is separate from the one that maintains MFC. I'm sure if you encourage them they might consider maintaining a special build of MFC and the CRT. It's all a matter of priorites, the MFC and CRT teams at Microsoft are really busy.
- Does MFC 7.0 Unicode version or CRT 7.0 (Visual Studio .NET) already link to MSLU? It just came out.
Answer: No it doesn't. MFC 7.0 was frozen/locked down in the summer of 2001, around the same time that MSLU was being released. See also the answer to question 4.
Before you start:
- Install Visual Studio 6 with SP5 including all necessary files.
The first thing you need to do is make sure that when you install Visual Studio 6 that you check on both the Unicode MFC versions, and the CRT source code. Both are turned off by default. After this install Visual Studio 6 Service Pack 5.
- 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\VC98. Find the MFC\SRC folder and the CRT\SRC folder.
- Install the Platform SDK and copy the unicows.lib file to your VC98\LIB folder.
How to build the CRT with MSLU
The first thing we want to do is make a backup of our VC98\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 VC98\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: MSVCRT.DLL, MSVCP60.DLL, and MSVCIRT.DLL. Since we are building both debug and release builds it makes a total of six DLLs we need to build.
There is a provided batch file bldwin95.bat that will build the all CRT DLLs but the makefile does not seem to be there. The Makefile IS provided, but it a hidden manner. The three files are ext_mkf, ext_mkf.inc, and ext_mkf.sub. If you copy these to makefile, makefile.inc, and makefile.sub then the batch file will work, i.e.copy ext_mkf Makefile
copy ext_mkf.inc Makefile.inc
copy ext_mkf.sub Makefile.sub
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:MSLURT.DLL MSLURTD.DLL MSLUP60.DLL MSLUP60D.DLL MSLUIRT.DLL MSLUIRTD.DLL
Warning: 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 MSLURT.RC
copy SAMPLE_I.RC MSLUIRT.RC
copy SAMPLE_P.RC MSLUP60.RC
copy SAMPLE_I.DEF MSLUIRT.DEF
copy SAMPLD_I.DEF MSLUIRTD.DEF
copy SAMPLE_P.DEF MSLUP60.DEF
copy SAMPLD_P.DEF MSLUP60D.DEF
copy Intel\_SAMPLE_.DEF Intel\MSLURT.DEF
copy Intel\_SAMPLD_.DEF Intel\MSLURTD.DEF
Next 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=MSLURT
- The path to the Visual Studio folder needs to be changed (the default is MSDEV)V6TOOLS=C:\Program Files\Microsoft Visual Studio\VC98
- Some of the commands need to be surrounded in quotes because of the location of the VC98 folder has spaces in the path.
line 331 change to:RC_INCS="-I$(V6TOOLS)\include"
line 1728, 1770, 1810, 1853, 1898, 1941 change to:"$(V6TOOLS)\include\winver.h" \
- We want to fix the PDB file creation
line 381-383 change to:RELEASE_DLL_DBG_PDB = $(PDBDIR_CPU)\$(DEBUG_DLL_NAME).pdb
RELEASE_DLLCPP_DBG_PDB = $(PDBDIR_CPU)\$(DEBUG_DLLCPP_NAME).pdb
RELEASE_DLLIOS_DBG_PDB = $(PDBDIR_CPU)\$(DEBUG_DLLIOS_NAME).pdb
line 474 change to:$(CRT_RELDIR) $(RELDIR_CPU) :
line 987 change to:xdll : $(OBJROOT) $(OBJCPUDIR) $(OBJDIR_DLL_DBG) $(RELDIR_CPU) xothers \
after line 1746 add:-pdb:$(RELEASE_DLL_PDB)
after line 1789 add:-pdb:$(RELEASE_DLLCPP_PDB)
after line 1829 add:-pdb:$(RELEASE_DLLIOS_PDB)
- We want to link to unicows.lib before any other lib files.
line 1750, 1794, 1835, 1878, 1925, 1969 change 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.lib
Once we make these changes, we are ready to build the DLLs. It's simple - just do the following from a command prompt in the C:\SRC folder:set V6TOOLS=C:\Program Files\Microsoft Visual Studio\VC98
"C:\Program Files\Microsoft Visual Studio\VC98\BIN\VCVARS32"
Once 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 MSLURT.LIB MSVCRT.LIB
copy MSLURTD.LIB MSVCRTD.LIB
copy MSLUP60.LIB MSVCPRT.LIB
copy MSLUP60D.LIB MSVCPRTD.LIB
copy MSLUIRT.LIB MSVCIRT.LIB
copy MSLUIRTD.LIB MSVCIRTD.LIB
The reason we do this is so we can link our existing apps (and build MFC) without having to muck around with 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 VC98\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 Unicode version with MSLU
First we will make a backup of the following folders (and all subfolders of): VC98\MFC\LIB, and VC98\MFC\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 actually 5 different DLLs:
- MFC42U.DLL (Unicode Release)
- MFC42UD.DLL (Unicode Debug)
- MFCN42UD.DLL (Unicode Debug - Network classes)
- MFCO42UD.DLL (Unicode Debug - OLE classes)
- MFCD42UD.DLL (Unicode Debug - Database classes)
Notice that the release version is a single DLL whereas the debug version is split up into 4 different DLLs. Presumably, this was done to reduce load times when debugging. This makes it a little less straightforward that it should be.
To build MFC, there are 4 provided Makefiles in the VC98\MFC\SRC folder named:
The MFCDLL.MAK builds the first two DLLs, and each of the others builds the rest.
First, we will change each of the 4 .MAK files to link to Unicows.lib. In each file, after the line that states:link @<<
insert the following two 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.lib
They 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 numbers to insert the above two lines after are:
- MFCDLL.MAK - line 206
- MFCNET.MAK - line 134
- MFCOLE.MAK - line 134
- MFCDB.MAK - line 140
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:
We are going over the 8.3 naming convention here, but only for some of the debug builds which we will not be shipping, so we should be fine.
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 38,39, 46, and 47, i.e.#define MFC42_DLL "MFC42LUD.DLL"
#define MFCO42_DLL "MFCO42LUD.DLL"
#define MFC42_DLL "MFC42LU.DLL"
#define MFCO42_DLL "MFCO42LU.DLL"
In DLLNET.CPP, change lines 37 and 43, i.e.#define MFC42_DLL "MFC42LUD.DLL"
#define MFC42_DLL "MFC42LU.DLL"
In DLLOLE.CPP, change lines 38 and 44, i.e.#define MFC42_DLL "MFC42LUD.DLL"
#define MFC42_DLL "MFC42LU.DLL"
Now we need to change the hard-coded reference to the name of the CRT. Since we changed it when rebuilt the CRT, we need to change the following file: DLLINIT.CPP.
In DLLINIT.CPP, change lines 371 and 373, i.e.#define MSVCRT_DLL "MSLURTD.DLL"
#define MSVCRT_DLL "MSLURT.DLL"
While we're in DLLINIT.CPP we need to get rid of the block that prevents loading of MFC (if we haven't already done so). Change the line 391 from #ifdef _UNICODE to #if 0 (this will prevent that piece of code from being included). For more info on this issue and other MFC/MSLU issues, click here.
Since we renamed the DLLs, we need to rename the DEF files that are linked to the DLLs. They are located in the VC98\MFC\SRC\INTEL folder. i.e.copy MFC42U.DEF MFC42LU.DEF
copy MFC42UD.DEF MFC42LUD.DEF
copy MFCN42UD.DEF MFCN42LUD.DEF
copy MFCO42UD.DEF MFCO42LUD.DEF
copy MFCD42UD.DEF MFCD42LUD.DEF
We need to open each of the above new DEF files up in notepad and change the LIBRARY line to match the name of the DEF file.
Now we're ready to build the versions of MFC:
Create a new batch file called buildmfc.bat in the MFC\SRC folder with the following content:nmake -f mfcdll.mak libname=MFC42L DEBUG=0 UNICODE=1 /a
nmake -f mfcdll.mak libname=MFC42L DEBUG=1 UNICODE=1 /a
nmake -f mfcdb.mak libname=MFCD42L DEBUG=1 UNICODE=1 /a
nmake -f mfcnet.mak libname=MFCN42L DEBUG=1 UNICODE=1 /a
nmake -f mfcole.mak libname=MFCO42L DEBUG=1 UNICODE=1 /a
Above we are building a release and debug versions of the main DLL (MFC42LU(D).DLL), and debug versions of the rest.
Run the batch file, and then you will be done. If you need to rebuild any time in the future you now have a convenient batch file to do so. The DLL files will be created in the VC98\MFC\SRC folder. The LIB and PDB files will be created in the MFC\LIB folder.
After the building is done, we need to copy the created LIBs in the VC98\MFC\LIB folder back to their original names (overwriting what's there) so that any of our apps that we link will use the new DLLs. i.e.copy MFC42LU.LIB MFC42U.LIB
copy MFC42LUD.LIB MFC42UD.LIB
copy MFCN42LUD.LIB MFCN42UD.LIB
copy MFCO42LUD.LIB MFCO42UD.LIB
copy MFCD42LUD.LIB MFCD42UD.LIB
The 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.
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 VC98\LIB and VC98\MFC\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.
JUAN F TREJO on 11 Oct 2009 5:49 AM:
I'M STUDENT IN COMPUTER SCIENCE BUT DO NOT LIKE THE PROGRAM
THAT WRITE AT THE COLLEGE.
2006/04/15 MFC/CRT build instructions tweaklets
2006/03/26 Non-default paths and instructions....
go to newer or older post, or back to index or month or day