Converting a project to Unicode: Part 8 (Fitting MSLU into the mix)

by Michael S. Kaplan, published on 2007/01/04 06:01 -05:00, original URI:

Previous posts in this series (including today's!):

(If you are just tuning in and want to start now you can grab the current source from here)

As I mentioned almost from the start, one of the big downsides to converting setup.exe to Unicode is that Win9x doesn't support Unicode.

Lucky for us we have the Microsoft Layer for Unicode on Windows 95, 98, and Me Systems, huh?

Anyway, just as regular reader Dean Harding suggested, a logical step at this point in the series is to add MSLU support to our project. :-)

Now since MSLU makes no sense in the ANSI build, we will start with the makefile.uni that was added yesterday in Part 7 that you can find in the source code download above:

# Copyright (c) Microsoft Corporation.  All Rights Reserved.
# Processor independent Unicode makefile,
# perhaps one day intended for Platform SDK
# Target only i386


!include <ntwin32.mak>

#include support for unicode
cflags   = $(cflags) -D_UNICODE -DUNICODE

!include <makefile> 

Now the key to supporting MSLU is making sure to add unicows.lib to the link list prior to all of the .LIB files that it uses (and incidentally prior to the ones that contain source code that use it ,though we do not have any of those in this case).

Some casual spluenking through ntwin32.mak indicates that it is the baselibs variable that includes the basic libraries like kernel32.dll. So all we have to do is add unicows.lib prior to the other lib files in baselibs and then MSLU has been integrated!

To verify this, you can use the following command to look at all of the functions that are imported by our setup.exe:

link -dump -imports WIN2000_DEBUG\setup.exe

(this command works a lot like dumpbin.exe does) 

As I have mentioned before talking about dumpbin, when a function that used to come from a regular OS lib starts coming from unicows.lib, it seems to disapear from the list of imported functions. It almost reminds me of that Douglas Adams story I quoted in When you think it couldn't get any harder, it gets easier, where you are using the fact that certain items are not where you expect them as proof of what is going on.

If you run that line with the baselibs line in makefile.uni and compare it to the results of when it isn't there, you will see the bulk of the "W" functions disappear. As if they fell out of the hole in the ship caused by the meteorite or something. :-) 

And there is one more interesting thing you might have to do here that the output indicates. We'll look at it really quickly to see what I am talking about (the interesting ones are marked in RED):

E:\setup.exe>link -dump -imports WIN2000_DEBUG\setup.exe

Microsoft (R) COFF/PE Dumper Version 8.00.50727.762
Copyright (C) Microsoft Corporation.  All rights reserved.

Dump of file WIN2000_DEBUG\setup.exe


  Section contains the following imports:

                451014 Import Address Table
                45F434 Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference

                  257 LoadResource
                  25C LocalFree
                  1FF GlobalFree
                  1F8 GlobalAlloc
                  380 VerifyVersionInfoW
                  37D VerSetConditionMask
                  142 GetCurrentProcess
                   3A CompareStringA
                  17F GetModuleHandleA
                  17D GetModuleFileNameA
                  1F3 GetWindowsDirectoryA
                  1C1 GetSystemDirectoryA
                  252 LoadLibraryA
                  229 InterlockedExchange
                  265 LockResource
                  15A GetExitCodeProcess
                  171 GetLastError
                   34 CloseHandle
                   EE FlushFileBuffers
                  388 VirtualQuery
                   53 CreateFileA
                  3CC lstrlenA
                  135 GetConsoleOutputCP
                  399 WriteConsoleA
                  337 SetStdHandle
                  1E2 GetTimeZoneInformation
                  133 GetConsoleMode
                  122 GetConsoleCP
                  31B SetFilePointer
                  223 InitializeCriticalSection
                  381 VirtualAlloc
                  328 SetLastError
                   F8 FreeLibrary
                  21A HeapReAlloc
                  244 LCMapStringA
                  2EE SetConsoleCtrlHandler
                  28D OutputDebugStringA
                   78 DebugBreak
                  2A3 QueryPerformanceCounter
                  1DF GetTickCount
                  146 GetCurrentThreadId
                  143 GetCurrentProcessId
                  1CA GetSystemTimeAsFileTime
                  35E TerminateProcess
                  36E UnhandledExceptionFilter
                  34A SetUnhandledExceptionFilter
                  216 HeapFree
                  1E9 GetVersionExA
                  210 HeapAlloc
                  1A3 GetProcessHeap
                  22C InterlockedIncrement
                  228 InterlockedDecrement
                  239 IsDebuggerPresent
                  2D7 RtlUnwind
                   FD GetACP
                  193 GetOEMCP
                  365 TlsGetValue
                  363 TlsAlloc
                  366 TlsSetValue
                  364 TlsFree
                  145 GetCurrentThread
                  220 HeapValidate
                  233 IsBadReadPtr
                  2A7 RaiseException
                   81 DeleteCriticalSection
                   98 EnterCriticalSection
                  251 LeaveCriticalSection
                   C0 FatalAppExitA
                   B9 ExitProcess
                   F6 FreeEnvironmentStringsA
                  155 GetEnvironmentStrings
                  110 GetCommandLineA
                  111 GetCommandLineW
                  324 SetHandleCount
                  1B9 GetStdHandle
                  166 GetFileType
                  1B7 GetStartupInfoA
                  214 HeapDestroy
                  212 HeapCreate
                  383 VirtualFree
                  3A4 WriteFile
                  1E0 GetTimeFormatA
                  147 GetDateFormatA
                  1BA GetStringTypeA
                  174 GetLocaleInfoA
                  241 IsValidLocale
                   AF EnumSystemLocalesA
                  1E3 GetUserDefaultLCID
                  313 SetEnvironmentVariableA

                451000 Import Address Table
                45F420 Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference

                   1D AllocateAndInitializeSid
                   E2 FreeSid

                45117C Import Address Table
                45F59C Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference

                   E1 ExitWindowsEx
                  24D SetCursor
                  111 GetDlgItem
                  15D GetSystemMetrics
                  1ED MsgWaitForMultipleObjects
                   99 DestroyWindow
                  1EC MoveWindow
                  256 SetFocus
                  292 ShowWindow
                  257 SetForegroundWindow
                  2AA TranslateMessage
                  174 GetWindowRect

                45100C Import Address Table
                45F42C Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference

                   5D InitCommonControlsEx

                4511BC Import Address Table
                45F5DC Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference

                   49 URLDownloadToCacheFileW

                4511B0 Import Address Table
                45F5D0 Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference

                   65 InternetCanonicalizeUrlW
                    D DeleteUrlCacheEntryW


        4000 .data
        F000 .rdata
        3000 .rsrc
       50000 .text

