CreateWindowExW函数主要用来根据已经注册窗口类来创建一个窗口。它在User32.dll实现代码如下:
#001 HWND WINAPI
#002 CreateWindowExW(DWORD dwExStyle,
#003 LPCWSTR lpClassName,
#004 LPCWSTR lpWindowName,
#005 DWORD dwStyle,
#006 int x,
#007 int y,
#008 int nWidth,
#009 int nHeight,
#010 HWND hWndParent,
#011 HMENU hMenu,
#012 HINSTANCE hInstance,
#013 LPVOID lpParam)
#014 {
#015 MDICREATESTRUCTW mdi;
#016 HWND hwnd;
#017
如果创建的窗口是MDI的子窗口,就需要进入下面处理。
#018 if (dwExStyle & WS_EX_MDICHILD)
#019 {
#020 POINT mPos[2];
#021 UINT id = 0;
#022 HWND top_child;
#023
设置MDI窗口的属性。
#024 /* lpParams of WM_[NC]CREATE is different for MDI children.
#025 * MDICREATESTRUCT members have the originally passed values.
#026 */
#027 mdi.szClass = lpClassName;
#028 mdi.szTitle = lpWindowName;
#029 mdi.hOwner = hInstance;
#030 mdi.x = x;
#031 mdi.y = y;
#032 mdi.cx = nWidth;
#033 mdi.cy = nHeight;
#034 mdi.style = dwStyle;
#035 mdi.lParam = (LPARAM)lpParam;
#036
#037 lpParam = (LPVOID)&mdi;
#038
检查MDI的子窗口是否有不允许出现的窗口显示类型。
#039 if (GetWindowLongW(hWndParent, GWL_STYLE) & MDIS_ALLCHILDSTYLES)
#040 {
#041 if (dwStyle & WS_POPUP)
#042 {
#043 WARN("WS_POPUP with MDIS_ALLCHILDSTYLES is not allowed/n");
#044 return(0);
#045 }
#046 dwStyle |= (WS_CHILD | WS_CLIPSIBLINGS);
#047 }
#048 else
#049 {
#050 dwStyle &= ~WS_POPUP;
#051 dwStyle |= (WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CAPTION |
#052 WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX);
#053 }
#054
获取父窗口的子窗口。
#055 top_child = GetWindow(hWndParent, GW_CHILD);
#056
恢复当前显示的子窗口。
#057 if (top_child)
#058 {
#059 /* Restore current maximized child */
#060 if((dwStyle & WS_VISIBLE) && IsZoomed(top_child))
#061 {
#062 TRACE("Restoring current maximized child %p/n", top_child);
#063 SendMessageW( top_child, WM_SETREDRAW, FALSE, 0 );
#064 ShowWindow(top_child, SW_RESTORE);
#065 SendMessageW( top_child, WM_SETREDRAW, TRUE, 0 );
#066 }
#067 }
#068
计算子窗口要显示的位置。
#069 MDI_CalcDefaultChildPos(hWndParent, -1, mPos, 0, &id);
#070
如果不是弹出的窗口类型,窗口有菜单。
#071 if (!(dwStyle & WS_POPUP)) hMenu = (HMENU)id;
#072
计算窗口的位置和窗口的大小。
#073 if (dwStyle & (WS_CHILD | WS_POPUP))
#074 {
#075 if (x == CW_USEDEFAULT || x == CW_USEDEFAULT16)
#076 {
#077 x = mPos[0].x;
#078 y = mPos[0].y;
#079 }
#080 if (nWidth == CW_USEDEFAULT || nWidth == CW_USEDEFAULT16 || !nWidth)
#081 nWidth = mPos[1].x;
#082 if (nHeight == CW_USEDEFAULT || nHeight == CW_USEDEFAULT16 || !nHeight)
#083 nHeight = mPos[1].y;
#084 }
#085 }
#086
调用函数User32CreateWindowEx来更进一步窗口管理。
#087 hwnd = User32CreateWindowEx(dwExStyle,
#088 (LPCSTR) lpClassName,
#089 (LPCSTR) lpWindowName,
#090 dwStyle,
#091 x,
#092 y,
#093 nWidth,
#094 nHeight,
#095 hWndParent,
#096 hMenu,
#097 hInstance,
#098 lpParam,
#099 TRUE);
#100 return hwnd;
#101 }
接着再来分析函数User32CreateWindowEx的实现,代码如下:
#001 HWND WINAPI
#002 User32CreateWindowEx(DWORD dwExStyle,
#003 LPCSTR lpClassName,
#004 LPCSTR lpWindowName,
#005 DWORD dwStyle,
#006 int x,
#007 int y,
#008 int nWidth,
#009 int nHeight,
#010 HWND hWndParent,
#011 HMENU hMenu,
#012 HINSTANCE hInstance,
#013 LPVOID lpParam,
#014 BOOL Unicode)
#015 {
#016 UNICODE_STRING WindowName;
#017 UNICODE_STRING ClassName;
#018 WNDCLASSEXA wceA;
#019 WNDCLASSEXW wceW;
#020 HWND Handle;
#021
#022 #if 0
#023 DbgPrint("[window] User32CreateWindowEx style %d, exstyle %d, parent %d/n", dwStyle, dwExStyle, hWndParent);
#024 #endif
#025
检查和转换窗口类的名称。
#026 if (IS_ATOM(lpClassName))
#027 {
#028 RtlInitUnicodeString(&ClassName, NULL);
#029 ClassName.Buffer = (LPWSTR)lpClassName;
#030 }
#031 else
#032 {
#033 if(Unicode)
#034 RtlInitUnicodeString(&ClassName, (PCWSTR)lpClassName);
#035 else
#036 {
#037 if (!RtlCreateUnicodeStringFromAsciiz(&(ClassName), (PCSZ)lpClassName))
#038 {
#039 SetLastError(ERROR_OUTOFMEMORY);
#040 return (HWND)0;
#041 }
#042 }
#043 }
#044
检查和处理窗口的名称。
#045 if (Unicode)
#046 RtlInitUnicodeString(&WindowName, (PCWSTR)lpWindowName);
#047 else
#048 {
#049 if (!RtlCreateUnicodeStringFromAsciiz(&WindowName, (PCSZ)lpWindowName))
#050 {
#051 if (!IS_ATOM(lpClassName))
#052 {
#053 RtlFreeUnicodeString(&ClassName);
#054 }
#055 SetLastError(ERROR_OUTOFMEMORY);
#056 return (HWND)0;
#057 }
#058 }
#059
从窗口类里获取菜单并加载。
#060 if(!hMenu && (dwStyle & (WS_OVERLAPPEDWINDOW | WS_POPUP)))
#061 {
#062 if(Unicode)
#063 {
#064 wceW.cbSize = sizeof(WNDCLASSEXW);
#065 if(GetClassInfoExW(hInstance, (LPCWSTR)lpClassName, &wceW) && wceW.lpszMenuName)
#066 {
#067 hMenu = LoadMenuW(hInstance, wceW.lpszMenuName);
#068 }
#069 }
#070 else
#071 {
#072 wceA.cbSize = sizeof(WNDCLASSEXA);
#073 if(GetClassInfoExA(hInstance, lpClassName, &wceA) && wceA.lpszMenuName)
#074 {
#075 hMenu = LoadMenuA(hInstance, wceA.lpszMenuName);
#076 }
#077 }
#078 }
#079
下面调用内核函数NtUserCreateWindowEx来创建窗口。
#080 Handle = NtUserCreateWindowEx(dwExStyle,
#081 &ClassName,
#082 &WindowName,
#083 dwStyle,
#084 x,
#085 y,
#086 nWidth,
#087 nHeight,
#088 hWndParent,
#089 hMenu,
#090 hInstance,
#091 lpParam,
#092 SW_SHOW,
#093 FALSE,
#094 0);
#095
#096 #if 0
#097 DbgPrint("[window] NtUserCreateWindowEx() == %d/n", Handle);
#098 #endif
#099
删除UNICODE占用的空间。
#100 if(!Unicode)
#101 {
#102 RtlFreeUnicodeString(&WindowName);
#103
#104 if (!IS_ATOM(lpClassName))
#105 {
#106 RtlFreeUnicodeString(&ClassName);
#107 }
#108 }
返回创建成功的窗口句柄。
#109 return Handle;
#110 }