When MSLU meets ATL or WTL

by Michael S. Kaplan, published on 2005/02/04 16:09 -05:00, original URI: http://blogs.msdn.com/b/michkap/archive/2005/02/04/367430.aspx

(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 
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  
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++

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)

# Jonathan on 4 Feb 2005 3:25 PM:

Godot? Was that the code name for MSLU?

# Samuel Beckett (actually, Michael Kaplan) on 4 Feb 2005 4:17 PM:

Indeed, it was!

Yuhong Bao on 4 Jan 2008 4:12 PM:

Actual GPFs can still happen on x86 and x64 processors, but they are more rare in Win32 and does not exist on any other arch.

Michael S. Kaplan on 4 Jan 2008 4:29 PM:

Yes, that is why the footnote says "but GPFs are pretty hard to come by".


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