Those functions marked in Red are the ones that may either not exist or may only be present as stubs on Win9x (according to either Platform SDK docs or unclear issues in Platform SDK docs e.g. the one I first mentioned in MSLU doesn't support wininet.dll). So in theory there my be a little more work here whether that includes perhaps separately wrapping these functions in a delayload kind of wrapper or sending some email to the Platform SDK folks or just deciding not to go down the MSLU road in the case of a new version of MSKLC that doesn't run on Win9x anyway? :-)

The small number of functions in this case will require some specific investigation:

In any case, although this step is one you should always do when adding MSLU to a project you are converting to Unicode, it is a step that you may not really need to do otherwise....

Plus that first function, which becomes VerifyVersionInfoA on ANSI builds, would indicate that setup.exe won't run on Win9x anyway? More research is definitely needed, I think!

On a slightly unrelated note, a question:

If you have to support MFC or some other library it will change what you do with the baselibs a bit. Anyone care to guess what would have to change? :-)


This post brought to you by  (U+a0a8, a.k.a. YI SYLLABLE HMUR)

# Ben Bryant on 4 Jan 2007 11:56 AM:

Nothing innacurate here (and a good series Michael!), but as usual the unstated implication is that MSLU adds Unicode support to Win9x, though it really only allows a Unicode exe to run on Win9x ANSI APIs. So not multilingual on those old OSes but MSLU is still very valuable because the same exe can run and support most text likely to be encountered on old Windows boxes.

# orcmid on 4 Jan 2007 12:22 PM:

What a great series.   It completely makes up for shipping of Raymond Chen's book being delayed until March, according to

There's some juicy material here.  I'm anxious to see more in this area.

Although probably no help regarding MSLU and MSKC, I've run into the problem of functions not existing prior to Windows 2000.  In my case, I tried running on Windows 9x anyhow, confirming that the loader puts up a nice error message and the program isn't started.  The workaround for such API entries is to LoadLibrary kernel32.dll and use GetProcAddress to find the entry.  The program can work around any failure to get the address, rather than letting the loader fail.  I have some alpha-level code where I need to do that before I complete stabilization and allow the code to be deployed in beta release.  

# orcmid on 10 Jan 2007 11:17 PM:

And yesterday, I receive an e-mail from that says Raymond's book has been shipeed.  Wow, now I feel like they are getting it to me early.  Actually, it is arriving inside the original schedule.  I have no idea why they said "not until March" less than a week ago.

referenced by

2007/12/24 VS just got served!, aka The ??? Shift, aka 'Converting a project to Unicode???' No, it's 'Converting a project??? ToUnicode!!!'

2007/01/11 Having problems with MSLU?

2007/01/06 Sorting The Old New Thing All Out

2007/01/05 Converting a project to Unicode: Part 9 (The project's postpartum postmortem)

go to newer or older post, or back to index or month or day