Index: win32ss/user/ntuser/class.c =================================================================== --- win32ss/user/ntuser/class.c (revision 72214) +++ win32ss/user/ntuser/class.c (working copy) @@ -255,7 +255,9 @@ // comparisons, remove registration of the atom if not zeroed. if (Class->atomClassName) IntDeregisterClassAtom(Class->atomClassName); - + // Dereference non-versioned class name + if(Class->atomNVClassName) + IntDeregisterClassAtom(Class->atomNVClassName); if (Class->pdce) { DceFreeClassDCE(((PDCE)Class->pdce)->hDC); @@ -420,25 +422,39 @@ IN PUNICODE_STRING ClassName) { RTL_ATOM Atom = (RTL_ATOM)0; - /* Update the base class first */ Class = Class->pclsBase; - - if (!IntRegisterClassAtom(ClassName, - &Atom)) + if(ClassName->Length > 0) { - return FALSE; + if (!IntRegisterClassAtom(ClassName, + &Atom)) + { + ERR("RegisterClassAtom failed ! %x\n", EngGetLastError()); + return FALSE; + } } + else + { + if(IS_ATOM(ClassName->Buffer)) + { + Atom = (ATOM)((ULONG_PTR)ClassName->Buffer & 0xffff); // XXX: are we missing refcount here ? + } + else + { + EngSetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + } - IntDeregisterClassAtom(Class->atomClassName); + IntDeregisterClassAtom(Class->atomNVClassName); - Class->atomClassName = Atom; + Class->atomNVClassName = Atom; /* Update the clones */ Class = Class->pclsClone; while (Class != NULL) { - Class->atomClassName = Atom; + Class->atomNVClassName = Atom; Class = Class->pclsNext; } @@ -1000,6 +1016,7 @@ FASTCALL IntCreateClass(IN CONST WNDCLASSEXW* lpwcx, IN PUNICODE_STRING ClassName, + IN PUNICODE_STRING ClassVersion, IN PUNICODE_STRING MenuName, IN DWORD fnID, IN DWORD dwFlags, @@ -1008,7 +1025,7 @@ { SIZE_T ClassSize; PCLS Class = NULL; - RTL_ATOM Atom; + RTL_ATOM Atom, verAtom; WNDPROC WndProc; PWSTR pszMenuName = NULL; NTSTATUS Status = STATUS_SUCCESS; @@ -1023,6 +1040,13 @@ return NULL; } + if (!IntRegisterClassAtom(ClassVersion, + &verAtom)) + { + ERR("Failed to register version class atom!\n"); + return NULL; + } + ClassSize = sizeof(*Class) + lpwcx->cbClsExtra; if (MenuName->Length != 0) { @@ -1054,7 +1078,8 @@ Class->rpdeskParent = Desktop; Class->pclsBase = Class; - Class->atomClassName = Atom; + Class->atomClassName = verAtom; + Class->atomNVClassName = Atom; Class->fnid = fnID; Class->CSF_flags = dwFlags; @@ -1181,6 +1206,7 @@ Class); Class = NULL; + IntDeregisterClassAtom(verAtom); IntDeregisterClassAtom(Atom); } } @@ -1193,12 +1219,13 @@ UserHeapFree(pszMenuName); IntDeregisterClassAtom(Atom); + IntDeregisterClassAtom(verAtom); EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); } - TRACE("Created class 0x%p with name %wZ and proc 0x%p for atom 0x%x and hInstance 0x%p, global %u\n", - Class, ClassName, Class->lpfnWndProc, Atom, Class->hModule, Class->Global); + TRACE("Created class 0x%p with name %wZ and proc 0x%p for atom 0x%x and version atom 0x%x and hInstance 0x%p, global %u\n", + Class, ClassName, Class->lpfnWndProc, Atom, verAtom, Class->hModule, Class->Global); return Class; } @@ -1429,6 +1456,7 @@ RTL_ATOM UserRegisterClass(IN CONST WNDCLASSEXW* lpwcx, IN PUNICODE_STRING ClassName, + IN PUNICODE_STRING ClassVersion, IN PUNICODE_STRING MenuName, IN DWORD fnID, IN DWORD dwFlags) @@ -1446,7 +1474,7 @@ pi = pti->ppi; // Need only to test for two conditions not four....... Fix more whine tests.... - if ( IntGetAtomFromStringOrAtom( ClassName, &ClassAtom) && + if ( IntGetAtomFromStringOrAtom( ClassVersion, &ClassAtom) && ClassAtom != (RTL_ATOM)0 && !(dwFlags & CSF_SERVERSIDEPROC) ) // Bypass Server Sides { @@ -1481,6 +1509,7 @@ Class = IntCreateClass(lpwcx, ClassName, + ClassVersion, MenuName, fnID, dwFlags, @@ -1501,7 +1530,7 @@ (void)InterlockedExchangePointer((PVOID*)List, Class); - Ret = Class->atomClassName; + Ret = Class->atomNVClassName; } else { @@ -1624,7 +1653,7 @@ /* Query the class name */ Status = RtlQueryAtomInAtomTable(gAtomTable, - Atom ? Atom : Class->atomClassName, + Atom ? Atom : Class->atomNVClassName, NULL, NULL, szTemp, @@ -1658,7 +1687,7 @@ /* Query the atom name */ Status = RtlQueryAtomInAtomTable(gAtomTable, - Atom ? Atom : Class->atomClassName, + Atom ? Atom : Class->atomNVClassName, NULL, NULL, ClassName->Buffer, @@ -2165,7 +2194,7 @@ { PUNICODE_STRING Value = (PUNICODE_STRING)NewLong; - Ret = (ULONG_PTR)Class->atomClassName; + Ret = (ULONG_PTR)Class->atomNVClassName; if (!IntSetClassAtom(Class, Value)) { @@ -2319,6 +2348,7 @@ Class = IntCreateClass( &wc, &ClassName, + &ClassName, &MenuName, DefaultServerClasses[i].fiId, Flags, @@ -2366,7 +2396,7 @@ */ { WNDCLASSEXW CapturedClassInfo = {0}; - UNICODE_STRING CapturedName = {0}, CapturedMenuName = {0}; + UNICODE_STRING CapturedName = {0}, CapturedMenuName = {0}, CapturedVersion = {0}; RTL_ATOM Ret = (RTL_ATOM)0; PPROCESSINFO ppi = GetW32ProcessInfo(); BOOL Exception = FALSE; @@ -2404,6 +2434,7 @@ sizeof(WNDCLASSEXW)); CapturedName = ProbeForReadUnicodeString(ClassName); + CapturedVersion = ProbeForReadUnicodeString(ClsNVersion); ProbeForRead(pClassMenuName, sizeof(CLSMENUNAME), @@ -2439,6 +2470,21 @@ } } + if (CapturedVersion.Length != 0) + { + ProbeForRead(CapturedVersion.Buffer, + CapturedVersion.Length, + sizeof(WCHAR)); + } + else + { + if (!IS_ATOM(CapturedVersion.Buffer)) + { + ERR("NtUserRegisterClassExWOW ClassName Error!\n"); + goto InvalidParameter; + } + } + if (CapturedMenuName.Length != 0) { ProbeForRead(CapturedMenuName.Buffer, @@ -2475,6 +2521,7 @@ /* Register the class */ Ret = UserRegisterClass(&CapturedClassInfo, &CapturedName, + &CapturedVersion, &CapturedMenuName, fnID, Flags); @@ -2520,7 +2567,18 @@ /* Probe the parameters */ if (Offset == GCW_ATOM || Offset == GCLP_MENUNAME) { - Value = ProbeForReadUnicodeString((PUNICODE_STRING)dwNewLong); + /* FIXME: Resource ID can be passed directly without UNICODE_STRING ? */ + if(IS_ATOM(dwNewLong)) + { + Value.MaximumLength = 0; + Value.Length = 0; + Value.Buffer = (PWSTR)dwNewLong; + } + else + { + Value = ProbeForReadUnicodeString((PUNICODE_STRING)dwNewLong); + } + if (Value.Length & 1) { goto InvalidParameter; Index: win32ss/user/ntuser/window.c =================================================================== --- win32ss/user/ntuser/window.c (revision 72214) +++ win32ss/user/ntuser/window.c (working copy) @@ -2403,7 +2403,9 @@ NTSTATUS Status; LARGE_STRING lstrWindowName; LARGE_STRING lstrClassName; + LARGE_STRING lstrClsVersion; UNICODE_STRING ustrClassName; + UNICODE_STRING ustrClsVersion; CREATESTRUCTW Cs; HWND hwnd = NULL; PWND pwnd; @@ -2461,6 +2463,32 @@ ustrClassName.MaximumLength = (USHORT)min(lstrClassName.MaximumLength, MAXUSHORT); } + /* Check if the class version is an atom */ + if (IS_ATOM(plstrClsVersion)) + { + /* It is, pass the atom in the UNICODE_STRING */ + ustrClsVersion.Buffer = (PVOID)plstrClsVersion; + ustrClsVersion.Length = 0; + ustrClsVersion.MaximumLength = 0; + } + else + { + /* It's not, capture the class name */ + Status = ProbeAndCaptureLargeString(&lstrClsVersion, plstrClsVersion); + if (!NT_SUCCESS(Status)) + { + ERR("NtUserCreateWindowEx: failed to capture plstrClsVersion\n"); + /* Set last error, cleanup and return */ + SetLastNtError(Status); + goto cleanup; + } + + /* We pass it on as a UNICODE_STRING */ + ustrClsVersion.Buffer = lstrClsVersion.Buffer; + ustrClsVersion.Length = (USHORT)min(lstrClsVersion.Length, MAXUSHORT); // FIXME: LARGE_STRING truncated + ustrClsVersion.MaximumLength = (USHORT)min(lstrClsVersion.MaximumLength, MAXUSHORT); + } + /* Fill the CREATESTRUCTW */ /* we will keep here the original parameters */ Cs.style = dwStyle; @@ -2479,7 +2507,7 @@ UserEnterExclusive(); /* Call the internal function */ - pwnd = co_UserCreateWindowEx(&Cs, &ustrClassName, plstrWindowName, acbiBuffer); + pwnd = co_UserCreateWindowEx(&Cs, &ustrClsVersion, plstrWindowName, acbiBuffer); if(!pwnd) { Index: win32ss/user/user32/windows/class.c =================================================================== --- win32ss/user/user32/windows/class.c (revision 72214) +++ win32ss/user/user32/windows/class.c (working copy) @@ -710,7 +710,7 @@ break; case GCW_ATOM: - Ret = (ULONG_PTR)Class->atomClassName; + Ret = (ULONG_PTR)Class->atomNVClassName; break; case GCLP_HCURSOR: @@ -784,7 +784,7 @@ break; case GCW_ATOM: - Ret = (ULONG_PTR)Class->atomClassName; + Ret = (ULONG_PTR)Class->atomNVClassName; break; case GCLP_HCURSOR: @@ -1503,7 +1503,7 @@ { Atom = NtUserRegisterClassExWOW( &WndClass, &ClassName, - NULL, //PUNICODE_STRING ClsNVersion, + &ClassName, //PUNICODE_STRING ClsNVersion, &clsMenuName, fnID, dwFlags, Index: win32ss/user/user32/windows/window.c =================================================================== --- win32ss/user/user32/windows/window.c (revision 72214) +++ win32ss/user/user32/windows/window.c (working copy) @@ -267,7 +267,7 @@ { Handle = NtUserCreateWindowEx(dwExStyle, plstrClassName, - NULL, + plstrClassName, &WindowName, dwStyle, x, Index: modules/rostests/apitests/win32nt/CMakeLists.txt =================================================================== --- modules/rostests/apitests/win32nt/CMakeLists.txt (revision 72214) +++ modules/rostests/apitests/win32nt/CMakeLists.txt (working copy) @@ -44,6 +44,7 @@ # ntuser/NtUserCallNoParam.c # ntuser/NtUserCallOneParam.c ntuser/NtUserCountClipboardFormats.c + ntuser/NtUserCreateWindowEx.c # ntuser/NtUserEnumDisplayMonitors.c ntuser/NtUserEnumDisplaySettings.c ntuser/NtUserFindExistingCursorIcon.c Index: modules/rostests/apitests/win32nt/ntuser/NtUserCreateWindowEx.c =================================================================== --- modules/rostests/apitests/win32nt/ntuser/NtUserCreateWindowEx.c (nonexistent) +++ modules/rostests/apitests/win32nt/ntuser/NtUserCreateWindowEx.c (working copy) @@ -0,0 +1,232 @@ +/* + * PROJECT: ReactOS api tests + * LICENSE: GPL - See COPYING in the top level directory + * PURPOSE: Test for NtUserCreateWindowEx + * PROGRAMMERS: + */ + +#include + +static inline HWND CreateWnd( + HINSTANCE hinst, + PLARGE_STRING clsName, + PLARGE_STRING clsVer, + PLARGE_STRING wndName) +{ + return NtUserCreateWindowEx( + WS_EX_OVERLAPPEDWINDOW, + clsName, + clsVer, + wndName, + WS_CAPTION, + CW_USEDEFAULT, + CW_USEDEFAULT, + 100, + 100, + NULL, + NULL, + hinst, + 0, + 0, + NULL); +} + + +/* WndProc for class1 */ + +LRESULT CALLBACK wndProc1(HWND hwnd, UINT msg, WPARAM wPrm, LPARAM lPrm) +{ + return DefWindowProc(hwnd, msg, wPrm, lPrm); +} + +/* WndProc for class2 */ +LRESULT CALLBACK wndProc2(HWND hwnd, UINT msg, WPARAM wPrm, LPARAM lPrm) +{ + return DefWindowProc(hwnd, msg, wPrm, lPrm); +} + + +START_TEST(NtUserCreateWindowEx) +{ + HINSTANCE hinst = GetModuleHandle(NULL); + WNDCLASSEXW wclex = {0}; + WNDCLASSEXW wclex2 = {0}; + WNDCLASSEXW res = {0}; + + /* Unicode strings for NtRegisterClassExWOW */ + UNICODE_STRING cls = {14, 32, L"MyClass"}; + UNICODE_STRING ver_cls = {12, 32, L"v2test"}; + UNICODE_STRING another_cls = {10, 32, L"Dummy"}; + UNICODE_STRING menu = {10, 10, L"MuMnu"}; + + /* LARGE_STRING for NtUserCreateWindowEx */ + LARGE_STRING l_dummy = {14, 32, 0, L"DummyMe"}; + LARGE_STRING l_wndName = {32, 32, 0, L""}; + LARGE_STRING l_cls = {cls.Length, 32, 0, cls.Buffer}; + LARGE_STRING l_ver_cls = {ver_cls.Length, 32, 0, ver_cls.Buffer}; + WCHAR bufMe[255] = {0}; + UNICODE_STRING capture = {255, 255, bufMe}; + PWSTR pwstr = NULL; + CLSMENUNAME clsMenuName, outClsMnu = {0}; + ATOM atom, atom2, yolo; + HWND hwnd; + + clsMenuName.pszClientAnsiMenuName = "MuMnu"; + clsMenuName.pwszClientUnicodeMenuName = menu.Buffer; + clsMenuName.pusMenuName = &menu; + + wclex.cbSize = sizeof(WNDCLASSEXW); + wclex.style = 0; + wclex.lpfnWndProc = wndProc1; + wclex.cbClsExtra = 2; + wclex.cbWndExtra = 4; + wclex.hInstance = hinst; + wclex.hIcon = NULL; + wclex.hCursor = NULL; + wclex.hbrBackground = CreateSolidBrush(RGB(4,7,5)); + wclex.lpszMenuName = menu.Buffer; + wclex.lpszClassName = cls.Buffer; + wclex.hIconSm = NULL; + memcpy(&wclex2, &wclex, sizeof(wclex)); + wclex2.lpfnWndProc = wndProc2; + + /* Register our first version */ + atom = NtUserRegisterClassExWOW( + &wclex, /* wndClass */ + &cls, /* ClassName */ + &cls, /* Version */ + &clsMenuName, /* MenuName */ + 0, + 0, + NULL); + TEST(atom != 0); + + /* Register second version */ + atom2 = NtUserRegisterClassExWOW( + &wclex2, /* wndClass */ + &cls, /* ClassName */ + &ver_cls, /* Version */ + &clsMenuName, /* MenuName */ + 0, + 0, + NULL); + + yolo = NtUserRegisterClassExWOW( + &wclex2, /* wndClass */ + &another_cls, /* ClassName */ + &another_cls, /* Version */ + &clsMenuName, /* MenuName */ + 0, + 0, + NULL); + + TEST(NtUserGetWOWClass(hinst, &ver_cls) != 0); + + TEST(atom2 != 0); + TEST(atom == atom2 && (atom | atom2) != 0); + + /* Now, create our first window */ + hwnd = CreateWnd(hinst, &l_cls, &l_cls, &l_wndName); + TEST(hwnd != 0); + if(hwnd) + { + /* Test some settings about the window */ + TEST((WNDPROC)GetWindowLongPtrW(hwnd, GWLP_WNDPROC) == wndProc1); + + /* Check class name isn't versioned */ + TEST(NtUserGetClassName(hwnd, TRUE, &capture) != 0); + TEST(wcscmp(capture.Buffer, cls.Buffer) == 0); + TEST(wcscmp(capture.Buffer, ver_cls.Buffer) != 0); + ZeroMemory(capture.Buffer, 255); + + /* Check what return GetClassLong */ + TEST(GetClassLong(hwnd, GCW_ATOM) == atom); + TEST(NtUserSetClassLong(hwnd, GCW_ATOM, yolo, FALSE) == atom); + NtUserGetClassName(hwnd, TRUE, &capture); + TEST(wcscmp(capture.Buffer, another_cls.Buffer) == 0); + + /* Finally destroy it */ + DestroyWindow(hwnd); + } + + /* Create our second version */ + hwnd = CreateWnd(hinst, &l_cls, &l_ver_cls, &l_wndName); + TEST(hwnd != 0); + if(hwnd) + { + /* Test settings about window */ + TEST((WNDPROC)GetWindowLongPtrW(hwnd, GWLP_WNDPROC) == wndProc2); + + /* Check class name isn't versioned */ + TEST(NtUserGetClassName(hwnd, TRUE, &capture) != 0); + TEST(wcscmp(capture.Buffer, cls.Buffer) == 0); + TEST(wcscmp(capture.Buffer, ver_cls.Buffer) != 0); + ZeroMemory(capture.Buffer, 255); + + /* Check what return GetClassLong */ + TEST(GetClassLong(hwnd, GCW_ATOM) == atom); + + /* Finally destroy it */ + DestroyWindow(hwnd); + } + + /* Create a nonexistent window */ + hwnd = CreateWnd(hinst, &l_cls, &l_dummy, &l_wndName); + TEST(hwnd == 0); + if(hwnd) DestroyWindow(hwnd); + + /* Get non-versioned class info */ + res.cbSize = sizeof(res); + SetLastError(0); + TEST(NtUserGetClassInfo(hinst, &cls, &res, &pwstr, 0) != 0); + TEST(GetLastError() == 0); + TEST(res.cbSize == wclex.cbSize); + TEST(res.style == wclex.style); + TEST(res.lpfnWndProc == wclex.lpfnWndProc); + TEST(res.cbClsExtra == wclex.cbClsExtra); + TEST(res.cbWndExtra == wclex.cbWndExtra); + TEST(res.hInstance == wclex.hInstance); + TEST(res.hIcon == wclex.hIcon); + TEST(res.hCursor == wclex.hCursor); + TEST(res.hbrBackground == wclex.hbrBackground); + TEST(res.lpszMenuName == 0); + TEST(res.lpszClassName == 0); + TEST(res.hIconSm == wclex.hIconSm); + + /* Get versioned class info */ + TEST(NtUserGetClassInfo(hinst, &ver_cls, &res, &pwstr, 0) != 0); + TEST(GetLastError() == 0); + TEST(res.cbSize == wclex2.cbSize); + TEST(res.style == wclex2.style); + TEST(res.lpfnWndProc == wclex2.lpfnWndProc); + TEST(res.cbClsExtra == wclex2.cbClsExtra); + TEST(res.cbWndExtra == wclex2.cbWndExtra); + TEST(res.hInstance == wclex2.hInstance); + TEST(res.hIcon == wclex2.hIcon); + TEST(res.hCursor == wclex2.hCursor); + TEST(res.hbrBackground == wclex2.hbrBackground); + TEST(res.lpszMenuName == 0); + TEST(res.lpszClassName == 0); + TEST(res.hIconSm == wclex2.hIconSm); + + /* Create a new window from our old class. Since we set a new class atom, + * it should be set to our new atom + */ + hwnd = NULL; + hwnd = CreateWnd(hinst, &l_cls, &l_cls, &l_wndName); + TEST(hwnd != NULL); + if(hwnd) + { + TEST(GetClassLong(hwnd, GCW_ATOM) == yolo); + TEST(NtUserGetClassName(hwnd, TRUE, &capture) != 0); + TEST(wcscmp(capture.Buffer, another_cls.Buffer) == 0); + DestroyWindow(hwnd); + } + + /* Test class destruction */ + TEST(NtUserUnregisterClass(&cls, hinst, (PCLSMENUNAME)0xbad) != 0); + TEST(NtUserUnregisterClass(&ver_cls, hinst, &outClsMnu) != 0); + TEST(NtUserUnregisterClass(&another_cls, hinst, &outClsMnu) != 0); + TEST(NtUserUnregisterClass(&menu, hinst, &outClsMnu) == 0); + +} Index: modules/rostests/apitests/win32nt/testlist.c =================================================================== --- modules/rostests/apitests/win32nt/testlist.c (revision 72214) +++ modules/rostests/apitests/win32nt/testlist.c (working copy) @@ -44,6 +44,7 @@ //extern void func_NtUserCallNoParam(void); //extern void func_NtUserCallOneParam(void); extern void func_NtUserCountClipboardFormats(void); +extern void func_NtUserCreateWindowEx(void); //extern void func_NtUserEnumDisplayMonitors(void); extern void func_NtUserEnumDisplaySettings(void); extern void func_NtUserFindExistingCursorIcon(void); @@ -105,6 +106,7 @@ //{ "NtUserCallNoParam", func_NtUserCallNoParam }, //{ "NtUserCallOneParam", func_NtUserCallOneParam }, { "NtUserCountClipboardFormats", func_NtUserCountClipboardFormats }, + { "NtUserCreateWindowEx", func_NtUserCreateWindowEx }, //{ "NtUserEnumDisplayMonitors", func_NtUserEnumDisplayMonitors }, { "NtUserEnumDisplaySettings", func_NtUserEnumDisplaySettings }, { "NtUserFindExistingCursorIcon", func_NtUserFindExistingCursorIcon },