C# - PInvoke, gotchas on the RegisterClassEx and the CreateWindowEx

本文探讨了使用CreateWindowEx创建窗口时遇到“无法找到窗口类”的异常问题,并提供了详细的代码示例及解决方案,通过调整RegisterClassEx和CreateWindowEx的调用方式来解决此问题。

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

MSDN 写道
lpClassName [in, optional]
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()

CreateWindowEx function

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值