David Connell

Software Developer - Red Gate Software

Common Controls and "invalid window class name"

Published Wednesday, November 09, 2005 12:09 PM

Last week came across a weird issue. A .NET 1.1 program was throwing up a weird exception when running on XP SP1, with a manifest. (The problem disappeared if the manifest was removed). We were sent the stack trace
exception name: System.ComponentModel.Win32Exception
description: Invalid window class name.
handler: user interface
------------------------------ STACK TRACE BEGINS
------------------------------
   at System.Windows.Forms.WindowClass.RegisterClass()
   at System.Windows.Forms.WindowClass.Create(
String className, Int32 classStyle) at System.Windows.Forms.NativeWindow.CreateHandle(CreateParams cp) at System.Windows.Forms.Control.CreateHandle() at System.Windows.Forms.ProgressBar.CreateHandle() at System.Windows.Forms.Control.CreateControl(
Boolean fIgnoreVisible) at System.Windows.Forms.Control.CreateControl(
Boolean fIgnoreVisible) at System.Windows.Forms.Control.CreateControl() at System.Windows.Forms.Control.OnVisibleChanged(EventArgs e) at System.Windows.Forms.ScrollableControl.OnVisibleChanged(
EventArgs e) at System.Windows.Forms.Control.OnParentVisibleChanged(EventArgs e) at System.Windows.Forms.Control.OnVisibleChanged(EventArgs e) at System.Windows.Forms.ScrollableControl.OnVisibleChanged(
EventArgs e) at System.Windows.Forms.Form.OnVisibleChanged(EventArgs e) at System.Windows.Forms.Control.SetVisibleCore(Boolean value) at System.Windows.Forms.Form.SetVisibleCore(Boolean value) at System.Windows.Forms.Control.set_Visible(Boolean value) at System.Windows.Forms.Form.ShowDialog(IWin32Window owner)

This highlighted several issues. Firstly that native windows are only created in .NET when they are needed and not in the constructor or InitializeComponent. Which seems to makes sense given that Windows are a precious resources and should only be created at the last moment, just surprising. The next issue was that it was apparent from the stack trace that the ProgressBar was failing to be created but why? So we dusted down Lutz Roeder’s excellent reflector tool and found out exactly what was going on in RegisterClass. It is as follows
private void RegisterClass()
{ 
  NativeMethods.WNDCLASS_D wndclass_d1 = new NativeMethods.WNDCLASS_D(); 
  if (NativeWindow.userDefWindowProc == IntPtr.Zero) 
  { 
    string text1 = (Marshal.SystemDefaultCharSize == 1) ? 
"DefWindowProcA" : "DefWindowProcW"; NativeWindow.userDefWindowProc = UnsafeNativeMethods.GetProcAddress( UnsafeNativeMethods.GetModuleHandle("user32.dll"), text1); if (NativeWindow.userDefWindowProc == IntPtr.Zero) { throw new Win32Exception(); } } if (this.className == null) { wndclass_d1.hbrBackground =
UnsafeNativeMethods.GetStockObject(5); wndclass_d1.style = this.classStyle; this.defWindowProc = NativeWindow.userDefWindowProc; this.windowClassName = "Window." +
Convert.ToString(this.classStyle, 0x10); this.hashCode = 0; } else { NativeMethods.WNDCLASS_I wndclass_i1 =
new NativeMethods.WNDCLASS_I(); bool flag1 = UnsafeNativeMethods.GetClassInfo(
IntPtr.Zero, this.className, wndclass_i1); int num1 = SafeNativeMethods.GetLastError(); if (!flag1) { throw new Win32Exception(num1, SR.GetString("InvalidWndClsName")); } wndclass_d1.style = wndclass_i1.style; wndclass_d1.cbClsExtra = wndclass_i1.cbClsExtra; wndclass_d1.cbWndExtra = wndclass_i1.cbWndExtra; wndclass_d1.hIcon = wndclass_i1.hIcon; wndclass_d1.hCursor = wndclass_i1.hCursor; wndclass_d1.hbrBackground = wndclass_i1.hbrBackground; wndclass_d1.lpszMenuName =
Marshal.PtrToStringAuto(wndclass_i1.lpszMenuName); this.defWindowProc = wndclass_i1.lpfnWndProc; this.windowClassName = this.className; this.hashCode = this.className.GetHashCode(); } string[] textArray1 = new string[5] { Application.WindowsFormsVersion, ".", this.windowClassName, ".app", Convert.ToString(AppDomain.CurrentDomain.GetHashCode(), 0x10) } ; this.windowClassName = string.Concat(textArray1); this.windowProc = new NativeMethods.WndProc(this.Callback); wndclass_d1.lpfnWndProc = this.windowProc; wndclass_d1.hInstance = UnsafeNativeMethods.GetModuleHandle(null); wndclass_d1.lpszClassName = this.windowClassName; if (UnsafeNativeMethods.RegisterClass(wndclass_d1) == IntPtr.Zero) { this.windowProc = null; throw new Win32Exception(); } this.registered = true; }
After a couple of false starts we realized that the exception was being thrown by the code UnsafeNativeMethods.GetClassInfo. You may be wondering how we were able to get in and find out what was going on?
Well the answer was given by Dan Archer and he pointed us in the direction of Detours. (Check out research.microsoft.com/sn/detours/ for more information. There are some example detours projects on code projects.)
The Detours project effectively allows the developer to log any Win32 API call.

