Blog - Title

MSLU

Sorting it all Out
Michael Kaplan's random stuff of dubious value
Be sure to read the disclaimer here first!
  • Sorting it all Out

    What code page does MSLU convert with?

    • 7 Comments

    The Microsoft Layer for Unicode is usually just a very simple wrapper over the non-Unicode APIs on Window 95, 98, and Me, that uses the default system code page to do the conversions.

    There are of course exceptions to this; there are times when Win9x will do just fine with the original Unicode strings, and when that is the case, MSLU works hard to honor that support. I will talk about those cases another day.

    (A lot of the information in this post has already been published in that article that was written for MSDN Magazine entitled MSLU: Develop Unicode Applications for Windows 9x Platforms with the Microsoft Layer for Unicode. I just did not have the same space restrictions here!)

    But the question of what code page to use is not one that is always universally clear.

    I would say there are six different buckets that the various functions fall into:

    • Some use the default system codepage (CP_ACP), the one returned by the GetACP function;
      (e.g. functions like CreateDC, and most of the other functions that MSLU wrapped)
    • Some use the default system OEM code page (CP_OEMCP), the one returned by the GetOEMCP() function;
      (e.g. functions like CharToOemBuff)
    • Some could use either of the above two code pages, depending on the return from the AreFileApisANSI function;
      (e.g. file managment functions like FindFirstFile)
    • Some use the ACP of a particular LCID, as returned by a call to GetLocaleInfo with the LOCALE_IDEFAULTANSICODEPAGE flag;
      (e.g. all of the NLS APIs that take LCID parameters, like GetNumberFormat)
    • Some use the ACP based on a particular device context handle (HDC), which has a Charset associated with it;
      (e.g. most of the GDI functions that take an HDC parameter, like GetTextExtentExPoint)
    • A very few use other mechanisms entirely, we'll call it the miscellaneous bucket.
      (e.g. GetClipboardData, which can use all manner of code pages depending on whether CF_LOCALE is specified on the clipboard or synthetic formats are being supported)

    The decisions over what to use for the conversion were not arbitrary.

    Because, believe it or not, there are many internal pieces of Win9x that actually do support Unicode. This is a fact that Chris Wendt reminded me of years ago, when he pointed to TrueType fonts, large pieces of the GDI that uses those fonts, the NLS data, all of the COM interfaces in the Shell, and more. And he was right; Unicode support is sometimes more than just the interface.  

    Thus in cases where conversion was already being done by the operating system, the goal was to match that conversion that the OS itself was doing. Because any time we did not match the OS behavior, all of the string outside of ASCII was probably corrupted.

    So the answer to the question first posed in the title of this post (What code page does MSLU convert with?) is that it really depends. On the function you are calling, sometimes on how you are calling it, and sometimes on the way other functions have set the stage....

     

    This post brought to you by "ܜ" (U+071c, a.k.a. SYRIAC LETTER TETH GARSHUNI)

  • Sorting it all Out

    Why UnicoWS.dll forwards to the OS on Unicode platforms

    • 5 Comments

    The original version of the Microsoft Layer for Unicode's DLL only ran on Win9x. Ever.

    Now it had the loader , mind you, which would properly call the operating system or MSLU depending on where you were running, but there was no code in place to handle what would happen if you stuck the DLL on your Windows 2000 box and took the trouble to call LoadLibrary/GetProcAddress on the various APIs.

    Two things changed that, though:

    1) From my past heritage, I understood that the MSLU loader, while a very cool and elegant solution (cf Challenges behind MSLU: the loader! (Part 1) and Exploiting the linker's rules in unicows.lib), really did not solve the problem for the legions of developers using C#, VB.Net, Classic VB, Visual FoxPro, VBA, and so on. They were still kind of left out of the mix, and they really did constitue a majority of potential callers.

    2) Many assumptions that MSLU makes about the platform simply cannot be sustained for very long -- trying to use MSLU on an NT platform where the dll code is called will probably crash very soon into the run, if we did not aggressively work to disallow it. This was especially true in relation to window management.

    After some intense discussion internally, the decision to address the problem was made. I got to add the small bit of code to each API to make sure if not running on Win9x, then forward to the operating system. The actual size/perf hit of this code was pretty small even on Win9x, but the ability to make sure MSLU wuld simply work (rather than providing a list of things that would not work) was pretty stellar.

    I also ran into an interesting optimization issue.

    Let's say that this is one of the functions (it is not, but it is close enough):

    int __stdcall
    GodotAddFontResourceW(LPCWSTR lpszFilename)
    {
        if(IS_ON_NT())
        {
            return AddFontResourceW(lpszFilename);
        }
        else
        {

            // Insert the wrapper here to convert the
            // the string and call AddFontResourceA.
        }
    }

    Now I and others looked at that code and the fact that we were getting parameters that were identical to what the OS wanted -- so couldn't we just jump right to it in assembly language, changing the

    return AddFontResourceW(lpszFilename);

    to a

    __asm jmp AddFontResourceW

    instead?

    I mean, thinking about functions that take a lot of parameters -- the time you would save pushing parameters onto the stack twice alone would be a win, right?

    Unfortunately, this assumption is actually wrong. Using a jmp here actually makes the DLL 50% bigger and a little slower, too!

    It turns out that the compiler is smart enough all on its own to handle these kinds of situations. But when you plunk down inline assembly, it stops doing many of the basic optimizations. So I can actually do better here by not trying to be quite so clever....

    I have to admit that it was a little sad since I had not been able to use inline assembler in a long time. It  was exciting to do it again, though it would have been cooler if I had been able to actually use it. :-)

    Anyway, thats why and how MSLU forwards calls that belong to the OS any time they are received.

     

    This post brought to you by "" (U+0ab2, a.k.a. GUJARATI LETTER LA)

  • Sorting it all Out

    How can I use MSLU without shipping UnicoWS.dll?

    • 5 Comments

    People often look at the Microsoft Layer for Unicode (MSLU) and wonder how they could avoid shipping an extra file.

    Recently, Hans Hansen posted a message to the microsoft.public.platformsdk.mslayerforunicode, asking the question in the following way:

    Hello!

    On
    http://msdn.microsoft.com/msdnmag/issues/01/10/MSLU/default.aspx it is described how you can link MSLU into your program. But according to this page, the program will then depend on the unicows.dll file on the Windows 9x platforms. Is it possible to instead statically link wit MSLU so that this dll is not required?

    I have been doing some googleing and there seems to be different opinions on this issue.\

    I use Microsoft Visual Studio .NET 2003. My program is a plain C++ program and I do not use ATL or MFC.

    Best,

    Hans

    Unfortunately, the answer is that this option is not available -- you have to ship the DLL if you are running on Win9x.

    The obvious question that people ask at that point is why things were designed this way.

    Although to be frank, I can never tell when people asking if it is out of genuine curiousity or if it is to try to find a flaw in the reasoning that would convince me to change the whole direction of the project and give them exactly what they want. I always assume that the answer is the (usually mature) former rather than the (usually immature) latter, although I am probably underestimating the childish nature that developers often have (I am including myself here when I talk about that, since I still feel like a developer no matter what my title is).

    Remember that the #1 performance goal was to not hurt the performance of applications running on NT-based platforms. This only makes sense since the project was being done primarily at the behest of a developer in the Windows Division and the payment for the project was approved by a Windows Vice President. Shipping something that would help encourage migration off Win9x or at least to take the best possible advantage of NT-specific features is a good thing, shipping something that slows down the platform even a little is not.

    But the DLL itself does add some performance considerations. While it is itself loaded with a "delay load type" functionality, once it is actually loaded it does not itself "delay load" all of its own dependants. Thus loading UnicoWS.dll in a process will load a bunch of DLLs. There is a hit that only happens if you load the DLL, so if its full functionality was included in a .LIB, a lot of extra work would have had to happen to make sure that performance on NT-based platforms was not adversely affected.

    It would also mean adding a whole bunch of code to your application binary that is simply never used on NT-based platforms. Now it would not mean adding the full size of the DLL to every project, but it would mean adding however much you are using in the way of APIs.

    Now admittedly neither of these problems is unsolvable, but I learned a long time ago that successful software projects are based on focused and targetted goals and visions. Which does not work well with the idea of trying to solve every puzzle and fix every problem even if the problems are not part of the core scenario you are trying to address. I suspect that had the focus of MSLU been as unfocused as all that, it would have either never shipped or it would have been a year later and with a lot more bloat. And it may not have even been terribly usable after everything was said and done.

    Which is not to say that a bit of undisciplined havoc never sneaks in (for example, think about the support for resource updating. Or the work to make sure that even those who do try to use UnicoWS.dll on NT-based platfoms will still see the calls forwarded to the OS).

    But if you keep your primary project goals in mind, then the occasional diversion will not kill you. Or the project. Or the people who are doing the triage on features. The key is to not waste too much time or effort on items that are outside the scope of the project.

    Which in this case (unfortunately) included the "never ship an extra file, even on Win9x platforms" feature....

     

    This post brought to you by "ڇ" (U+0687, ARABIC LETTER TCHEHEH)

  • Sorting it all Out

    Why does MSLU wrap ________ ?

    • 9 Comments

    Most of the APIs that MSLU wraps are on the list for obvious reasons. But in that huge list, there are several that are not....

    1) There is, for example, the GetProcAddress function. It takes a string, but never a Unicode string, on NT or otherwise. So why would it need to be wrapped?

    Well, it turns out that the GetMonitorInfo function, defined in multimon.h, is not just a simple prototype. There is a bunch of complex code in it that conditionally calls various APIs, including GetProcAddress, to get a function pointer to replace any call to GetMonitorInfo. Because of this, MSLU could not wrap the GetMonitorInfo function, because the wrapper would never be used. The only way to allow the to wrapper to work was to wrap GetProcAddress and look for where someone was trying to retrieve the address of GetMonitorInfoA or GetMonitorInfoW!

    2) And then there is the EnableWindow function, which does not even take a string. Why on earth would MSLU need to wrap it?

    Back in the early days, MSLU was eager to convince people who had Unicode layers of their own to consider switching to MSLU. As a part of that, it picked up several bug fixes from other layers so that people would not have to feel like they were losing functionality for switching. One of those layers was the "VSAnsi" layer used by Visual Studio, which had an EnableWindow fix best described in a comment in the MSLU source code:

            // We wrap this API in order to get consistent behavior on both
            // NT and Win9x. It seems that on NT, any non-zero value of bEnable
            // will enable the window.  Based on empirical data, Win9x seems to
            // look at only the low 16 bits of bEnable.  This causes a problem,
            // for example, if the caller passes a count of items with the intent
            // to enable a window when the count is >0.  That will fail when the
            // count is a multiple of 64K, but will be nearly impossible to find
            // and fix the problem. See Visual Studio 7 ###### for an example
            // of such a bug.

    Now there were not many APIs that were taken to this kind of extreme, but the VSAnsi library was at that time owned by Lars Bergstrom (a very smart developer) and was probably the second most useful source of interesting buglets for MSLU to fix while wrapping (right after Office, which had a vitual monopoly on interesting Win9x GDI bug fixes that even external customers had found by spluenking through the exports in the MSO library, over the years). Given the unique expertise the wrapper code writing seemed to require, Lars and I probably did a ton of code reviews of each others' work before MSLU and the VSAnsi that shipped in VS 7.0 first got released. The EnableWindow bug fix was one of those little extra bug fixes that we picked up.

    3) Then there are the FindResourceW and FindResourceExW functions. Now note that Unicode versions of these functions actually exist on Win9x, so there is no good reason to wrap them, right?

    Well, actually, wrong. Both APIs took resource type and name parameters that could be either strings or numbers, but there was a bug in Windows 95 and Windows 98 (fixed in Windows Me) where in some cases the LocalFree function was being called on the parameter even though the LocalAlloc function was never called to allocate the string (it was a passed in parameter). This would potentially corrupt the heap. So MSLU would do the extra work to copy string parameters to stack variables, because an attempt to call LocalFree on a stack variable would simply fail with a last error of ERROR_INVALID_HANDLE. On Windows Me it would just call the OS directly since the bug had been fixed....

     

    This post brought to you by "ㆆ" (U+3186, a.k.a. HANGUL LETTER YEORINHIEUH)

  • Sorting it all Out

    Cleaning out the suggestion box a bit

    • 3 Comments

    The theme for this post is going to be disappointing people's hopes.... picking out suggestions that do not go along with the probable desires of those asking the questions.

    The first question comes from marius mihalca:

    Sorry to bother you with this but I found no other way to send you a message. I fallowed all the stept from this article (How to build the 7.1 MFC and the CRT DLLs with MSLU) but my simple VC71 generated application can't display unicode on Windows98 SE.

    Everything sims OK except the fact that instead of bulgarin (or greek) language my application displays ? and _ (or other characters). I am positive that unicows.dll is loaded.

    I tried to make an simple editbox and tried to paste some unicode text. The editbox shows incorect text.

    How can I be sure that my applicatin is using unicows.dll correctly?

    Please help.
    10x

    MSLU is actually behaving as designed here. The Microsoft Layer for Unicode is not, as some people seem to think, a library that provides Unicode support for Win9x. It is, rather, a thin layer over the non-Unicode Win9x that allows one to write a Unicode application. It will still convert those Unicode string parameters from the various Win32 API functions out of Unicode in order to call the underlying operating system. The key is that the Unicode application, when run on an operating system that supports Unicode, will allow one to get full Unicode support.

    This design is the very one that Julie Bennett originally envisioned and that Cathy Wissink and I wrote about for MSDN Magazine.

    Now there are components that provide a degree of actual Unicode support on Win9x, such as Uniscribe, RichEdit, the Shell Common Controls (version 5.80 and later), and others. But those items do not include MSLU, which is not a rewrite of those 550+ APIs but a wrapper around them. I know this may be disappointing to those who were hoping that it was indeed a Unicode solution of Win9x, but it has never been documented anywhere as such a solution....


    On April 13, I heard from AC:

    I'm receiving this question from the developers who'd like to have the possibility to make the language customization for the users of Windows 9x/ME which would allow them to later more gracefully upgrade to NT/2K/XP:

    - how can they add the additional code page to the Windows 9x/ME?

    The goal is to be able to enter unicode characters by typing them. Good enough MS KLC equivalents for Win 9x/ME already exist.

    I know that once was probably considered not so good idea to open the specs of Windows 9x/ME .NLS files. But I guess now it shouldn't be such a problem?

    Well, the answer to this will also not thrill. There is not going to be any kind of opening up of code pages or other .NLS files on Win9x or elsewhere. In practice, this would not help graceful upgrades but it would delay the time before people did upgrade to a NT/2K/XP by offering an incomplete solution for other languages today, in a way that hurts interoperability.


    In the beginning of February, Richard Caruana asked:

    Do you know if the following can or will be instituted :

    1. To be able to have a core fontlet/character in the centre which can have overlay accents or diacritics which can be added/overlaid/plyed at run-time in a unicode text editor.
    2. A section for developers in the UNICODE font (~ 1000 character spaces or less if the above idea can be instituted )

    Also, do you know where I can download a unicode character search program which if you paste in the character (chinese) it gives you the character code (eg 5CFO) and a definition.

    Well, #1 is sort of there now, although it only works well when the font author does the hard work of having all the appropriate code points and attachment points defined.

    #2 is sort of there with now, although the private use area of Unicode is only for private use and not for interchange, and I honestly don't see how it could help this situation anyway.

    As for the place to get info about Unicode characters, check out the Unicode Character Search (by fileformat.info) that will accept both characters and code points. I do not know offhand about downloadable tools, but if you could reach my blog then you can reach that site, I suppose. Right? :-)


    Then, back in the end of January, G* asked:

    Care to take a break from the wonders of Unicode and elaborate on the pain that is the console codepage 437?

    Sorry, no. :-)

    I am all about the Unicode thing.

    Unless you have something particular in mind beyond what I have talked about in prior posts about encoding issues, like this one?


    Ok, that is enough disapointment for one day. I'll try to work harder to meet people's expectations/wishes next time....

     

    This post brought to you by "峰" (U+5cf0, an ideograph meaning "peak, summit; hump of camel" according to the Unihan database)

  • Sorting it all Out

    Reason #8 to not be so anxious to update your Platform SDK?

    • 6 Comments

    Back in March, I gave Reason #124 to update your Platform SDK from time to time (it was to pick up a fix to an AppVerifer bug that existed in unicows.lib).

    A comment from Nektar suggested that there were good reasons to be cautious about doing such updates. And he gave seven very good reasons.

    I will now, to prove that I can argue both sides of an issue, suggest an eighth. :-)

    Many people who have been compiling with VC 6.0 have noted a "debugging information corrupt" error showing up in their debug builds that include MSLU.

    As usual, one of our internationalization MVPs (Ted) stepped up explain in the most recent post to the microsoft.public.platformsdk.mslayerunicode newsgroup:

    ...that's a known issue with the February 2003 SDK and later.  The unicows.lib contains debugging information compatible only the VC 7 and above.  There's nothing that can be done about this, unfortunately. Microsoft decided that Visual Studio 6.0 is no longer a supported platform for any future Platform SDKs.

    I thought I would explain a bit of the backstory about the issue with the Platform SDK. :-)

    Basically three facts unintentionally cause the problem. Since there are three we can call it a conspiracy (sorry, my Law & Order background is showing through again!). The three facts are:

    1. The .LIB files that make up the Platform SDK are built in the same tree as Windows itself, so that day by day the files like kernel32.lib will have in them whatever they ought to have for being considered a .LIB file in the version of the Platform SDK that goes with that version of Windows.
    2. The toolset used to build Windows (compiler, linker, etc.) is upgraded regularly, to pick up fixes of the sorts of bugs that only a project as huge and complex as Windows can find.
    3. The folks who build the toolset over in the Developer Division upgrade the format of debugging information from time to time, and then sometimes remove the older formats as the years pass. Sometimes the formats are not compatible with the older toolsets.

    When you add these three facts together with a fourth fact:

    1. The MSLU .lib file, unicows.lib, has code inside of it

    the unintentional conspiracy causes the problem with the older toolset in VC6 not working with the debugging information in unicows.lib. The retail build still works just fine, at least. But the debug build will not work.

    Luckily Ted gives the workaround:

    To workaround this issue, you'll have to find another unicows.lib from an SDK before February 2003 SDK (e.g. the October 2002 SDK).  You can obtain that here:

    http://groups.google.co.uk/groups?hl=en&lr=&selm=ev4JMaN0EHA.804%40TK2MSFTNGP12.phx.gbl

    The older .LIB file will allow VC6 to work properly.

     

    This post brought to you by "ű" (U+0171, a.k.a. LATIN SMALL LETTER U WITH DOUBLE ACUTE)

  • Sorting it all Out

    AVICAP32.DLL sucks (from the MSLU point of view)

    • 14 Comments

    It is all about perspective.

    I am sure there are people who look at this DLL as being the answer to their prayers in terms of providing helpful interface to AVI capabilities.

    But from my point of view, it kind of sucks. :-(

    The other day someone with the handle PRR posted the following to the microsoft.public.platformsdk.mslayerforunicode newsgroup:

    Problem description: If unicows.lib is included in the project, floating point control word may become invalid during program startup on Windows 98/95 machines (not tested on ME).

    Compiler platform: MS VS.NET 2003 Pro, Platform SDK Feb 2003, Unicows.dll 1.1.3790, Win XP Pro, P4@2.4G, 1G RAM,

    Steps to repro:

    • Create new Win32 Console Application (default settings).
    • Under Project Property Pages dialog choose Release configuration
    • Add following to Linker/Command 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 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 secur32.lib oleacc.lib oledlg.lib sensapi.lib

    • Modify main cpp file to following:

    #include "stdafx.h"
    #include <stdio.h>
    #include <float.h>

    int _tmain(int argc, _TCHAR* argv[])
    {
        printf("%x\n", _control87(0, 0));
        return 0;
    }

    • Compile the Release project and run on Win98SE PC.
      Output: 40003
      Expected output: 9001f

    The problem was reproduced on machine, which has a clean install of Win95OSR2 + clean upgrade to Win98SE. It does not happen all the time. If program should return 9001f, reboot Win98 and try again.

    Note: It does not matter, whether project uses Multi-byte or Unicode charset.

    Note: As soon as unicows.lib is removed from project, program starts acting as expected.

    Now for the record, it is not MSLU that is doing this. In order to have maximum compatibility with every version of Win9x (including Windows 95), there is no dependency whatsoever on the C Runtime. So it is defnitely not setting the floating point stuff. It is actually harder to do this than I realized, but Phil Lucido helped me shed the dependency while still using stuff like structured exception handling....

    Now I knew this issue had come up before but honestly could not remember what it was. Luckily, Ted (who unlike me remembered this issue) came to the rescue with the answer for PRR, and a workaround:

    The thing that actually destroys the floating point is avicap32.dll which unicows.dll is dependent on.  Several searches will come up with information about this.

    The solution is to create your own AVICAP32.DLL stub DLL that sits in the same folder as unicows.dll (if you don't rely on functionality in that DLL).

    The problem is that unicows.lib is statically linked to many of the system DLLs that it has to call for the functions it wraps, and AVICAP32.DLL does indeed change these settings in the process. Whether you wanted it to or not.

    Now this DLL only has two APIs that MSLU wraps: capCreateCaptureWindow and capGetDriverDescription. For all of the trouble that they cause with this floating point crap, I wish no one had noticed these two APIs that were missed for so long (they were added to MSLU on March 24, 2001 and I doubt anyone has actually used the wrappers since then, beyond the meager tests I wrote!).

    Back then, I had toyed with the idea of delay loading all of the DLLs and functions being called, but somewhere in the 15+ DLLs and 550+ APIs it just seemed like an excessive amount of work. And it never ended up happening. It probably should have happened for this one DLL/two functions to work around the floating point problem, but it is not really worth rev'ing the DLL for that one change. I'll put it on the list of things to triage, if and when....

     

    This post brought to you by "" (U+0c8a, a.k.a. KANNADA LETTER UU)
    A letter that is selcom seen on Win9x but well represented in the Tunga font that ships with Windows XP and Server 2003!

  • Sorting it all Out

    Why UNICOWS.LIB is so big, but the amount of extra size to those who include it is small

    • 0 Comments

    I have hinted around about this problem before, but now Raymond Chen explains how the technology works with a lot more detail in his post Why does the debugger show me the wrong function?

    Note that the default behavior with DELAYLOAD when the DLL or the function cannot be loaded is to throw an exception. This was not the solution that MSLU uses for the problem (as I said bfore, we do not use the default DELAYLOAD, so we are not locked into its default behavior!).

    In the MSLU case, it has failure stubs that it uses rather than throwing exceptions -- for every single API that is wrapped, there is a defined stub that will set up the failure path so that if the function cannot be called for some reason, it will fail gracefully. If someone up higher on the callstack fails, it is not MSLU's fault!

    (This is why MFC crashes if you link it with MSLU yet do not include the DLL -- because although MSLU is so braced to handle failure when it needs to that some would say it has an end-of-the-world complex, MFC's paranoid sense of denial does not let it contemplate the failure of base APIs like GetSystemDirectory. I guess all big libraries need one sort of therapy or another!)

    But if you think about what most failure stubs are, they are simple functions -- how many ways are there to say "take three parameters, set last error to ERROR_CALL_NOT_IMPLEMENTED, return NULL", when all is said and done? Really just one. So all of that folding together of identical code happens on almost every compile/link in MSLU. Even with over 500 failure stubs included, one for each API that the Microsoft Layer for Unicode wraps.

  • Sorting it all Out

    Reason #124 to update your Platform SDK from time to time

    • 5 Comments

    Antti Nivala asked in the microsoft.public.platformsdk.mslayerforunicode newsgroup:

    Our software uses MSLU. I noticed that if I run our programs with Application Verifier 2.50 with the Handles test enabled, I get a bunch of Invalid Handle exceptions during module loading. In the end, these prevent the program from running. The call stack points to MSLU. If I rebuild the program without MSLU, the Invalid Handle exceptions no longer appear.

    Is this a known issue? Is it OK, or is MSLU (or I) doing something that is not perfectly OK?

    Note that I am not saying that MSLU would have caused any real problems in the normal use of our program. I just want to confirm why the Handles test of Application Verifier 2.50 fails if MSLU is used.

    Here's some relevant output from WinDbg:

    ===========================================================
    VERIFIER STOP 00000300: pid 0xCD8: invalid handle exception for current stack trace

     00000000 : (null)
     00000000 : (null)
     00000000 : (null)
     00000000 : (null)
    ===========================================================

    (cd8.d14): Invalid handle - code c0000008 (!!! second chance !!!)
    eax=c0000008 ebx=7c8097ad ecx=0012fde4 edx=7c90eb3d esi=00000001 edi=7c800000
    eip=7c90eb74 esp=0012fd90 ebp=0012fde0 iopl=0         nv up ei pl nz na po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000 efl=00000206
    ntdll!KiRaiseUserExceptionDispatcher+0x37:
    7c90eb74 8b0424           mov     eax,[esp]
    ss:0023:0012fd90=c0000008
    0:000> kb
    ChildEBP RetAddr  Args to Child
    0012fde0 7c90eb93 7c90d9eb 7c83928f 00400000 ntdll!KiRaiseUserExceptionDispatcher+0x37
    0012fe00 004577dc 00400000 004a5864 00000004 ntdll!KiFastSystemCallRet+0x4
    0012fe3c 00456b67 00484cec 00484fb0 004a5864 MFStatus!ResolveThunk+0xc9 [d:\xpclient\sdktools\unicows\delay\resolve.c @ 188]
    0012fe90 0045cb59 004a81d0 00000fa0 7c80b529 MFStatus!kernel32_GetProcAddress_Thunk+0x1f [d:\xpclient\sdktools\unicows\delay\thunk_stub.c @ 46]
    0012fea4 0045c977 00459336 00000094 00000005 MFStatus!_mtinitlocks+0x2b [f:\vs70builds\3077\vc\crtbld\crt\src\mlock.c @ 133]
    0012fea8 00459336 00000094 00000005 00000001 MFStatus!_mtinit+0x5 [f:\vs70builds\3077\vc\crtbld\crt\src\tidtable.c @ 117]
    0012ffc0 7c816d4f 7c911978 ffffffff 7ffdf000 MFStatus!wWinMainCRTStartup+0xe0 [f:\vs70builds\3077\vc\crtbld\crt\src\crt0.c @ 184]
    0012fff0 00000000 00459256 00000000 78746341 kernel32!BaseProcessStart+0x23

    Thanks in advance!

    Antti

    This is a bug I was pretty sure I was familiar with, so I asked "What version of the .LIB are you using?". Antti replied:

    I think it was from Platform SDK July 2002 edition. Anyway, I upgraded to using the LIB version that ships with Platform SDK February 2003 and the problem disappeared.

    Thanks :-)

    Antti

    Yep, that was the problem. It has to do with the MSLU loader (contained in unicows.lib) which does the following:

    1. Takes its bunch of data segments, one per API that might be called, with all of the necessary info for the API (the name, the name of the "stub" function to call if the API cannot be found, a pointer to our ResolveThunk() function, etc.).
    2. The first time an API is called, ResolveThunk() is called with all the API information in #1.
    3. ResolveThunk() figures out what API should be called. It fixes up that pointer to itself, changing it to the actual API.
    4. Call FlushInstructionCache() to flush out any cached information for the modified code in the change to #3
    5. All future calls to the API go directly to it, making ResolveThunk() a "call once per API" function.

    The problem with this bug is that it turns out step #4 was not needed, so the call was pretty much ignored on all platforms. Unfortunately, the handle being used in the call was not valid for the call to FlushInstructionCache() and AppVerifier is an application that works hard to find such problems.

    The fix was simple enough -- just remove the call to FlushInstructionCache. It was doing nothing other than breaking AppVerifier anyway.

    The bug was customer-reported (customers use our tools, even if we do not always do it  :-) ), and I had the fix in the next Platform SDK....

    The moral of the story? Update your Platform SDK from time to time -- it may fix impotant bugs! :-)

     

    This post brought to you by "!" (U+ff01, FULLWIDTH EXCLAMATION MARK)

  • Sorting it all Out

    The ShellExecuteEx wrapper in MSLU

    • 2 Comments

    The ShellExecuteEx wrapper in MSLU is a little bit broken.

    Recently posted to the microsoft.public.platformsdk.mslayerforunicode:

    The code looks a bit like this:

    TCHAR filename[MAX_PATH];
    TCHAR paramStr[MAX_PATH];
    _tcscpy (filename, _T("C:\myapp.exe"));
    _tcscpy(paramStr, _T("params"));

    [...]

    SHELLEXECUTEINFO ShExecInfo;
    ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
    ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
    ShExecInfo.hwnd = NULL;
    ShExecInfo.lpVerb = NULL;
    ShExecInfo.lpFile = filename;
    ShExecInfo.lpParameters = paramStr;
    ShExecInfo.lpDirectory = NULL;
    ShExecInfo.nShow = SW_HIDE;
    ShExecInfo.hInstApp = NULL;

    BOOL bExec = ShellExecuteEx(&ShExecInfo);

    And it crashes within the ShellExecuteEx() call. I should also mention that this does not happen with every parameter string. However, I experimented a bit further, and found that the crash goes away if I initialize the SHELLEXECUTEINFO structure to zero, ie:

    SHELLEXECUTEINFO ShExecInfo = {0};

    Not really sure why or if this truely fixes the problem, but hopefully it'll prevent the crash.

    Well, his workaround works, and quite well at that. And it is indeed an MSLU bug.

    The problem? Well, MSLU is a tad over-eager, converting every string in the struct. Despite the fact that there are flags that one can pass to the API that limit which strings are valid. Obviously the NULL case works well, but the case where it is not NULL due to the memory not being initialized? Not so lucky there -- and the crash happens.

    The workaround? Simple, as given above -- just initialize the SHELLEXECUTEINFO struct.

    Obviously not a great answer, but so few reports over these past few years and who knows how many downloads? It is on the list as something that could be fixed if there is a future release, but looking at the Product Life-Cycle Information for Win9x. Obviously given all this and the fact that all Win9x platforms are in paid support phases (or no longer supported), the bar has to go up for MSLU fixes. It was over a year since the release before last (1.0.4018.0) to the most recent release (1.1.3790.0), and there are currently no bugs that meet the bar of not reasonably fixable by developers using MSLU.

    I'm not expecting this to be the most popular decision with which I have ever been involved, but nearly four years after its release, the bulk of the bugs that have been reported have been fixed, and things have been slowing down for a while now.

    So let's take a look at the infomation about overriding MSLU APIs. It is discussed here and here and with samples here and here. Any time one does not like a wrapper provided by MSLU, one can override it. Inside that override, one can even use LoadLibrary/GetProcAddress to call the MSLU API eventually. Such as after making sure to clear out unused fields in a SHELLEXECUTEINFO struct. :-)

    I'm still around, I still watch the microsoft.public.platformsdk.mslayerforunicode newsgroup, and I still will post stuff here when it might be of interest....

    This post brought to you by "₯" (U+20af, a.k.a. DRACHMA SIGN)

  • Sorting it all Out

    Challenges behind MSLU: The hardest API to wrap

    • 3 Comments

    MSLU covers a lot of territory, considering how small it is -- less than 250k yet it covers over 500 APIs!

    One of the questions I was often asked was what I thought was the most difficult API to wrap.

    My answer, without hesitation, was always the same: FormatMessage.

    Let's look at how the Platform SDK describes it:

    The FormatMessage function formats a message string. The function requires a message definition as input. The message definition can come from a buffer passed into the function. It can come from a message table resource in an already-loaded module. Or the caller can ask the function to search the system's message table resource(s) for the message definition. The function finds the message definition in a message table resource based on a message identifier and a language identifier. The function copies the formatted message text to an output buffer, processing any embedded insert sequences if requested.

    Ok, so it sounds complicated. When I started out, I figured it would be easy. Of the 32 different Unicode layers inside of Microsoft almost half of them had a FormatMessage wrapper already. With so
    much code to work from, we should be covered, right? The reality was far stranger, unfortunately! It seems that even with 14 samples to steal from, no one had ever implemented the whole function. After all, FormatMessage is pretty complicated:

    You can load from system messages, from your own binary, or format a string you pass in

    • The API can allocate the string or you can allocate it yourself
    • You can use inserts in the form of an array of arguments (including strings!)
    • You can use inserts in the form of a va_list * (which can also include strings!)
    • Just about every 4-byte arg type is supported (only some of the larger ones like the float types are not allowed)

    Turns out no one had ever done it all -- in fact, none of them used arguments at all, except in the simplest of ways to avoid problems (i.e. a version that only accepted strings, a version that never accepted them, and so on). I won't names or anything, but I really don't have to -- from my point of view everyone stunk at this point, and I did not care how many interesting bugs for ExtTextOutW on Win9x had been fixed....

    So, the MSLU Triage Committee met (which sounds more impressive than it was -- it was just my lead and a PM and me talking). We decided that ideally MSLU ought to just to do it right, for all of the cases. After all, you never know what a user might be depending on!

    But trying to completely support all possible aspects was tricky. After all, you have to accept an arbitrary number of parameters, only some of which might be strings (and of course all strings have to be converted!). They can be used in any order and although it is not documented it it entirely legal to skip inserts in the actual string being used (thank God for beta testers, thats all I can say!).

    In the end, I kept a very simple strategy to handle all the weirdnesses of inserts:

    1. Preload the string, using the FORMAT_MESSAGE_IGNORE_INSERTS dwFlag so that the raw string could be obtained (and the API allocates this time)
    2. Parse the string for insert tags (which are always in the %n format, up to %99), being sure to get the highest id number and also to store whether each insert has a string in it per the raw string
    3. Free the allocated string, we do not need it anymore
    4. Build a new array of items based on the old array or va_list (converting all strings out of Unicode as needed).
    5. Call the OS API with the newly created inserts, using the allocation preference of the one who called us.

    Lots of minor flourishes about allocations and language info, etc., but in the end this was a pretty hellaciously complicated API to try to totally support!

     

    This post brought to you by "ฑ" (U+0e11, a.k.a. THAI CHARACTER THO NANGMONTHO)

  • Sorting it all Out

    Why/how MSLU came to be, and more

    • 16 Comments

    A question that I used to be asked all the time (and even to this day am still asked on a regular basis) is Why did Microsoft create MSLU?

    I'll see if I can shed some light on this today.

    (Sometimes people ask less charitable versions of the question, like Why did Microsoft wait for so long to make MSLU? or Why did you wait to make MSLU until it was too late? but I'll stick to the nice version of the question!).

    Note that at the time it was written I was not yet an employee of Microsoft, so I was definitely not involved in all of the core strategic decisions behind why a product ships. Think of what follows as a bunch of somewhat educated suppositions, many years after the actual events took place.

     

    As it is also the story of how I got my first contract with the group that I subsquently joined MS as a full-time employee, it is very much related to why I work where I do!

    Anyway, here is the story....

    Back some time in 2000, I was first starting to look at the globalization namespace in the .NET Framework. There was not much in the way of documentation available to me, but I did find a detailed spec that told a lot of the story but still left me with some questions. I noticed that the author (Julie Bennett) was a software design engineer, who I did not know. So I sent her a piece of mail asking if she was free for lunch some time to talk about some System.Globalization stuff. I did not hear back from her right away, so i figured she was not comfortable talking about it prior to release since I was not really working on any of the development teams involved with the project....

    Then she emailed me out of the blue and mentioned that she remembered I had asked about lunch, and if I was still interested she had some time the next day. I thought -- Cool! I can fine out about these things that do not seem to be captured in the spec!

    She had a very different set of motivations, though.

    You see, for many years she had been trying to convince people of the need for a Unicode layer for Windows 9x, because too many people needed to write for all platforms and so for ease of release management were not writing unicode applications. Even articles like the one F. Avery Bishop wrote for MSJ back in 1999 (Design a Single Unicode Appthat Runs on Both Windows 98 and Windows 2000) were not making a huge dent in this problem. And it would really hurt the whole story for international support if new "Unicode only" languages would not work in third party applications.

    So for her, this lunch was sort of an informational. She had a project in mind and thought that I would be a great person to work on it. Of course I had a job already and was not looking for a new one at the time, and probably would have said no to lunch if I knew it was an informational. Luckily she did not tell me....

    Anyway, at one point we were talking about random NLS stuff and I bemoaned the fact that "THEY" had not just made the whole "Traditional" vs "Modern" Spanish sort thing go away into an alternate sort rather than a new LANGID. She gently pointed out to me (in classic "My Dear Boy..." style!) that the concept of alternate sorts did not yet exist; they were in fact created largely due to the postmortem of the "Traditional Spanish sort" issue to make sure such a problem would not happen again.

    But when she laid out the idea of a layer that would be able to let people write Unicode applications that would do incredible things on Windows 2000 while still being functional on Win9x, I was amazed. When she asked whether I would I be interested in doing this project if she could get people behind the idea of making it happen, I think I said Hell yes! or something equally coherent. It sounded like an amazing project to be involved with!

    Had I been less dumb, I would have known that "THEY" I was complaining about with the Spanish thing was actually "HER" since she was the developer who owned the area. and would have been quite embarrassed that I wanted a contract from someone who I had just dissed their work. I think she knew that the effect on me would be more powerful if I found out myself1 -- which I did, not too long after. :-)

    MSLU was an adventure of a project, with sometimes invaluable, often interesting, and always entertaining help from folks like

    • Raymond (random Win9x-specific strangeness that he always knew what was causing the problems)
    • Bryan (architecture of the MSLU loader, our own private delay load solution)
    • Dan (author of the original delayload, who helped Bryan architect the MSLU solution)
    • Jay (the mysteries of DllMain, TLS allocation, and eventually Fusion assistance)
    • Phil (working without the CRT, since not all Win9x platforms had one!)
    • Cathy (the only person in the universe who could ever PM such a project, and also the one who came up with just about every internal and external name we had)
    • Mike (assistance with subclassing, Shell team style)
    • Gerardo (who has forgotten more about window management and user messaging than most people will ever know)
    • Barry (generating and working with thunks)
    • most of all Julie (for providing the vision for MSLU in the first place)

    Microsoft is an excellent place to work, and folks like these are one of most impotant reasons!

    By April 2001, Cathy Wissink and I were able to announce MSLU at the 18th International Unicode Conference in Hong Kong (in a special add-on talk that was not in the original schedule!).

    We also wrote an article for the October 2001 MSDN Magazine entitled MSLU: Develop Unicode Applications for Windows 9x Platforms with the Microsoft Layer for Unicode which talked about both many of the motivations for doing the project as well as technical information about using MSLU.

    So why did it take so long for people to see it was an important project to do? It is hard to say (I was certainly not in the meetings Julie would have with Execs about doing the work!), though if I had to guess it would be that everyone had really moved on mentally to Windows 2000 and it took a while to admit that fuller migration strategies were needed to get developers writing Unicode applications. The code name for the project was Godot, based on the Samuel Beckett play Waiting for Godot, which should give some hint as to the feelings of those who knew all along how important the project was yet did not know if anyone else would ever realize it. We just had to wait for it, so we could all be saved.

    Though the confusion of many people was evident when they wondered if it was some type of managed reference (i.e. Go.dotnet or somesuch).

    A final note for this post. Serge Wautier asked (from the suggestion box):

    I always wondered why Begin/End/UpdateResourceA() were not implemented in Win9x. Was there a technical reason ? You recently wrote that all the work had been done to make the API Win9x friendly. Then why didn't it show up ?

    Note that I'm asking just for the historical anecdote. I am an happy user of MSLU

    I cannot really take responsibility for this part, it was really due to the hard work of Matt Curland (author of the excellent Advanced Visual Basic 6: Power Techniques for Everyday Programs from which I took code for my own book, making modifications so it would be more internationally friendly). Matt was a full-time MS employee back then and was working on his cool resource editor. He wanted to support it on Win9x, too so he asked me if he could take a look at the code for the resource updating functions BeginUpdateResource, UpdateResource, and EndUpdateResource, and after getting the okay from my management I sent him the source file. After converting it to be able to work on Win9x (which was no mean feat!) he asked if he could ship a DLL with the code, but management was not as thrilled about that idea (the distance between "code to look at"and "code to ship to customers" is a large one!). I cannot remember which of us now, but one of us suggested that to get out of this stalemate maybe it could be added to MSLU. This did sit better with management, so it was added and working well by the 1.0.3703.0 release.

    Now I added both "A" and "W" versions because it just seemed kind of insane not to in this case. But all of this happened years after the last version of Win9x ever shipped, and as far as I know no one ever really considered adding these APIs to Win9x. The effort to make them Win9x-friendly was not a trivial one. The only reason it really happened is because Matt is an excellent developer who is fond of full and elegant solutions to problems.... :-)

     

    1 - Incidentally, its really one of the big reasons I took that contract, once approval for the project happened (something that she said was much easier with a development resource raring to go!). It takes a class act to be insulted (even unknowingly and unintentionally) and not treat it as an attack. I decided I wanted to stay at least long enough to learn how to do that sort of thing! Now it is years later, I work full time for Microsoft in the group (GIFT) where she is a Director with over 130 reports. I still have not completely learned the lesson, but there are times that I can see progress....

     

    This post brought to you by "∞" (U+221e, a.k.a. INFINITY)

  • Sorting it all Out

    When MSLU meets ATL or WTL

    • 4 Comments

    (originally posted elsewhere back in March of 2002)

    If you are using the Microsoft Layer for Unicode on Windows 95/98/Me Systems (MSLU) in a project that uses ATL or WTL, there are some things you need to do to make it work.

    1) Avoid the _ATL_MIN_CRT macro -- this macro appears to be incompatible with MSLU.

    2) Problems with garbage text in window title bars -- It is a problem with the usage of ::DefWindowProc and ::CallWindowProc in ATL and WTL.  The way to correct this problem is to at the very start of your program (before any ATL/WTL windowing code is called) add the following code:

    //
    // Resolve UNICOWS's thunk (required)
    //
    ::DefWindowProc (NULL, 0, 0, 0);

    Tim Smith explained it best:

    The problem is that if you create an ATL window prior to ::DefWindowProc being called, then m_pfnSuperWindowProc points to the thunk [in the loader] and not the resolved address.  Then when ATL passes m_pfnSuperWindowProc into ::CallWindowProc as part of the WM_SETTEXT message, MSLU doesn't realize that it is being passed [its own] ::DefWindowProc and thus does an extra level of text conversion.  By invoking ::DefWindowProc at the start of the program, then when ATL creates a window and stores the address of ::DefWindowProc in m_pfnSuperWindowProc, it is storing the address of the MSLU routine that the MSLU ::CallWindowProc realizes does not need conversion.  In general, if you are using ATL/WTL, just add that line of code at the start of your program and be done with it.  It also should be added to any DLL that uses ATL windows [which have the same issue].

    Please note that the above issue has been addressed in WTL 7.0 and thus only applies to earlier versions of WTL.

    The preceeding is what was posted back in March of 2002; what follows is new.

    Back in July of 2004, a developer at Microsoft was looking around rather frantically for the dev. owner of MSLU (a job not made easier by the fact that all of the references are to my old vendor alias rather than my new FTE one). When he finally found me, he had the following report:

    Hi there, I’m hitting an a/v in unicows.dll that only happens under win98 (not winME) . Where can I get sources so I can see exactly what’s going on?

    ...this is for the msn installer; we’re using the official release, version 1.0.4018.0.

    I knew this was a smart guy1 and thought I would try to assist him rather than makr him try to set up source debugging for unicows.dll. I asked for the callstack with the AV, and he quickly got it to me:

    unicows.dll!_gwcslen()  + 0x11
    unicows.dll!_GodotToCpgCchOnHeap@20()  + 0x21
    unicows.dll!_GodotTransmitMessage@44()  + 0x1c2
    unicows.dll!_GodotDefWindowProcW@16()  + 0x2d 
    KERNEL32.DLL!bff958f8()  
    unicows.dll!_GodotTransmitMessage@44()  + 0x1ed
    unicows.dll!_GodotCallWindowProcW@20()  + 0x52
    msninst.dll!ATL::CWindowImplBaseT<ATL::CWindow,ATL::CWinTraits<1442840576,0> >::WindowProc(HWND__ * hWnd=0x01924240, unsigned int uMsg=129, unsigned int wParam=0, long lParam=5889640)  Line 3020 + 0x13   C++
    msninst.dll!ATL::CWindowImplBaseT<ATL::CWindow,ATL::CWinTraits<1442840576,0> >::StartWindowProc(HWND__ * hWnd=0x0000055c, unsigned int uMsg=129, unsigned int wParam=0, long lParam=5889640)  Line 2999 + 0xf  C++
    unicows.dll!_GodotDoCallback@28()  + 0x1a5 
    unicows.dll!_WindowProc@16()  + 0x2a  
    KERNEL32.DLL!bff7363b() 
    KERNEL32.DLL!bff94407() 
    KERNEL32.DLL!bff719b8() 
    msninst.dll!ATL::CWindowImplBaseT<ATL::CWindow,ATL::CWinTraits<1442840576,0> >::Create(HWND__ * hWndParent=0x00000000, ATL::_U_RECT rect={...}, const unsigned short * szWindowName=0x27284008, unsigned long dwStyle=2147483648, unsigned long dwExStyle=0, ATL::_U_MENUorID MenuOrID={...}, unsigned short atom=49905, void * lpCreateParam=0x00000000)  Line 3063  C++
    msninst.dll!ATL::CWindowImpl<CMsnInstaller,ATL::CWindow,ATL::CWinTraits<1442840576,0> >::Create(HWND__ * hWndParent=0x00000000, ATL::_U_RECT rect={...}, const unsigned short * szWindowName=0x27284008, unsigned long dwStyle=2147483648, unsigned long dwExStyle=0, ATL::_U_MENUorID MenuOrID={...}, void * lpCreateParam=0x00000000)  Line 3135 + 0x1d   C++
    msninst.dll!CMsnInstaller::FinalConstruct()  Line 87  C++
    msninst.dll!ATL::CComCreator<ATL::CComObject<CMsnInstaller> >::CreateInstance(void * pv=0x00000000, const _GUID & riid={...}, void * * ppv=0x0059e4e0)  Line 1764 + 0x7C++
    msninst.dll!ATL::CComCreator2<ATL::CComCreator<ATL::CComObject<CMsnInstaller> >,ATL::CComCreator<ATL::CComAggObject<CMsnInstaller> > >::CreateInstance(void * pv=0x00000000, const _GUID & riid={...}, void * * ppv=0x0059e4e0)  Line 1833C++
    msninst.dll!ATL::CComClassFactory::CreateInstance(IUnknown * pUnkOuter=0x00000000, const _GUID & riid={...}, void * * ppvObj=0x0059e4e0)  Line 3183 C++
    OLE32.DLL!7ff2cb20()

    Geek that I am, I suddenly knew what the bug was, but was completely wrong. However, after one guess I was right, it was the same bug as above.

    Only now instead of messing up window text it was crashing!

    He was able to implement the workaround and all was good.

    Now I have actually tried in the past to get the ATL folks to make the same change that Nenad Stefanovic agreed to made in WTL 7.0, but they have refused (usually for for two reasons):

    1. Its kind of a messy hack which most people never have to deal with;
    2. The Win9x platform is not going to be supported much longer anyway

    Now they do own their library and I would probably resent it if they told me how to write MSLU so we have agreed to disagree. Though if any ATL folks wanted to reconsider the issue now that an internal dev has run into a crash bug caused by it, feel free to ping me offline and I can provide details on issue. :-)

     

    1 - It always annoys me a little when people call a crash a "GPF" since we have not really had general protection faults (GPF) for quite some time due to virtual memory. We have AVs (access violations) and IPFs (invalid page faults), but GPFs are pretty hard to come by. I mentally tag people who are more careful about their language a being a bit smarter, perhaps unfairly but at least consistently.

     

    This post brought to you by "" (U+2120, SERVICE MARK)

  • Sorting it all Out

    Challenges behind MSLU: the loader! (Part 1)

    • 5 Comments

    (This post is based on one that was sent to the microsoft.public.platformsdk.mslayerforunicode newsgroup back in June of 2001)

    It all started simply enough: the Windows division decided to fund a project for a Unicode Layer for Win9x to help people be able to fully use the NT functionality that had been around all the time and was getting better each version. Obviously, anything that was going to slow down applications on WinNT/Win2K/WinXP was unacceptable. So there had to be a way to make sure this layer (MSLU) did not slow down a Unicode application running on the NT platform.

    We started with the DELAYLOAD technology, introduced in Visual C++ 6.0. The purpose of delay loading is (according to MSDN):

    "The Visual C++ linker now supports the delayed loading of DLLs. This relieves you of the need to use the Win32 SDK functions LoadLibrary and GetProcAddress to implement DLL delayed loading."

    All well and good, right? Well, they quickly found another side effect: if you were never going to call a function at all, then you could delay the loading of the DLL till hell froze over!

    And then quickly found yet another cool side effect: if you used their capability to override the loading via a hook function, you could redirect the effort and be sure on the NT platform to call the original API instead of the delayloaded entry point in your other DLL. Cool, right?

    Well, we thought so. :-)

    Of course, we quickly ran into problems with this approach. Developers might want to use the delayload stuff themselves, for their own DLLs. They might even have custom hooks already! It would not be too friendly to take over a technology and make people choose which functionality they preferred: delay loading or Unicode on Win9x.

    Luckily, Microsoft has a lot of smart people in it. After working with one of the long-time devs on the Windows team (Bryan Tuttle) and on the linker team over in VC++ (the "father of the Microsoft linker" and original developer of delay load, Dan Spalding), we had a new solution: the MSLU loader!

    (and for the record, all of the credit goes to Bryan and Dan; everything cool I did with the solution was due to the fact that they provided the solution in the first place!)

    Now delay load works by dynamically creating the stubs at compile time that are needed for the program, but we did not need to duplicate *that* functionality since the APIs being wrapped are a static list -- no need for dynamic work here. So the MSLU loader is basically contained in the .LIB file and contains all of the information for every single function that is wrapped or stubbed. That is the reason why a DLL that is less than 200K ended up with a .LIB file that is over 2mb -- because it contains a lot of information (it also contains all of the failure stubs so that if for some reason the API cannot be called due to low memory, etc., the failure case would be handled gracefully).

    Of course, the size might freak people out: who wants to add 2mb to their binary's size? Don't worry, thats not a problem, either. Remember that most APIs are pretty much identical as far as the linker is concerned, even in failure stubs: functions that take three parameters and return FALSE look the same whether its AddMonitorW, EnumDateFormatsW, or SetLocaleInfoW. What this means is that your debug build (no optimizations) will be a little bit bigger since you are going to have a lot of data that is identical for separate APIs, but if you do any kind of optimization (either for size or for speed) then all of these identical bits of code will be merged such that even large, complex applications only add a little bit of size.

    More MSLU loader trivia -- neither 'dumpbin /imports' nor Steve Miller's Dependency Walker can detect the loader, so you have to look for it indirectly. How's that? Well, it is fairly easy to see if every single Unicode API that takes string parameters suddenly disappears from Dependency Walker's tree. You can even use such a technique to look into incorrect unicows.lib integration -- if those imports are on the list then it means you have your .LIB files out of order.

    Does it all sound hideously complex? Well, a lot of work got done in the design so that all of the actual complexity is hidden, and all you have to do is link to the .LIB file, which gives you the MSLU loader free and clear (literally free, since it comes with the downloadable Platform SDK!). It made for a very interesting bit of technology (a custom delay loader) hidden away inside another interesting bit of technology (a Unicode layer for Win9x).

    All of it would have been impossible without some of the really smart people in VC++ and Windows. :-)

  • Sorting it all Out

    MSLU is not "open source" but I think that is really okay

    • 11 Comments

    A few days ago I posted about the Updated EULA for the Microsoft Layer for Unicode (MSLU) and one person left the following comment:

    Unfortunately, it seems that the new EULA for MSLU is still not compatible with open source software. I know that MS sees open source as a bit of a threat and so is trying to close it off as much as possible, it is just disappointing that something as useful as MSLU is closed off this way. The clauses in question are 3.1 (ii) windows only, (iii) no less resrictive agreed EULA and the end clause of 3.1, no further redistribution. I would love to be wrong in my interpretation of this however...

    I replied but I got a bunch of other questions about the reply so I thought I should make it a regular post, with clarifications. Here goes, wuth my response to the above comment and question, with all of the new clarifying text I am adding marked in bold. Note that I am not a lawyer, so I am not really talking about legalities. This is juat a developer talking to a bunch of other developers about how the world looks to me:

    I am not sure what difference it really makes in this case whether MSLU was compatible with open source for the following reasons.

    1) It is not possible to use unicows.dll on any platform other than Win95, 98, or Me. Ever. Not even a simulator of one of them there operating systems, because the DLL depends on a lot of Win9x internals that do not exist anywhere else. In tests here, even on an NT platform a non-console application would tend to crash rather quickly when forced to run with MSLU, which is the reason that you cannot run it any such a speciaal mode.The platfrom limitation in the EULA has the same de facto power as a clause enforcing gravity (which is to say, the circumstances of reality force it on the developer). 

    2) You are allowed to redistribute it (unicows.dll) with a product; the only rule against redistribution of unicows.dll beyond the platform stuff is intended to keep the licensee who agreed to the EULA as the one who architects usage, which is to say who plans out the application that calls the APIs provided by MSLU. Everyone else is a consumer of calls. If you want to be a producer of calls, you have to agree to the license. So what?

    2.5) As a bonus, those who agree to the license get the .PDB file for debugging and the right to call any of the APIs provided in unicows.dll.

    3) MSLU is not open source, but it is "open binary" in the sense that you can use a loader override to replace MSLU function calls any time you prefer your own implementation. This has always been true and in fact I know of people who use their own (already written) Unicode layer based on Avery Bishop's article Design a Single Unicode App that Runs on Both Windows 98 and Windows 2000 but then use the MSLU lib file to do the platform gating since its cooler and more automatic -- all legal and does not even require you to agree to the MSLU license since you are not using any components in the redist. You do have to agree to whatever license the Platform SDK might require, but I assume that it does not have objectionable limitations as far as unicows.lib is concerned since nobody hs ever complained about those terms to me.

    4) All the redist rules intend to say are that you cannot redistribute the package to producers of API calls to MSLU unless they agree to the same license that you did (or one that is more restrictive). So the open source project that can run on Win9x just has to have the developers agree to the same license you did and thats it. Easy. An open source project that is designed to be able to run on Win9x is entirely allowed as long as the people who are involved in the project do that one thing. If not, then the only limitation on them is that they would not be able to run it on Win9x. Which they may not have even cared about doing anyway?

    So the only thing that is "blocked" is a thoretical open source project which uses MSLU but which ships with source and which you do not want to have them agree to the EULA and which they want to run on Win9x as well. MSLU is provided free of charge to help encourage people to write Unicode applications (more info in the article Cathy Wissink and I wrote), so the EULA is really not that high of a price to pay to be able to use it.

    That theoretical open source project has a fairly outrageous set of conditions, and I will lose no sleep over hurting any community of users that is so unwilling to consider restrictions of any kind that they probably would have objected to the gravity clause had we put it into the EULA too....

    I was the person who worked really hard to make sure the EULA would accurately reflect the spirit and intent under which the Microsoft Layer for Unicode on Win9x Systems was originally provided (large companies who asked for EULA clarifications would probably have been happy with just a letter from the Legal department, but regular developers would not be helped in that case).

    I personally have no problem with any form of collaborative development project anywhere, and that includes open source projects. If I were coming do to work for someone I'd hope for at least that much respect was paid to me. It can't hurt that much to give that same respect to Microsoft, at least long enough to hit a YES button in a dialog. :-)

Page 4 of 5 (68 items) 12345