在ReactOS的应用程序里,每个有窗口的应用程序都需要注册一个窗口类,然后根据窗口类来创建窗口。注册窗口的调用函数就是RegisterClassW,这个函数是UNICODE的版本,其实还有多节字版本,但代码是差不多的。这里主要分析UNICODE版本的实现,其代码如下:
#001 ATOM WINAPI
#002 RegisterClassW(CONST WNDCLASSW *lpWndClass)
#003 {
lpWndClass是应用程序定义窗口类的结构指针。
声明一个扩展窗口类。
#004 WNDCLASSEXW Class;
#005
检查窗口类的指针是否为空,如果为空就是非法,直接返回。
#006 if (lpWndClass == NULL)
#007 return 0;
#008
拷贝老版本的窗口类结构到扩展窗口类结构里。
#009 RtlCopyMemory(&Class.style, lpWndClass, sizeof(WNDCLASSW));
#010 Class.cbSize = sizeof(WNDCLASSEXW);
#011 Class.hIconSm = NULL;
#012
调用函数RegisterClassExW来注册窗口类。
#013 return RegisterClassExW(&Class);
#014 }
接着来分析函数RegisterClassExW的实现,它的代码如下:
#001 ATOM WINAPI
#002 RegisterClassExW(CONST WNDCLASSEXW *lpwcx)
#003 {
#004 ATOM Atom;
#005 WNDCLASSEXW WndClass;
#006 UNICODE_STRING ClassName;
#007 UNICODE_STRING MenuName = {0};
#008 HMENU hMenu = NULL;
#009
判断输入的结构是否有效。
#010 if (lpwcx == NULL || lpwcx->cbSize != sizeof(WNDCLASSEXW) ||
#011 lpwcx->cbClsExtra < 0 || lpwcx->cbWndExtra < 0 ||
#012 lpwcx->lpszClassName == NULL)
#013 {
#014 SetLastError(ERROR_INVALID_PARAMETER);
#015 return 0;
#016 }
#017
#018 /*
#019 * On real Windows this looks more like:
#020 * if (lpwcx->hInstance == User32Instance &&
#021 * *(PULONG)((ULONG_PTR)NtCurrentTeb() + 0x6D4) & 0x400)
#022 * But since I have no idea what the magic field in the
#023 * TEB structure means, I rather decided to omit that.
#024 * -- Filip Navara
#025 */
如果窗口的实例句柄是用户的句柄,就退出。
#026 if (lpwcx->hInstance == User32Instance)
#027 {
#028 SetLastError(ERROR_INVALID_PARAMETER);
#029 return 0;
#030 }
#031
如果实例句柄为空,就使用函数GetModuleHandleW获取当前模板的句柄。
#032 /* Yes, this is correct. We should modify the passed structure. */
#033 if (lpwcx->hInstance == NULL)
#034 ((WNDCLASSEXW*)lpwcx)->hInstance = GetModuleHandleW(NULL);
#035
拷贝窗口类的结构。
#036 RtlCopyMemory(&WndClass, lpwcx, sizeof(WNDCLASSEXW));
#037
如果应用程序没有创建小图标,那么这里就创建一个默认的小图标。
#038 if (NULL == WndClass.hIconSm)
#039 {
#040 WndClass.hIconSm = CreateSmallIcon(WndClass.hIcon);
#041 }
#042
如果有系统菜单,就需要加载系统菜单。
#043 if (WndClass.lpszMenuName != NULL)
#044 {
转换菜单的名称。
#045 if (!IS_INTRESOURCE(WndClass.lpszMenuName))
#046 {
#047 if (WndClass.lpszMenuName[0])
#048 {
#049 RtlInitUnicodeString(&MenuName, WndClass.lpszMenuName);
#050 }
#051 }
#052 else
#053 {
#054 MenuName.Buffer = (LPWSTR)WndClass.lpszMenuName;
#055 }
#056
从资源里加载菜单。
#057 if (MenuName.Buffer != NULL)
#058 hMenu = LoadMenuW(WndClass.hInstance, WndClass.lpszMenuName);
#059 }
#060
转换窗口类的名称。
#061 if (IS_ATOM(WndClass.lpszClassName))
#062 {
#063 ClassName.Length =
#064 ClassName.MaximumLength = 0;
#065 ClassName.Buffer = (LPWSTR)WndClass.lpszClassName;
#066 }
#067 else
#068 {
#069 RtlInitUnicodeString(&ClassName, WndClass.lpszClassName);
#070 }
#071
通过系统中断调用内核函数NtUserRegisterClassEx。
#072 Atom = (ATOM)NtUserRegisterClassEx(&WndClass,
#073 &ClassName,
#074 &MenuName,
#075 NULL,
#076 0,
#077 hMenu);
#078
#079 TRACE("atom=%04x wndproc=%p hinst=%p bg=%p style=%08x clsExt=%d winExt=%d class=%p/n",
#080 Atom, lpwcx->lpfnWndProc, lpwcx->hInstance, lpwcx->hbrBackground,
#081 lpwcx->style, lpwcx->cbClsExtra, lpwcx->cbWndExtra, WndClass);
#082
#083 return Atom;
#084 }
从前面的分析可以知道,函数NtUserRegisterClassEx调用,也是通过系统中断来调用内核函数,也就是调用Win32k.sys文件里的函数。