So we decided to log the following API calls LoadLibraryExW, LoadLibraryExA, LoadLibraryW, LoadLibraryA, FreeLibrary, RegisterClassW, GetClassInfoW, UnregisterClassW. We then asked the customer to re-run the program but with this logging enabled.
We were then sent back the following log (I have sumerized it for ease of reading. Comments are in different color.)
Load default DLLs (eg ole32.dll, msvcrt.dll, mscoree.dll, kernel32.dll …) 
LoadLibraryExW(
C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\mscorwks.dll,0,8) LoadLibraryA(SHLWAPI.dll) LoadLibraryExW(comctl32.dll,0,0) Register Common Control Window classes RegisterClassW(12e58c) Style 4003, menu class msctls_progress32 LoadLibraryExW(comctl32.dll,0,0) (loaded many times but fine as it
return back Handler to the original)
Lots of stuff……
LoadLibraryExW(C:\WINDOWS\System32\SQLSRV32.dll,0,8) Load loads of DLLs LoadLibraryA(C:\WINDOWS\System32\COMCTL32.DLL) LoadLibraryExA(C:\WINDOWS\System32\COMCTL32.DLL,0,0) LoadLibraryExW(C:\WINDOWS\System32\COMCTL32.DLL,0,0) Gets the windows class of the common controls…. GetClassInfoW(77340000,msctls_progress32,12c9b0) Does some more stuff FreeLibrary (C:\WINDOWS\System32\SQLSRV32.dll) Free all allocated libs FreeLibrary(c:\Windows\system32\comctl32.dll)
(Unregisters all common controls) GetClassInfoW(77340000,msctls_progress32,12e730) GetClassInfoW(,,) -> c04f Error = 0 UnregisterClassW(msctls_progress32,77340000) Program then continued until attempted to display the progress bar GetClassInfoW(0,msctls_progress32,1074de0) GetClassInfoW(,,) -> 0 Error = 1411 (class not registered)

It was now apparent that SQKSRV32.DLL directly loaded up the old/un-themed common controls dll, which had the nasty side effect of registering and then un-registering the common controls windows classes when the DLL was unloaded. Unfortunately the program had not finished and so it failed when trying to access the common controls for the first time. (This then explained why running without a manifest the program worked correctly.)
We were able to verify that this was the problem by asking the user for their version of MDAC, (which happened to be MDAC 2.8) and then installing it on our test machine. At last we were able to then replicate this user issue. It appears to be XP SP1a/MDAC 2.8 issue.
by James

Comments

No Comments
You need to sign in to comment on this blog

















<November 2005>
SuMoTuWeThFrSa
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910
Using Powershell to Generate Table-Creation Scripts
 For all of us who learn best by trying out examples, Bob Sheldon produces a PowerShell script file for... Read more...

Configuring Exchange Server 2007 to Support Information Rights Management
 In Exchange Server 2007, Information Rights management is easy to set up once you have set up the... Read more...

SQL Response: The dim sum interview
 Richard Morris met David and Nigel of the SQL Response team, in a dim sum Restaurant in Cambridge. They... Read more...

Why This SQL Server DBA is Learning Powershell
 Ron describes how he decided to study Powershell as a single scripting system to automate all the... Read more...

Using Covering Indexes to Improve Query Performance
 Designers of database systems will often assume that the use of a clustered index is always the best... Read more...