mirror of
https://github.com/reactos/reactos.git
synced 2024-07-03 19:24:20 +00:00
![Joachim Henze](/assets/img/avatar_default.png)
The main motivation to port all that back is to get rid of Win32K-BSODs 0x50 like CORE-13907 that could be triggered by user-mode-applications. Technical reason was CORE-14857 "RtlImageNtHeaderEx needs SEH" and issues with hooks+callbacks. All other tickets mentioned in here were regressions that we experienced on the long journey towards perfecting the fixes on master head, and those were not happening yet in the older builds, so they are mentioned in here only to allow tracking where that code went into. Thanks to Jim Tabor, Mark Jansen & Thomas Faber By squashed backmerge of the following commits: 0.4.15-dev-3440-ga89844f740
0.4.15-dev-3430-g9cff384c22
0.4.14-dev-1287-g568b6d0558
0.4.14-dev-1255-g177ae91bf6
0.4.14-dev-1241-g915a5764a9
0.4.14-dev-1240-gd8add40e89
0.4.14-dev-1002-gfeb7df42b4
JIRA-tickets: CORE-13907, CORE-16769, CORE-14857, CORE-17856, CORE-17857, partially CORE-13019. We also had some more dupes of CORE-13907 in JIRA, but I linked all of them (transitively) towards the initial report, and resolved them without setting a fixVer for them. In more detail: -------------- [USER32] Fix BSOD 0x50 in 'WineVDM + Castle Of Winds' CORE-17856 CORE-17857 CORE-17856 BSOD 0x50 when starting Castle of the Winds second time, with WineVDM CORE-17857 BSOD 0x50 on shutdown after closing Castle of the Winds with WineVDM 0.4.15-dev-3430-g9cff384c22
0.4.15-dev-3440-ga89844f740
-------------- [NTUSER] Fix Strings and Format to Hooks Allocate heap instead of data segment to be used for callbacks on user side. Move and correct initial hook call out setup. Use it in more than one hook call. This fixes issues with strings out of alignment and use of kernel pointers. See CORE-13907 (HXD-portable BSOD) and CORE-16769 (HXD-portable exception) Small wow update. cherry picked from commit 0.4.14-dev-1287-g568b6d0558
-------------- [RTL] Introduce RtlpImageNtHeader, which implements the required functionality. ntdll and ntoskrnl now have a wrapper for this, with SEH. This protects the function against malformed / bad images, whilst still being able to use the code in freeldr et al. Idea from Thomas. CORE-14857 cherry picked from commit 0.4.14-dev-1255-g177ae91bf6
-------------- [WIN32SS] Form Sanity to Hook Callbacks Fix WH_CALLWNDPROC/RET data to user hook calls. Helps with CORE-13907 "(HXD-portable BSOD)" Fixes the logging of "(win32ss/user/ntuser/callback.c:748) err: Failure to make Callback! Status 0xc00000fd" within CORE-13019, but not the whole ticket. cherry picked from commit 0.4.14-dev-1241-g915a5764a9
-------------- [USER32] Fix null return. See CORE-16769 "HxD 1.7.7.0 portable unhandled exception" cherry picked from commit 0.4.14-dev-1240-gd8add40e89
-------------- [WIN32SS] Fix a typo in dbg print cherry picked from commit 0.4.14-dev-1002-gfeb7df42b4
1946 lines
47 KiB
C
1946 lines
47 KiB
C
/*
|
|
* PROJECT: ReactOS user32.dll
|
|
* COPYRIGHT: GPL - See COPYING in the top level directory
|
|
* FILE: win32ss/user/user32/windows/class.c
|
|
* PURPOSE: Window classes
|
|
* PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
|
|
* UPDATE HISTORY:
|
|
* 09-05-2001 CSH Created
|
|
*/
|
|
|
|
#include <user32.h>
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(user32);
|
|
|
|
#define USE_VERSIONED_CLASSES
|
|
|
|
/* From rtl/actctx.c and must match! */
|
|
struct strsection_header
|
|
{
|
|
DWORD magic;
|
|
ULONG size;
|
|
DWORD unk1[3];
|
|
ULONG count;
|
|
ULONG index_offset;
|
|
DWORD unk2[2];
|
|
ULONG global_offset;
|
|
ULONG global_len;
|
|
};
|
|
|
|
struct wndclass_redirect_data
|
|
{
|
|
ULONG size;
|
|
DWORD res;
|
|
ULONG name_len;
|
|
ULONG name_offset; /* versioned name offset */
|
|
ULONG module_len;
|
|
ULONG module_offset;/* container name offset */
|
|
};
|
|
|
|
//
|
|
// Use wine hack to process extended context classes.
|
|
//
|
|
/***********************************************************************
|
|
* is_comctl32_class
|
|
*/
|
|
LPCWSTR is_comctl32_class( const WCHAR *name )
|
|
{
|
|
static const WCHAR classesW[][20] =
|
|
{
|
|
{'C','o','m','b','o','B','o','x','E','x','3','2',0},
|
|
{'m','s','c','t','l','s','_','h','o','t','k','e','y','3','2',0},
|
|
{'m','s','c','t','l','s','_','p','r','o','g','r','e','s','s','3','2',0},
|
|
{'m','s','c','t','l','s','_','s','t','a','t','u','s','b','a','r','3','2',0},
|
|
{'m','s','c','t','l','s','_','t','r','a','c','k','b','a','r','3','2',0},
|
|
{'m','s','c','t','l','s','_','u','p','d','o','w','n','3','2',0},
|
|
{'N','a','t','i','v','e','F','o','n','t','C','t','l',0},
|
|
{'R','e','B','a','r','W','i','n','d','o','w','3','2',0},
|
|
{'S','y','s','A','n','i','m','a','t','e','3','2',0},
|
|
{'S','y','s','D','a','t','e','T','i','m','e','P','i','c','k','3','2',0},
|
|
{'S','y','s','H','e','a','d','e','r','3','2',0},
|
|
{'S','y','s','I','P','A','d','d','r','e','s','s','3','2',0},
|
|
{'S','y','s','L','i','s','t','V','i','e','w','3','2',0},
|
|
{'S','y','s','M','o','n','t','h','C','a','l','3','2',0},
|
|
{'S','y','s','P','a','g','e','r',0},
|
|
{'S','y','s','T','a','b','C','o','n','t','r','o','l','3','2',0},
|
|
{'S','y','s','T','r','e','e','V','i','e','w','3','2',0},
|
|
{'T','o','o','l','b','a','r','W','i','n','d','o','w','3','2',0},
|
|
{'t','o','o','l','t','i','p','s','_','c','l','a','s','s','3','2',0},
|
|
};
|
|
|
|
int min = 0, max = (sizeof(classesW) / sizeof(classesW[0])) - 1;
|
|
|
|
while (min <= max)
|
|
{
|
|
int res, pos = (min + max) / 2;
|
|
if (!(res = strcmpiW( name, classesW[pos] ))) return classesW[pos];
|
|
if (res < 0) max = pos - 1;
|
|
else min = pos + 1;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
LPCWSTR
|
|
FASTCALL
|
|
ClassNameToVersion(
|
|
const void* lpszClass,
|
|
LPCWSTR lpszMenuName,
|
|
LPCWSTR *plpLibFileName,
|
|
HANDLE *pContext,
|
|
BOOL bAnsi)
|
|
{
|
|
LPCWSTR VersionedClass = NULL;
|
|
#ifdef USE_VERSIONED_CLASSES
|
|
NTSTATUS Status;
|
|
#endif
|
|
UNICODE_STRING SectionName;
|
|
WCHAR SectionNameBuf[MAX_PATH] = {0};
|
|
ACTCTX_SECTION_KEYED_DATA KeyedData = { sizeof(KeyedData) };
|
|
|
|
if(!lpszClass)
|
|
{
|
|
ERR("Null class given !\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (IS_ATOM(lpszClass))
|
|
{
|
|
RtlInitEmptyUnicodeString(&SectionName, SectionNameBuf, sizeof(SectionNameBuf));
|
|
if(!NtUserGetAtomName(LOWORD((DWORD_PTR)lpszClass), &SectionName))
|
|
{
|
|
ERR("Couldn't get atom name for atom %x !\n", LOWORD((DWORD_PTR)lpszClass));
|
|
return NULL;
|
|
}
|
|
SectionName.Length = wcslen(SectionNameBuf) * sizeof(WCHAR);
|
|
TRACE("ClassNameToVersion got name %wZ from atom\n", &SectionName);
|
|
}
|
|
else
|
|
{
|
|
if (bAnsi)
|
|
{
|
|
ANSI_STRING AnsiString;
|
|
RtlInitAnsiString(&AnsiString, lpszClass);
|
|
RtlInitEmptyUnicodeString(&SectionName, SectionNameBuf, sizeof(SectionNameBuf));
|
|
RtlAnsiStringToUnicodeString(&SectionName, &AnsiString, FALSE);
|
|
}
|
|
else
|
|
{
|
|
RtlInitUnicodeString(&SectionName, lpszClass);
|
|
}
|
|
}
|
|
#ifdef USE_VERSIONED_CLASSES
|
|
Status = RtlFindActivationContextSectionString( FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX,
|
|
NULL,
|
|
ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION,
|
|
&SectionName,
|
|
&KeyedData );
|
|
|
|
if (NT_SUCCESS(Status) && KeyedData.ulDataFormatVersion == 1)
|
|
{
|
|
struct strsection_header *SectionHeader = KeyedData.lpSectionBase;
|
|
|
|
/* Find activation context */
|
|
if(SectionHeader && SectionHeader->count > 0)
|
|
{
|
|
struct wndclass_redirect_data *WindowRedirectionData = KeyedData.lpData;
|
|
if(WindowRedirectionData && WindowRedirectionData->module_len)
|
|
{
|
|
LPCWSTR lpLibFileName;
|
|
|
|
VersionedClass = (WCHAR*)((BYTE*)WindowRedirectionData + WindowRedirectionData->name_offset);
|
|
lpLibFileName = (WCHAR*)((BYTE*)KeyedData.lpSectionBase + WindowRedirectionData->module_offset);
|
|
TRACE("Returning VersionedClass=%S, plpLibFileName=%S for class %S\n", VersionedClass, lpLibFileName, SectionName.Buffer);
|
|
|
|
if (pContext) *pContext = KeyedData.hActCtx;
|
|
if (plpLibFileName) *plpLibFileName = lpLibFileName;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (KeyedData.hActCtx)
|
|
RtlReleaseActivationContext(KeyedData.hActCtx);
|
|
#endif
|
|
|
|
#ifndef DEFAULT_ACTIVATION_CONTEXTS_SUPPORTED
|
|
/* This block is a hack! */
|
|
if (!VersionedClass)
|
|
{
|
|
/*
|
|
* In windows the default activation context always contains comctl32v5
|
|
* In reactos we don't have a default activation context so we
|
|
* mimic wine here.
|
|
*/
|
|
VersionedClass = is_comctl32_class(SectionName.Buffer);
|
|
if (VersionedClass)
|
|
{
|
|
if (pContext) *pContext = 0;
|
|
if (plpLibFileName) *plpLibFileName = L"comctl32";
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* The returned strings are pointers in the activation context and
|
|
* will get freed when the activation context gets freed
|
|
*/
|
|
return VersionedClass;
|
|
}
|
|
|
|
//
|
|
// Ref: http://yvs-it.blogspot.com/2010/04/initcommoncontrolsex.html
|
|
//
|
|
BOOL
|
|
FASTCALL
|
|
VersionRegisterClass(
|
|
PCWSTR pszClass,
|
|
LPCWSTR lpLibFileName,
|
|
HANDLE Contex,
|
|
HMODULE * phLibModule)
|
|
{
|
|
BOOL Ret = FALSE;
|
|
HMODULE hLibModule = NULL;
|
|
PREGISTERCLASSNAMEW pRegisterClassNameW;
|
|
UNICODE_STRING ClassName;
|
|
WCHAR ClassNameBuf[MAX_PATH] = {0};
|
|
RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_EXTENDED Frame = { sizeof(Frame), 1 };
|
|
|
|
ERR("VersionRegisterClass: Attempting to call RegisterClassNameW in %S.\n", lpLibFileName);
|
|
|
|
RtlActivateActivationContextUnsafeFast(&Frame, Contex);
|
|
|
|
_SEH2_TRY
|
|
{
|
|
hLibModule = LoadLibraryW(lpLibFileName);
|
|
if (hLibModule)
|
|
{
|
|
if ((pRegisterClassNameW = (void*)GetProcAddress(hLibModule, "RegisterClassNameW")))
|
|
{
|
|
if (IS_ATOM(pszClass))
|
|
{
|
|
RtlInitEmptyUnicodeString(&ClassName, ClassNameBuf, sizeof(ClassNameBuf));
|
|
if (!NtUserGetAtomName(LOWORD((DWORD_PTR)pszClass), &ClassName))
|
|
{
|
|
ERR("Error while verifying ATOM\n");
|
|
_SEH2_YIELD(goto Error_Exit);
|
|
}
|
|
pszClass = ClassName.Buffer;
|
|
}
|
|
Ret = pRegisterClassNameW(pszClass);
|
|
}
|
|
else
|
|
{
|
|
WARN("No RegisterClassNameW PROC\n");
|
|
}
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
ERR("Got exception while trying to call RegisterClassNameW!\n");
|
|
}
|
|
_SEH2_END
|
|
|
|
Error_Exit:
|
|
if (Ret || !hLibModule)
|
|
{
|
|
if (phLibModule) *phLibModule = hLibModule;
|
|
}
|
|
else
|
|
{
|
|
DWORD dwLastError = GetLastError();
|
|
FreeLibrary(hLibModule);
|
|
SetLastError(dwLastError);
|
|
}
|
|
|
|
RtlDeactivateActivationContextUnsafeFast(&Frame);
|
|
return Ret;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
GetClassInfoExA(
|
|
HINSTANCE hInstance,
|
|
LPCSTR lpszClass,
|
|
LPWNDCLASSEXA lpwcx)
|
|
{
|
|
UNICODE_STRING ClassName = {0};
|
|
LPCSTR pszMenuName;
|
|
HMODULE hLibModule = NULL;
|
|
DWORD dwLastError;
|
|
BOOL Ret, ClassFound = FALSE, ConvertedString = FALSE;
|
|
LPCWSTR lpszClsVersion;
|
|
HANDLE pCtx = NULL;
|
|
LPCWSTR lpLibFileName = NULL;
|
|
|
|
TRACE("%p class/atom: %s/%04x %p\n", hInstance,
|
|
IS_ATOM(lpszClass) ? NULL : lpszClass,
|
|
IS_ATOM(lpszClass) ? lpszClass : 0,
|
|
lpwcx);
|
|
|
|
if (!lpwcx)
|
|
{
|
|
SetLastError(ERROR_NOACCESS);
|
|
return FALSE;
|
|
}
|
|
|
|
if (hInstance == User32Instance)
|
|
{
|
|
hInstance = NULL;
|
|
}
|
|
|
|
if (lpszClass == NULL)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
lpszClsVersion = ClassNameToVersion(lpszClass, NULL, &lpLibFileName, &pCtx, TRUE);
|
|
if (lpszClsVersion)
|
|
{
|
|
RtlInitUnicodeString(&ClassName, lpszClsVersion);
|
|
}
|
|
else if (IS_ATOM(lpszClass))
|
|
{
|
|
ClassName.Buffer = (PWSTR)((ULONG_PTR)lpszClass);
|
|
}
|
|
else
|
|
{
|
|
ConvertedString = TRUE;
|
|
if (!RtlCreateUnicodeStringFromAsciiz(&ClassName, lpszClass))
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (!RegisterDefaultClasses)
|
|
{
|
|
TRACE("RegisterSystemControls\n");
|
|
RegisterSystemControls();
|
|
}
|
|
|
|
for(;;)
|
|
{
|
|
Ret = NtUserGetClassInfo(hInstance,
|
|
&ClassName,
|
|
(LPWNDCLASSEXW)lpwcx,
|
|
(LPWSTR *)&pszMenuName,
|
|
TRUE);
|
|
if (Ret) break;
|
|
if (!lpLibFileName) break;
|
|
if (!ClassFound)
|
|
{
|
|
dwLastError = GetLastError();
|
|
if ( dwLastError == ERROR_CANNOT_FIND_WND_CLASS ||
|
|
dwLastError == ERROR_CLASS_DOES_NOT_EXIST )
|
|
{
|
|
ClassFound = VersionRegisterClass(ClassName.Buffer, lpLibFileName, pCtx, &hLibModule);
|
|
if (ClassFound) continue;
|
|
}
|
|
}
|
|
if (hLibModule)
|
|
{
|
|
dwLastError = GetLastError();
|
|
FreeLibrary(hLibModule);
|
|
SetLastError(dwLastError);
|
|
hLibModule = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (Ret)
|
|
{
|
|
lpwcx->lpszClassName = lpszClass;
|
|
// lpwcx->lpszMenuName = pszMenuName;
|
|
}
|
|
|
|
if (ConvertedString)
|
|
{
|
|
RtlFreeUnicodeString(&ClassName);
|
|
}
|
|
|
|
return Ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
GetClassInfoExW(
|
|
HINSTANCE hInstance,
|
|
LPCWSTR lpszClass,
|
|
LPWNDCLASSEXW lpwcx)
|
|
{
|
|
UNICODE_STRING ClassName = {0};
|
|
LPWSTR pszMenuName;
|
|
HMODULE hLibModule = NULL;
|
|
DWORD dwLastError;
|
|
BOOL Ret, ClassFound = FALSE;
|
|
LPCWSTR lpszClsVersion;
|
|
HANDLE pCtx = NULL;
|
|
LPCWSTR lpLibFileName = NULL;
|
|
|
|
TRACE("%p class/atom: %S/%04x %p\n", hInstance,
|
|
IS_ATOM(lpszClass) ? NULL : lpszClass,
|
|
IS_ATOM(lpszClass) ? lpszClass : 0,
|
|
lpwcx);
|
|
|
|
/* From wine, for speed only, ReactOS supports the correct return in
|
|
* Win32k. cbSize is ignored.
|
|
*/
|
|
if (!lpwcx)
|
|
{
|
|
SetLastError( ERROR_NOACCESS );
|
|
return FALSE;
|
|
}
|
|
|
|
if (hInstance == User32Instance)
|
|
{
|
|
hInstance = NULL;
|
|
}
|
|
|
|
if (lpszClass == NULL)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
lpszClsVersion = ClassNameToVersion(lpszClass, NULL, &lpLibFileName, &pCtx, FALSE);
|
|
if (lpszClsVersion)
|
|
{
|
|
RtlInitUnicodeString(&ClassName, lpszClsVersion);
|
|
}
|
|
else if (IS_ATOM(lpszClass))
|
|
{
|
|
ClassName.Buffer = (PWSTR)((ULONG_PTR)lpszClass);
|
|
}
|
|
else
|
|
{
|
|
RtlInitUnicodeString(&ClassName, lpszClass);
|
|
}
|
|
|
|
if (!RegisterDefaultClasses)
|
|
{
|
|
TRACE("RegisterSystemControls\n");
|
|
RegisterSystemControls();
|
|
}
|
|
|
|
for(;;)
|
|
{
|
|
Ret = NtUserGetClassInfo( hInstance,
|
|
&ClassName,
|
|
lpwcx,
|
|
&pszMenuName,
|
|
FALSE);
|
|
if (Ret) break;
|
|
if (!lpLibFileName) break;
|
|
if (!ClassFound)
|
|
{
|
|
dwLastError = GetLastError();
|
|
if ( dwLastError == ERROR_CANNOT_FIND_WND_CLASS ||
|
|
dwLastError == ERROR_CLASS_DOES_NOT_EXIST )
|
|
{
|
|
ClassFound = VersionRegisterClass(ClassName.Buffer, lpLibFileName, pCtx, &hLibModule);
|
|
if (ClassFound) continue;
|
|
}
|
|
}
|
|
if (hLibModule)
|
|
{
|
|
dwLastError = GetLastError();
|
|
FreeLibrary(hLibModule);
|
|
SetLastError(dwLastError);
|
|
hLibModule = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (Ret)
|
|
{
|
|
lpwcx->lpszClassName = lpszClass;
|
|
// lpwcx->lpszMenuName = pszMenuName;
|
|
}
|
|
return Ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
GetClassInfoA(
|
|
HINSTANCE hInstance,
|
|
LPCSTR lpClassName,
|
|
LPWNDCLASSA lpWndClass)
|
|
{
|
|
WNDCLASSEXA wcex;
|
|
BOOL retval;
|
|
|
|
retval = GetClassInfoExA(hInstance, lpClassName, &wcex);
|
|
if (retval)
|
|
{
|
|
lpWndClass->style = wcex.style;
|
|
lpWndClass->lpfnWndProc = wcex.lpfnWndProc;
|
|
lpWndClass->cbClsExtra = wcex.cbClsExtra;
|
|
lpWndClass->cbWndExtra = wcex.cbWndExtra;
|
|
lpWndClass->hInstance = wcex.hInstance;
|
|
lpWndClass->hIcon = wcex.hIcon;
|
|
lpWndClass->hCursor = wcex.hCursor;
|
|
lpWndClass->hbrBackground = wcex.hbrBackground;
|
|
lpWndClass->lpszMenuName = wcex.lpszMenuName;
|
|
lpWndClass->lpszClassName = wcex.lpszClassName;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
GetClassInfoW(
|
|
HINSTANCE hInstance,
|
|
LPCWSTR lpClassName,
|
|
LPWNDCLASSW lpWndClass)
|
|
{
|
|
WNDCLASSEXW wcex;
|
|
BOOL retval;
|
|
|
|
retval = GetClassInfoExW(hInstance, lpClassName, &wcex);
|
|
if (retval)
|
|
{
|
|
lpWndClass->style = wcex.style;
|
|
lpWndClass->lpfnWndProc = wcex.lpfnWndProc;
|
|
lpWndClass->cbClsExtra = wcex.cbClsExtra;
|
|
lpWndClass->cbWndExtra = wcex.cbWndExtra;
|
|
lpWndClass->hInstance = wcex.hInstance;
|
|
lpWndClass->hIcon = wcex.hIcon;
|
|
lpWndClass->hCursor = wcex.hCursor;
|
|
lpWndClass->hbrBackground = wcex.hbrBackground;
|
|
lpWndClass->lpszMenuName = wcex.lpszMenuName;
|
|
lpWndClass->lpszClassName = wcex.lpszClassName;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
//
|
|
// Based on find_winproc... Fixes many whine tests......
|
|
//
|
|
ULONG_PTR FASTCALL
|
|
IntGetClsWndProc(PWND pWnd, PCLS Class, BOOL Ansi)
|
|
{
|
|
INT i;
|
|
ULONG_PTR gcpd, Ret = 0;
|
|
// If server side, sweep through proc list and return the client side proc.
|
|
if (Class->CSF_flags & CSF_SERVERSIDEPROC)
|
|
{ // Always scan through the list due to wine class "deftest".
|
|
for ( i = FNID_FIRST; i <= FNID_SWITCH; i++)
|
|
{
|
|
if (GETPFNSERVER(i) == Class->lpfnWndProc)
|
|
{
|
|
if (Ansi)
|
|
Ret = (ULONG_PTR)GETPFNCLIENTA(i);
|
|
else
|
|
Ret = (ULONG_PTR)GETPFNCLIENTW(i);
|
|
}
|
|
}
|
|
return Ret;
|
|
}
|
|
// Set return proc.
|
|
Ret = (ULONG_PTR)Class->lpfnWndProc;
|
|
// Return the proc if one of the FnId default class type.
|
|
if (Class->fnid <= FNID_GHOST && Class->fnid >= FNID_BUTTON)
|
|
{
|
|
if (Ansi)
|
|
{ // If match return the right proc by type.
|
|
if (GETPFNCLIENTW(Class->fnid) == Class->lpfnWndProc)
|
|
Ret = (ULONG_PTR)GETPFNCLIENTA(Class->fnid);
|
|
}
|
|
else
|
|
{
|
|
if (GETPFNCLIENTA(Class->fnid) == Class->lpfnWndProc)
|
|
Ret = (ULONG_PTR)GETPFNCLIENTW(Class->fnid);
|
|
}
|
|
}
|
|
// Return on change or Ansi/Unicode proc equal.
|
|
if ( Ret != (ULONG_PTR)Class->lpfnWndProc ||
|
|
Ansi == !!(Class->CSF_flags & CSF_ANSIPROC) )
|
|
return Ret;
|
|
|
|
/* We have an Ansi and Unicode swap! If Ansi create Unicode proc handle.
|
|
This will force CallWindowProc to deal with it. */
|
|
gcpd = NtUserGetCPD( UserHMGetHandle(pWnd),
|
|
(Ansi ? UserGetCPDA2U : UserGetCPDU2A )|UserGetCPDWndtoCls,
|
|
Ret);
|
|
|
|
return (gcpd ? gcpd : Ret);
|
|
}
|
|
|
|
//
|
|
// Based on IntGetClsWndProc
|
|
//
|
|
WNDPROC FASTCALL
|
|
IntGetWndProc(PWND pWnd, BOOL Ansi)
|
|
{
|
|
INT i;
|
|
WNDPROC gcpd, Ret = 0;
|
|
PCLS Class = DesktopPtrToUser(pWnd->pcls);
|
|
|
|
if (!Class) return Ret;
|
|
|
|
if (pWnd->state & WNDS_SERVERSIDEWINDOWPROC)
|
|
{
|
|
for ( i = FNID_FIRST; i <= FNID_SWITCH; i++)
|
|
{
|
|
if (GETPFNSERVER(i) == pWnd->lpfnWndProc)
|
|
{
|
|
if (Ansi)
|
|
Ret = GETPFNCLIENTA(i);
|
|
else
|
|
Ret = GETPFNCLIENTW(i);
|
|
}
|
|
}
|
|
return Ret;
|
|
}
|
|
// Wine Class tests:
|
|
/* Edit controls are special - they return a wndproc handle when
|
|
GetWindowLongPtr is called with a different A/W.
|
|
On the other hand there is no W->A->W conversion so this control
|
|
is treated specially.
|
|
*/
|
|
if (Class->fnid == FNID_EDIT)
|
|
Ret = pWnd->lpfnWndProc;
|
|
else
|
|
{
|
|
// Set return proc.
|
|
Ret = pWnd->lpfnWndProc;
|
|
|
|
if (Class->fnid <= FNID_GHOST && Class->fnid >= FNID_BUTTON)
|
|
{
|
|
if (Ansi)
|
|
{
|
|
if (GETPFNCLIENTW(Class->fnid) == pWnd->lpfnWndProc)
|
|
Ret = GETPFNCLIENTA(Class->fnid);
|
|
}
|
|
else
|
|
{
|
|
if (GETPFNCLIENTA(Class->fnid) == pWnd->lpfnWndProc)
|
|
Ret = GETPFNCLIENTW(Class->fnid);
|
|
}
|
|
}
|
|
// Return on the change.
|
|
if ( Ret != pWnd->lpfnWndProc)
|
|
return Ret;
|
|
}
|
|
|
|
if ( Ansi == !!(pWnd->state & WNDS_ANSIWINDOWPROC) )
|
|
return Ret;
|
|
|
|
gcpd = (WNDPROC)NtUserGetCPD( UserHMGetHandle(pWnd),
|
|
(Ansi ? UserGetCPDA2U : UserGetCPDU2A )|UserGetCPDWindow,
|
|
(ULONG_PTR)Ret);
|
|
|
|
return (gcpd ? gcpd : Ret);
|
|
}
|
|
|
|
static ULONG_PTR FASTCALL
|
|
IntGetClassLongA(PWND Wnd, PCLS Class, int nIndex)
|
|
{
|
|
ULONG_PTR Ret = 0;
|
|
|
|
if (nIndex >= 0)
|
|
{
|
|
if (nIndex + sizeof(ULONG_PTR) < nIndex ||
|
|
nIndex + sizeof(ULONG_PTR) > Class->cbclsExtra)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
}
|
|
else
|
|
Ret = *(PULONG_PTR)((ULONG_PTR)(Class + 1) + nIndex);
|
|
}
|
|
else
|
|
{
|
|
switch (nIndex)
|
|
{
|
|
case GCL_CBWNDEXTRA:
|
|
Ret = (ULONG_PTR)Class->cbwndExtra;
|
|
break;
|
|
|
|
case GCL_CBCLSEXTRA:
|
|
Ret = (ULONG_PTR)Class->cbclsExtra;
|
|
break;
|
|
|
|
case GCL_HBRBACKGROUND:
|
|
Ret = (ULONG_PTR)Class->hbrBackground;
|
|
if (Ret != 0 && Ret < 0x4000)
|
|
Ret = (ULONG_PTR)GetSysColorBrush((ULONG)Ret - 1);
|
|
break;
|
|
|
|
case GCL_HMODULE:
|
|
//ERR("Cls 0x%x GCL_HMODULE 0x%x\n", Wnd->pcls, Class->hModule);
|
|
Ret = (ULONG_PTR)Class->hModule;
|
|
break;
|
|
|
|
case GCL_MENUNAME:
|
|
Ret = (ULONG_PTR)Class->lpszClientAnsiMenuName;
|
|
break;
|
|
|
|
case GCL_STYLE:
|
|
Ret = (ULONG_PTR)Class->style;
|
|
break;
|
|
|
|
case GCW_ATOM:
|
|
Ret = (ULONG_PTR)Class->atomNVClassName;
|
|
break;
|
|
|
|
case GCLP_HCURSOR:
|
|
Ret = Class->spcur ? (ULONG_PTR)((PPROCMARKHEAD)SharedPtrToUser(Class->spcur))->h : 0;
|
|
break;
|
|
|
|
case GCLP_HICON:
|
|
Ret = Class->spicn ? (ULONG_PTR)((PPROCMARKHEAD)SharedPtrToUser(Class->spicn))->h : 0;
|
|
break;
|
|
|
|
case GCLP_HICONSM:
|
|
Ret = Class->spicnSm ? (ULONG_PTR)((PPROCMARKHEAD)SharedPtrToUser(Class->spicnSm))->h : 0;
|
|
break;
|
|
|
|
case GCLP_WNDPROC:
|
|
Ret = IntGetClsWndProc(Wnd, Class, TRUE);
|
|
break;
|
|
|
|
default:
|
|
SetLastError(ERROR_INVALID_INDEX);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Ret;
|
|
}
|
|
|
|
static ULONG_PTR FASTCALL
|
|
IntGetClassLongW (PWND Wnd, PCLS Class, int nIndex)
|
|
{
|
|
ULONG_PTR Ret = 0;
|
|
|
|
if (nIndex >= 0)
|
|
{
|
|
if (nIndex + sizeof(ULONG_PTR) < nIndex ||
|
|
nIndex + sizeof(ULONG_PTR) > Class->cbclsExtra)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
}
|
|
else
|
|
Ret = *(PULONG_PTR)((ULONG_PTR)(Class + 1) + nIndex);
|
|
}
|
|
else
|
|
{
|
|
switch (nIndex)
|
|
{
|
|
case GCL_CBWNDEXTRA:
|
|
Ret = (ULONG_PTR)Class->cbwndExtra;
|
|
break;
|
|
|
|
case GCL_CBCLSEXTRA:
|
|
Ret = (ULONG_PTR)Class->cbclsExtra;
|
|
break;
|
|
|
|
case GCLP_HBRBACKGROUND:
|
|
Ret = (ULONG_PTR)Class->hbrBackground;
|
|
if (Ret != 0 && Ret < 0x4000)
|
|
Ret = (ULONG_PTR)GetSysColorBrush((ULONG)Ret - 1);
|
|
break;
|
|
|
|
case GCL_HMODULE:
|
|
Ret = (ULONG_PTR)Class->hModule;
|
|
break;
|
|
|
|
case GCLP_MENUNAME:
|
|
Ret = (ULONG_PTR)Class->lpszClientUnicodeMenuName;
|
|
break;
|
|
|
|
case GCL_STYLE:
|
|
Ret = (ULONG_PTR)Class->style;
|
|
break;
|
|
|
|
case GCW_ATOM:
|
|
Ret = (ULONG_PTR)Class->atomNVClassName;
|
|
break;
|
|
|
|
case GCLP_HCURSOR:
|
|
Ret = Class->spcur ? (ULONG_PTR)((PPROCMARKHEAD)SharedPtrToUser(Class->spcur))->h : 0;
|
|
break;
|
|
|
|
case GCLP_HICON:
|
|
Ret = Class->spicn ? (ULONG_PTR)((PPROCMARKHEAD)SharedPtrToUser(Class->spicn))->h : 0;
|
|
break;
|
|
|
|
case GCLP_HICONSM:
|
|
Ret = Class->spicnSm ? (ULONG_PTR)((PPROCMARKHEAD)SharedPtrToUser(Class->spicnSm))->h : 0;
|
|
break;
|
|
|
|
case GCLP_WNDPROC:
|
|
Ret = IntGetClsWndProc(Wnd, Class, FALSE);
|
|
break;
|
|
|
|
default:
|
|
SetLastError(ERROR_INVALID_INDEX);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Ret;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
DWORD WINAPI
|
|
GetClassLongA(HWND hWnd, int nIndex)
|
|
{
|
|
PWND Wnd;
|
|
PCLS Class;
|
|
ULONG_PTR Ret = 0;
|
|
|
|
TRACE("%p %d\n", hWnd, nIndex);
|
|
|
|
Wnd = ValidateHwnd(hWnd);
|
|
if (!Wnd)
|
|
return 0;
|
|
|
|
_SEH2_TRY
|
|
{
|
|
Class = DesktopPtrToUser(Wnd->pcls);
|
|
if (Class != NULL)
|
|
{
|
|
#ifdef _WIN64
|
|
switch (nIndex)
|
|
{
|
|
case GCLP_HBRBACKGROUND:
|
|
case GCLP_HCURSOR:
|
|
case GCLP_HICON:
|
|
case GCLP_HICONSM:
|
|
case GCLP_HMODULE:
|
|
case GCLP_MENUNAME:
|
|
case GCLP_WNDPROC:
|
|
SetLastError(ERROR_INVALID_INDEX);
|
|
break;
|
|
|
|
default:
|
|
Ret = IntGetClassLongA(Wnd, Class, nIndex);
|
|
break;
|
|
}
|
|
#else
|
|
Ret = IntGetClassLongA(Wnd, Class, nIndex);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
WARN("Invalid class for hwnd 0x%p!\n", hWnd);
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Ret = 0;
|
|
}
|
|
_SEH2_END;
|
|
|
|
return (DWORD)Ret;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
DWORD WINAPI
|
|
GetClassLongW ( HWND hWnd, int nIndex )
|
|
{
|
|
PWND Wnd;
|
|
PCLS Class;
|
|
ULONG_PTR Ret = 0;
|
|
|
|
TRACE("%p %d\n", hWnd, nIndex);
|
|
|
|
Wnd = ValidateHwnd(hWnd);
|
|
if (!Wnd)
|
|
return 0;
|
|
|
|
_SEH2_TRY
|
|
{
|
|
Class = DesktopPtrToUser(Wnd->pcls);
|
|
if (Class != NULL)
|
|
{
|
|
#ifdef _WIN64
|
|
switch (nIndex)
|
|
{
|
|
case GCLP_HBRBACKGROUND:
|
|
case GCLP_HCURSOR:
|
|
case GCLP_HICON:
|
|
case GCLP_HICONSM:
|
|
case GCLP_HMODULE:
|
|
case GCLP_MENUNAME:
|
|
case GCLP_WNDPROC:
|
|
SetLastError(ERROR_INVALID_INDEX);
|
|
break;
|
|
|
|
default:
|
|
Ret = IntGetClassLongW(Wnd, Class, nIndex);
|
|
break;
|
|
}
|
|
#else
|
|
Ret = IntGetClassLongW(Wnd, Class, nIndex);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
WARN("Invalid class for hwnd 0x%p!\n", hWnd);
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Ret = 0;
|
|
}
|
|
_SEH2_END;
|
|
|
|
return (DWORD)Ret;
|
|
}
|
|
|
|
#ifdef _WIN64
|
|
/*
|
|
* @implemented
|
|
*/
|
|
ULONG_PTR
|
|
WINAPI
|
|
GetClassLongPtrA(HWND hWnd,
|
|
INT nIndex)
|
|
{
|
|
PWND Wnd;
|
|
PCLS Class;
|
|
ULONG_PTR Ret = 0;
|
|
|
|
TRACE("%p %d\n", hWnd, nIndex);
|
|
|
|
Wnd = ValidateHwnd(hWnd);
|
|
if (!Wnd)
|
|
return 0;
|
|
|
|
_SEH2_TRY
|
|
{
|
|
Class = DesktopPtrToUser(Wnd->pcls);
|
|
if (Class != NULL)
|
|
{
|
|
Ret = IntGetClassLongA(Wnd, Class, nIndex);
|
|
}
|
|
else
|
|
{
|
|
WARN("Invalid class for hwnd 0x%p!\n", hWnd);
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Ret = 0;
|
|
}
|
|
_SEH2_END;
|
|
|
|
return Ret;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
ULONG_PTR
|
|
WINAPI
|
|
GetClassLongPtrW(HWND hWnd,
|
|
INT nIndex)
|
|
{
|
|
PWND Wnd;
|
|
PCLS Class;
|
|
ULONG_PTR Ret = 0;
|
|
|
|
TRACE("%p %d\n", hWnd, nIndex);
|
|
|
|
Wnd = ValidateHwnd(hWnd);
|
|
if (!Wnd)
|
|
return 0;
|
|
|
|
_SEH2_TRY
|
|
{
|
|
Class = DesktopPtrToUser(Wnd->pcls);
|
|
if (Class != NULL)
|
|
{
|
|
Ret = IntGetClassLongW(Wnd, Class, nIndex);
|
|
}
|
|
else
|
|
{
|
|
WARN("Invalid class for hwnd 0x%p!\n", hWnd);
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Ret = 0;
|
|
}
|
|
_SEH2_END;
|
|
|
|
return Ret;
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
int WINAPI
|
|
GetClassNameA(
|
|
HWND hWnd,
|
|
LPSTR lpClassName,
|
|
int nMaxCount)
|
|
{
|
|
WCHAR tmpbuf[MAX_ATOM_LEN + 1];
|
|
int len;
|
|
|
|
if (nMaxCount <= 0) return 0;
|
|
if (!GetClassNameW( hWnd, tmpbuf, sizeof(tmpbuf)/sizeof(WCHAR) )) return 0;
|
|
RtlUnicodeToMultiByteN( lpClassName, nMaxCount - 1, (PULONG)&len, tmpbuf, strlenW(tmpbuf) * sizeof(WCHAR) );
|
|
lpClassName[len] = 0;
|
|
|
|
TRACE("%p class/atom: %s/%04x %x\n", hWnd,
|
|
IS_ATOM(lpClassName) ? NULL : lpClassName,
|
|
IS_ATOM(lpClassName) ? lpClassName : 0,
|
|
nMaxCount);
|
|
|
|
return len;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
int
|
|
WINAPI
|
|
GetClassNameW(
|
|
HWND hWnd,
|
|
LPWSTR lpClassName,
|
|
int nMaxCount)
|
|
{
|
|
UNICODE_STRING ClassName;
|
|
int Result;
|
|
|
|
RtlInitEmptyUnicodeString(&ClassName,
|
|
lpClassName,
|
|
nMaxCount * sizeof(WCHAR));
|
|
|
|
Result = NtUserGetClassName(hWnd,
|
|
FALSE,
|
|
&ClassName);
|
|
|
|
TRACE("%p class/atom: %S/%04x %x\n", hWnd,
|
|
IS_ATOM(lpClassName) ? NULL : lpClassName,
|
|
IS_ATOM(lpClassName) ? lpClassName : 0,
|
|
nMaxCount);
|
|
|
|
return Result;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
WORD
|
|
WINAPI
|
|
GetClassWord(
|
|
HWND hwnd,
|
|
int offset)
|
|
{
|
|
PWND Wnd;
|
|
PCLS class;
|
|
WORD retvalue = 0;
|
|
|
|
if (offset < 0) return GetClassLongA( hwnd, offset );
|
|
|
|
Wnd = ValidateHwnd(hwnd);
|
|
if (!Wnd)
|
|
return 0;
|
|
|
|
class = DesktopPtrToUser(Wnd->pcls);
|
|
if (class == NULL) return 0;
|
|
|
|
if (offset <= class->cbclsExtra - sizeof(WORD))
|
|
memcpy( &retvalue, (char *)(class + 1) + offset, sizeof(retvalue) );
|
|
else
|
|
SetLastError( ERROR_INVALID_INDEX );
|
|
|
|
return retvalue;
|
|
}
|
|
|
|
|
|
LONG_PTR IntGetWindowLong( HWND hwnd, INT offset, UINT size, BOOL unicode )
|
|
{
|
|
LONG_PTR retvalue = 0;
|
|
WND *wndPtr;
|
|
|
|
if (offset == GWLP_HWNDPARENT)
|
|
{
|
|
HWND parent = GetAncestor( hwnd, GA_PARENT );
|
|
if (parent == GetDesktopWindow()) parent = GetWindow( hwnd, GW_OWNER );
|
|
return (ULONG_PTR)parent;
|
|
}
|
|
|
|
if (!(wndPtr = ValidateHwnd( hwnd )))
|
|
{
|
|
SetLastError( ERROR_INVALID_WINDOW_HANDLE );
|
|
return 0;
|
|
}
|
|
|
|
if (offset >= 0 && wndPtr->fnid != FNID_DESKTOP)
|
|
{
|
|
if (offset > (int)(wndPtr->cbwndExtra - size))
|
|
{
|
|
WARN("Invalid offset %d\n", offset );
|
|
SetLastError( ERROR_INVALID_INDEX );
|
|
return 0;
|
|
}
|
|
retvalue = *((LONG_PTR *)((PCHAR)(wndPtr + 1) + offset));
|
|
|
|
/* WINE: special case for dialog window procedure */
|
|
//if ((offset == DWLP_DLGPROC) && (size == sizeof(LONG_PTR)) && (wndPtr->flags & WIN_ISDIALOG))
|
|
// retvalue = (LONG_PTR)IntGetWndProc( (WNDPROC)retvalue, unicode );
|
|
return retvalue;
|
|
}
|
|
|
|
switch(offset)
|
|
{
|
|
case GWLP_USERDATA: retvalue = wndPtr->dwUserData; break;
|
|
case GWL_STYLE: retvalue = wndPtr->style; break;
|
|
case GWL_EXSTYLE: retvalue = wndPtr->ExStyle; break;
|
|
case GWLP_ID: retvalue = wndPtr->IDMenu; break;
|
|
case GWLP_HINSTANCE: retvalue = (ULONG_PTR)wndPtr->hModule; break;
|
|
#if 0
|
|
/* -1 is an undocumented case which returns WW* */
|
|
/* source: http://www.geoffchappell.com/studies/windows/win32/user32/structs/wnd/index.htm*/
|
|
case -1: retvalue = (ULONG_PTR)&wndPtr->ww; break;
|
|
#else
|
|
/* We don't have a WW but WND already contains the same fields in the right order, */
|
|
/* so we can return a pointer to its first field */
|
|
case -1: retvalue = (ULONG_PTR)&wndPtr->state; break;
|
|
#endif
|
|
case GWLP_WNDPROC:
|
|
{
|
|
if (!TestWindowProcess(wndPtr))
|
|
{
|
|
SetLastError(ERROR_ACCESS_DENIED);
|
|
retvalue = 0;
|
|
ERR("Outside Access and Denied!\n");
|
|
break;
|
|
}
|
|
retvalue = (ULONG_PTR)IntGetWndProc(wndPtr, !unicode);
|
|
break;
|
|
}
|
|
default:
|
|
WARN("Unknown offset %d\n", offset );
|
|
SetLastError( ERROR_INVALID_INDEX );
|
|
break;
|
|
}
|
|
return retvalue;
|
|
|
|
}
|
|
/*
|
|
* @implemented
|
|
*/
|
|
LONG
|
|
WINAPI
|
|
GetWindowLongA ( HWND hWnd, int nIndex )
|
|
{
|
|
return IntGetWindowLong( hWnd, nIndex, sizeof(LONG), FALSE );
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
LONG
|
|
WINAPI
|
|
GetWindowLongW(HWND hWnd, int nIndex)
|
|
{
|
|
return IntGetWindowLong( hWnd, nIndex, sizeof(LONG), TRUE );
|
|
}
|
|
|
|
#ifdef _WIN64
|
|
/*
|
|
* @implemented
|
|
*/
|
|
LONG_PTR
|
|
WINAPI
|
|
GetWindowLongPtrA(HWND hWnd,
|
|
INT nIndex)
|
|
{
|
|
return IntGetWindowLong( hWnd, nIndex, sizeof(LONG_PTR), FALSE );
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
LONG_PTR
|
|
WINAPI
|
|
GetWindowLongPtrW(HWND hWnd,
|
|
INT nIndex)
|
|
{
|
|
return IntGetWindowLong( hWnd, nIndex, sizeof(LONG_PTR), TRUE );
|
|
|
|
}
|
|
#endif // _WIN64
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
WORD
|
|
WINAPI
|
|
GetWindowWord(HWND hWnd, int nIndex)
|
|
{
|
|
switch(nIndex)
|
|
{
|
|
case GWLP_ID:
|
|
case GWLP_HINSTANCE:
|
|
case GWLP_HWNDPARENT:
|
|
break;
|
|
default:
|
|
if (nIndex < 0)
|
|
{
|
|
WARN("Invalid offset %d\n", nIndex );
|
|
SetLastError( ERROR_INVALID_INDEX );
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
return IntGetWindowLong( hWnd, nIndex, sizeof(WORD), FALSE );
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
UINT
|
|
WINAPI
|
|
RealGetWindowClassW(
|
|
HWND hwnd,
|
|
LPWSTR pszType,
|
|
UINT cchType)
|
|
{
|
|
UNICODE_STRING ClassName;
|
|
|
|
RtlInitEmptyUnicodeString(&ClassName,
|
|
pszType,
|
|
cchType * sizeof(WCHAR));
|
|
|
|
return NtUserGetClassName(hwnd,TRUE,&ClassName);
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
UINT
|
|
WINAPI
|
|
RealGetWindowClassA(
|
|
HWND hwnd,
|
|
LPSTR pszType,
|
|
UINT cchType)
|
|
{
|
|
WCHAR tmpbuf[MAX_ATOM_LEN + 1];
|
|
UINT len;
|
|
|
|
if ((INT)cchType <= 0) return 0;
|
|
if (!RealGetWindowClassW( hwnd, tmpbuf, sizeof(tmpbuf)/sizeof(WCHAR) )) return 0;
|
|
RtlUnicodeToMultiByteN( pszType, cchType - 1, (PULONG)&len, tmpbuf, strlenW(tmpbuf) * sizeof(WCHAR) );
|
|
pszType[len] = 0;
|
|
return len;
|
|
}
|
|
|
|
/*
|
|
* Create a small icon based on a standard icon
|
|
*/
|
|
#if 0 // Keep vintage code from revision 18764 by GvG!
|
|
static HICON
|
|
CreateSmallIcon(HICON StdIcon)
|
|
{
|
|
HICON SmallIcon = NULL;
|
|
ICONINFO StdInfo;
|
|
int SmallIconWidth;
|
|
int SmallIconHeight;
|
|
BITMAP StdBitmapInfo;
|
|
HDC hSourceDc = NULL;
|
|
HDC hDestDc = NULL;
|
|
ICONINFO SmallInfo;
|
|
HBITMAP OldSourceBitmap = NULL;
|
|
HBITMAP OldDestBitmap = NULL;
|
|
|
|
SmallInfo.hbmColor = NULL;
|
|
SmallInfo.hbmMask = NULL;
|
|
|
|
/* We need something to work with... */
|
|
if (NULL == StdIcon)
|
|
{
|
|
goto cleanup;
|
|
}
|
|
|
|
SmallIconWidth = GetSystemMetrics(SM_CXSMICON);
|
|
SmallIconHeight = GetSystemMetrics(SM_CYSMICON);
|
|
if (! GetIconInfo(StdIcon, &StdInfo))
|
|
{
|
|
ERR("Failed to get icon info for icon 0x%x\n", StdIcon);
|
|
goto cleanup;
|
|
}
|
|
if (! GetObjectW(StdInfo.hbmMask, sizeof(BITMAP), &StdBitmapInfo))
|
|
{
|
|
ERR("Failed to get bitmap info for icon 0x%x bitmap 0x%x\n",
|
|
StdIcon, StdInfo.hbmColor);
|
|
goto cleanup;
|
|
}
|
|
if (StdBitmapInfo.bmWidth == SmallIconWidth &&
|
|
StdBitmapInfo.bmHeight == SmallIconHeight)
|
|
{
|
|
/* Icon already has the correct dimensions */
|
|
return StdIcon;
|
|
}
|
|
|
|
hSourceDc = CreateCompatibleDC(NULL);
|
|
if (NULL == hSourceDc)
|
|
{
|
|
ERR("Failed to create source DC\n");
|
|
goto cleanup;
|
|
}
|
|
hDestDc = CreateCompatibleDC(NULL);
|
|
if (NULL == hDestDc)
|
|
{
|
|
ERR("Failed to create dest DC\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
OldSourceBitmap = SelectObject(hSourceDc, StdInfo.hbmColor);
|
|
if (NULL == OldSourceBitmap)
|
|
{
|
|
ERR("Failed to select source color bitmap\n");
|
|
goto cleanup;
|
|
}
|
|
SmallInfo.hbmColor = CreateCompatibleBitmap(hSourceDc, SmallIconWidth,
|
|
SmallIconHeight);
|
|
if (NULL == SmallInfo.hbmColor)
|
|
{
|
|
ERR("Failed to create color bitmap\n");
|
|
goto cleanup;
|
|
}
|
|
OldDestBitmap = SelectObject(hDestDc, SmallInfo.hbmColor);
|
|
if (NULL == OldDestBitmap)
|
|
{
|
|
ERR("Failed to select dest color bitmap\n");
|
|
goto cleanup;
|
|
}
|
|
if (! StretchBlt(hDestDc, 0, 0, SmallIconWidth, SmallIconHeight,
|
|
hSourceDc, 0, 0, StdBitmapInfo.bmWidth,
|
|
StdBitmapInfo.bmHeight, SRCCOPY))
|
|
{
|
|
ERR("Failed to stretch color bitmap\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
if (NULL == SelectObject(hSourceDc, StdInfo.hbmMask))
|
|
{
|
|
ERR("Failed to select source mask bitmap\n");
|
|
goto cleanup;
|
|
}
|
|
SmallInfo.hbmMask = CreateCompatibleBitmap(hSourceDc, SmallIconWidth, SmallIconHeight);
|
|
if (NULL == SmallInfo.hbmMask)
|
|
{
|
|
ERR("Failed to create mask bitmap\n");
|
|
goto cleanup;
|
|
}
|
|
if (NULL == SelectObject(hDestDc, SmallInfo.hbmMask))
|
|
{
|
|
ERR("Failed to select dest mask bitmap\n");
|
|
goto cleanup;
|
|
}
|
|
if (! StretchBlt(hDestDc, 0, 0, SmallIconWidth, SmallIconHeight,
|
|
hSourceDc, 0, 0, StdBitmapInfo.bmWidth,
|
|
StdBitmapInfo.bmHeight, SRCCOPY))
|
|
{
|
|
ERR("Failed to stretch mask bitmap\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
SmallInfo.fIcon = TRUE;
|
|
SmallInfo.xHotspot = SmallIconWidth / 2;
|
|
SmallInfo.yHotspot = SmallIconHeight / 2;
|
|
SmallIcon = CreateIconIndirect(&SmallInfo);
|
|
if (NULL == SmallIcon)
|
|
{
|
|
ERR("Failed to create icon\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
if (NULL != SmallInfo.hbmMask)
|
|
{
|
|
DeleteObject(SmallInfo.hbmMask);
|
|
}
|
|
if (NULL != OldDestBitmap)
|
|
{
|
|
SelectObject(hDestDc, OldDestBitmap);
|
|
}
|
|
if (NULL != SmallInfo.hbmColor)
|
|
{
|
|
DeleteObject(SmallInfo.hbmColor);
|
|
}
|
|
if (NULL != hDestDc)
|
|
{
|
|
DeleteDC(hDestDc);
|
|
}
|
|
if (NULL != OldSourceBitmap)
|
|
{
|
|
SelectObject(hSourceDc, OldSourceBitmap);
|
|
}
|
|
if (NULL != hSourceDc)
|
|
{
|
|
DeleteDC(hSourceDc);
|
|
}
|
|
|
|
return SmallIcon;
|
|
}
|
|
#endif
|
|
|
|
ATOM WINAPI
|
|
RegisterClassExWOWW(WNDCLASSEXW *lpwcx,
|
|
LPDWORD pdwWowData,
|
|
WORD fnID,
|
|
DWORD dwFlags,
|
|
BOOL ChkRegCls)
|
|
{
|
|
ATOM Atom;
|
|
WNDCLASSEXW WndClass;
|
|
UNICODE_STRING ClassName;
|
|
UNICODE_STRING ClassVersion;
|
|
UNICODE_STRING MenuName = {0};
|
|
CLSMENUNAME clsMenuName;
|
|
ANSI_STRING AnsiMenuName;
|
|
LPCWSTR lpszClsVersion;
|
|
|
|
if (lpwcx == NULL || lpwcx->cbSize != sizeof(WNDCLASSEXW) ||
|
|
lpwcx->cbClsExtra < 0 || lpwcx->cbWndExtra < 0 ||
|
|
lpwcx->lpszClassName == NULL)
|
|
{
|
|
TRACE("RegisterClassExWOWW Invalid Parameter Error!\n");
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
if (ChkRegCls)
|
|
{
|
|
if (!RegisterDefaultClasses) RegisterSystemControls();
|
|
}
|
|
/*
|
|
* On real Windows this looks more like:
|
|
* if (lpwcx->hInstance == User32Instance &&
|
|
* *(PULONG)((ULONG_PTR)NtCurrentTeb() + 0x6D4) & 0x400)
|
|
* But since I have no idea what the magic field in the
|
|
* TEB structure means, I rather decided to omit that.
|
|
* -- Filip Navara
|
|
|
|
GetWin32ClientInfo()->dwExpWinVer & (WINVER == 0x400)
|
|
*/
|
|
if (lpwcx->hInstance == User32Instance)
|
|
{
|
|
TRACE("RegisterClassExWOWW User32Instance!\n");
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return 0;
|
|
}
|
|
/* Yes, this is correct. We should modify the passed structure. */
|
|
if (lpwcx->hInstance == NULL)
|
|
((WNDCLASSEXW*)lpwcx)->hInstance = GetModuleHandleW(NULL);
|
|
|
|
RtlCopyMemory(&WndClass, lpwcx, sizeof(WNDCLASSEXW));
|
|
/*
|
|
if (NULL == WndClass.hIconSm)
|
|
{
|
|
WndClass.hIconSm = CreateSmallIcon(WndClass.hIcon);
|
|
}
|
|
*/
|
|
RtlInitEmptyAnsiString(&AnsiMenuName, NULL, 0);
|
|
if (WndClass.lpszMenuName != NULL)
|
|
{
|
|
if (!IS_INTRESOURCE(WndClass.lpszMenuName))
|
|
{
|
|
if (WndClass.lpszMenuName[0])
|
|
{
|
|
RtlInitUnicodeString(&MenuName, WndClass.lpszMenuName);
|
|
RtlUnicodeStringToAnsiString( &AnsiMenuName, &MenuName, TRUE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MenuName.Buffer = (LPWSTR)WndClass.lpszMenuName;
|
|
AnsiMenuName.Buffer = (PCHAR)WndClass.lpszMenuName;
|
|
}
|
|
}
|
|
|
|
if (IS_ATOM(WndClass.lpszClassName))
|
|
{
|
|
ClassName.Length =
|
|
ClassName.MaximumLength = 0;
|
|
ClassName.Buffer = (LPWSTR)WndClass.lpszClassName;
|
|
}
|
|
else
|
|
{
|
|
RtlInitUnicodeString(&ClassName, WndClass.lpszClassName);
|
|
}
|
|
|
|
ClassVersion = ClassName;
|
|
if (fnID == 0)
|
|
{
|
|
lpszClsVersion = ClassNameToVersion(lpwcx->lpszClassName, NULL, NULL, NULL, FALSE);
|
|
if (lpszClsVersion)
|
|
{
|
|
RtlInitUnicodeString(&ClassVersion, lpszClsVersion);
|
|
}
|
|
}
|
|
|
|
clsMenuName.pszClientAnsiMenuName = AnsiMenuName.Buffer;
|
|
clsMenuName.pwszClientUnicodeMenuName = MenuName.Buffer;
|
|
clsMenuName.pusMenuName = &MenuName;
|
|
|
|
Atom = NtUserRegisterClassExWOW( &WndClass,
|
|
&ClassName,
|
|
&ClassVersion,
|
|
&clsMenuName,
|
|
fnID,
|
|
dwFlags,
|
|
pdwWowData);
|
|
|
|
TRACE("atom=%04x wndproc=%p hinst=%p bg=%p style=%08x clsExt=%d winExt=%d class=%p\n",
|
|
Atom, lpwcx->lpfnWndProc, lpwcx->hInstance, lpwcx->hbrBackground,
|
|
lpwcx->style, lpwcx->cbClsExtra, lpwcx->cbWndExtra, WndClass);
|
|
|
|
return Atom;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
ATOM WINAPI
|
|
RegisterClassExA(CONST WNDCLASSEXA *lpwcx)
|
|
{
|
|
RTL_ATOM Atom;
|
|
WNDCLASSEXW WndClass;
|
|
WCHAR mname[MAX_BUFFER_LEN];
|
|
WCHAR cname[MAX_BUFFER_LEN];
|
|
|
|
RtlCopyMemory(&WndClass, lpwcx, sizeof(WNDCLASSEXA));
|
|
|
|
if (WndClass.lpszMenuName != NULL)
|
|
{
|
|
if (!IS_INTRESOURCE(WndClass.lpszMenuName))
|
|
{
|
|
if (WndClass.lpszMenuName[0])
|
|
{
|
|
if (!MultiByteToWideChar( CP_ACP, 0, lpwcx->lpszMenuName, -1, mname, MAX_ATOM_LEN + 1 )) return 0;
|
|
|
|
WndClass.lpszMenuName = mname;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!IS_ATOM(WndClass.lpszClassName))
|
|
{
|
|
if (!MultiByteToWideChar( CP_ACP, 0, lpwcx->lpszClassName, -1, cname, MAX_ATOM_LEN + 1 )) return 0;
|
|
|
|
WndClass.lpszClassName = cname;
|
|
}
|
|
|
|
Atom = RegisterClassExWOWW( &WndClass,
|
|
0,
|
|
0,
|
|
CSF_ANSIPROC,
|
|
TRUE);
|
|
|
|
TRACE("A atom=%04x wndproc=%p hinst=%p bg=%p style=%08x clsExt=%d winExt=%d class=%p\n",
|
|
Atom, lpwcx->lpfnWndProc, lpwcx->hInstance, lpwcx->hbrBackground,
|
|
lpwcx->style, lpwcx->cbClsExtra, lpwcx->cbWndExtra, WndClass);
|
|
|
|
return (ATOM)Atom;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
ATOM WINAPI
|
|
RegisterClassExW(CONST WNDCLASSEXW *lpwcx)
|
|
{
|
|
ATOM Atom;
|
|
|
|
Atom = RegisterClassExWOWW( (WNDCLASSEXW *)lpwcx, 0, 0, 0, TRUE);
|
|
|
|
TRACE("W atom=%04x wndproc=%p hinst=%p bg=%p style=%08x clsExt=%d winExt=%d\n",
|
|
Atom, lpwcx->lpfnWndProc, lpwcx->hInstance, lpwcx->hbrBackground,
|
|
lpwcx->style, lpwcx->cbClsExtra, lpwcx->cbWndExtra);
|
|
|
|
return Atom;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
ATOM WINAPI
|
|
RegisterClassA(CONST WNDCLASSA *lpWndClass)
|
|
{
|
|
WNDCLASSEXA Class;
|
|
|
|
if (lpWndClass == NULL)
|
|
return 0;
|
|
|
|
RtlCopyMemory(&Class.style, lpWndClass, sizeof(WNDCLASSA));
|
|
Class.cbSize = sizeof(WNDCLASSEXA);
|
|
Class.hIconSm = NULL;
|
|
|
|
return RegisterClassExA(&Class);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
ATOM WINAPI
|
|
RegisterClassW(CONST WNDCLASSW *lpWndClass)
|
|
{
|
|
WNDCLASSEXW Class;
|
|
|
|
if (lpWndClass == NULL)
|
|
return 0;
|
|
|
|
RtlCopyMemory(&Class.style, lpWndClass, sizeof(WNDCLASSW));
|
|
Class.cbSize = sizeof(WNDCLASSEXW);
|
|
Class.hIconSm = NULL;
|
|
|
|
return RegisterClassExW(&Class);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
DWORD
|
|
WINAPI
|
|
SetClassLongA (HWND hWnd,
|
|
int nIndex,
|
|
LONG dwNewLong)
|
|
{
|
|
PSTR lpStr = (PSTR)(ULONG_PTR)dwNewLong;
|
|
UNICODE_STRING Value = {0};
|
|
BOOL Allocated = FALSE;
|
|
DWORD Ret;
|
|
|
|
/* FIXME - portability!!!! */
|
|
|
|
if (nIndex == GCL_MENUNAME && lpStr != NULL)
|
|
{
|
|
if (!IS_INTRESOURCE(lpStr))
|
|
{
|
|
if (!RtlCreateUnicodeStringFromAsciiz(&Value,
|
|
lpStr))
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return 0;
|
|
}
|
|
|
|
Allocated = TRUE;
|
|
}
|
|
else
|
|
Value.Buffer = (PWSTR)lpStr;
|
|
|
|
dwNewLong = (LONG_PTR)&Value;
|
|
}
|
|
else if (nIndex == GCW_ATOM && lpStr != NULL)
|
|
{
|
|
if (!IS_ATOM(lpStr))
|
|
{
|
|
if (!RtlCreateUnicodeStringFromAsciiz(&Value,
|
|
lpStr))
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return 0;
|
|
}
|
|
|
|
Allocated = TRUE;
|
|
}
|
|
else
|
|
Value.Buffer = (PWSTR)lpStr;
|
|
|
|
dwNewLong = (LONG_PTR)&Value;
|
|
}
|
|
|
|
Ret = (DWORD)NtUserSetClassLong(hWnd,
|
|
nIndex,
|
|
dwNewLong,
|
|
TRUE);
|
|
|
|
if (Allocated)
|
|
{
|
|
RtlFreeUnicodeString(&Value);
|
|
}
|
|
|
|
return Ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
DWORD
|
|
WINAPI
|
|
SetClassLongW(HWND hWnd,
|
|
int nIndex,
|
|
LONG dwNewLong)
|
|
{
|
|
PWSTR lpStr = (PWSTR)(ULONG_PTR)dwNewLong;
|
|
UNICODE_STRING Value = {0};
|
|
|
|
TRACE("%p %d %lx\n", hWnd, nIndex, dwNewLong);
|
|
|
|
/* FIXME - portability!!!! */
|
|
|
|
if (nIndex == GCL_MENUNAME && lpStr != NULL)
|
|
{
|
|
if (!IS_INTRESOURCE(lpStr))
|
|
{
|
|
RtlInitUnicodeString(&Value,
|
|
lpStr);
|
|
}
|
|
else
|
|
Value.Buffer = lpStr;
|
|
|
|
dwNewLong = (LONG_PTR)&Value;
|
|
}
|
|
else if (nIndex == GCW_ATOM && lpStr != NULL)
|
|
{
|
|
if (!IS_ATOM(lpStr))
|
|
{
|
|
RtlInitUnicodeString(&Value,
|
|
lpStr);
|
|
}
|
|
else
|
|
Value.Buffer = lpStr;
|
|
|
|
dwNewLong = (LONG_PTR)&Value;
|
|
}
|
|
|
|
return (DWORD)NtUserSetClassLong(hWnd,
|
|
nIndex,
|
|
dwNewLong,
|
|
FALSE);
|
|
}
|
|
|
|
#ifdef _WIN64
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
ULONG_PTR
|
|
WINAPI
|
|
SetClassLongPtrA(HWND hWnd,
|
|
INT nIndex,
|
|
LONG_PTR dwNewLong)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
ULONG_PTR
|
|
WINAPI
|
|
SetClassLongPtrW(HWND hWnd,
|
|
INT nIndex,
|
|
LONG_PTR dwNewLong)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return 0;
|
|
}
|
|
#endif // _WIN64
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
WORD
|
|
WINAPI
|
|
SetClassWord(
|
|
HWND hWnd,
|
|
int nIndex,
|
|
WORD wNewWord)
|
|
/*
|
|
* NOTE: Obsoleted in 32-bit windows
|
|
*/
|
|
{
|
|
if ((nIndex < 0) && (nIndex != GCW_ATOM))
|
|
return 0;
|
|
|
|
return (WORD) SetClassLongW ( hWnd, nIndex, wNewWord );
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
WORD
|
|
WINAPI
|
|
SetWindowWord ( HWND hWnd,int nIndex,WORD wNewWord )
|
|
{
|
|
switch(nIndex)
|
|
{
|
|
case GWLP_ID:
|
|
case GWLP_HINSTANCE:
|
|
case GWLP_HWNDPARENT:
|
|
break;
|
|
default:
|
|
if (nIndex < 0)
|
|
{
|
|
WARN("Invalid offset %d\n", nIndex );
|
|
SetLastError( ERROR_INVALID_INDEX );
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
return NtUserSetWindowLong( hWnd, nIndex, wNewWord, FALSE );
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
LONG
|
|
WINAPI
|
|
DECLSPEC_HOTPATCH
|
|
SetWindowLongA(
|
|
HWND hWnd,
|
|
int nIndex,
|
|
LONG dwNewLong)
|
|
{
|
|
return NtUserSetWindowLong(hWnd, nIndex, dwNewLong, TRUE);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
LONG
|
|
WINAPI
|
|
SetWindowLongW(
|
|
HWND hWnd,
|
|
int nIndex,
|
|
LONG dwNewLong)
|
|
{
|
|
return NtUserSetWindowLong(hWnd, nIndex, dwNewLong, FALSE);
|
|
}
|
|
|
|
#ifdef _WIN64
|
|
/*
|
|
* @implemented
|
|
*/
|
|
LONG_PTR
|
|
WINAPI
|
|
SetWindowLongPtrA(HWND hWnd,
|
|
INT nIndex,
|
|
LONG_PTR dwNewLong)
|
|
{
|
|
return NtUserSetWindowLongPtr(hWnd, nIndex, dwNewLong, TRUE);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
LONG_PTR
|
|
WINAPI
|
|
SetWindowLongPtrW(HWND hWnd,
|
|
INT nIndex,
|
|
LONG_PTR dwNewLong)
|
|
{
|
|
return NtUserSetWindowLongPtr(hWnd, nIndex, dwNewLong, FALSE);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
UnregisterClassA(
|
|
LPCSTR lpClassName,
|
|
HINSTANCE hInstance)
|
|
{
|
|
UNICODE_STRING ClassName = {0};
|
|
BOOL Ret;
|
|
LPCWSTR lpszClsVersion;
|
|
BOOL ConvertedString = FALSE;
|
|
|
|
TRACE("class/atom: %s/%04x %p\n",
|
|
IS_ATOM(lpClassName) ? NULL : lpClassName,
|
|
IS_ATOM(lpClassName) ? lpClassName : 0,
|
|
hInstance);
|
|
|
|
lpszClsVersion = ClassNameToVersion(lpClassName, NULL, NULL, NULL, TRUE);
|
|
if (lpszClsVersion)
|
|
{
|
|
RtlInitUnicodeString(&ClassName, lpszClsVersion);
|
|
}
|
|
else if (!IS_ATOM(lpClassName))
|
|
{
|
|
ConvertedString = TRUE;
|
|
if (!RtlCreateUnicodeStringFromAsciiz(&ClassName, lpClassName))
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ClassName.Buffer = (PWSTR)((ULONG_PTR)lpClassName);
|
|
}
|
|
|
|
Ret = NtUserUnregisterClass(&ClassName, hInstance, 0);
|
|
|
|
if (ConvertedString)
|
|
RtlFreeUnicodeString(&ClassName);
|
|
|
|
return Ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
UnregisterClassW(
|
|
LPCWSTR lpClassName,
|
|
HINSTANCE hInstance)
|
|
{
|
|
UNICODE_STRING ClassName = {0};
|
|
LPCWSTR lpszClsVersion;
|
|
|
|
TRACE("class/atom: %S/%04x %p\n",
|
|
IS_ATOM(lpClassName) ? NULL : lpClassName,
|
|
IS_ATOM(lpClassName) ? lpClassName : 0,
|
|
hInstance);
|
|
|
|
lpszClsVersion = ClassNameToVersion(lpClassName, NULL, NULL, NULL, FALSE);
|
|
if (lpszClsVersion)
|
|
{
|
|
RtlInitUnicodeString(&ClassName, lpszClsVersion);
|
|
}
|
|
else if (!IS_ATOM(lpClassName))
|
|
{
|
|
RtlInitUnicodeString(&ClassName, lpClassName);
|
|
}
|
|
else
|
|
{
|
|
ClassName.Buffer = (PWSTR)((ULONG_PTR)lpClassName);
|
|
}
|
|
|
|
return NtUserUnregisterClass(&ClassName, hInstance, 0);
|
|
}
|
|
|
|
/* EOF */
|