by Michael S. Kaplan, published on 2007/01/04 06:01 -05:00, original URI: http://blogs.msdn.com/b/michkap/archive/2007/01/04/1409380.aspx
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:
# THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
# ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
# PARTICULAR PURPOSE.
#
# Copyright (c) Microsoft Corporation. All Rights Reserved.
#
#
# Processor independent Unicode makefile,
# perhaps one day intended for Platform SDK
#
# Target only i386
# FILE : MAKEFILE.UNI
!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
File Type: EXECUTABLE IMAGE
Section contains the following imports:
KERNEL32.dll
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
ADVAPI32.dll
451000 Import Address Table
45F420 Import Name Table
0 time date stamp
0 Index of first forwarder reference
1D AllocateAndInitializeSid
E2 FreeSid
USER32.dll
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
COMCTL32.dll
45100C Import Address Table
45F42C Import Name Table
0 time date stamp
0 Index of first forwarder reference
5D InitCommonControlsEx
urlmon.dll
4511BC Import Address Table
45F5DC Import Name Table
0 time date stamp
0 Index of first forwarder reference
49 URLDownloadToCacheFileW
WININET.dll
4511B0 Import Address Table
45F5D0 Import Name Table
0 time date stamp
0 Index of first forwarder reference
65 InternetCanonicalizeUrlW
D DeleteUrlCacheEntryW
Summary
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 amazon.com.
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 amazon.com 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/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)