I get an exception message like "cannot find window class" with the following code snippet.
bool bRet; WNDCLASS wc; lpszCmdLine = null; hInstance = System.Diagnostics.Process.GetCurrentProcess().Handle; WNDCLASSEX wndClass = new WNDCLASSEX(); wndClass.cbSize = Marshal.SizeOf(typeof(WNDCLASSEX)); string szName = "HelloWin"; wndClass.style = (int) (ClassStyles.HorizontalRedraw | ClassStyles.VerticalRedraw); wndClass.lpfnWndProc = Marshal.GetFunctionPointerForDelegate((WndProc)((hWnd, message, wParam, lParam) => { IntPtr hdc; PAINTSTRUCT ps; RECT rect; switch ((WM)message) { case WM.PAINT: hdc = WinAPI.BeginPaint(hWnd, out ps); WinAPI.GetClientRect(hWnd, out rect); WinAPI.DrawText(hdc, "Hello, Windows 98!", -1, ref rect, Win32_DT_Constant.DT_SINGLELINE | Win32_DT_Constant.DT_CENTER | Win32_DT_Constant.DT_VCENTER); WinAPI.EndPaint(hWnd, ref ps); return IntPtr.Zero; break; case WM.DESTROY: WinAPI.PostQuitMessage(0); return IntPtr.Zero; break; } return WinAPI.DefWindowProc(hWnd, (WM)message, wParam, lParam); } )); wndClass.cbClsExtra = 0; wndClass.cbWndExtra = 0; wndClass.hInstance = hInstance; wndClass.hIcon = WinAPI.LoadIcon( IntPtr.Zero, new IntPtr((int)SystemIcons.IDI_APPLICATION)); //wndClass.hCursor = WinAPI.LoadCursor(IntPtr.Zero, (int)IdcStandardCursor.IDC_ARROW); wndClass.hCursor = WinAPI.LoadCursor(IntPtr.Zero, (int)Win32_IDC_Constants.IDC_ARROW); wndClass.hbrBackground = WinAPI.GetStockObject(StockObjects.WHITE_BRUSH); wndClass.lpszMenuName = null; wndClass.lpszClassName = szName; //WindowStyleEx.WS_EX_OVERLAPPEDWINDOW ushort regResult = (ushort)WinAPI.RegisterClassEx(ref wndClass); // change the varie RegisterClassEx2 if (regResult == 0) { int lastError = Marshal.GetLastWin32Error(); string errorMessage = new Win32Exception(lastError).Message; WinAPI.MessageBox(IntPtr.Zero, "This program requires windows NT!", szName, (int) MessageBoxOptions.IconError); } // this varie of CreateWindowEx do no work IntPtr hwnd = WinAPI.CreateWindowEx( 0, szName, "The Hello Program", WindowStyles.WS_OVERLAPPEDWINDOW, Win32_CW_Constant.CW_USEDEFAULT, Win32_CW_Constant.CW_USEDEFAULT, Win32_CW_Constant.CW_USEDEFAULT, Win32_CW_Constant.CW_USEDEFAULT, IntPtr.Zero, IntPtr.Zero, hInstance, IntPtr.Zero); if (hwnd == IntPtr.Zero) { int lastError = Marshal.GetLastWin32Error(); string errorMessage = new Win32Exception(lastError).Message; } WinAPI.ShowWindow(hwnd, ShowWindowCommands.Normal); WinAPI.UpdateWindow(hwnd); WinAPI.UpdateWindow(hwnd); MSG msg; while (WinAPI.GetMessage(out msg, IntPtr.Zero, 0, 0) != 0) { WinAPI.TranslateMessage(ref msg); WinAPI.DispatchMessage(ref msg); }
The error happens at this block of code.
if (hwnd == IntPtr.Zero) { int lastError = Marshal.GetLastWin32Error(); string errorMessage = new Win32Exception(lastError).Message; }
Why this is failing, we are creating a WNDCLASSEX, and initialize the ClassName to "HelloWin", and then we try to create a windows by that name "HelloWin", but it told us that "Cannot find the window class"? why ?
Check on the refernces, and MSDN on the CreateWindowEx function.
Type: LPCTSTR
A null-terminated string or a class atom created by a previous call to the RegisterClass or RegisterClassEx function. The atom must be in the low-order word of lpClassName; the high-order word must be zero. If lpClassName is a string, it specifies the window class name. The class name can be any name registered with RegisterClass or RegisterClassEx, provided that the module that registers the class is also the module that creates the window. The class name can also be any of the predefined system class names.
it seems there is a known issue regarding the null terminated string represented clas name to the CreateWindowEx function, so we SHOULD use the ATOM variant of the CreateWindowEx function. So we will revise the RegisterClassEx and the CreatWindowEx call as such.
UInt16 regRest = WinAPI.RegisterClassEx2(ref wndClass);
and
IntPtr hwnd = WinAPI.CreateWindowEx2( 0, regRest, "The hello proram", WindowStyles.WS_OVERLAPPEDWINDOW, Win32_CW_Constant.CW_USEDEFAULT, Win32_CW_Constant.CW_USEDEFAULT, Win32_CW_Constant.CW_USEDEFAULT, Win32_CW_Constant.CW_USEDEFAULT, IntPtr.Zero, IntPtr.Zero, hInstance, IntPtr.Zero);
So, as you can see the result of the RegisterClassEx call - the ATOM value is passed to the CreateWindowEx2 function..
In order to support this kind of call, we have to declare variant of the RegisterClassEx or CreateWindowEx as such.
// To use CreateWindow, just pass 0 as the first argument. [DllImport("user32.dll", SetLastError = true, EntryPoint="CreateWindowEx")] public static extern IntPtr CreateWindowEx( WindowStylesEx dwExStyle, string lpClassName, string lpWindowName, WindowStyles dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam); // Create a window, but accept a atom value. [DllImport("user32.dll", SetLastError = true, EntryPoint="CreateWindowEx")] public static extern IntPtr CreateWindowEx2( WindowStylesEx dwExStyle, UInt16 lpClassName, string lpWindowName, WindowStyles dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam);
so we have import the CreateWindowEx method which accepts a String class name just as the CreateWndowEx, and the method which takes ATOM value as the class name and rename it to CreateWindowEx2..
and because return type denote a ATOM value, you may have the following RegisterClassEx calls .
[DllImport("user32.dll")] //public static extern short RegisterClassEx([In] ref WNDCLASS lpwcx); public static extern short RegisterClassEx([In] ref WNDCLASSEX lpwcx); [DllImport("user32.dll", SetLastError = true, EntryPoint = "RegisterClassEx")] public static extern UInt16 RegisterClassEx2([In] ref WNDCLASSEX lpwcx);
An ATOM value is a UInt16 idenfier. The RegisterClassEx2 function has given a different name RegisterClassEx2.
References:
Problems with p/Invoke CreateWindowEx() and RegisterEx()