NtUserRegisterClassEx函数是Win32k.sys里实现窗口类的注册,那么窗口类的注册是什么意思呢?到底注册是为了什么样的目的呢?下面就通过实现代码的分析来解决这些问题,代码如下:
#001 RTL_ATOM APIENTRY
#002 NtUserRegisterClassEx(IN CONST WNDCLASSEXW* lpwcx,
#003 IN PUNICODE_STRING ClassName,
#004 IN PUNICODE_STRING MenuName,
#005 IN WNDPROC wpExtra,
#006 IN DWORD Flags,
#007 IN HMENU hMenu)
#008
#009 /*
#010 * FUNCTION:
#011 * Registers a new class with the window manager
#012 * ARGUMENTS:
#013 * lpwcx = Win32 extended window class structure
#014 * bUnicodeClass = Whether to send ANSI or unicode strings
#015 * to window procedures
#016 * wpExtra = Extra window procedure, if this is not null, its used for the second window procedure for standard controls.
#017 * RETURNS:
#018 * Atom identifying the new class
#019 */
#020 {
#021 WNDCLASSEXW CapturedClassInfo = {0};
#022 UNICODE_STRING CapturedName = {0}, CapturedMenuName = {0};
#023 RTL_ATOM Ret = (RTL_ATOM)0;
#024
如果注册标志有问题,就直接返回出错。
#025 if (Flags & ~REGISTERCLASS_ALL)
#026 {
#027 SetLastWin32Error(ERROR_INVALID_FLAGS);
#028 return Ret;
#029 }
#030
增加用户进入锁,以便后面的代码进行临界区访问。
#031 UserEnterExclusive();
#032
使用异常机制处理用户输入来的参数。
#033 _SEH2_TRY
#034 {
查注册类的结构大小是否符合内核的注册类的结构大小,如果不符合就返回出错。
#035 /* Probe the parameters and basic parameter checks */
#036 if (ProbeForReadUint(&lpwcx->cbSize) != sizeof(WNDCLASSEXW))
#037 {
#038 goto InvalidParameter;
#039 }
#040
检查读取是否出错。
#041 ProbeForRead(lpwcx,
#042 sizeof(WNDCLASSEXW),
#043 sizeof(ULONG));
拷贝用户空间的注册类信息。
#044 RtlCopyMemory(&CapturedClassInfo,
#045 lpwcx,
#046 sizeof(WNDCLASSEXW));
#047
读取注册窗口类的名称。
#048 CapturedName = ProbeForReadUnicodeString(ClassName);
读取注册窗口类的菜单名称。
#049 CapturedMenuName = ProbeForReadUnicodeString(MenuName);
#050
窗口类的非法参数检查。
#051 if (CapturedName.Length & 1 || CapturedMenuName.Length & 1 ||
#052 CapturedClassInfo.cbClsExtra < 0 ||
#053 CapturedClassInfo.cbClsExtra + CapturedName.Length +
#054 CapturedMenuName.Length + sizeof(WINDOWCLASS) < CapturedClassInfo.cbClsExtra ||
#055 CapturedClassInfo.cbWndExtra < 0 ||
#056 CapturedClassInfo.hInstance == NULL)
#057 {
#058 goto InvalidParameter;
#059 }
#060
#061 if (CapturedName.Length != 0)
#062 {
#063 ProbeForRead(CapturedName.Buffer,
#064 CapturedName.Length,
#065 sizeof(WCHAR));
#066 }
#067 else
#068 {
#069 if (!IS_ATOM(CapturedName.Buffer))
#070 {
#071 goto InvalidParameter;
#072 }
#073 }
#074
#075 if (CapturedMenuName.Length != 0)
#076 {
#077 ProbeForRead(CapturedMenuName.Buffer,
#078 CapturedMenuName.Length,
#079 sizeof(WCHAR));
#080 }
#081 else if (CapturedMenuName.Buffer != NULL &&
#082 !IS_INTRESOURCE(CapturedMenuName.Buffer))
#083 {
#084 InvalidParameter:
#085 SetLastWin32Error(ERROR_INVALID_PARAMETER);
#086 _SEH2_LEAVE;
#087 }
#088
调用函数UserRegisterClass来更进一步处理注册过程。
#089 /* Register the class */
#090 Ret = UserRegisterClass(&CapturedClassInfo,
#091 &CapturedName,
#092 &CapturedMenuName,
#093 hMenu, /* FIXME - pass pointer */
#094 wpExtra,
#095 Flags);
#096
#097 }
#098 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
#099 {
#100 SetLastNtError(_SEH2_GetExceptionCode());
#101 }
#102 _SEH2_END;
#103
退出临界区。
#104 UserLeave();
#105
#106 return Ret;
#107 }
#108
上面调用函数UserRegisterClass来更进一步处理,因而再接着分析这个函数的实现,它的代码如下:
#001 RTL_ATOM
#002 UserRegisterClass(IN CONST WNDCLASSEXW* lpwcx,
#003 IN PUNICODE_STRING ClassName,
#004 IN PUNICODE_STRING MenuName,
#005 IN HANDLE hMenu, /* FIXME */
#006 IN WNDPROC wpExtra,
#007 IN DWORD dwFlags)
#008 {
#009 PTHREADINFO pti;
#010 PW32THREADINFO ti;
#011 PW32PROCESSINFO pi;
#012 PWINDOWCLASS Class;
#013 RTL_ATOM ClassAtom;
#014 RTL_ATOM Ret = (RTL_ATOM)0;
#015
#016 /* NOTE: Accessing the buffers in ClassName and MenuName may raise exceptions! */
#017
获取当前线程信息。
#018 pti = PsGetCurrentThreadWin32Thread();
#019 ti = GetW32ThreadInfo();
#020 if (ti == NULL || !ti->kpi->RegisteredSysClasses)
#021 {
#022 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
#023 return (RTL_ATOM)0;
#024 }
#025
#026 pi = ti->kpi;
#027
查找以前是否注册过相同名称的窗口类。
#028 /* try to find a previously registered class */
#029 ClassAtom = IntGetClassAtom(ClassName,
#030 lpwcx->hInstance,
#031 pi,
#032 &Class,
#033 NULL);
#034 if (ClassAtom != (RTL_ATOM)0)
#035 {
如果窗口类已经注册过,就返回相应的出错信息。
#036 if (lpwcx->style & CS_GLOBALCLASS)
#037 {
#038 // global classes shall not have same names as system classes
#039 if (Class->Global || Class->System)
#040 {
#041 DPRINT("Class 0x%p does already exist!/n", ClassAtom);
#042 SetLastWin32Error(ERROR_CLASS_ALREADY_EXISTS);
#043 return (RTL_ATOM)0;
#044 }
#045 }
#046 else if ( !Class->Global && !Class->System)
#047 {
#048 // local class already exists
#049 DPRINT("Class 0x%p does already exist!/n", ClassAtom);
#050 SetLastWin32Error(ERROR_CLASS_ALREADY_EXISTS);
#051 return (RTL_ATOM)0;
#052 }
#053 }
#054
调用函数IntCreateClass来创建一个窗口类。
#055 Class = IntCreateClass(lpwcx,
#056 ClassName,
#057 MenuName,
#058 wpExtra,
#059 dwFlags,
#060 pti->Desktop,
#061 pi);
#062
如果创建注册类成功,就把这个窗口类保存到系统相应的列表里。
#063 if (Class != NULL)
#064 {
#065 PWINDOWCLASS *List;
#066
保存菜单句柄。
#067 /* FIXME - pass the PMENU pointer to IntCreateClass instead! */
#068 Class->hMenu = hMenu;
#069
根据注册类的使用范围来保存不同的系统列表里。
#070 /* Register the class */
#071 if (Class->System)
#072 List = &pi->SystemClassList;
#073 else if (Class->Global)
#074 List = &pi->GlobalClassList;
#075 else
#076 List = &pi->LocalClassList;
#077
列表插入操作。
#078 Class->Next = *List;
#079 (void)InterlockedExchangePointer((PVOID*)List,
#080 Class);
#081
#082 Ret = Class->Atom;
#083 }
#084
#085 return Ret;
#086 }
通过上面的分析,可以了解到注册一个窗口类,就是分配一个新的窗口类内存结构,然后设置结构的字段,最后把它保存到合适的列表里。