You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1844 lines
54 KiB
1844 lines
54 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: class.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* This module contains RegisterClass and the related window class management
|
|
* functions.
|
|
*
|
|
* History:
|
|
* 10-16-90 DarrinM Ported functions from Win 3.0 sources.
|
|
* 02-01-91 mikeke Added Revalidation code (None)
|
|
* 04-08-91 DarrinM C-S-ized and removed global/public class support.
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
/*
|
|
* These arrays are used by Get/SetClassWord/Long.
|
|
*
|
|
* INDEX_OFFSET must refer to the first entry of afClassDWord[]
|
|
*/
|
|
#define INDEX_OFFSET GCLP_HICONSM
|
|
CONST BYTE afClassDWord[-INDEX_OFFSET] = {
|
|
FIELD_SIZE(CLS, spicnSm), // GCL_HICONSM (-34)
|
|
0,
|
|
FIELD_SIZE(CLS, atomNVClassName), // GCW_ATOM (-32)
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
FIELD_SIZE(CLS, style), // GCL_STYLE (-26)
|
|
0,
|
|
FIELD_SIZE(CLS, lpfnWndProc), // GCL_WNDPROC (-24)
|
|
0,
|
|
0,
|
|
0,
|
|
FIELD_SIZE(CLS, cbclsExtra), // GCL_CBCLSEXTRA (-20)
|
|
0,
|
|
FIELD_SIZE(CLS, cbwndExtra), // GCL_CBWNDEXTRA (-18)
|
|
0,
|
|
FIELD_SIZE(CLS, hModule), // GCL_HMODULE (-16)
|
|
0,
|
|
FIELD_SIZE(CLS, spicn), // GCL_HICON (-14)
|
|
0,
|
|
FIELD_SIZE(CLS, spcur), // GCL_HCURSOR (-12)
|
|
0,
|
|
FIELD_SIZE(CLS, hbrBackground), // GCL_HBRBACKGROUND (-10)
|
|
0,
|
|
FIELD_SIZE(CLS, lpszMenuName), // GCL_HMENUNAME (-8)
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0
|
|
};
|
|
|
|
CONST BYTE aiClassOffset[-INDEX_OFFSET] = {
|
|
FIELD_OFFSET(CLS, spicnSm), // GCL_HICONSM
|
|
0,
|
|
FIELD_OFFSET(CLS, atomNVClassName), // GCW_ATOM
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
FIELD_OFFSET(CLS, style), // GCL_STYLE
|
|
0,
|
|
FIELD_OFFSET(CLS, lpfnWndProc), // GCL_WNDPROC
|
|
0,
|
|
0,
|
|
0,
|
|
FIELD_OFFSET(CLS, cbclsExtra), // GCL_CBCLSEXTRA
|
|
0,
|
|
FIELD_OFFSET(CLS, cbwndExtra), // GCL_CBWNDEXTRA
|
|
0,
|
|
FIELD_OFFSET(CLS, hModule), // GCL_HMODULE
|
|
0,
|
|
FIELD_OFFSET(CLS, spicn), // GCL_HICON
|
|
0,
|
|
FIELD_OFFSET(CLS, spcur), // GCL_HCURSOR
|
|
0,
|
|
FIELD_OFFSET(CLS, hbrBackground), // GCL_HBRBACKGROUND
|
|
0,
|
|
FIELD_OFFSET(CLS, lpszMenuName), // GCL_MENUNAME
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0
|
|
};
|
|
|
|
/***************************************************************************\
|
|
* _RegisterClassEx (API)
|
|
*
|
|
* This stub calls InternalRegisterClass to do its work and then does some
|
|
* additional work to save a pointer to the client-side menu name string.
|
|
* The menu string is returned by _GetClassInfo so the client can fix up
|
|
* a valid entry for the WNDCLASS lpszMenuName field.
|
|
*
|
|
* History:
|
|
* 04-26-91 DarrinM Created.
|
|
\***************************************************************************/
|
|
ATOM _RegisterClassEx(
|
|
LPWNDCLASSVEREX cczpwc,
|
|
PCLSMENUNAME pcmn,
|
|
WORD fnid,
|
|
DWORD dwFlags,
|
|
LPDWORD pdwWOW)
|
|
{
|
|
PCLS pcls;
|
|
PTHREADINFO ptiCurrent = PtiCurrent();
|
|
|
|
/*
|
|
* NOTE -- lpszClassName and lpszMenuName in the wndclass may be client-side
|
|
* pointers. Use of those fields must be protected in try blocks.
|
|
*/
|
|
|
|
/*
|
|
* Convert a possible CallProc Handle into a real address. They may
|
|
* have kept the CallProc Handle from some previous mixed GetClassinfo
|
|
* or SetWindowLong.
|
|
*/
|
|
if (ISCPDTAG(cczpwc->lpfnWndProc)) {
|
|
PCALLPROCDATA pCPD;
|
|
if (pCPD = HMValidateHandleNoRip((HANDLE)cczpwc->lpfnWndProc, TYPE_CALLPROC)) {
|
|
cczpwc->lpfnWndProc = (WNDPROC)pCPD->pfnClientPrevious;
|
|
}
|
|
}
|
|
|
|
pcls = InternalRegisterClassEx(cczpwc, fnid, dwFlags | ((ptiCurrent->TIF_flags & TIF_16BIT)? CSF_WOWCLASS : 0));
|
|
if (pcls != NULL) {
|
|
|
|
pcls->lpszClientUnicodeMenuName = pcmn->pwszClientUnicodeMenuName;
|
|
pcls->lpszClientAnsiMenuName = pcmn->pszClientAnsiMenuName;
|
|
|
|
/*
|
|
* copy 5 WOW dwords.
|
|
*/
|
|
if (pdwWOW && (ptiCurrent->TIF_flags & TIF_16BIT)) {
|
|
RtlCopyMemory (PWCFromPCLS(pcls), pdwWOW, sizeof(WC));
|
|
}
|
|
|
|
if ((ptiCurrent->TIF_flags & TIF_16BIT) && ptiCurrent->ptdb) {
|
|
pcls->hTaskWow = ptiCurrent->ptdb->hTaskWow;
|
|
} else {
|
|
pcls->hTaskWow = 0;
|
|
}
|
|
|
|
/*
|
|
* For some (presumably good) reason Win 3.1 changed RegisterClass
|
|
* to return the classes classname atom.
|
|
*/
|
|
return pcls->atomNVClassName;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* ClassAlloc
|
|
* ClassFree
|
|
*
|
|
* Generic allocation routines that discriminate between desktop heap
|
|
* and pool.
|
|
*
|
|
* History:
|
|
* 08-07-95 JimA Created
|
|
\***************************************************************************/
|
|
|
|
PVOID ClassAlloc(
|
|
PDESKTOP pdesk,
|
|
DWORD cbAlloc,
|
|
ULONG tag)
|
|
{
|
|
PVOID pvalloc;
|
|
|
|
if (pdesk) {
|
|
/*
|
|
* NTRAID#NTBUG9-411175-2001/06/06-jasonsch.
|
|
*/
|
|
pvalloc = DesktopAllocAlways(pdesk, cbAlloc, tag);
|
|
} else {
|
|
pvalloc = UserAllocPoolWithQuotaZInit(cbAlloc, TAG_CLASS);
|
|
}
|
|
|
|
return pvalloc;
|
|
}
|
|
|
|
VOID ClassFree(
|
|
PDESKTOP pdesk,
|
|
PVOID pvfree)
|
|
{
|
|
if (pdesk != NULL) {
|
|
DesktopFree(pdesk, pvfree);
|
|
} else {
|
|
UserFreePool(pvfree);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* ValidateAndLockCursor
|
|
*
|
|
* Win95 comaptible validation
|
|
*
|
|
* History:
|
|
* 12-19-95 GerardoB Created
|
|
\***************************************************************************/
|
|
BOOL ValidateAndLockCursor(
|
|
PCURSOR *ppcursor,
|
|
BOOL fIs40Compat)
|
|
{
|
|
PCURSOR pcur;
|
|
|
|
if (*ppcursor == NULL) {
|
|
return TRUE;
|
|
}
|
|
|
|
pcur = HMValidateHandleNoSecure(*ppcursor, TYPE_CURSOR);
|
|
if (pcur == NULL) {
|
|
RIPMSGF1(RIP_WARNING,
|
|
"Invalid Cursor or Icon: 0x%p",
|
|
*ppcursor);
|
|
if (fIs40Compat) {
|
|
RIPERR0(ERROR_INVALID_PARAMETER,
|
|
RIP_VERBOSE,
|
|
"RegisterClass: Invalid Parameter");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
*ppcursor = NULL;
|
|
Lock(ppcursor, pcur);
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* InternalRegisterClass
|
|
*
|
|
* This API is called by applications or the system to register private or
|
|
* global (public) window classes. If a class with the same name already
|
|
* exists the call will fail, except in the special case where an application
|
|
* registers a private class with the same name as a global class. In this
|
|
* case the private class supercedes the global class for that application.
|
|
*
|
|
* History:
|
|
* 10-15-90 DarrinM Ported from Win 3.0 sources.
|
|
\***************************************************************************/
|
|
PCLS InternalRegisterClassEx(
|
|
LPWNDCLASSVEREX cczlpwndcls,
|
|
WORD fnid,
|
|
DWORD CSF_flags)
|
|
{
|
|
BOOL fIs40Compat;
|
|
ULONG_PTR dwT;
|
|
PCLS pcls;
|
|
LPWSTR pszT1;
|
|
ATOM atomT;
|
|
PTHREADINFO ptiCurrent;
|
|
HANDLE hModule;
|
|
PDESKTOP pdesk;
|
|
ULONG cch;
|
|
UNICODE_STRING UString;
|
|
ANSI_STRING AString;
|
|
|
|
/*
|
|
* NOTE -- lpszClassName and lpszMenuName in the wndclass may be client-side
|
|
* pointers. Use of those fields must be protected in try blocks.
|
|
*/
|
|
CheckCritIn();
|
|
|
|
ptiCurrent = PtiCurrent();
|
|
|
|
/*
|
|
* Don't allow 4.0 apps to register a class using hModuleWin
|
|
* LATER GerardoB: Our client side classes use hmodUser (USER32) while
|
|
* our server side classes use hWinInstance (WIN32K). We should change
|
|
* CreateThreadInfo and LW_RegisterWindows so all classes use hModUser.
|
|
*/
|
|
hModule = cczlpwndcls->hInstance;
|
|
if (!(CSF_flags & (CSF_SYSTEMCLASS | CSF_SERVERSIDEPROC))
|
|
&& (hModule == hModuleWin)
|
|
&& (LOWORD(ptiCurrent->dwExpWinVer) >= VER40)) {
|
|
|
|
RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "InternalRegisterClassEx: Invalid hInstance (Cannot use system's hInstance)");
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* As of NT 4.0 we no longer honor CS_BYTEALIGNCLIENT or CS_BYTEALIGNWINDOW
|
|
*/
|
|
if (cczlpwndcls->style & (CS_BYTEALIGNCLIENT | CS_BYTEALIGNWINDOW)) {
|
|
RIPMSG0(RIP_VERBOSE, "CS_BYTEALIGNCLIENT and CS_BYTEALIGNWINDOW styles no longer honored.");
|
|
}
|
|
|
|
/*
|
|
* Does this class exist as a private class? If so, fail.
|
|
*/
|
|
atomT = FindClassAtom(cczlpwndcls->lpszClassNameVer);
|
|
|
|
if (atomT != 0 && !(CSF_flags & CSF_SERVERSIDEPROC)) {
|
|
/*
|
|
* First check private classes. If already exists, return error.
|
|
*/
|
|
if (_InnerGetClassPtr(atomT, &ptiCurrent->ppi->pclsPrivateList,
|
|
hModule) != NULL) {
|
|
RIPERR1(ERROR_CLASS_ALREADY_EXISTS, RIP_VERBOSE, "RegisterClass: Class already exists %lx", (DWORD)atomT);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Now only check public classes if CS_GLOBALCLASS is set. If it
|
|
* isn't set, then this will allow an application to re-register
|
|
* a private class to take precedence over a public class.
|
|
*/
|
|
if (cczlpwndcls->style & CS_GLOBALCLASS) {
|
|
if (_InnerGetClassPtr(atomT, &ptiCurrent->ppi->pclsPublicList, NULL) != NULL) {
|
|
RIPERR0(ERROR_CLASS_ALREADY_EXISTS, RIP_VERBOSE, "RegisterClass: Global Class already exists");
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Alloc space for the class.
|
|
*/
|
|
if (ptiCurrent->TIF_flags & TIF_SYSTEMTHREAD) {
|
|
pdesk = NULL;
|
|
} else {
|
|
pdesk = ptiCurrent->rpdesk;
|
|
}
|
|
pcls = (PCLS)ClassAlloc(pdesk, sizeof(CLS) + cczlpwndcls->cbClsExtra + (CSF_flags & CSF_WOWCLASS ? sizeof(WC):0), DTAG_CLASS);
|
|
if (pcls == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
LockDesktop(&pcls->rpdeskParent, pdesk, LDL_CLS_DESKPARENT1, (ULONG_PTR)pcls);
|
|
pcls->pclsBase = pcls;
|
|
|
|
/*
|
|
* Copy over the shared part of the class structure.
|
|
*/
|
|
UserAssert(FIELD_OFFSET(WNDCLASSEX, style) == FIELD_OFFSET(COMMON_WNDCLASS, style));
|
|
RtlCopyMemory(&pcls->style, &(cczlpwndcls->style),
|
|
sizeof(COMMON_WNDCLASS) - FIELD_OFFSET(COMMON_WNDCLASS, style));
|
|
|
|
/*
|
|
* Copy CSF_SERVERSIDEPROC, CSF_ANSIPROC (etc.) flags
|
|
*/
|
|
pcls->CSF_flags = LOWORD(CSF_flags);
|
|
pcls->fnid = fnid;
|
|
if (fnid) {
|
|
CBFNID(fnid) = (WORD)(pcls->cbwndExtra + sizeof(WND));
|
|
|
|
#ifndef LAZY_CLASS_INIT
|
|
if (!(pcls->CSF_flags & CSF_SERVERSIDEPROC) && ptiCurrent->pClientInfo != NULL) {
|
|
/*
|
|
* Clear the bit so new threads in this process
|
|
* won't bother to reregister the client-side USER classes.
|
|
*/
|
|
try {
|
|
ptiCurrent->pClientInfo->CI_flags &= ~CI_REGISTERCLASSES;
|
|
} except (W32ExceptionHandler(TRUE, RIP_WARNING)) {
|
|
goto ValidateError1;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* If this wndproc happens to be a client wndproc stub for a server
|
|
* wndproc, then remember the server wndproc! This should be rare: why
|
|
* would an application re-register a class that isn't "subclassed"?
|
|
*/
|
|
if (!(pcls->CSF_flags & CSF_SERVERSIDEPROC)) {
|
|
dwT = MapClientToServerPfn((ULONG_PTR)pcls->lpfnWndProc);
|
|
if (dwT != 0) {
|
|
pcls->CSF_flags |= CSF_SERVERSIDEPROC;
|
|
pcls->CSF_flags &= ~CSF_ANSIPROC;
|
|
pcls->lpfnWndProc = (WNDPROC_PWND)dwT;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Win95 compatible validation.
|
|
*
|
|
* hbrBackground was validated by GDI in the client side
|
|
* NULL hInstances are mapped to GetModuleHandle(NULL) in the client
|
|
* side
|
|
*/
|
|
|
|
fIs40Compat = (CSF_flags & CSF_WIN40COMPAT) != 0;
|
|
|
|
if (!ValidateAndLockCursor(&pcls->spcur, fIs40Compat)) {
|
|
goto ValidateError1;
|
|
}
|
|
|
|
if (!ValidateAndLockCursor(&pcls->spicn, fIs40Compat)) {
|
|
goto ValidateError2;
|
|
}
|
|
|
|
if (!ValidateAndLockCursor(&pcls->spicnSm, fIs40Compat)) {
|
|
goto ValidateError3;
|
|
}
|
|
|
|
/*
|
|
* Add the class name to the atom table.
|
|
*/
|
|
if (IS_PTR(cczlpwndcls->lpszClassName)) {
|
|
atomT = UserAddAtom(cczlpwndcls->lpszClassName, FALSE);
|
|
} else {
|
|
atomT = PTR_TO_ID(cczlpwndcls->lpszClassName);
|
|
}
|
|
|
|
if (atomT == 0) {
|
|
goto AtomError1;
|
|
}
|
|
pcls->atomNVClassName = atomT;
|
|
|
|
if (IS_PTR(cczlpwndcls->lpszClassNameVer)) {
|
|
atomT = UserAddAtom(cczlpwndcls->lpszClassNameVer, FALSE);
|
|
} else {
|
|
atomT = PTR_TO_ID(cczlpwndcls->lpszClassNameVer);
|
|
}
|
|
|
|
if (atomT == 0) {
|
|
goto AtomError2;
|
|
}
|
|
pcls->atomClassName = atomT;
|
|
|
|
/*
|
|
* Make an ANSI version of the class name to optimize
|
|
* GetClassNameA for WOW.
|
|
*/
|
|
if (IS_PTR(cczlpwndcls->lpszClassName)) {
|
|
try {
|
|
RtlInitUnicodeString(&UString, cczlpwndcls->lpszClassName);
|
|
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
|
|
goto MemError2;
|
|
}
|
|
#ifdef FE_SB // InternalRegisterClassEx()
|
|
cch = UString.Length + 1;
|
|
#else
|
|
cch = UString.Length / sizeof(WCHAR) + 1;
|
|
#endif // FE_SB
|
|
} else {
|
|
cch = 7; // 1 char for '#', 5 for '65536'.
|
|
}
|
|
|
|
/*
|
|
* Allocate the ANSI name buffer and convert the unicode name
|
|
* to ANSI.
|
|
*/
|
|
pcls->lpszAnsiClassName = (LPSTR)ClassAlloc(pdesk, cch, DTAG_TEXT);
|
|
if (pcls->lpszAnsiClassName == NULL) {
|
|
goto MemError2;
|
|
}
|
|
|
|
/*
|
|
* Form the ANSI class name.
|
|
*/
|
|
if (IS_PTR(cczlpwndcls->lpszClassName)) {
|
|
|
|
/*
|
|
* Class name is a string.
|
|
*/
|
|
AString.Length = 0;
|
|
AString.MaximumLength = (USHORT)cch;
|
|
AString.Buffer = pcls->lpszAnsiClassName;
|
|
try {
|
|
RtlUnicodeStringToAnsiString(&AString, &UString, FALSE);
|
|
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
|
|
goto MemError3;
|
|
}
|
|
} else {
|
|
|
|
/*
|
|
* Class name is an integer atom.
|
|
*/
|
|
pcls->lpszAnsiClassName[0] = L'#';
|
|
RtlIntegerToChar(PTR_TO_ID(cczlpwndcls->lpszClassName), 10, cch - 1,
|
|
&pcls->lpszAnsiClassName[1]);
|
|
}
|
|
|
|
/*
|
|
* Make local copy of menu name.
|
|
*/
|
|
pszT1 = pcls->lpszMenuName;
|
|
|
|
if (pszT1 != NULL) {
|
|
if (IS_PTR(pszT1)) {
|
|
try {
|
|
RtlInitUnicodeString(&UString, pszT1);
|
|
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
|
|
goto MemError3;
|
|
}
|
|
if (UString.Length == 0) {
|
|
|
|
/*
|
|
* app passed an empty string for the name
|
|
*/
|
|
pcls->lpszMenuName = NULL;
|
|
} else {
|
|
UNICODE_STRING strMenuName;
|
|
|
|
/*
|
|
* Alloc space for the Menu Name.
|
|
*/
|
|
if (!AllocateUnicodeString(&strMenuName, &UString)) {
|
|
/*
|
|
* The unlock call is delayed after the free such that if this is
|
|
* the last reference on the desktop, the desktop heap is not
|
|
* destroyed before we free the objects.
|
|
*/
|
|
PDESKTOP rpdesk;
|
|
MemError3:
|
|
ClassFree(pdesk, pcls->lpszAnsiClassName);
|
|
MemError2:
|
|
UserDeleteAtom(pcls->atomClassName);
|
|
AtomError2:
|
|
UserDeleteAtom(pcls->atomNVClassName);
|
|
AtomError1:
|
|
Unlock(&pcls->spicnSm);
|
|
ValidateError3:
|
|
Unlock(&pcls->spicn);
|
|
ValidateError2:
|
|
Unlock(&pcls->spcur);
|
|
ValidateError1:
|
|
rpdesk = pcls->rpdeskParent;
|
|
pcls->rpdeskParent = NULL;
|
|
ClassFree(pdesk, pcls);
|
|
/*
|
|
* NOTE: Using pobj after freeing the object is not a
|
|
* problem because UnlockDesktop uses the value for
|
|
* tracking and doesn't dereference the pointer. If this
|
|
* ever changes we'll get a BC.
|
|
*/
|
|
UnlockDesktop(&rpdesk, LDU_CLS_DESKPARENT1, (ULONG_PTR)pcls);
|
|
return NULL;
|
|
}
|
|
|
|
pcls->lpszMenuName = strMenuName.Buffer;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((CSF_flags & CSF_SERVERSIDEPROC) || (pcls->style & CS_GLOBALCLASS)) {
|
|
if (pcls->CSF_flags & CSF_SYSTEMCLASS) {
|
|
pcls->pclsNext = gpclsList;
|
|
gpclsList = pcls;
|
|
} else {
|
|
pcls->pclsNext = ptiCurrent->ppi->pclsPublicList;
|
|
ptiCurrent->ppi->pclsPublicList = pcls;
|
|
}
|
|
} else {
|
|
pcls->pclsNext = ptiCurrent->ppi->pclsPrivateList;
|
|
ptiCurrent->ppi->pclsPrivateList = pcls;
|
|
}
|
|
|
|
return pcls;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* _UnregisterClass (API)
|
|
*
|
|
* This API function is used to unregister a window class previously
|
|
* registered by the Application.
|
|
*
|
|
* Returns:
|
|
* TRUE if successful.
|
|
* FALSE otherwise.
|
|
*
|
|
* NOTE:
|
|
* 1. The class name must have been registered earlier by this client
|
|
* through RegisterClass().
|
|
* 2. The class name should not be one of the predefined control classes.
|
|
* 3. All windows created with this class must be destroyed before calling
|
|
* this function.
|
|
*
|
|
* History:
|
|
* 10-15-90 DarrinM Ported from Win 3.0 sources.
|
|
* 03-09-94 BradG Fixed bug when ATOM was passed in
|
|
\***************************************************************************/
|
|
|
|
BOOL _UnregisterClass(
|
|
LPCWSTR ccxlpszClassName,
|
|
HANDLE hModule,
|
|
PCLSMENUNAME pcmn)
|
|
{
|
|
ATOM atomT;
|
|
PPCLS ppcls;
|
|
PTHREADINFO ptiCurrent;
|
|
|
|
CheckCritIn();
|
|
|
|
ptiCurrent = PtiCurrent();
|
|
|
|
/*
|
|
* Check whether the given ClassName is already registered by the
|
|
* Application with the given handle.
|
|
* Return error, if either the Class does not exist or it does not
|
|
* belong to the calling process.
|
|
*/
|
|
|
|
/*
|
|
* bradg (3/9/95) - Must first check to see if an ATOM has been passed
|
|
*/
|
|
atomT = FindClassAtom(ccxlpszClassName);
|
|
ppcls = _InnerGetClassPtr(atomT, &ptiCurrent->ppi->pclsPrivateList, hModule);
|
|
if (ppcls == NULL) {
|
|
/*
|
|
* Maybe this is a public class.
|
|
*/
|
|
ppcls = _InnerGetClassPtr(atomT, &ptiCurrent->ppi->pclsPublicList, NULL);
|
|
if (ppcls == NULL) {
|
|
RIPERR1(ERROR_CLASS_DOES_NOT_EXIST, RIP_WARNING, "UnregisterClass: Class does not exist; atom=%lX", (DWORD)atomT);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If any windows created with this class still exist return an error.
|
|
*/
|
|
if ((*ppcls)->cWndReferenceCount != 0) {
|
|
RIPERR0(ERROR_CLASS_HAS_WINDOWS, RIP_WARNING, "UnregisterClass: Class still has window");
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Return client side pointers for cleanup
|
|
*/
|
|
pcmn->pszClientAnsiMenuName = (*ppcls)->lpszClientAnsiMenuName;
|
|
pcmn->pwszClientUnicodeMenuName = (*ppcls)->lpszClientUnicodeMenuName;
|
|
pcmn->pusMenuName = NULL;
|
|
|
|
/*
|
|
* Release the Window class and related information.
|
|
*/
|
|
DestroyClass(ppcls);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
PCLS _GetWOWClass(
|
|
HANDLE hModule,
|
|
LPCWSTR ccxlpszClassName)
|
|
{
|
|
PCLS pcls;
|
|
PPCLS ppcls = NULL;
|
|
ATOM atomT;
|
|
PTHREADINFO ptiCurrent;
|
|
|
|
CheckCritInShared();
|
|
|
|
ptiCurrent = PtiCurrentShared();
|
|
|
|
/*
|
|
* Is this class registered as a private class?
|
|
*/
|
|
atomT = UserFindAtom(ccxlpszClassName);
|
|
if (atomT != 0) {
|
|
ppcls = GetClassPtr(atomT, ptiCurrent->ppi, hModule);
|
|
}
|
|
|
|
if (ppcls == NULL) {
|
|
RIPERR0(ERROR_CLASS_DOES_NOT_EXIST, RIP_VERBOSE, "");
|
|
return NULL;
|
|
}
|
|
|
|
pcls = *ppcls;
|
|
|
|
if (ptiCurrent->rpdesk != pcls->rpdeskParent) {
|
|
pcls = pcls->pclsClone;
|
|
while (pcls != NULL) {
|
|
if (ptiCurrent->rpdesk == pcls->rpdeskParent) {
|
|
goto Done;
|
|
}
|
|
pcls = pcls->pclsNext;
|
|
}
|
|
RIPERR0(ERROR_CLASS_DOES_NOT_EXIST, RIP_VERBOSE, "");
|
|
return NULL;
|
|
}
|
|
Done:
|
|
return pcls;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* GetClassInfo (API)
|
|
*
|
|
* This function checks if the given class name is registered already. If the
|
|
* class is not found, it returns 0; If the class is found, then all the
|
|
* relevant information from the CLS structure is copied into the WNDCLASS
|
|
* structure pointed to by the lpWndCls argument. If successful, it returns
|
|
* the class name atom
|
|
*
|
|
* NOTE: hmod was used to distinguish between different task's public classes.
|
|
* Now that public classes are gone, hmod isn't used anymore. We just search
|
|
* the applications private class for a match and if none is found we search
|
|
* the system classes.
|
|
*
|
|
* History:
|
|
* 10-15-90 DarrinM Ported from Win 3.0 sources.
|
|
* 04-08-91 DarrinM Removed public classes.
|
|
* 04-26-91 DarrinM Streamlined to work with the client-side API.
|
|
* 03-09-95 BradG Fixed bug when ATOM was passed in.
|
|
\***************************************************************************/
|
|
ATOM _GetClassInfoEx(
|
|
HANDLE hModule,
|
|
LPCWSTR ccxlpszClassName,
|
|
LPWNDCLASSEX pwc,
|
|
LPWSTR *ppszMenuName,
|
|
BOOL bAnsi)
|
|
{
|
|
PCLS pcls;
|
|
PPCLS ppcls;
|
|
ATOM atomT;
|
|
PTHREADINFO ptiCurrent;
|
|
DWORD dwCPDType = 0;
|
|
|
|
CheckCritIn();
|
|
|
|
ptiCurrent = PtiCurrent();
|
|
|
|
/*
|
|
* These are done first so if we don't find the class, and therefore
|
|
* fail, the return thank won't try to copy back these (nonexistant)
|
|
* strings.
|
|
*/
|
|
pwc->lpszMenuName = NULL;
|
|
pwc->lpszClassName = NULL;
|
|
|
|
/*
|
|
* Must first check to see if an ATOM has been passed.
|
|
*/
|
|
atomT = FindClassAtom(ccxlpszClassName);
|
|
|
|
/*
|
|
* Windows 3.1 does not perform the class search with
|
|
* a null hModule. If an application supplies a NULL
|
|
* hModule, they search on hModuleWin instead.
|
|
*/
|
|
|
|
if (hModule == NULL) {
|
|
hModule = hModClient;
|
|
}
|
|
|
|
ppcls = GetClassPtr(atomT, ptiCurrent->ppi, hModule);
|
|
|
|
|
|
if (ppcls == NULL) {
|
|
RIPERR0(ERROR_CLASS_DOES_NOT_EXIST, RIP_VERBOSE, "GetClassInfo: Class does not exist");
|
|
return 0;
|
|
}
|
|
|
|
pcls = *ppcls;
|
|
|
|
/*
|
|
* Copy all the fields common to CLS and WNDCLASS structures except
|
|
* the lpszMenuName and lpszClassName which will be filled in by the
|
|
* client-side piece of GetClassInfo.
|
|
*/
|
|
|
|
/*
|
|
* Return public bits only.
|
|
*/
|
|
pwc->style = pcls->style & CS_VALID;
|
|
|
|
/*
|
|
* Corel Depth 6.0 calls GetClassInfo (COMBOBOX) and registers a class
|
|
* using the same name and style bits. This works OK on Win95 because
|
|
* their "system" (combo, edit, etc) classes are not CS_GLOBALCLASS.
|
|
* So we've got to mask this bit out for our classes.
|
|
*/
|
|
|
|
/*
|
|
* Bug 17998. If the app is 32bit and WinVer is less than 4.0 don't mask
|
|
* out the CS_GLOBALCLASS bit.
|
|
*/
|
|
|
|
if ((pcls->fnid != 0) &&
|
|
((LOWORD(ptiCurrent->dwExpWinVer) >= VER40) || (ptiCurrent->TIF_flags & TIF_16BIT)) ) {
|
|
pwc->style &= ~CS_GLOBALCLASS;
|
|
}
|
|
|
|
|
|
pwc->cbClsExtra = pcls->cbclsExtra;
|
|
pwc->cbWndExtra = pcls->cbwndExtra;
|
|
|
|
/*
|
|
* Stop 32-bit apps from inadvertantly using hModuleWin as their hInstance
|
|
* when they register a window class.
|
|
*/
|
|
if (LOWORD(ptiCurrent->dwExpWinVer) >= VER40) {
|
|
/*
|
|
* This is actually, Win95 behavior -- the USER.EXE hModule gets thunked
|
|
* to NULL on the way out of the 16->32 bit thunk. Note -- if we ever
|
|
* need to support 16-bit 4.0 apps (shudder), this may need to change.
|
|
*/
|
|
if (hModule == hModClient) {
|
|
pwc->hInstance = NULL;
|
|
} else {
|
|
pwc->hInstance = hModule;
|
|
}
|
|
} else {
|
|
/*
|
|
* Win NT 3.1/3.51 returned the hInstance from the class. Note that this
|
|
* is incompatible with Win 3.1. WoW has hacks for 16-bit apps.
|
|
*/
|
|
|
|
if ((pcls->hModule == hModuleWin) || (pcls->hModule == hModClient)) {
|
|
pwc->hInstance = hModClient;
|
|
} else {
|
|
pwc->hInstance = pcls->hModule;
|
|
}
|
|
}
|
|
|
|
pwc->hIcon = PtoH(pcls->spicn);
|
|
pwc->hCursor = PtoH(pcls->spcur);
|
|
pwc->hbrBackground = pcls->hbrBackground;
|
|
|
|
/*
|
|
* Need to hide the small icon if it's USER created.
|
|
*/
|
|
if (pcls->spicnSm && (pcls->spicnSm->CURSORF_flags & CURSORF_SECRET)) {
|
|
pwc->hIconSm = NULL;
|
|
} else {
|
|
pwc->hIconSm = PtoH(pcls->spicnSm);
|
|
}
|
|
|
|
/*
|
|
* If its a server proc then map it to a client proc. If not we may have
|
|
* to create a CPD.
|
|
*/
|
|
if (pcls->CSF_flags & CSF_SERVERSIDEPROC) {
|
|
pwc->lpfnWndProc =
|
|
(WNDPROC)MapServerToClientPfn((ULONG_PTR)pcls->lpfnWndProc, bAnsi);
|
|
} else {
|
|
pwc->lpfnWndProc = (WNDPROC)MapClientNeuterToClientPfn(pcls, 0, bAnsi);
|
|
|
|
/*
|
|
* If the client mapping didn't change the window proc then see if
|
|
* we need a callproc handle.
|
|
*/
|
|
if (pwc->lpfnWndProc == (WNDPROC)pcls->lpfnWndProc) {
|
|
/*
|
|
* Need to return a CallProc handle if there is an Ansi/Unicode mismatch
|
|
*/
|
|
if (bAnsi != !!(pcls->CSF_flags & CSF_ANSIPROC)) {
|
|
dwCPDType |= bAnsi ? CPD_ANSI_TO_UNICODE : CPD_UNICODE_TO_ANSI;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dwCPDType) {
|
|
ULONG_PTR dwCPD;
|
|
|
|
dwCPD = GetCPD(pcls, dwCPDType | CPD_CLASS, (ULONG_PTR)pwc->lpfnWndProc);
|
|
|
|
if (dwCPD) {
|
|
pwc->lpfnWndProc = (WNDPROC)dwCPD;
|
|
} else {
|
|
RIPMSG0(RIP_WARNING, "GetClassInfo unable to alloc CPD returning handle");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Return the stashed pointer to the client-side menu name string.
|
|
*/
|
|
if (bAnsi) {
|
|
*ppszMenuName = (LPWSTR)pcls->lpszClientAnsiMenuName;
|
|
} else {
|
|
*ppszMenuName = pcls->lpszClientUnicodeMenuName;
|
|
}
|
|
return pcls->atomNVClassName;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* _SetClassWord (API)
|
|
*
|
|
* Set a class word. Positive index values set application class words
|
|
* while negative index values set system class words. The negative
|
|
* indices are published in WINDOWS.H.
|
|
*
|
|
* History:
|
|
* 10-16-90 darrinm Wrote.
|
|
\***************************************************************************/
|
|
WORD _SetClassWord(
|
|
PWND pwnd,
|
|
int index,
|
|
WORD value)
|
|
{
|
|
WORD wOld;
|
|
WORD UNALIGNED *pw;
|
|
PCLS pcls;
|
|
|
|
CheckCritIn();
|
|
|
|
if (GETPTI(pwnd)->ppi != PpiCurrent()) {
|
|
RIPERR1(ERROR_ACCESS_DENIED, RIP_WARNING, "SetClassWord: different process: index 0x%lx", index);
|
|
return 0;
|
|
}
|
|
|
|
pcls = pwnd->pcls->pclsBase;
|
|
if ((index < 0) || ((UINT)index + sizeof(WORD) > (UINT)pcls->cbclsExtra)) {
|
|
RIPERR1(ERROR_INVALID_INDEX,
|
|
RIP_WARNING,
|
|
"SetClassWord: invalid index 0x%x", index);
|
|
return 0;
|
|
} else {
|
|
pw = (WORD UNALIGNED *)((BYTE *)(pcls + 1) + index);
|
|
wOld = *pw;
|
|
*pw = value;
|
|
pcls = pcls->pclsClone;
|
|
while (pcls != NULL) {
|
|
pw = (WORD UNALIGNED *)((BYTE *)(pcls + 1) + index);
|
|
*pw = value;
|
|
pcls = pcls->pclsNext;
|
|
}
|
|
return wOld;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxSetClassLong (API)
|
|
*
|
|
* Set a class long. Positive index values set application class longs
|
|
* while negative index values set system class longs. The negative
|
|
* indices are published in WINDOWS.H.
|
|
*
|
|
* History:
|
|
* 10-16-90 darrinm Wrote.
|
|
\***************************************************************************/
|
|
ULONG_PTR xxxSetClassLongPtr(
|
|
PWND pwnd,
|
|
int index,
|
|
ULONG_PTR value,
|
|
BOOL bAnsi)
|
|
{
|
|
ULONG_PTR dwOld;
|
|
PCLS pcls;
|
|
|
|
CheckLock(pwnd);
|
|
CheckCritIn();
|
|
|
|
if (GETPTI(pwnd)->ppi != PpiCurrent()) {
|
|
RIPERR1(ERROR_ACCESS_DENIED, RIP_WARNING, "SetClassLongPtr: different process: index 0x%lx", index);
|
|
return 0;
|
|
}
|
|
|
|
if (index < 0) {
|
|
return xxxSetClassData(pwnd, index, value, bAnsi);
|
|
} else {
|
|
pcls = pwnd->pcls->pclsBase;
|
|
if ((UINT)index + sizeof(ULONG_PTR) > (UINT)pcls->cbclsExtra) {
|
|
RIPERR1(ERROR_INVALID_INDEX,
|
|
RIP_WARNING,
|
|
"SetClassLongPtr: invalid index 0x%x", index);
|
|
return 0;
|
|
} else {
|
|
ULONG_PTR UNALIGNED *pudw;
|
|
pudw = (ULONG_PTR UNALIGNED *)((BYTE *)(pcls + 1) + index);
|
|
dwOld = *pudw;
|
|
*pudw = value;
|
|
pcls = pcls->pclsClone;
|
|
while (pcls != NULL) {
|
|
pudw = (ULONG_PTR UNALIGNED *)((BYTE *)(pcls + 1) + index);
|
|
*pudw = value;
|
|
pcls = pcls->pclsNext;
|
|
}
|
|
return dwOld;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef _WIN64
|
|
DWORD xxxSetClassLong(
|
|
PWND pwnd,
|
|
int index,
|
|
DWORD value,
|
|
BOOL bAnsi)
|
|
{
|
|
DWORD dwOld;
|
|
PCLS pcls;
|
|
|
|
CheckLock(pwnd);
|
|
CheckCritIn();
|
|
|
|
if (GETPTI(pwnd)->ppi != PpiCurrent()) {
|
|
RIPERR1(ERROR_ACCESS_DENIED, RIP_WARNING, "SetClassLong: different process: index 0x%lx", index);
|
|
return 0;
|
|
}
|
|
|
|
if (index < 0) {
|
|
if (index < INDEX_OFFSET || afClassDWord[index - INDEX_OFFSET] > sizeof(DWORD)) {
|
|
RIPERR1(ERROR_INVALID_INDEX,
|
|
RIP_WARNING,
|
|
"SetClassLong: invalid index 0x%x", index);
|
|
return 0;
|
|
}
|
|
return (DWORD)xxxSetClassData(pwnd, index, value, bAnsi);
|
|
} else {
|
|
pcls = pwnd->pcls->pclsBase;
|
|
if ((UINT)index + sizeof(DWORD) > (UINT)pcls->cbclsExtra) {
|
|
RIPERR1(ERROR_INVALID_INDEX,
|
|
RIP_WARNING,
|
|
"SetClassLong: invalid index 0x%x", index);
|
|
return 0;
|
|
} else {
|
|
DWORD UNALIGNED *pudw;
|
|
pudw = (DWORD UNALIGNED *)((BYTE *)(pcls + 1) + index);
|
|
dwOld = *pudw;
|
|
*pudw = value;
|
|
pcls = pcls->pclsClone;
|
|
while (pcls != NULL) {
|
|
pudw = (DWORD UNALIGNED *)((BYTE *)(pcls + 1) + index);
|
|
*pudw = value;
|
|
pcls = pcls->pclsNext;
|
|
}
|
|
return dwOld;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
PPCLS _InnerGetClassPtr(
|
|
ATOM atom,
|
|
PPCLS ppcls,
|
|
HANDLE hModule)
|
|
{
|
|
if (atom == 0)
|
|
return NULL;
|
|
|
|
while (*ppcls != NULL) {
|
|
if ((*ppcls)->atomClassName == atom &&
|
|
(hModule == NULL || HIWORD((ULONG_PTR)(*ppcls)->hModule) == HIWORD((ULONG_PTR)hModule)) &&
|
|
!((*ppcls)->CSF_flags & CSF_WOWDEFERDESTROY)) {
|
|
return ppcls;
|
|
}
|
|
|
|
ppcls = (PPCLS)*ppcls;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* GetClassPtr
|
|
*
|
|
* Note: This returns a "pointer-to-PCLS" and not "PCLS".
|
|
*
|
|
* Scan the passed-in class list for the specified class. Return NULL if
|
|
* the class isn't in the list.
|
|
*
|
|
* History:
|
|
* 10-16-90 darrinm Ported this puppy.
|
|
* 04-08-91 DarrinM Rewrote to remove global classes.
|
|
* 08-14-92 FritzS Changed check to HIWORD only to allow Wow apps to
|
|
* share window classes between instances of an app.
|
|
(For Wow apps, HiWord of hInstance is 16-bit module,
|
|
and LoWord is 16-bit hInstance
|
|
\***************************************************************************/
|
|
|
|
PPCLS GetClassPtr(
|
|
ATOM atom,
|
|
PPROCESSINFO ppi,
|
|
HANDLE hModule)
|
|
{
|
|
PPCLS ppcls;
|
|
|
|
/*
|
|
* First search public then private then usersrv registered classes
|
|
*/
|
|
ppcls = _InnerGetClassPtr(atom, &ppi->pclsPrivateList, hModule);
|
|
if (ppcls)
|
|
return ppcls;
|
|
|
|
ppcls = _InnerGetClassPtr(atom, &ppi->pclsPublicList, NULL);
|
|
if (ppcls)
|
|
return ppcls;
|
|
|
|
/*
|
|
* Next seach public and private classes and override hmodule;
|
|
* some apps (bunny) do a GetClassInfo(dialog) and RegisterClass
|
|
* and only change the wndproc which set the hmodule to be just
|
|
* like usersrv created it even though it is in the app's public
|
|
* or private class list
|
|
*/
|
|
|
|
/*
|
|
* Later -- since we are no longer returning hModuleWin to any app,
|
|
* we may only need to check for hModClient. Check this out.
|
|
* FritzS
|
|
*/
|
|
|
|
ppcls = _InnerGetClassPtr(atom, &ppi->pclsPrivateList, hModClient);
|
|
if (ppcls)
|
|
return ppcls;
|
|
|
|
ppcls = _InnerGetClassPtr(atom, &ppi->pclsPublicList, hModClient);
|
|
if (ppcls)
|
|
return ppcls;
|
|
|
|
/*
|
|
* Search the system class list
|
|
*/
|
|
ppcls = _InnerGetClassPtr(atom, &gpclsList, NULL);
|
|
return ppcls;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* UnlockAndFreeCPDs -
|
|
*
|
|
* Safe way to unlock and free a linked list of CPDs. Need to do it this
|
|
* way in case the Thread's objects have already been marked for destruction.
|
|
*
|
|
* History 2/10/95 SanfordS Created
|
|
\***************************************************************************/
|
|
|
|
VOID UnlockAndFreeCPDs(
|
|
PCALLPROCDATA *ppCPD)
|
|
{
|
|
PCALLPROCDATA pCPD;
|
|
|
|
while ((pCPD = *ppCPD) != NULL) {
|
|
/*
|
|
* Unlink the CPD from the list.
|
|
*/
|
|
*ppCPD = pCPD->spcpdNext;
|
|
pCPD->spcpdNext = NULL;
|
|
|
|
/*
|
|
* Mark it for destruction.
|
|
*/
|
|
if (!HMIsMarkDestroy(pCPD)) {
|
|
HMMarkObjectDestroy(pCPD);
|
|
}
|
|
|
|
/*
|
|
* Unlock it and it will be destroyed.
|
|
*/
|
|
Unlock(&pCPD);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DestroyClassBrush
|
|
*
|
|
* Destroy the brush of the class if it's a brush, it's not a system
|
|
* brush and no other class is using it
|
|
*
|
|
* History:
|
|
* 4-10-96 CLupu Created
|
|
\***************************************************************************/
|
|
|
|
void DestroyClassBrush(
|
|
PCLS pcls)
|
|
{
|
|
PPROCESSINFO ppi = PpiCurrent();
|
|
PCLS pclsWalk;
|
|
int nInd;
|
|
BOOL bRet;
|
|
/*
|
|
* Return if it's not a real brush
|
|
*/
|
|
if (pcls->hbrBackground <= (HBRUSH)(COLOR_MAX))
|
|
return;
|
|
|
|
/*
|
|
* Don't delete the system brushes
|
|
*/
|
|
for (nInd = 0; nInd < COLOR_MAX; nInd++) {
|
|
if (pcls->hbrBackground == SYSHBRUSH(nInd))
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Walk the process public public list
|
|
*/
|
|
pclsWalk = ppi->pclsPublicList;
|
|
|
|
while (pclsWalk) {
|
|
if (pclsWalk != pcls && pclsWalk->hbrBackground == pcls->hbrBackground)
|
|
return;
|
|
|
|
pclsWalk = pclsWalk->pclsNext;
|
|
}
|
|
|
|
/*
|
|
* Walk the process private class list
|
|
*/
|
|
pclsWalk = ppi->pclsPrivateList;
|
|
|
|
while (pclsWalk) {
|
|
if (pclsWalk != pcls && pclsWalk->hbrBackground == pcls->hbrBackground)
|
|
return;
|
|
|
|
pclsWalk = pclsWalk->pclsNext;
|
|
}
|
|
|
|
/*
|
|
* Finaly walk the system class list
|
|
*/
|
|
pclsWalk = gpclsList;
|
|
|
|
while (pclsWalk) {
|
|
if (pclsWalk != pcls && pclsWalk->hbrBackground == pcls->hbrBackground)
|
|
return;
|
|
|
|
pclsWalk = pclsWalk->pclsNext;
|
|
}
|
|
|
|
bRet = GreDeleteObject(pcls->hbrBackground);
|
|
|
|
#if DBG
|
|
if (!bRet)
|
|
RIPERR1(ERROR_INVALID_HANDLE, RIP_WARNING,
|
|
"DestroyClassBrush: failed to destroy brush %#p", pcls->hbrBackground);
|
|
#endif
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* DestroyClass
|
|
*
|
|
* Delete the window class. First, destroy any DCs that are attached to the
|
|
* class. Then delete classname atom. Then free the other stuff that was
|
|
* allocated when the class was registered and unlink the class from the
|
|
* master class list.
|
|
*
|
|
* History:
|
|
* 10-16-90 darrinm Ported this puppy.
|
|
\***************************************************************************/
|
|
VOID DestroyClass(
|
|
PPCLS ppcls)
|
|
{
|
|
PPCLS ppclsClone;
|
|
PCLS pcls;
|
|
PDESKTOP rpdesk;
|
|
|
|
pcls = *ppcls;
|
|
|
|
UserAssert(pcls->cWndReferenceCount == 0);
|
|
|
|
/*
|
|
* If this is a base class, destroy all clones before deleting
|
|
* stuff.
|
|
*/
|
|
if (pcls == pcls->pclsBase) {
|
|
ppclsClone = &pcls->pclsClone;
|
|
while (*ppclsClone != NULL) {
|
|
DestroyClass(ppclsClone);
|
|
}
|
|
|
|
UserDeleteAtom(pcls->atomClassName);
|
|
UserDeleteAtom(pcls->atomNVClassName);
|
|
|
|
/*
|
|
* No freeing if it's an integer resource.
|
|
*/
|
|
if (IS_PTR(pcls->lpszMenuName)) {
|
|
UserFreePool(pcls->lpszMenuName);
|
|
}
|
|
|
|
/*
|
|
* Free up the class dc if there is one.
|
|
*/
|
|
if (pcls->pdce != NULL)
|
|
DestroyCacheDC(NULL, pcls->pdce->hdc);
|
|
|
|
/*
|
|
* Delete the hBrBackground brush if nobody else is
|
|
* using it.
|
|
*/
|
|
DestroyClassBrush(pcls);
|
|
}
|
|
|
|
/*
|
|
* If we created the small icon delete it
|
|
*/
|
|
DestroyClassSmIcon(pcls);
|
|
|
|
/*
|
|
* Unlock cursor and icon
|
|
*/
|
|
Unlock(&pcls->spicn);
|
|
Unlock(&pcls->spicnSm);
|
|
Unlock(&pcls->spcur);
|
|
|
|
/*
|
|
* Free any CallProcData objects associated with this class
|
|
*/
|
|
if (pcls->spcpdFirst) {
|
|
UnlockAndFreeCPDs(&pcls->spcpdFirst);
|
|
}
|
|
|
|
/*
|
|
* Point the previous guy at the guy we currently point to.
|
|
*/
|
|
*ppcls = pcls->pclsNext;
|
|
|
|
/*
|
|
* The unlock call is delayed after the free such that if this is
|
|
* the last reference on the desktop, the desktop heap is not
|
|
* destroyed before we free the objects.
|
|
*/
|
|
rpdesk = pcls->rpdeskParent;
|
|
pcls->rpdeskParent = NULL;
|
|
|
|
ClassFree(rpdesk, pcls->lpszAnsiClassName);
|
|
ClassFree(rpdesk, pcls);
|
|
|
|
/*
|
|
* NOTE: Using pobj after freeing the object is not a problem because
|
|
* UnlockDesktop uses the value for tracking and doesn't dereference
|
|
* the pointer. If this ever changes we'll get a BC.
|
|
*/
|
|
UnlockDesktop(&rpdesk, LDU_CLS_DESKPARENT2, (ULONG_PTR)pcls);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* GetClassIcoCur
|
|
*
|
|
* Returns the pwnd's class icon/cursor. This is called by _GetClassData
|
|
* from the client side because PCURSORs are allocated from POOL (so the
|
|
* client cannot do PtoH on them). NtUserCallHwndParam does the PtoH translation
|
|
*
|
|
* History:
|
|
* 11-19-90 darrinm Wrote.
|
|
\***************************************************************************/
|
|
PCURSOR GetClassIcoCur(
|
|
PWND pwnd,
|
|
int index)
|
|
{
|
|
PCLS pcls = pwnd->pcls;
|
|
PCURSOR pcur;
|
|
|
|
switch (index) {
|
|
case GCLP_HICON:
|
|
pcur = pcls->spicn;
|
|
break;
|
|
|
|
case GCLP_HCURSOR:
|
|
pcur = pcls->spcur;
|
|
break;
|
|
|
|
case GCLP_HICONSM:
|
|
pcur = pcls->spicnSm;
|
|
break;
|
|
|
|
default:
|
|
RIPMSG2(RIP_WARNING,
|
|
"GetWndIcoCur: Invalid index: 0x%x pwnd: 0x%p",
|
|
index,
|
|
pwnd);
|
|
pcur = NULL;
|
|
}
|
|
|
|
return pcur;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxSetClassCursor
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
ULONG_PTR xxxSetClassCursor(
|
|
PWND pwnd,
|
|
PCLS pcls,
|
|
DWORD index,
|
|
ULONG_PTR dwData)
|
|
{
|
|
ULONG_PTR dwOld;
|
|
|
|
CheckLock(pwnd);
|
|
|
|
if ((HANDLE)dwData != NULL) {
|
|
dwData = (ULONG_PTR)HMValidateHandle((HANDLE)dwData, TYPE_CURSOR);
|
|
if ((PVOID)dwData == NULL) {
|
|
if (index == GCLP_HICON || index == GCLP_HICONSM) {
|
|
RIPERR0(ERROR_INVALID_ICON_HANDLE, RIP_WARNING, "SetClassData: invalid icon");
|
|
} else {
|
|
RIPERR0(ERROR_INVALID_CURSOR_HANDLE, RIP_WARNING, "SetClassData: invalid cursor");
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Handle the locking issue.
|
|
*/
|
|
pcls = pcls->pclsBase;
|
|
switch (index) {
|
|
case GCLP_HICON:
|
|
case GCLP_HICONSM:
|
|
dwOld = (ULONG_PTR)xxxSetClassIcon(pwnd, pcls, (PCURSOR)dwData, index);
|
|
break;
|
|
|
|
case GCLP_HCURSOR:
|
|
dwOld = (ULONG_PTR)Lock(&pcls->spcur, dwData);
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Now set it for each clone class.
|
|
*/
|
|
pcls = pcls->pclsClone;
|
|
while (pcls != NULL) {
|
|
switch(index) {
|
|
case GCLP_HICON:
|
|
case GCLP_HICONSM:
|
|
xxxSetClassIcon(pwnd, pcls, (PCURSOR)dwData, index);
|
|
break;
|
|
|
|
case GCLP_HCURSOR:
|
|
Lock(&pcls->spcur, dwData);
|
|
break;
|
|
}
|
|
pcls = pcls->pclsNext;
|
|
}
|
|
|
|
return (ULONG_PTR)PtoH((PVOID)dwOld);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* SetClassData
|
|
*
|
|
* SetClassWord and SetClassLong are now identical routines because they both
|
|
* can return DWORDs. This single routine performs the work for them both
|
|
* by using two arrays; afClassDWord to determine whether the result should be
|
|
* a WORD or a DWORD, and aiClassOffset to find the correct offset into the
|
|
* CLS structure for a given GCL_ or GCL_ index.
|
|
*
|
|
* History:
|
|
* 11-19-90 darrinm Wrote.
|
|
\***************************************************************************/
|
|
ULONG_PTR xxxSetClassData(
|
|
PWND pwnd,
|
|
int index,
|
|
ULONG_PTR dwData,
|
|
BOOL bAnsi)
|
|
{
|
|
PCLS pcls = pwnd->pcls;
|
|
BYTE *pb;
|
|
ULONG_PTR dwT;
|
|
ULONG_PTR dwOld;
|
|
DWORD dwCPDType = 0;
|
|
PCLSMENUNAME pcmn;
|
|
UNICODE_STRING strMenuName, UString;
|
|
|
|
CheckLock(pwnd);
|
|
|
|
switch(index) {
|
|
case GCLP_WNDPROC:
|
|
|
|
/*
|
|
* If the application (client) subclasses a class that has a server -
|
|
* side window proc we must return a client side proc stub that it
|
|
* can call.
|
|
*/
|
|
if (pcls->CSF_flags & CSF_SERVERSIDEPROC) {
|
|
dwOld = MapServerToClientPfn((ULONG_PTR)pcls->lpfnWndProc, bAnsi);
|
|
pcls->CSF_flags &= ~CSF_SERVERSIDEPROC;
|
|
|
|
UserAssert(!(pcls->CSF_flags & CSF_ANSIPROC));
|
|
if (bAnsi) {
|
|
pcls->CSF_flags |= CSF_ANSIPROC;
|
|
}
|
|
} else {
|
|
dwOld = MapClientNeuterToClientPfn(pcls, 0, bAnsi);
|
|
|
|
/*
|
|
* If the client mapping didn't change the window proc then see if
|
|
* we need a callproc handle.
|
|
*/
|
|
if (dwOld == (ULONG_PTR)pcls->lpfnWndProc) {
|
|
/*
|
|
* Need to return a CallProc handle if there is an Ansi/Unicode mismatch
|
|
*/
|
|
if (bAnsi != !!(pcls->CSF_flags & CSF_ANSIPROC)) {
|
|
dwCPDType |= bAnsi ? CPD_ANSI_TO_UNICODE : CPD_UNICODE_TO_ANSI;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dwCPDType) {
|
|
ULONG_PTR dwCPD;
|
|
|
|
dwCPD = GetCPD(pcls, dwCPDType | CPD_CLASS, dwOld);
|
|
|
|
if (dwCPD) {
|
|
dwOld = dwCPD;
|
|
} else {
|
|
RIPMSG0(RIP_WARNING, "GetClassLong unable to alloc CPD returning handle");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Convert a possible CallProc Handle into a real address. They may
|
|
* have kept the CallProc Handle from some previous mixed GetClassinfo
|
|
* or SetWindowLong.
|
|
*/
|
|
if (ISCPDTAG(dwData)) {
|
|
PCALLPROCDATA pCPD;
|
|
if (pCPD = HMValidateHandleNoRip((HANDLE)dwData, TYPE_CALLPROC)) {
|
|
dwData = pCPD->pfnClientPrevious;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If an app 'unsubclasses' a server-side window proc we need to
|
|
* restore everything so SendMessage and friends know that it's
|
|
* a server-side proc again. Need to check against client side
|
|
* stub addresses.
|
|
*/
|
|
pcls->lpfnWndProc = (WNDPROC_PWND)dwData;
|
|
if ((dwT = MapClientToServerPfn(dwData)) != 0) {
|
|
pcls->lpfnWndProc = (WNDPROC_PWND)dwT;
|
|
pcls->CSF_flags |= CSF_SERVERSIDEPROC;
|
|
pcls->CSF_flags &= ~CSF_ANSIPROC;
|
|
} else {
|
|
if (bAnsi) {
|
|
pcls->CSF_flags |= CSF_ANSIPROC;
|
|
} else {
|
|
pcls->CSF_flags &= ~CSF_ANSIPROC;
|
|
}
|
|
}
|
|
if (pcls->CSF_flags & CSF_WOWCLASS) {
|
|
PWC pwc = PWCFromPCLS(pcls);
|
|
pwc->hMod16 = (pcls->CSF_flags & CSF_SERVERSIDEPROC) ? 0 : xxxClientWOWGetProcModule(pcls->lpfnWndProc);
|
|
}
|
|
|
|
return dwOld;
|
|
break;
|
|
|
|
case GCLP_HICON:
|
|
case GCLP_HICONSM:
|
|
case GCLP_HCURSOR:
|
|
return xxxSetClassCursor(pwnd, pcls, index, dwData);
|
|
break;
|
|
|
|
|
|
case GCL_WOWMENUNAME:
|
|
if (pcls->CSF_flags & CSF_WOWCLASS) {
|
|
PWCFromPCLS(pcls)->vpszMenu = (DWORD)dwData;
|
|
} else {
|
|
UserAssert(FALSE);
|
|
}
|
|
break;
|
|
|
|
case GCL_CBCLSEXTRA:
|
|
if (pcls->CSF_flags & CSF_WOWCLASS) {
|
|
/*
|
|
* yes -- we can do this for WOW classes only.
|
|
*/
|
|
if (pcls->CSF_flags & CSF_WOWEXTRA) {
|
|
dwOld = PWCFromPCLS(pcls)->iClsExtra;
|
|
PWCFromPCLS(pcls)->iClsExtra = LOWORD(dwData);
|
|
return dwOld;
|
|
} else {
|
|
PWCFromPCLS(pcls)->iClsExtra = LOWORD(dwData);
|
|
pcls->CSF_flags |= CSF_WOWEXTRA;
|
|
return pcls->cbclsExtra;
|
|
}
|
|
}
|
|
RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "Attempt to change cbClsExtra");
|
|
break;
|
|
|
|
case GCLP_MENUNAME:
|
|
pcmn = (PCLSMENUNAME) dwData;
|
|
|
|
/*
|
|
* pcmn->pusMenuName->Buffer is a client-side address.
|
|
*/
|
|
|
|
dwOld = (ULONG_PTR) pcls->lpszMenuName;
|
|
/* Is it a string? */
|
|
if (IS_PTR(pcmn->pusMenuName->Buffer)) {
|
|
try {
|
|
RtlInitUnicodeString(&UString, pcmn->pusMenuName->Buffer);
|
|
} except (W32ExceptionHandler(TRUE, RIP_WARNING)) {
|
|
break;
|
|
}
|
|
/* Empty String? */
|
|
if (UString.Length == 0) {
|
|
pcls->lpszMenuName = NULL;
|
|
} else {
|
|
/* Make a copy of the string */
|
|
if (!AllocateUnicodeString(&strMenuName, &UString)) {
|
|
RIPMSG0(RIP_WARNING, "xxxSetClassData: GCL_MENUNAME AllocateUnicodeString failed");
|
|
break;
|
|
}
|
|
|
|
pcls->lpszMenuName = strMenuName.Buffer;
|
|
}
|
|
} else {
|
|
/* Just copy the id */
|
|
pcls->lpszMenuName = pcmn->pusMenuName->Buffer;
|
|
}
|
|
/* Don't return the kernel side pointer */
|
|
pcmn->pusMenuName = NULL;
|
|
|
|
/* Free old string, if any */
|
|
if (IS_PTR(dwOld)) {
|
|
UserFreePool((PVOID)dwOld);
|
|
}
|
|
|
|
/* Return client side pointers */
|
|
dwOld = (ULONG_PTR) pcls->lpszClientAnsiMenuName;
|
|
pcls->lpszClientAnsiMenuName = pcmn->pszClientAnsiMenuName;
|
|
pcmn->pszClientAnsiMenuName = (LPSTR)dwOld;
|
|
|
|
dwOld = (ULONG_PTR) pcls->lpszClientUnicodeMenuName;
|
|
pcls->lpszClientUnicodeMenuName = pcmn->pwszClientUnicodeMenuName;
|
|
pcmn->pwszClientUnicodeMenuName = (LPWSTR)dwOld;
|
|
|
|
return (bAnsi ? (ULONG_PTR) pcmn->pszClientAnsiMenuName : (ULONG_PTR) pcmn->pwszClientUnicodeMenuName);
|
|
|
|
default:
|
|
/*
|
|
* All other indexes go here...
|
|
*/
|
|
index -= INDEX_OFFSET;
|
|
|
|
/*
|
|
* Only let valid indices go through; if aiClassOffset is zero
|
|
* then we have no mapping for this negative index so it must
|
|
* be a bogus index.
|
|
*/
|
|
if ((index < 0) || (aiClassOffset[index] == 0)) {
|
|
RIPERR1(ERROR_INVALID_INDEX,
|
|
RIP_WARNING,
|
|
"SetClassLong: invalid index 0x%x", index);
|
|
return 0;
|
|
}
|
|
|
|
pcls = pcls->pclsBase;
|
|
pb = ((BYTE *)pcls) + aiClassOffset[index];
|
|
|
|
if (afClassDWord[index] == sizeof(DWORD)) {
|
|
dwOld = *(DWORD *)pb;
|
|
*(DWORD *)pb = (DWORD)dwData;
|
|
} else if (afClassDWord[index] == sizeof(ULONG_PTR)) {
|
|
dwOld = *(ULONG_PTR *)pb;
|
|
*(ULONG_PTR *)pb = dwData;
|
|
} else {
|
|
dwOld = (DWORD)*(WORD *)pb;
|
|
*(WORD *)pb = (WORD)dwData;
|
|
}
|
|
|
|
pcls = pcls->pclsClone;
|
|
while (pcls != NULL) {
|
|
pb = ((BYTE *)pcls) + aiClassOffset[index];
|
|
|
|
if (afClassDWord[index] == sizeof(DWORD)) {
|
|
dwOld = *(DWORD *)pb;
|
|
*(DWORD *)pb = (DWORD)dwData;
|
|
} else if (afClassDWord[index] == sizeof(ULONG_PTR)) {
|
|
dwOld = *(ULONG_PTR *)pb;
|
|
*(ULONG_PTR *)pb = dwData;
|
|
} else {
|
|
dwOld = (DWORD)*(WORD *)pb;
|
|
*(WORD *)pb = (WORD)dwData;
|
|
}
|
|
pcls = pcls->pclsNext;
|
|
}
|
|
|
|
return dwOld;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* ReferenceClass
|
|
*
|
|
* Clones the class if it is a different desktop than the new window and
|
|
* increments the class window count(s).
|
|
*
|
|
* History:
|
|
* 12-11-93 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
BOOL ReferenceClass(
|
|
PCLS pcls,
|
|
PWND pwnd)
|
|
{
|
|
DWORD cbName;
|
|
PCLS pclsClone;
|
|
PDESKTOP pdesk;
|
|
|
|
/*
|
|
* If the window is on the same desktop as the base class, just
|
|
* increment the window count.
|
|
*/
|
|
if (pcls->rpdeskParent == pwnd->head.rpdesk) {
|
|
pcls->cWndReferenceCount++;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* The window is not on the base desktop. Try to find a cloned
|
|
* class.
|
|
*/
|
|
for (pclsClone = pcls->pclsClone; pclsClone != NULL;
|
|
pclsClone = pclsClone->pclsNext) {
|
|
if (pclsClone->rpdeskParent == pwnd->head.rpdesk) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we can't find one, clone the base class.
|
|
*/
|
|
if (pclsClone == NULL) {
|
|
pdesk = pwnd->head.rpdesk;
|
|
pclsClone = ClassAlloc(pdesk, sizeof(CLS) + pcls->cbclsExtra + (pcls->CSF_flags & CSF_WOWCLASS ?sizeof(WC):0), DTAG_CLASS);
|
|
if (pclsClone == NULL) {
|
|
RIPMSG0(RIP_WARNING, "ReferenceClass: Failed Clone-Class Allocation");
|
|
return FALSE;
|
|
}
|
|
|
|
RtlCopyMemory(pclsClone, pcls, sizeof(CLS) + pcls->cbclsExtra + (pcls->CSF_flags & CSF_WOWCLASS?sizeof(WC):0));
|
|
|
|
cbName = strlen(pcls->lpszAnsiClassName) + 1;
|
|
pclsClone->lpszAnsiClassName = ClassAlloc(pdesk, cbName, DTAG_TEXT);
|
|
if (pclsClone->lpszAnsiClassName == NULL) {
|
|
ClassFree(pdesk, pclsClone);
|
|
RIPMSG0(RIP_WARNING, "ReferenceClass: No Clone Class Name");
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Everything has been allocated, now lock everything down.
|
|
* NULL pointers in clone to prevent Lock() from incorrectly
|
|
* decrementing object reference count
|
|
*/
|
|
pclsClone->rpdeskParent = NULL;
|
|
LockDesktop(&pclsClone->rpdeskParent, pdesk,
|
|
LDL_CLS_DESKPARENT2, (ULONG_PTR)pclsClone);
|
|
pclsClone->pclsNext = pcls->pclsClone;
|
|
pclsClone->pclsClone = NULL;
|
|
pcls->pclsClone = pclsClone;
|
|
RtlCopyMemory(pclsClone->lpszAnsiClassName, pcls->lpszAnsiClassName, cbName);
|
|
|
|
pclsClone->spicn = pclsClone->spicnSm = pclsClone->spcur = NULL;
|
|
|
|
Lock(&pclsClone->spicn, pcls->spicn);
|
|
Lock(&pclsClone->spicnSm, pcls->spicnSm);
|
|
Lock(&pclsClone->spcur, pcls->spcur);
|
|
pclsClone->spcpdFirst = NULL;
|
|
pclsClone->cWndReferenceCount = 0;
|
|
}
|
|
|
|
/*
|
|
* Increment reference counts.
|
|
*/
|
|
pcls->cWndReferenceCount++;
|
|
pclsClone->cWndReferenceCount++;
|
|
pwnd->pcls = pclsClone;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* DereferenceClass
|
|
*
|
|
* Decrements the class window count in the base class. If it's the
|
|
* last window of a clone class, destroy the clone.
|
|
*
|
|
* History:
|
|
* 12-11-93 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
VOID DereferenceClass(
|
|
PWND pwnd)
|
|
{
|
|
PCLS pcls = pwnd->pcls;
|
|
PPCLS ppcls;
|
|
|
|
UserAssert(pcls->cWndReferenceCount >= 1);
|
|
|
|
pwnd->pcls = NULL;
|
|
|
|
pcls->cWndReferenceCount--;
|
|
if (pcls != pcls->pclsBase) {
|
|
|
|
UserAssert(pcls->pclsBase->cWndReferenceCount >= 1);
|
|
|
|
pcls->pclsBase->cWndReferenceCount--;
|
|
|
|
if (pcls->cWndReferenceCount == 0) {
|
|
ppcls = &pcls->pclsBase->pclsClone;
|
|
while ((*ppcls) != pcls)
|
|
ppcls = &(*ppcls)->pclsNext;
|
|
UserAssert(ppcls);
|
|
DestroyClass(ppcls);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* DestroyProcessesClasses
|
|
*
|
|
* History:
|
|
* 04-07-91 DarrinM Created.
|
|
\***************************************************************************/
|
|
|
|
VOID DestroyProcessesClasses(
|
|
PPROCESSINFO ppi)
|
|
{
|
|
PPCLS ppcls;
|
|
|
|
/*
|
|
* Destroy the private classes first
|
|
*/
|
|
ppcls = &(ppi->pclsPrivateList);
|
|
while (*ppcls != NULL) {
|
|
DestroyClass(ppcls);
|
|
}
|
|
|
|
/*
|
|
* Then the cloned public classes
|
|
*/
|
|
ppcls = &(ppi->pclsPublicList);
|
|
while (*ppcls != NULL) {
|
|
DestroyClass(ppcls);
|
|
}
|
|
}
|