mirror of
https://github.com/reactos/reactos.git
synced 2024-09-11 21:34:09 +00:00
![Timo Kreuzer](/assets/img/avatar_default.png)
This must be done field by field, since the alignment of the structures is different on _WIN64
1968 lines
48 KiB
C
1968 lines
48 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;
|
|
}
|
|
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;
|
|
|
|
/* These MUST be copied manually, since on 64 bit architectures the
|
|
alignment of the members is different between the 2 structs! */
|
|
Class.style = lpWndClass->style;
|
|
Class.lpfnWndProc = lpWndClass->lpfnWndProc;
|
|
Class.cbClsExtra = lpWndClass->cbClsExtra;
|
|
Class.cbWndExtra = lpWndClass->cbWndExtra;
|
|
Class.hInstance = lpWndClass->hInstance;
|
|
Class.hIcon = lpWndClass->hIcon;
|
|
Class.hCursor = lpWndClass->hCursor;
|
|
Class.hbrBackground = lpWndClass->hbrBackground;
|
|
Class.lpszMenuName = lpWndClass->lpszMenuName;
|
|
Class.lpszClassName = lpWndClass->lpszClassName;
|
|
|
|
Class.cbSize = sizeof(WNDCLASSEXA);
|
|
Class.hIconSm = NULL;
|
|
|
|
return RegisterClassExA(&Class);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
ATOM WINAPI
|
|
RegisterClassW(CONST WNDCLASSW *lpWndClass)
|
|
{
|
|
WNDCLASSEXW Class;
|
|
|
|
if (lpWndClass == NULL)
|
|
return 0;
|
|
|
|
/* These MUST be copied manually, since on 64 bit architectures the
|
|
alignment of the members is different between the 2 structs! */
|
|
Class.style = lpWndClass->style;
|
|
Class.lpfnWndProc = lpWndClass->lpfnWndProc;
|
|
Class.cbClsExtra = lpWndClass->cbClsExtra;
|
|
Class.cbWndExtra = lpWndClass->cbWndExtra;
|
|
Class.hInstance = lpWndClass->hInstance;
|
|
Class.hIcon = lpWndClass->hIcon;
|
|
Class.hCursor = lpWndClass->hCursor;
|
|
Class.hbrBackground = lpWndClass->hbrBackground;
|
|
Class.lpszMenuName = lpWndClass->lpszMenuName;
|
|
Class.lpszClassName = lpWndClass->lpszClassName;
|
|
|
|
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 */
|