xref: /reactos/win32ss/user/ntuser/class.c (revision 201f00ab)
1 /*
2  * COPYRIGHT:        See COPYING in the top level directory
3  * PROJECT:          ReactOS Win32k subsystem
4  * PURPOSE:          Window classes
5  * FILE:             win32ss/user/ntuser/class.c
6  * PROGRAMER:        Thomas Weidenmueller <w3seek@reactos.com>
7  */
8 
9 #include <win32k.h>
10 #include <unaligned.h>
11 
12 DBG_DEFAULT_CHANNEL(UserClass);
13 
14 static PWSTR ControlsList[] =
15 {
16   L"Button",
17   L"Edit",
18   L"Static",
19   L"ListBox",
20   L"ScrollBar",
21   L"ComboBox",
22   L"MDIClient",
23   L"ComboLBox",
24   L"DDEMLEvent",
25   L"DDEMLMom",
26   L"DMGClass",
27   L"DDEMLAnsiClient",
28   L"DDEMLUnicodeClient",
29   L"DDEMLAnsiServer",
30   L"DDEMLUnicodeServer",
31   L"IME",
32   L"Ghost",
33 };
34 
35 static NTSTATUS IntDeregisterClassAtom(IN RTL_ATOM Atom);
36 
37 REGISTER_SYSCLASS DefaultServerClasses[] =
38 {
39   { ((PWSTR)WC_DESKTOP),
40     CS_GLOBALCLASS|CS_DBLCLKS,
41     NULL, // Use User32 procs
42     sizeof(ULONG)*2,
43     (HICON)OCR_NORMAL,
44     (HBRUSH)(COLOR_BACKGROUND),
45     FNID_DESKTOP,
46     ICLS_DESKTOP
47   },
48   { ((PWSTR)WC_SWITCH),
49     CS_VREDRAW|CS_HREDRAW|CS_SAVEBITS,
50     NULL, // Use User32 procs
51     sizeof(LONG_PTR), // See user32_apitest GetClassInfo, 0: Pointer to ALTTABINFO
52     (HICON)OCR_NORMAL,
53     NULL,
54     FNID_SWITCH,
55     ICLS_SWITCH
56   },
57   { ((PWSTR)WC_MENU),
58     CS_DBLCLKS|CS_SAVEBITS|CS_DROPSHADOW,
59     NULL, // Use User32 procs
60     16, // See user32_apitest GetClassInfo, PopupMenuWndProcW
61     (HICON)OCR_NORMAL,
62     (HBRUSH)(COLOR_MENU + 1),
63     FNID_MENU,
64     ICLS_MENU
65   },
66   { L"ScrollBar",
67     CS_DBLCLKS|CS_VREDRAW|CS_HREDRAW|CS_PARENTDC,
68     NULL, // Use User32 procs
69     sizeof(SBWND)-sizeof(WND),
70     (HICON)OCR_NORMAL,
71     NULL,
72     FNID_SCROLLBAR,
73     ICLS_SCROLLBAR
74   },
75 #if 0
76   { ((PWSTR)((ULONG_PTR)(WORD)(0x8006))), // Tooltips
77     CS_PARENTDC|CS_DBLCLKS,
78     NULL, // Use User32 procs
79     0,
80     (HICON)OCR_NORMAL,
81     0,
82     FNID_TOOLTIPS,
83     ICLS_TOOLTIPS
84   },
85 #endif
86   { ((PWSTR)WC_ICONTITLE), // IconTitle is here for now...
87     0,
88     NULL, // Use User32 procs
89     0,
90     (HICON)OCR_NORMAL,
91     0,
92     FNID_ICONTITLE,
93     ICLS_ICONTITLE
94   },
95   { L"Message",
96     CS_GLOBALCLASS,
97     NULL, // Use User32 procs
98     0,
99     (HICON)OCR_NORMAL,
100     NULL,
101     FNID_MESSAGEWND,
102     ICLS_HWNDMESSAGE
103   }
104 };
105 
106 static struct
107 {
108     int FnId;
109     int ClsId;
110 }  FnidToiCls[] =
111 { /* Function Ids to Class indexes. */
112  { FNID_SCROLLBAR,  ICLS_SCROLLBAR},
113  { FNID_ICONTITLE,  ICLS_ICONTITLE},
114  { FNID_MENU,       ICLS_MENU},
115  { FNID_DESKTOP,    ICLS_DESKTOP},
116  { FNID_SWITCH,     ICLS_SWITCH},
117  { FNID_MESSAGEWND, ICLS_HWNDMESSAGE},
118  { FNID_BUTTON,     ICLS_BUTTON},
119  { FNID_COMBOBOX,   ICLS_COMBOBOX},
120  { FNID_COMBOLBOX,  ICLS_COMBOLBOX},
121  { FNID_DIALOG,     ICLS_DIALOG},
122  { FNID_EDIT,       ICLS_EDIT},
123  { FNID_LISTBOX,    ICLS_LISTBOX},
124  { FNID_MDICLIENT,  ICLS_MDICLIENT},
125  { FNID_STATIC,     ICLS_STATIC},
126  { FNID_IME,        ICLS_IME},
127  { FNID_GHOST,      ICLS_GHOST},
128  { FNID_TOOLTIPS,   ICLS_TOOLTIPS}
129 };
130 
131 BOOL
132 FASTCALL
LookupFnIdToiCls(int FnId,int * iCls)133 LookupFnIdToiCls(int FnId, int *iCls )
134 {
135   int i;
136 
137   for ( i = 0; i < ARRAYSIZE(FnidToiCls); i++)
138   {
139      if (FnidToiCls[i].FnId == FnId)
140      {
141         if (iCls) *iCls = FnidToiCls[i].ClsId;
142         return TRUE;
143      }
144   }
145   if (iCls) *iCls = 0;
146   return FALSE;
147 }
148 
149 _Must_inspect_result_
150 NTSTATUS
151 NTAPI
152 ProbeAndCaptureUnicodeStringOrAtom(
153     _Out_ _When_(return>=0, _At_(pustrOut->Buffer, _Post_ _Notnull_)) PUNICODE_STRING pustrOut,
154     __in_data_source(USER_MODE) _In_ PUNICODE_STRING pustrUnsafe)
155 {
156     NTSTATUS Status = STATUS_SUCCESS;
157     UNICODE_STRING ustrCopy;
158 
159     /* Default to NULL */
160     RtlInitEmptyUnicodeString(pustrOut, NULL, 0);
161 
162     _SEH2_TRY
163     {
164         ProbeForRead(pustrUnsafe, sizeof(UNICODE_STRING), 1);
165 
166         ustrCopy = *pustrUnsafe;
167 
168         /* Validate the string */
169         if ((ustrCopy.Length & 1) || (ustrCopy.Buffer == NULL))
170         {
171             /* This is not legal */
172             _SEH2_YIELD(return STATUS_INVALID_PARAMETER);
173         }
174 
175         /* Check if this is an atom */
176         if (IS_ATOM(ustrCopy.Buffer))
177         {
178             /* Copy the atom, length is 0 */
179             pustrOut->MaximumLength = pustrOut->Length = 0;
180             pustrOut->Buffer = ustrCopy.Buffer;
181         }
182         else
183         {
184             /* Get the length, maximum length includes zero termination */
185             pustrOut->Length = ustrCopy.Length;
186             pustrOut->MaximumLength = pustrOut->Length + sizeof(WCHAR);
187 
188             /* Allocate a buffer */
189             pustrOut->Buffer = ExAllocatePoolWithTag(PagedPool,
190                                                      pustrOut->MaximumLength,
191                                                      TAG_STRING);
192             if (!pustrOut->Buffer)
193             {
194                 _SEH2_YIELD(return STATUS_INSUFFICIENT_RESOURCES);
195             }
196 
197             /* Copy the string and zero terminate it */
198             ProbeForRead(ustrCopy.Buffer, pustrOut->Length, 1);
199             RtlCopyMemory(pustrOut->Buffer, ustrCopy.Buffer, pustrOut->Length);
200             pustrOut->Buffer[pustrOut->Length / sizeof(WCHAR)] = L'\0';
201         }
202     }
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)203     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
204     {
205         /* Check if we already allocated a buffer */
206         if (pustrOut->Buffer)
207         {
208             /* Free the buffer */
209             ExFreePoolWithTag(pustrOut->Buffer, TAG_STRING);
210             Status = _SEH2_GetExceptionCode();
211         }
212     }
213     _SEH2_END;
214 
215     return Status;
216 }
217 
218 /* WINDOWCLASS ***************************************************************/
219 
220 static VOID
IntFreeClassMenuName(IN OUT PCLS Class)221 IntFreeClassMenuName(IN OUT PCLS Class)
222 {
223     /* Free the menu name, if it was changed and allocated */
224     if (Class->lpszClientUnicodeMenuName != NULL && Class->MenuNameIsString)
225     {
226         UserHeapFree(Class->lpszClientUnicodeMenuName);
227         Class->lpszClientUnicodeMenuName = NULL;
228         Class->lpszClientAnsiMenuName = NULL;
229     }
230 }
231 
232 static VOID
IntDestroyClass(IN OUT PCLS Class)233 IntDestroyClass(IN OUT PCLS Class)
234 {
235     PDESKTOP pDesk;
236 
237     /* There shouldn't be any clones anymore */
238     ASSERT(Class->cWndReferenceCount == 0);
239     ASSERT(Class->pclsClone == NULL);
240 
241     if (Class->pclsBase == Class)
242     {
243         PCALLPROCDATA CallProc, NextCallProc;
244 
245         /* Destroy allocated callproc handles */
246         CallProc = Class->spcpdFirst;
247         while (CallProc != NULL)
248         {
249             NextCallProc = CallProc->spcpdNext;
250 
251             CallProc->spcpdNext = NULL;
252             DestroyCallProc(CallProc);
253 
254             CallProc = NextCallProc;
255         }
256 
257         // Fixes running the static test then run class test issue.
258         // Some applications do not use UnregisterClass before exiting.
259         // Keep from reusing the same atom with case insensitive
260         // comparisons, remove registration of the atom if not zeroed.
261         if (Class->atomClassName)
262             IntDeregisterClassAtom(Class->atomClassName);
263         // Dereference non-versioned class name
264         if (Class->atomNVClassName)
265             IntDeregisterClassAtom(Class->atomNVClassName);
266 
267         if (Class->pdce)
268         {
269            DceFreeClassDCE(Class->pdce);
270            Class->pdce = NULL;
271         }
272 
273         IntFreeClassMenuName(Class);
274     }
275 
276     if (Class->spicn)
277         UserDereferenceObject(Class->spicn);
278     if (Class->spcur)
279         UserDereferenceObject(Class->spcur);
280     if (Class->spicnSm)
281     {
282         UserDereferenceObject(Class->spicnSm);
283         /* Destroy the icon if we own it */
284         if ((Class->CSF_flags & CSF_CACHEDSMICON)
285                 && !(UserObjectInDestroy(UserHMGetHandle(Class->spicnSm))))
286             IntDestroyCurIconObject(Class->spicnSm);
287     }
288 
289     pDesk = Class->rpdeskParent;
290     Class->rpdeskParent = NULL;
291 
292     /* Free the structure */
293     if (pDesk != NULL)
294     {
295         DesktopHeapFree(pDesk, Class);
296     }
297     else
298     {
299         UserHeapFree(Class);
300     }
301 }
302 
303 
304 /* Clean all process classes. all process windows must cleaned first!! */
DestroyProcessClasses(PPROCESSINFO Process)305 void FASTCALL DestroyProcessClasses(PPROCESSINFO Process )
306 {
307     PCLS Class;
308     PPROCESSINFO pi = (PPROCESSINFO)Process;
309 
310     if (pi != NULL)
311     {
312         /* Free all local classes */
313         Class = pi->pclsPrivateList;
314         while (Class != NULL)
315         {
316             pi->pclsPrivateList = Class->pclsNext;
317 
318             ASSERT(Class->pclsBase == Class);
319             IntDestroyClass(Class);
320 
321             Class = pi->pclsPrivateList;
322         }
323 
324         /* Free all global classes */
325         Class = pi->pclsPublicList;
326         while (Class != NULL)
327         {
328             pi->pclsPublicList = Class->pclsNext;
329 
330             ASSERT(Class->pclsBase == Class);
331             IntDestroyClass(Class);
332 
333             Class = pi->pclsPublicList;
334         }
335     }
336 }
337 
338 static BOOL
IntRegisterClassAtom(IN PUNICODE_STRING ClassName,OUT RTL_ATOM * pAtom)339 IntRegisterClassAtom(IN PUNICODE_STRING ClassName,
340                      OUT RTL_ATOM *pAtom)
341 {
342     WCHAR szBuf[65];
343     PWSTR AtomName = szBuf;
344     NTSTATUS Status;
345 
346     if (ClassName->Length != 0)
347     {
348         if (ClassName->Length + sizeof(UNICODE_NULL) > sizeof(szBuf))
349         {
350             AtomName = ExAllocatePoolWithTag(PagedPool,
351                                              ClassName->Length + sizeof(UNICODE_NULL),
352                                              TAG_USTR);
353 
354             if (AtomName == NULL)
355             {
356                 EngSetLastError(ERROR_OUTOFMEMORY);
357                 return FALSE;
358             }
359         }
360 
361         _SEH2_TRY
362         {
363             RtlCopyMemory(AtomName,
364                           ClassName->Buffer,
365                           ClassName->Length);
366         }
367         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
368         {
369             if (AtomName != szBuf)
370                 ExFreePoolWithTag(AtomName, TAG_USTR);
371             SetLastNtError(_SEH2_GetExceptionCode());
372             _SEH2_YIELD(return FALSE);
373         }
374         _SEH2_END;
375         AtomName[ClassName->Length / sizeof(WCHAR)] = UNICODE_NULL;
376     }
377     else
378     {
379         ASSERT(IS_ATOM(ClassName->Buffer));
380         AtomName = ClassName->Buffer;
381     }
382 
383     Status = RtlAddAtomToAtomTable(gAtomTable,
384                                    AtomName,
385                                    pAtom);
386 
387     if (AtomName != ClassName->Buffer && AtomName != szBuf)
388         ExFreePoolWithTag(AtomName, TAG_USTR);
389 
390 
391     if (!NT_SUCCESS(Status))
392     {
393         SetLastNtError(Status);
394         return FALSE;
395     }
396 
397     return TRUE;
398 }
399 
400 BOOL FASTCALL
RegisterControlAtoms(VOID)401 RegisterControlAtoms(VOID)
402 {
403     RTL_ATOM Atom;
404     UNICODE_STRING ClassName;
405     INT i = 0;
406 
407     while ( i < ICLS_DESKTOP)
408     {
409        RtlInitUnicodeString(&ClassName, ControlsList[i]);
410        if (IntRegisterClassAtom(&ClassName, &Atom))
411        {
412           gpsi->atomSysClass[i] = Atom;
413           TRACE("Reg Control Atom %ls: 0x%x\n", ControlsList[i], Atom);
414        }
415        i++;
416     }
417     return TRUE;
418 }
419 
420 static NTSTATUS
IntDeregisterClassAtom(IN RTL_ATOM Atom)421 IntDeregisterClassAtom(IN RTL_ATOM Atom)
422 {
423     return RtlDeleteAtomFromAtomTable(gAtomTable,
424                                       Atom);
425 }
426 
427 VOID
UserAddCallProcToClass(IN OUT PCLS Class,IN PCALLPROCDATA CallProc)428 UserAddCallProcToClass(IN OUT PCLS Class,
429                        IN PCALLPROCDATA CallProc)
430 {
431     PCLS BaseClass;
432 
433     ASSERT(CallProc->spcpdNext == NULL);
434 
435     BaseClass = Class->pclsBase;
436     ASSERT(CallProc->spcpdNext == NULL);
437     CallProc->spcpdNext = BaseClass->spcpdFirst;
438     BaseClass->spcpdFirst = CallProc;
439 
440     /* Update all clones */
441     Class = Class->pclsClone;
442     while (Class != NULL)
443     {
444         Class->spcpdFirst = BaseClass->spcpdFirst;
445         Class = Class->pclsNext;
446     }
447 }
448 
449 static BOOL
IntSetClassAtom(IN OUT PCLS Class,IN PUNICODE_STRING ClassName)450 IntSetClassAtom(IN OUT PCLS Class,
451                 IN PUNICODE_STRING ClassName)
452 {
453     RTL_ATOM Atom = (RTL_ATOM)0;
454 
455     /* Update the base class first */
456     Class = Class->pclsBase;
457     if (ClassName->Length > 0)
458     {
459         if (!IntRegisterClassAtom(ClassName,
460                                   &Atom))
461         {
462             ERR("RegisterClassAtom failed ! %x\n", EngGetLastError());
463             return FALSE;
464         }
465     }
466     else
467     {
468         if (IS_ATOM(ClassName->Buffer))
469         {
470             Atom = (ATOM)((ULONG_PTR)ClassName->Buffer & 0xffff); // XXX: are we missing refcount here ?
471         }
472         else
473         {
474             EngSetLastError(ERROR_INVALID_PARAMETER);
475             return FALSE;
476         }
477     }
478 
479     IntDeregisterClassAtom(Class->atomNVClassName);
480 
481     Class->atomNVClassName = Atom;
482 
483     /* Update the clones */
484     Class = Class->pclsClone;
485     while (Class != NULL)
486     {
487         Class->atomNVClassName = Atom;
488 
489         Class = Class->pclsNext;
490     }
491 
492     return TRUE;
493 }
494 
495 //
496 // Same as User32:IntGetClsWndProc.
497 //
498 WNDPROC FASTCALL
IntGetClassWndProc(PCLS Class,BOOL Ansi)499 IntGetClassWndProc(PCLS Class, BOOL Ansi)
500 {
501   INT i;
502   WNDPROC gcpd = NULL, Ret = NULL;
503 
504   if (Class->CSF_flags & CSF_SERVERSIDEPROC)
505   {
506      for ( i = FNID_FIRST; i <= FNID_SWITCH; i++)
507      {
508          if (GETPFNSERVER(i) == Class->lpfnWndProc)
509          {
510             if (Ansi)
511                Ret = GETPFNCLIENTA(i);
512             else
513                Ret = GETPFNCLIENTW(i);
514          }
515      }
516      return Ret;
517   }
518   Ret = Class->lpfnWndProc;
519 
520   if (Class->fnid <= FNID_GHOST && Class->fnid >= FNID_BUTTON)
521   {
522      if (Ansi)
523      {
524         if (GETPFNCLIENTW(Class->fnid) == Class->lpfnWndProc)
525            Ret = GETPFNCLIENTA(Class->fnid);
526      }
527      else
528      {
529         if (GETPFNCLIENTA(Class->fnid) == Class->lpfnWndProc)
530            Ret = GETPFNCLIENTW(Class->fnid);
531      }
532   }
533 
534   if ( Ret != Class->lpfnWndProc ||
535        Ansi == !!(Class->CSF_flags & CSF_ANSIPROC) )
536      return Ret;
537 
538   gcpd = (WNDPROC)UserGetCPD( Class,
539                        (Ansi ? UserGetCPDA2U : UserGetCPDU2A )|UserGetCPDClass,
540                        (ULONG_PTR)Ret);
541 
542   return (gcpd ? gcpd : Ret);
543 }
544 
545 
546 static
547 WNDPROC FASTCALL
IntSetClassWndProc(IN OUT PCLS Class,IN WNDPROC WndProc,IN BOOL Ansi)548 IntSetClassWndProc(IN OUT PCLS Class,
549                    IN WNDPROC WndProc,
550                    IN BOOL Ansi)
551 {
552    INT i;
553    PCALLPROCDATA pcpd;
554    WNDPROC Ret, chWndProc;
555 
556    Ret = IntGetClassWndProc(Class, Ansi);
557 
558    // If Server Side, downgrade to Client Side.
559    if (Class->CSF_flags & CSF_SERVERSIDEPROC)
560    {
561       if (Ansi) Class->CSF_flags |= CSF_ANSIPROC;
562       Class->CSF_flags &= ~CSF_SERVERSIDEPROC;
563       Class->Unicode = !Ansi;
564    }
565 
566    if (!WndProc) WndProc = Class->lpfnWndProc;
567 
568    chWndProc = WndProc;
569 
570    // Check if CallProc handle and retrieve previous call proc address and set.
571    if (IsCallProcHandle(WndProc))
572    {
573       pcpd = UserGetObject(gHandleTable, WndProc, TYPE_CALLPROC);
574       if (pcpd) chWndProc = pcpd->pfnClientPrevious;
575    }
576 
577    Class->lpfnWndProc = chWndProc;
578 
579    // Clear test proc.
580    chWndProc = NULL;
581 
582    // Switch from Client Side call to Server Side call if match. Ref: "deftest".
583    for ( i = FNID_FIRST; i <= FNID_SWITCH; i++)
584    {
585        if (GETPFNCLIENTW(i) == Class->lpfnWndProc)
586        {
587           chWndProc = GETPFNSERVER(i);
588           break;
589        }
590        if (GETPFNCLIENTA(i) == Class->lpfnWndProc)
591        {
592           chWndProc = GETPFNSERVER(i);
593           break;
594        }
595    }
596    // If match, set/reset to Server Side and clear ansi.
597    if (chWndProc)
598    {
599       Class->lpfnWndProc = chWndProc;
600       Class->Unicode = TRUE;
601       Class->CSF_flags &= ~CSF_ANSIPROC;
602       Class->CSF_flags |= CSF_SERVERSIDEPROC;
603    }
604    else
605    {
606       Class->Unicode = !Ansi;
607 
608       if (Ansi)
609          Class->CSF_flags |= CSF_ANSIPROC;
610       else
611          Class->CSF_flags &= ~CSF_ANSIPROC;
612    }
613 
614    /* Update the clones */
615    chWndProc = Class->lpfnWndProc;
616 
617    Class = Class->pclsClone;
618    while (Class != NULL)
619    {
620       Class->Unicode = !Ansi;
621       Class->lpfnWndProc = chWndProc;
622 
623       Class = Class->pclsNext;
624    }
625 
626    return Ret;
627 }
628 
629 static PCLS
IntGetClassForDesktop(IN OUT PCLS BaseClass,IN OUT PCLS * ClassLink,IN PDESKTOP Desktop)630 IntGetClassForDesktop(IN OUT PCLS BaseClass,
631                       IN OUT PCLS *ClassLink,
632                       IN PDESKTOP Desktop)
633 {
634     SIZE_T ClassSize;
635     PCLS Class;
636 
637     ASSERT(Desktop != NULL);
638     ASSERT(BaseClass->pclsBase == BaseClass);
639 
640     if (BaseClass->rpdeskParent == Desktop)
641     {
642         /* It is most likely that a window is created on the same
643            desktop as the window class. */
644 
645         return BaseClass;
646     }
647 
648     if (BaseClass->rpdeskParent == NULL)
649     {
650         ASSERT(BaseClass->cWndReferenceCount == 0);
651         ASSERT(BaseClass->pclsClone == NULL);
652 
653         /* Classes are also located in the shared heap when the class
654            was created before the thread attached to a desktop. As soon
655            as a window is created for such a class located on the shared
656            heap, the class is cloned into the desktop heap on which the
657            window is created. */
658         Class = NULL;
659     }
660     else
661     {
662         /* The user is asking for a class object on a different desktop,
663            try to find one! */
664         Class = BaseClass->pclsClone;
665         while (Class != NULL)
666         {
667             if (Class->rpdeskParent == Desktop)
668             {
669                 ASSERT(Class->pclsBase == BaseClass);
670                 ASSERT(Class->pclsClone == NULL);
671                 break;
672             }
673 
674             Class = Class->pclsNext;
675         }
676     }
677 
678     if (Class == NULL)
679     {
680         /* The window is created on a different desktop, we need to
681            clone the class object to the desktop heap of the window! */
682         ClassSize = sizeof(*BaseClass) + (SIZE_T)BaseClass->cbclsExtra;
683 
684         Class = DesktopHeapAlloc(Desktop,
685                                  ClassSize);
686 
687         if (Class != NULL)
688         {
689             /* Simply clone the class */
690             RtlCopyMemory( Class, BaseClass, ClassSize);
691 
692             /* Reference our objects */
693             if (Class->spcur)
694                 UserReferenceObject(Class->spcur);
695             if (Class->spicn)
696                 UserReferenceObject(Class->spicn);
697             if (Class->spicnSm)
698                 UserReferenceObject(Class->spicnSm);
699 
700             TRACE("Clone Class 0x%p hM 0x%p\n %S\n",Class, Class->hModule, Class->lpszClientUnicodeMenuName);
701 
702             /* Restore module address if default user class Ref: Bug 4778 */
703             if ( Class->hModule != hModClient &&
704                  Class->fnid <= FNID_GHOST    &&
705                  Class->fnid >= FNID_BUTTON )
706             {
707                Class->hModule = hModClient;
708                TRACE("Clone Class 0x%p Reset hM 0x%p\n",Class, Class->hModule);
709             }
710 
711             /* Update some pointers and link the class */
712             Class->rpdeskParent = Desktop;
713             Class->cWndReferenceCount = 0;
714 
715             if (BaseClass->rpdeskParent == NULL)
716             {
717                 /* We don't really need the base class on the shared
718                    heap anymore, delete it so the only class left is
719                    the clone we just created, which now serves as the
720                    new base class */
721                 ASSERT(BaseClass->pclsClone == NULL);
722                 ASSERT(Class->pclsClone == NULL);
723                 Class->pclsBase = Class;
724                 Class->pclsNext = BaseClass->pclsNext;
725 
726                 /* Replace the base class */
727                 (void)InterlockedExchangePointer((PVOID*)ClassLink,
728                                                  Class);
729 
730                 /* Destroy the obsolete copy on the shared heap */
731                 BaseClass->pclsBase = NULL;
732                 BaseClass->pclsClone = NULL;
733                 IntDestroyClass(BaseClass);
734             }
735             else
736             {
737                 /* Link in the clone */
738                 Class->pclsClone = NULL;
739                 Class->pclsBase = BaseClass;
740                 Class->pclsNext = BaseClass->pclsClone;
741                 (void)InterlockedExchangePointer((PVOID*)&BaseClass->pclsClone,
742                                                  Class);
743             }
744         }
745         else
746         {
747             EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
748         }
749     }
750     return Class;
751 }
752 
753 PCLS
IntReferenceClass(IN OUT PCLS BaseClass,IN OUT PCLS * ClassLink,IN PDESKTOP Desktop)754 IntReferenceClass(IN OUT PCLS BaseClass,
755                   IN OUT PCLS *ClassLink,
756                   IN PDESKTOP Desktop)
757 {
758     PCLS Class;
759     ASSERT(BaseClass->pclsBase == BaseClass);
760 
761     if (Desktop != NULL)
762     {
763         Class = IntGetClassForDesktop(BaseClass,
764                                       ClassLink,
765                                       Desktop);
766     }
767     else
768     {
769         Class = BaseClass;
770     }
771 
772     if (Class != NULL)
773     {
774         Class->cWndReferenceCount++;
775     }
776 
777     return Class;
778 }
779 
780 static
781 VOID
IntMakeCloneBaseClass(IN OUT PCLS Class,IN OUT PCLS * BaseClassLink,IN OUT PCLS * CloneLink)782 IntMakeCloneBaseClass(IN OUT PCLS Class,
783                       IN OUT PCLS *BaseClassLink,
784                       IN OUT PCLS *CloneLink)
785 {
786     PCLS Clone;
787 
788     ASSERT(Class->pclsBase != Class);
789     ASSERT(Class->pclsBase->pclsClone != NULL);
790     ASSERT(Class->rpdeskParent != NULL);
791     ASSERT(Class->cWndReferenceCount != 0);
792     ASSERT(Class->pclsBase->rpdeskParent != NULL);
793     ASSERT(Class->pclsBase->cWndReferenceCount == 0);
794 
795     /* Unlink the clone */
796     *CloneLink = Class->pclsNext;
797     Class->pclsClone = Class->pclsBase->pclsClone;
798 
799     /* Update the class information to make it a base class */
800     Class->pclsBase = Class;
801     Class->pclsNext = (*BaseClassLink)->pclsNext;
802 
803     /* Update all clones */
804     Clone = Class->pclsClone;
805     while (Clone != NULL)
806     {
807         ASSERT(Clone->pclsClone == NULL);
808         Clone->pclsBase = Class;
809 
810         Clone = Clone->pclsNext;
811     }
812 
813     /* Link in the new base class */
814     (void)InterlockedExchangePointer((PVOID*)BaseClassLink,
815                                      Class);
816 }
817 
818 
819 VOID
IntDereferenceClass(IN OUT PCLS Class,IN PDESKTOPINFO Desktop,IN PPROCESSINFO pi)820 IntDereferenceClass(IN OUT PCLS Class,
821                     IN PDESKTOPINFO Desktop,
822                     IN PPROCESSINFO pi)
823 {
824     PCLS *PrevLink, BaseClass, CurrentClass;
825 
826     ASSERT(Class->cWndReferenceCount >= 1);
827 
828     BaseClass = Class->pclsBase;
829 
830     if (--Class->cWndReferenceCount == 0)
831     {
832         if (BaseClass == Class)
833         {
834             ASSERT(Class->pclsBase == Class);
835 
836             TRACE("IntDereferenceClass 0x%p\n", Class);
837             /* Check if there are clones of the class on other desktops,
838                link the first clone in if possible. If there are no clones
839                then leave the class on the desktop heap. It will get moved
840                to the shared heap when the thread detaches. */
841             if (BaseClass->pclsClone != NULL)
842             {
843                 if (BaseClass->Global)
844                     PrevLink = &pi->pclsPublicList;
845                 else
846                     PrevLink = &pi->pclsPrivateList;
847 
848                 CurrentClass = *PrevLink;
849                 while (CurrentClass != BaseClass)
850                 {
851                     ASSERT(CurrentClass != NULL);
852 
853                     PrevLink = &CurrentClass->pclsNext;
854                     CurrentClass = CurrentClass->pclsNext;
855                 }
856 
857                 ASSERT(*PrevLink == BaseClass);
858 
859                 /* Make the first clone become the new base class */
860                 IntMakeCloneBaseClass(BaseClass->pclsClone,
861                                       PrevLink,
862                                       &BaseClass->pclsClone);
863 
864                 /* Destroy the class, there's still another clone of the class
865                    that now serves as a base class. Make sure we don't destruct
866                    resources shared by all classes (Base = NULL)! */
867                 BaseClass->pclsBase = NULL;
868                 BaseClass->pclsClone = NULL;
869                 IntDestroyClass(BaseClass);
870             }
871         }
872         else
873         {
874             TRACE("IntDereferenceClass1 0x%p\n", Class);
875 
876             /* Locate the cloned class and unlink it */
877             PrevLink = &BaseClass->pclsClone;
878             CurrentClass = BaseClass->pclsClone;
879             while (CurrentClass != Class)
880             {
881                 ASSERT(CurrentClass != NULL);
882 
883                 PrevLink = &CurrentClass->pclsNext;
884                 CurrentClass = CurrentClass->pclsNext;
885             }
886 
887             ASSERT(CurrentClass == Class);
888 
889             (void)InterlockedExchangePointer((PVOID*)PrevLink,
890                                              Class->pclsNext);
891 
892             ASSERT(Class->pclsBase == BaseClass);
893             ASSERT(Class->pclsClone == NULL);
894 
895             /* The class was just a clone, we don't need it anymore */
896             IntDestroyClass(Class);
897         }
898     }
899 }
900 
901 static BOOL
IntMoveClassToSharedHeap(IN OUT PCLS Class,IN OUT PCLS ** ClassLinkPtr)902 IntMoveClassToSharedHeap(IN OUT PCLS Class,
903                          IN OUT PCLS **ClassLinkPtr)
904 {
905     PCLS NewClass;
906     SIZE_T ClassSize;
907 
908     ASSERT(Class->pclsBase == Class);
909     ASSERT(Class->rpdeskParent != NULL);
910     ASSERT(Class->cWndReferenceCount == 0);
911     ASSERT(Class->pclsClone == NULL);
912 
913     ClassSize = sizeof(*Class) + (SIZE_T)Class->cbclsExtra;
914 
915     /* Allocate the new base class on the shared heap */
916     NewClass = UserHeapAlloc(ClassSize);
917     if (NewClass != NULL)
918     {
919         RtlCopyMemory(NewClass,
920                       Class,
921                       ClassSize);
922 
923         NewClass->rpdeskParent = NULL;
924         NewClass->pclsBase = NewClass;
925 
926         if (NewClass->spcur)
927             UserReferenceObject(NewClass->spcur);
928         if (NewClass->spicn)
929             UserReferenceObject(NewClass->spicn);
930         if (NewClass->spicnSm)
931             UserReferenceObject(NewClass->spicnSm);
932 
933         /* Replace the class in the list */
934         (void)InterlockedExchangePointer((PVOID*)*ClassLinkPtr,
935                                          NewClass);
936         *ClassLinkPtr = &NewClass->pclsNext;
937 
938         /* Free the obsolete class on the desktop heap */
939         Class->pclsBase = NULL;
940         IntDestroyClass(Class);
941         return TRUE;
942     }
943 
944     return FALSE;
945 }
946 
947 static VOID
IntCheckDesktopClasses(IN PDESKTOP Desktop,IN OUT PCLS * ClassList,IN BOOL FreeOnFailure,OUT BOOL * Ret)948 IntCheckDesktopClasses(IN PDESKTOP Desktop,
949                        IN OUT PCLS *ClassList,
950                        IN BOOL FreeOnFailure,
951                        OUT BOOL *Ret)
952 {
953     PCLS Class, NextClass, *Link;
954 
955     /* NOTE: We only need to check base classes! When classes are no longer needed
956              on a desktop, the clones will be freed automatically as soon as possible.
957              However, we need to move base classes to the shared heap, as soon as
958              the last desktop heap where a class is allocated on is about to be destroyed.
959              If we didn't move the class to the shared heap, the class would become
960              inaccessible! */
961 
962     ASSERT(Desktop != NULL);
963 
964     Link = ClassList;
965     Class = *Link;
966     while (Class != NULL)
967     {
968         NextClass = Class->pclsNext;
969 
970         ASSERT(Class->pclsBase == Class);
971 
972         if (Class->rpdeskParent == Desktop &&
973             Class->cWndReferenceCount == 0)
974         {
975             /* There shouldn't be any clones around anymore! */
976             ASSERT(Class->pclsClone == NULL);
977 
978             /* FIXME: If process is terminating, don't move the class but rather destroy it! */
979             /* FIXME: We could move the class to another desktop heap if there's still desktops
980                        mapped into the process... */
981 
982             /* Move the class to the shared heap */
983             if (IntMoveClassToSharedHeap(Class,
984                                          &Link))
985             {
986                 ASSERT(*Link == NextClass);
987             }
988             else
989             {
990                 ASSERT(NextClass == Class->pclsNext);
991 
992                 if (FreeOnFailure)
993                 {
994                     /* Unlink the base class */
995                     (void)InterlockedExchangePointer((PVOID*)Link,
996                                                      Class->pclsNext);
997 
998                     /* We can free the old base class now */
999                     Class->pclsBase = NULL;
1000                     IntDestroyClass(Class);
1001                 }
1002                 else
1003                 {
1004                     Link = &Class->pclsNext;
1005                     *Ret = FALSE;
1006                 }
1007             }
1008         }
1009         else
1010             Link = &Class->pclsNext;
1011 
1012         Class = NextClass;
1013     }
1014 }
1015 
1016 BOOL
IntCheckProcessDesktopClasses(IN PDESKTOP Desktop,IN BOOL FreeOnFailure)1017 IntCheckProcessDesktopClasses(IN PDESKTOP Desktop,
1018                               IN BOOL FreeOnFailure)
1019 {
1020     PPROCESSINFO pi;
1021     BOOL Ret = TRUE;
1022 
1023     pi = GetW32ProcessInfo();
1024 
1025     /* Check all local classes */
1026     IntCheckDesktopClasses(Desktop,
1027                            &pi->pclsPrivateList,
1028                            FreeOnFailure,
1029                            &Ret);
1030 
1031     /* Check all global classes */
1032     IntCheckDesktopClasses(Desktop,
1033                            &pi->pclsPublicList,
1034                            FreeOnFailure,
1035                            &Ret);
1036     if (!Ret)
1037     {
1038         ERR("Failed to move process classes from desktop 0x%p to the shared heap!\n", Desktop);
1039         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1040     }
1041 
1042     return Ret;
1043 }
1044 
1045 PCLS
1046 FASTCALL
IntCreateClass(IN CONST WNDCLASSEXW * lpwcx,IN PUNICODE_STRING ClassName,IN PUNICODE_STRING ClassVersion,IN PUNICODE_STRING MenuName,IN DWORD fnID,IN DWORD dwFlags,IN PDESKTOP Desktop,IN PPROCESSINFO pi)1047 IntCreateClass(IN CONST WNDCLASSEXW* lpwcx,
1048                IN PUNICODE_STRING ClassName,
1049                IN PUNICODE_STRING ClassVersion,
1050                IN PUNICODE_STRING MenuName,
1051                IN DWORD fnID,
1052                IN DWORD dwFlags,
1053                IN PDESKTOP Desktop,
1054                IN PPROCESSINFO pi)
1055 {
1056     SIZE_T ClassSize;
1057     PCLS Class = NULL;
1058     RTL_ATOM Atom, verAtom;
1059     WNDPROC WndProc;
1060     PWSTR pszMenuName = NULL;
1061     NTSTATUS Status = STATUS_SUCCESS;
1062 
1063     TRACE("lpwcx=%p ClassName=%wZ MenuName=%wZ dwFlags=%08x Desktop=%p pi=%p\n",
1064         lpwcx, ClassName, MenuName, dwFlags, Desktop, pi);
1065 
1066     if (!IntRegisterClassAtom(ClassName,
1067                               &Atom))
1068     {
1069         ERR("Failed to register class atom!\n");
1070         return NULL;
1071     }
1072 
1073     if (!IntRegisterClassAtom(ClassVersion,
1074                               &verAtom))
1075     {
1076         ERR("Failed to register version class atom!\n");
1077         IntDeregisterClassAtom(Atom);
1078         return NULL;
1079     }
1080 
1081     ClassSize = sizeof(*Class) + lpwcx->cbClsExtra;
1082     if (MenuName->Length != 0)
1083     {
1084         pszMenuName = UserHeapAlloc(MenuName->Length + sizeof(UNICODE_NULL) +
1085                                     RtlUnicodeStringToAnsiSize(MenuName));
1086         if (pszMenuName == NULL)
1087             goto NoMem;
1088     }
1089 
1090     if (Desktop != NULL)
1091     {
1092         Class = DesktopHeapAlloc(Desktop,
1093                                  ClassSize);
1094     }
1095     else
1096     {
1097         /* FIXME: The class was created before being connected
1098                   to a desktop. It is possible for the desktop window,
1099                   but should it be allowed for any other case? */
1100         TRACE("This CLASS has no Desktop to heap from! Atom %u\n",Atom);
1101         Class = UserHeapAlloc(ClassSize);
1102     }
1103 
1104     if (Class != NULL)
1105     {
1106         int iCls = 0;
1107 
1108         RtlZeroMemory(Class, ClassSize);
1109 
1110         Class->rpdeskParent = Desktop;
1111         Class->pclsBase = Class;
1112         Class->atomClassName = verAtom;
1113         Class->atomNVClassName = Atom;
1114         Class->fnid = fnID;
1115         Class->CSF_flags = dwFlags;
1116 
1117         if (LookupFnIdToiCls(Class->fnid, &iCls) && gpsi->atomSysClass[iCls] == 0)
1118         {
1119             gpsi->atomSysClass[iCls] = Class->atomClassName;
1120         }
1121 
1122         _SEH2_TRY
1123         {
1124             PWSTR pszMenuNameBuffer = pszMenuName;
1125 
1126             /* Need to protect with SEH since accessing the WNDCLASSEX structure
1127                and string buffers might raise an exception! We don't want to
1128                leak memory... */
1129             // What?! If the user interface was written correctly this would not be an issue!
1130             Class->lpfnWndProc = lpwcx->lpfnWndProc;
1131             Class->style = lpwcx->style;
1132             Class->cbclsExtra = lpwcx->cbClsExtra;
1133             Class->cbwndExtra = lpwcx->cbWndExtra;
1134             Class->hModule = lpwcx->hInstance;
1135             Class->spicn = lpwcx->hIcon ? UserGetCurIconObject(lpwcx->hIcon) : NULL;
1136             Class->spcur = lpwcx->hCursor ? UserGetCurIconObject(lpwcx->hCursor) : NULL;
1137             Class->spicnSm = lpwcx->hIconSm ? UserGetCurIconObject(lpwcx->hIconSm) : NULL;
1138             ////
1139             Class->hbrBackground = lpwcx->hbrBackground;
1140 
1141             /* Make a copy of the string */
1142             if (pszMenuNameBuffer != NULL)
1143             {
1144                 Class->MenuNameIsString = TRUE;
1145 
1146                 Class->lpszClientUnicodeMenuName = pszMenuNameBuffer;
1147                 RtlCopyMemory(Class->lpszClientUnicodeMenuName,
1148                               MenuName->Buffer,
1149                               MenuName->Length);
1150                 Class->lpszClientUnicodeMenuName[MenuName->Length / sizeof(WCHAR)] = UNICODE_NULL;
1151 
1152                 pszMenuNameBuffer += (MenuName->Length / sizeof(WCHAR)) + 1;
1153             }
1154             else
1155                 Class->lpszClientUnicodeMenuName = MenuName->Buffer;
1156 
1157             /* Save an ANSI copy of the string */
1158             if (pszMenuNameBuffer != NULL)
1159             {
1160                 ANSI_STRING AnsiString;
1161 
1162                 Class->lpszClientAnsiMenuName = (PSTR)pszMenuNameBuffer;
1163                 AnsiString.MaximumLength = (USHORT)RtlUnicodeStringToAnsiSize(MenuName);
1164                 AnsiString.Buffer = Class->lpszClientAnsiMenuName;
1165                 Status = RtlUnicodeStringToAnsiString(&AnsiString,
1166                                                       MenuName,
1167                                                       FALSE);
1168                 if (!NT_SUCCESS(Status))
1169                 {
1170                     ERR("Failed to convert unicode menu name to ansi!\n");
1171 
1172                     /* Life would've been much prettier if ntoskrnl exported RtlRaiseStatus()... */
1173                     _SEH2_LEAVE;
1174                 }
1175             }
1176             else
1177                 Class->lpszClientAnsiMenuName = (PSTR)MenuName->Buffer;
1178 
1179             /* Save kernel use menu name and ansi class name */
1180             Class->lpszMenuName = Class->lpszClientUnicodeMenuName; // FIXME!
1181             //Class->lpszAnsiClassName = FIXME
1182 
1183             /* Server Side overrides class calling type (A/W)!
1184                User32 whine test_builtinproc: "deftest"
1185                   built-in winproc - window A/W type automatically detected */
1186             if (!(Class->CSF_flags & CSF_SERVERSIDEPROC))
1187             {
1188                int i;
1189                WndProc = NULL;
1190           /* Due to the wine class "deftest" and most likely no FNID to reference
1191              from, sort through the Server Side list and compare proc addresses
1192              for match. This method will be used in related code.
1193            */
1194                for ( i = FNID_FIRST; i <= FNID_SWITCH; i++)
1195                { // Open ANSI or Unicode, just match, set and break.
1196                    if (GETPFNCLIENTW(i) == Class->lpfnWndProc)
1197                    {
1198                       WndProc = GETPFNSERVER(i);
1199                       break;
1200                    }
1201                    if (GETPFNCLIENTA(i) == Class->lpfnWndProc)
1202                    {
1203                       WndProc = GETPFNSERVER(i);
1204                       break;
1205                    }
1206                }
1207                if (WndProc)
1208                {  // If a hit, we are Server Side so set the right flags and proc.
1209                   Class->CSF_flags |= CSF_SERVERSIDEPROC;
1210                   Class->CSF_flags &= ~CSF_ANSIPROC;
1211                   Class->lpfnWndProc = WndProc;
1212                }
1213             }
1214 
1215             if (!(Class->CSF_flags & CSF_ANSIPROC))
1216                 Class->Unicode = TRUE;
1217 
1218             if (Class->style & CS_GLOBALCLASS)
1219                 Class->Global = TRUE;
1220         }
1221         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1222         {
1223             Status = _SEH2_GetExceptionCode();
1224         }
1225         _SEH2_END;
1226 
1227         if (!NT_SUCCESS(Status))
1228         {
1229             ERR("Failed creating the class: 0x%x\n", Status);
1230 
1231             SetLastNtError(Status);
1232 
1233             if (pszMenuName != NULL)
1234                 UserHeapFree(pszMenuName);
1235 
1236             DesktopHeapFree(Desktop,
1237                             Class);
1238             Class = NULL;
1239 
1240             IntDeregisterClassAtom(verAtom);
1241             IntDeregisterClassAtom(Atom);
1242         }
1243     }
1244     else
1245     {
1246 NoMem:
1247         ERR("Failed to allocate class on Desktop 0x%p\n", Desktop);
1248 
1249         if (pszMenuName != NULL)
1250             UserHeapFree(pszMenuName);
1251 
1252         IntDeregisterClassAtom(Atom);
1253         IntDeregisterClassAtom(verAtom);
1254 
1255         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1256     }
1257 
1258     TRACE("Created class 0x%p with name %wZ and proc 0x%p for atom 0x%x and version atom 0x%x and hInstance 0x%p, global %u\n",
1259           Class, ClassName, Class ? Class->lpfnWndProc : NULL, Atom, verAtom,
1260           Class ? Class->hModule : NULL , Class ? Class->Global : 0);
1261 
1262     return Class;
1263 }
1264 
1265 static PCLS
IntFindClass(IN RTL_ATOM Atom,IN HINSTANCE hInstance,IN PCLS * ClassList,OUT PCLS ** Link OPTIONAL)1266 IntFindClass(IN RTL_ATOM Atom,
1267              IN HINSTANCE hInstance,
1268              IN PCLS *ClassList,
1269              OUT PCLS **Link  OPTIONAL)
1270 {
1271     PCLS Class, *PrevLink = ClassList;
1272 
1273     Class = *PrevLink;
1274     while (Class != NULL)
1275     {
1276         if (Class->atomClassName == Atom &&
1277             (hInstance == NULL || Class->hModule == hInstance) &&
1278             !(Class->CSF_flags & CSF_WOWDEFERDESTROY))
1279         {
1280             ASSERT(Class->pclsBase == Class);
1281 
1282             if (Link != NULL)
1283                 *Link = PrevLink;
1284             break;
1285         }
1286 
1287         PrevLink = &Class->pclsNext;
1288         Class = Class->pclsNext;
1289     }
1290 
1291     return Class;
1292 }
1293 
_Success_(return)1294 _Success_(return)
1295 BOOL
1296 NTAPI
1297 IntGetAtomFromStringOrAtom(
1298     _In_ PUNICODE_STRING ClassName,
1299     _Out_ RTL_ATOM *Atom)
1300 {
1301     BOOL Ret = FALSE;
1302 
1303     if (ClassName->Length != 0)
1304     {
1305         WCHAR szBuf[65];
1306         PWSTR AtomName = szBuf;
1307         NTSTATUS Status = STATUS_INVALID_PARAMETER;
1308 
1309         *Atom = 0;
1310 
1311         /* NOTE: Caller has to protect the call with SEH! */
1312         if (ClassName->Length + sizeof(UNICODE_NULL) > sizeof(szBuf))
1313         {
1314             AtomName = ExAllocatePoolWithTag(PagedPool,
1315                                              ClassName->Length + sizeof(UNICODE_NULL),
1316                                              TAG_USTR);
1317             if (AtomName == NULL)
1318             {
1319                 EngSetLastError(ERROR_OUTOFMEMORY);
1320                 return FALSE;
1321             }
1322         }
1323 
1324         _SEH2_TRY
1325         {
1326             RtlCopyMemory(AtomName,
1327                           ClassName->Buffer,
1328                           ClassName->Length);
1329         }
1330         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1331         {
1332             if (AtomName != szBuf)
1333                 ExFreePoolWithTag(AtomName, TAG_USTR);
1334             SetLastNtError(_SEH2_GetExceptionCode());
1335             _SEH2_YIELD(return FALSE);
1336         }
1337         _SEH2_END;
1338         AtomName[ClassName->Length / sizeof(WCHAR)] = UNICODE_NULL;
1339 
1340         /* Lookup the atom */
1341         Status = RtlLookupAtomInAtomTable(gAtomTable, AtomName, Atom);
1342 
1343         if (AtomName != szBuf)
1344             ExFreePoolWithTag(AtomName, TAG_USTR);
1345 
1346         if (NT_SUCCESS(Status))
1347         {
1348             Ret = TRUE;
1349         }
1350         else
1351         {
1352             if (Status != STATUS_OBJECT_NAME_NOT_FOUND)
1353             {
1354                 SetLastNtError(Status);
1355             }
1356         }
1357     }
1358     else
1359     {
1360         if (ClassName->Buffer)
1361         {
1362             *Atom = (RTL_ATOM)((ULONG_PTR)ClassName->Buffer);
1363             Ret = TRUE;
1364         }
1365         else
1366         {
1367             *Atom = 0;
1368             EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST);
1369             Ret = FALSE;
1370         }
1371     }
1372 
1373     return Ret;
1374 }
1375 
1376 RTL_ATOM
IntGetClassAtom(_In_ PUNICODE_STRING ClassName,IN HINSTANCE hInstance OPTIONAL,IN PPROCESSINFO pi OPTIONAL,OUT PCLS * BaseClass OPTIONAL,OUT PCLS ** Link OPTIONAL)1377 IntGetClassAtom(
1378     _In_ PUNICODE_STRING ClassName,
1379     IN HINSTANCE hInstance  OPTIONAL,
1380     IN PPROCESSINFO pi  OPTIONAL,
1381     OUT PCLS *BaseClass  OPTIONAL,
1382     OUT PCLS **Link  OPTIONAL)
1383 {
1384     RTL_ATOM Atom = (RTL_ATOM)0;
1385 
1386     ASSERT(BaseClass != NULL);
1387 
1388     if (IntGetAtomFromStringOrAtom(ClassName, &Atom) &&
1389         Atom != (RTL_ATOM)0)
1390     {
1391         PCLS Class;
1392 
1393         /* Attempt to locate the class object */
1394 
1395         ASSERT(pi != NULL);
1396 
1397         /* Step 1: Try to find an exact match of locally registered classes */
1398         Class = IntFindClass(Atom,
1399                              hInstance,
1400                              &pi->pclsPrivateList,
1401                              Link);
1402         if (Class != NULL)
1403         {  TRACE("Step 1: 0x%p\n",Class );
1404             goto FoundClass;
1405         }
1406 
1407         /* Step 2: Try to find any globally registered class. The hInstance
1408                    is not relevant for global classes */
1409         Class = IntFindClass(Atom,
1410                              NULL,
1411                              &pi->pclsPublicList,
1412                              Link);
1413         if (Class != NULL)
1414         { TRACE("Step 2: 0x%p 0x%p\n",Class, Class->hModule);
1415             goto FoundClass;
1416         }
1417 
1418         /* Step 3: Try to find any local class registered by user32 */
1419         Class = IntFindClass(Atom,
1420                              hModClient,
1421                              &pi->pclsPrivateList,
1422                              Link);
1423         if (Class != NULL)
1424         { TRACE("Step 3: 0x%p\n",Class );
1425             goto FoundClass;
1426         }
1427 
1428         /* Step 4: Try to find any global class registered by user32 */
1429         Class = IntFindClass(Atom,
1430                              hModClient,
1431                              &pi->pclsPublicList,
1432                              Link);
1433         if (Class == NULL)
1434         {
1435             return (RTL_ATOM)0;
1436         }else{TRACE("Step 4: 0x%p\n",Class );}
1437 
1438 FoundClass:
1439         *BaseClass = Class;
1440     }
1441     else
1442     {
1443         Atom = 0;
1444     }
1445 
1446     return Atom;
1447 }
1448 
1449 PCLS
IntGetAndReferenceClass(PUNICODE_STRING ClassName,HINSTANCE hInstance,BOOL bDesktopThread)1450 IntGetAndReferenceClass(PUNICODE_STRING ClassName, HINSTANCE hInstance, BOOL bDesktopThread)
1451 {
1452    PCLS *ClassLink, Class = NULL;
1453    RTL_ATOM ClassAtom;
1454    PTHREADINFO pti;
1455 
1456    if (bDesktopThread)
1457        pti = gptiDesktopThread;
1458    else
1459        pti = PsGetCurrentThreadWin32Thread();
1460 
1461    if ( !(pti->ppi->W32PF_flags & W32PF_CLASSESREGISTERED ))
1462    {
1463       UserRegisterSystemClasses();
1464    }
1465 
1466    /* Check the class. */
1467 
1468    TRACE("Finding Class %wZ for hInstance 0x%p\n", ClassName, hInstance);
1469 
1470    ClassAtom = IntGetClassAtom(ClassName,
1471                                hInstance,
1472                                pti->ppi,
1473                                &Class,
1474                                &ClassLink);
1475 
1476    if (ClassAtom == (RTL_ATOM)0)
1477    {
1478       if (IS_ATOM(ClassName->Buffer))
1479       {
1480          ERR("Class 0x%p not found\n", ClassName->Buffer);
1481       }
1482       else
1483       {
1484          ERR("Class \"%wZ\" not found\n", ClassName);
1485       }
1486 
1487       return NULL;
1488    }
1489 
1490    TRACE("Referencing Class 0x%p with atom 0x%x\n", Class, ClassAtom);
1491    Class = IntReferenceClass(Class,
1492                              ClassLink,
1493                              pti->rpdesk);
1494    if (Class == NULL)
1495    {
1496        ERR("Failed to reference window class!\n");
1497        return NULL;
1498    }
1499 
1500    return Class;
1501 }
1502 
1503 RTL_ATOM
UserRegisterClass(IN CONST WNDCLASSEXW * lpwcx,IN PUNICODE_STRING ClassName,IN PUNICODE_STRING ClassVersion,IN PUNICODE_STRING MenuName,IN DWORD fnID,IN DWORD dwFlags)1504 UserRegisterClass(IN CONST WNDCLASSEXW* lpwcx,
1505                   IN PUNICODE_STRING ClassName,
1506                   IN PUNICODE_STRING ClassVersion,
1507                   IN PUNICODE_STRING MenuName,
1508                   IN DWORD fnID,
1509                   IN DWORD dwFlags)
1510 {
1511     PTHREADINFO pti;
1512     PPROCESSINFO pi;
1513     PCLS Class;
1514     RTL_ATOM ClassAtom;
1515     RTL_ATOM Ret = (RTL_ATOM)0;
1516 
1517     /* NOTE: Accessing the buffers in ClassName and MenuName may raise exceptions! */
1518 
1519     pti = GetW32ThreadInfo();
1520 
1521     pi = pti->ppi;
1522 
1523     // Need only to test for two conditions not four....... Fix more whine tests....
1524     if ( IntGetAtomFromStringOrAtom( ClassVersion, &ClassAtom) &&
1525                                      ClassAtom != (RTL_ATOM)0 &&
1526                                     !(dwFlags & CSF_SERVERSIDEPROC) ) // Bypass Server Sides
1527     {
1528        Class = IntFindClass( ClassAtom,
1529                              lpwcx->hInstance,
1530                             &pi->pclsPrivateList,
1531                              NULL);
1532 
1533        if (Class != NULL && !Class->Global)
1534        {
1535           // Local class already exists
1536           TRACE("Local Class 0x%x does already exist!\n", ClassAtom);
1537           EngSetLastError(ERROR_CLASS_ALREADY_EXISTS);
1538           return (RTL_ATOM)0;
1539        }
1540 
1541        if (lpwcx->style & CS_GLOBALCLASS)
1542        {
1543           Class = IntFindClass( ClassAtom,
1544                                 NULL,
1545                                &pi->pclsPublicList,
1546                                 NULL);
1547 
1548           if (Class != NULL && Class->Global)
1549           {
1550              TRACE("Global Class 0x%x does already exist!\n", ClassAtom);
1551              EngSetLastError(ERROR_CLASS_ALREADY_EXISTS);
1552              return (RTL_ATOM)0;
1553           }
1554        }
1555     }
1556 
1557     Class = IntCreateClass(lpwcx,
1558                            ClassName,
1559                            ClassVersion,
1560                            MenuName,
1561                            fnID,
1562                            dwFlags,
1563                            pti->rpdesk,
1564                            pi);
1565 
1566     if (Class != NULL)
1567     {
1568         PCLS *List;
1569 
1570         /* Register the class */
1571         if (Class->Global)
1572             List = &pi->pclsPublicList;
1573         else
1574             List = &pi->pclsPrivateList;
1575 
1576         Class->pclsNext = *List;
1577         (void)InterlockedExchangePointer((PVOID*)List,
1578                                          Class);
1579 
1580         Ret = Class->atomNVClassName;
1581     }
1582     else
1583     {
1584        ERR("UserRegisterClass: Yes, that is right, you have no Class!\n");
1585     }
1586 
1587     return Ret;
1588 }
1589 
1590 BOOL
UserUnregisterClass(IN PUNICODE_STRING ClassName,IN HINSTANCE hInstance,OUT PCLSMENUNAME pClassMenuName)1591 UserUnregisterClass(IN PUNICODE_STRING ClassName,
1592                     IN HINSTANCE hInstance,
1593                     OUT PCLSMENUNAME pClassMenuName)
1594 {
1595     PCLS *Link;
1596     PPROCESSINFO pi;
1597     RTL_ATOM ClassAtom;
1598     PCLS Class;
1599 
1600     pi = GetW32ProcessInfo();
1601 
1602     TRACE("UserUnregisterClass(%wZ, 0x%p)\n", ClassName, hInstance);
1603 
1604     /* NOTE: Accessing the buffer in ClassName may raise an exception! */
1605     ClassAtom = IntGetClassAtom(ClassName,
1606                                 hInstance,
1607                                 pi,
1608                                 &Class,
1609                                 &Link);
1610     if (ClassAtom == (RTL_ATOM)0)
1611     {
1612         EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST);
1613         TRACE("UserUnregisterClass: No Class found.\n");
1614         return FALSE;
1615     }
1616 
1617     ASSERT(Class != NULL);
1618 
1619     if (Class->cWndReferenceCount != 0 ||
1620         Class->pclsClone != NULL)
1621     {
1622         TRACE("UserUnregisterClass: Class has a Window. Ct %u : Clone 0x%p\n", Class->cWndReferenceCount, Class->pclsClone);
1623         EngSetLastError(ERROR_CLASS_HAS_WINDOWS);
1624         return FALSE;
1625     }
1626 
1627     /* Must be a base class! */
1628     ASSERT(Class->pclsBase == Class);
1629 
1630     /* Unlink the class */
1631     *Link = Class->pclsNext;
1632 
1633     if (NT_SUCCESS(IntDeregisterClassAtom(Class->atomClassName)))
1634     {
1635         TRACE("Class 0x%p\n", Class);
1636         TRACE("UserUnregisterClass: Good Exit!\n");
1637         Class->atomClassName = 0; // Don't let it linger...
1638         /* Finally free the resources */
1639         IntDestroyClass(Class);
1640         return TRUE;
1641     }
1642     ERR("UserUnregisterClass: Can not deregister Class Atom.\n");
1643     return FALSE;
1644 }
1645 
1646 INT
UserGetClassName(IN PCLS Class,IN OUT PUNICODE_STRING ClassName,IN RTL_ATOM Atom,IN BOOL Ansi)1647 UserGetClassName(IN PCLS Class,
1648                  IN OUT PUNICODE_STRING ClassName,
1649                  IN RTL_ATOM Atom,
1650                  IN BOOL Ansi)
1651 {
1652     NTSTATUS Status = STATUS_SUCCESS;
1653     WCHAR szStaticTemp[32];
1654     PWSTR szTemp = NULL;
1655     ULONG BufLen = sizeof(szStaticTemp);
1656     INT Ret = 0;
1657 
1658     /* Note: Accessing the buffer in ClassName may raise an exception! */
1659 
1660     _SEH2_TRY
1661     {
1662         if (Ansi)
1663         {
1664             PANSI_STRING AnsiClassName = (PANSI_STRING)ClassName;
1665             UNICODE_STRING UnicodeClassName;
1666 
1667             /* Limit the size of the static buffer on the stack to the
1668                size of the buffer provided by the caller */
1669             if (BufLen / sizeof(WCHAR) > AnsiClassName->MaximumLength)
1670             {
1671                 BufLen = AnsiClassName->MaximumLength * sizeof(WCHAR);
1672             }
1673 
1674             /* Find out how big the buffer needs to be */
1675             Status = RtlQueryAtomInAtomTable(gAtomTable,
1676                                              Class->atomClassName,
1677                                              NULL,
1678                                              NULL,
1679                                              szStaticTemp,
1680                                              &BufLen);
1681             if (Status == STATUS_BUFFER_TOO_SMALL)
1682             {
1683                 if (BufLen / sizeof(WCHAR) > AnsiClassName->MaximumLength)
1684                 {
1685                     /* The buffer required exceeds the ansi buffer provided,
1686                        pretend like we're using the ansi buffer and limit the
1687                        size to the buffer size provided */
1688                     BufLen = AnsiClassName->MaximumLength * sizeof(WCHAR);
1689                 }
1690 
1691                 /* Allocate a temporary buffer that can hold the unicode class name */
1692                 szTemp = ExAllocatePoolWithTag(PagedPool,
1693                                                BufLen,
1694                                                USERTAG_CLASS);
1695                 if (szTemp == NULL)
1696                 {
1697                     EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1698                     _SEH2_LEAVE;
1699                 }
1700 
1701                 /* Query the class name */
1702                 Status = RtlQueryAtomInAtomTable(gAtomTable,
1703                                                  Atom ? Atom : Class->atomNVClassName,
1704                                                  NULL,
1705                                                  NULL,
1706                                                  szTemp,
1707                                                  &BufLen);
1708             }
1709             else
1710                 szTemp = szStaticTemp;
1711 
1712             if (NT_SUCCESS(Status))
1713             {
1714                 /* Convert the atom name to ansi */
1715 
1716                 RtlInitUnicodeString(&UnicodeClassName,
1717                                      szTemp);
1718 
1719                 Status = RtlUnicodeStringToAnsiString(AnsiClassName,
1720                                                       &UnicodeClassName,
1721                                                       FALSE);
1722                 if (!NT_SUCCESS(Status))
1723                 {
1724                     SetLastNtError(Status);
1725                     _SEH2_LEAVE;
1726                 }
1727             }
1728 
1729             Ret = BufLen / sizeof(WCHAR);
1730         }
1731         else /* !ANSI */
1732         {
1733             BufLen = ClassName->MaximumLength;
1734 
1735             /* Query the atom name */
1736             Status = RtlQueryAtomInAtomTable(gAtomTable,
1737                                              Atom ? Atom : Class->atomNVClassName,
1738                                              NULL,
1739                                              NULL,
1740                                              ClassName->Buffer,
1741                                              &BufLen);
1742 
1743             if (!NT_SUCCESS(Status))
1744             {
1745                 SetLastNtError(Status);
1746                 _SEH2_LEAVE;
1747             }
1748 
1749             Ret = BufLen / sizeof(WCHAR);
1750         }
1751     }
1752     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1753     {
1754         SetLastNtError(_SEH2_GetExceptionCode());
1755     }
1756     _SEH2_END;
1757 
1758     if (Ansi && szTemp != NULL && szTemp != szStaticTemp)
1759     {
1760         ExFreePoolWithTag(szTemp, USERTAG_CLASS);
1761     }
1762 
1763     return Ret;
1764 }
1765 
1766 static BOOL
IntSetClassMenuName(IN PCLS Class,IN PUNICODE_STRING MenuName)1767 IntSetClassMenuName(IN PCLS Class,
1768                     IN PUNICODE_STRING MenuName)
1769 {
1770     BOOL Ret = FALSE;
1771 
1772     /* Change the base class first */
1773     Class = Class->pclsBase;
1774 
1775     if (MenuName->Length != 0)
1776     {
1777         ANSI_STRING AnsiString;
1778         PWSTR strBufW;
1779 
1780         AnsiString.MaximumLength = (USHORT)RtlUnicodeStringToAnsiSize(MenuName);
1781 
1782         strBufW = UserHeapAlloc(MenuName->Length + sizeof(UNICODE_NULL) +
1783                                 AnsiString.MaximumLength);
1784         if (strBufW != NULL)
1785         {
1786             _SEH2_TRY
1787             {
1788                 NTSTATUS Status;
1789 
1790                 /* Copy the unicode string */
1791                 RtlCopyMemory(strBufW,
1792                               MenuName->Buffer,
1793                               MenuName->Length);
1794                 strBufW[MenuName->Length / sizeof(WCHAR)] = UNICODE_NULL;
1795 
1796                 /* Create an ANSI copy of the string */
1797                 AnsiString.Buffer = (PSTR)(strBufW + (MenuName->Length / sizeof(WCHAR)) + 1);
1798                 Status = RtlUnicodeStringToAnsiString(&AnsiString,
1799                                                       MenuName,
1800                                                       FALSE);
1801                 if (!NT_SUCCESS(Status))
1802                 {
1803                     SetLastNtError(Status);
1804                     _SEH2_LEAVE;
1805                 }
1806 
1807                 Ret = TRUE;
1808             }
1809             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1810             {
1811                 SetLastNtError(_SEH2_GetExceptionCode());
1812             }
1813             _SEH2_END;
1814 
1815             if (Ret)
1816             {
1817                 /* Update the base class */
1818                 IntFreeClassMenuName(Class);
1819                 Class->lpszClientUnicodeMenuName = strBufW;
1820                 Class->lpszClientAnsiMenuName = AnsiString.Buffer;
1821                 Class->MenuNameIsString = TRUE;
1822 
1823                 /* Update the clones */
1824                 Class = Class->pclsClone;
1825                 while (Class != NULL)
1826                 {
1827                     Class->lpszClientUnicodeMenuName = strBufW;
1828                     Class->lpszClientAnsiMenuName = AnsiString.Buffer;
1829                     Class->MenuNameIsString = TRUE;
1830 
1831                     Class = Class->pclsNext;
1832                 }
1833             }
1834             else
1835             {
1836                 ERR("Failed to copy class menu name!\n");
1837                 UserHeapFree(strBufW);
1838             }
1839         }
1840         else
1841             EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1842     }
1843     else
1844     {
1845         ASSERT(IS_INTRESOURCE(MenuName->Buffer));
1846 
1847         /* Update the base class */
1848         IntFreeClassMenuName(Class);
1849         Class->lpszClientUnicodeMenuName = MenuName->Buffer;
1850         Class->lpszClientAnsiMenuName = (PSTR)MenuName->Buffer;
1851         Class->MenuNameIsString = FALSE;
1852 
1853         /* Update the clones */
1854         Class = Class->pclsClone;
1855         while (Class != NULL)
1856         {
1857             Class->lpszClientUnicodeMenuName = MenuName->Buffer;
1858             Class->lpszClientAnsiMenuName = (PSTR)MenuName->Buffer;
1859             Class->MenuNameIsString = FALSE;
1860 
1861             Class = Class->pclsNext;
1862         }
1863 
1864         Ret = TRUE;
1865     }
1866 
1867     return Ret;
1868 }
1869 
1870 static inline
1871 ULONG_PTR
IntGetSetClassLongPtr(PCLS Class,ULONG Index,ULONG_PTR NewValue,ULONG Size)1872 IntGetSetClassLongPtr(PCLS Class, ULONG Index, ULONG_PTR NewValue, ULONG Size)
1873 {
1874     PVOID Address = (PUCHAR)(&Class[1]) + Index;
1875     ULONG_PTR OldValue;
1876 
1877 #ifdef _WIN64
1878     if (Size == sizeof(LONG))
1879     {
1880         /* Values might be unaligned */
1881         OldValue = ReadUnalignedU32(Address);
1882         WriteUnalignedU32(Address, NewValue);
1883     }
1884     else
1885 #endif
1886     {
1887         /* Values might be unaligned */
1888         OldValue = ReadUnalignedUlongPtr(Address);
1889         WriteUnalignedUlongPtr(Address, NewValue);
1890     }
1891 
1892     return OldValue;
1893 }
1894 
1895 ULONG_PTR
UserSetClassLongPtr(IN PCLS Class,IN INT Index,IN ULONG_PTR NewLong,IN BOOL Ansi,IN ULONG Size)1896 UserSetClassLongPtr(IN PCLS Class,
1897                     IN INT Index,
1898                     IN ULONG_PTR NewLong,
1899                     IN BOOL Ansi,
1900                     IN ULONG Size)
1901 {
1902     ULONG_PTR Ret = 0;
1903 
1904     /* NOTE: For GCLP_MENUNAME and GCW_ATOM this function may raise an exception! */
1905 
1906     /* Change the information in the base class first, then update the clones */
1907     Class = Class->pclsBase;
1908 
1909     if (Index >= 0)
1910     {
1911         TRACE("SetClassLong(%d, %x)\n", Index, NewLong);
1912 
1913         if (((ULONG)Index + Size) < (ULONG)Index ||
1914             ((ULONG)Index + Size) > (ULONG)Class->cbclsExtra)
1915         {
1916             EngSetLastError(ERROR_INVALID_PARAMETER);
1917             return 0;
1918         }
1919 
1920         Ret = IntGetSetClassLongPtr(Class, Index, NewLong, Size);
1921 
1922         /* Update the clones */
1923         Class = Class->pclsClone;
1924         while (Class != NULL)
1925         {
1926             IntGetSetClassLongPtr(Class, Index, NewLong, Size);
1927             Class = Class->pclsNext;
1928         }
1929 
1930         return Ret;
1931     }
1932 
1933     switch (Index)
1934     {
1935         case GCL_CBWNDEXTRA:
1936             Ret = (ULONG_PTR)Class->cbwndExtra;
1937             Class->cbwndExtra = (INT)NewLong;
1938 
1939             /* Update the clones */
1940             Class = Class->pclsClone;
1941             while (Class != NULL)
1942             {
1943                 Class->cbwndExtra = (INT)NewLong;
1944                 Class = Class->pclsNext;
1945             }
1946 
1947             break;
1948 
1949         case GCL_CBCLSEXTRA:
1950             EngSetLastError(ERROR_INVALID_PARAMETER);
1951             break;
1952 
1953         case GCLP_HBRBACKGROUND:
1954             Ret = (ULONG_PTR)Class->hbrBackground;
1955             Class->hbrBackground = (HBRUSH)NewLong;
1956 
1957             /* Update the clones */
1958             Class = Class->pclsClone;
1959             while (Class != NULL)
1960             {
1961                 Class->hbrBackground = (HBRUSH)NewLong;
1962                 Class = Class->pclsNext;
1963             }
1964             break;
1965 
1966         case GCLP_HCURSOR:
1967         {
1968             PCURICON_OBJECT NewCursor = NULL;
1969 
1970             if (NewLong)
1971             {
1972                 NewCursor = UserGetCurIconObject((HCURSOR)NewLong);
1973                 if (!NewCursor)
1974                 {
1975                     EngSetLastError(ERROR_INVALID_CURSOR_HANDLE);
1976                     return 0;
1977                 }
1978             }
1979 
1980             if (Class->spcur)
1981             {
1982                 Ret = (ULONG_PTR)UserHMGetHandle(Class->spcur);
1983                 UserDereferenceObject(Class->spcur);
1984             }
1985             else
1986             {
1987                 Ret = 0;
1988             }
1989 
1990             if (Ret == NewLong)
1991             {
1992                 /* It's a nop */
1993                 return Ret;
1994             }
1995 
1996             Class->spcur = NewCursor;
1997 
1998             /* Update the clones */
1999             Class = Class->pclsClone;
2000             while (Class != NULL)
2001             {
2002                 if (Class->spcur)
2003                     UserDereferenceObject(Class->spcur);
2004                 if (NewCursor)
2005                     UserReferenceObject(NewCursor);
2006                 Class->spcur = NewCursor;
2007                 Class = Class->pclsNext;
2008             }
2009 
2010             break;
2011         }
2012 
2013         // MSDN:
2014         // hIconSm, A handle to a small icon that is associated with the window class.
2015         // If this member is NULL, the system searches the icon resource specified by
2016         // the hIcon member for an icon of the appropriate size to use as the small icon.
2017         //
2018         case GCLP_HICON:
2019         {
2020             PCURICON_OBJECT NewIcon = NULL;
2021             PCURICON_OBJECT NewSmallIcon = NULL;
2022 
2023             if (NewLong)
2024             {
2025                 NewIcon = UserGetCurIconObject((HCURSOR)NewLong);
2026                 if (!NewIcon)
2027                 {
2028                     EngSetLastError(ERROR_INVALID_ICON_HANDLE);
2029                     return 0;
2030                 }
2031             }
2032 
2033             if (Class->spicn)
2034             {
2035                 Ret = (ULONG_PTR)UserHMGetHandle(Class->spicn);
2036                 UserDereferenceObject(Class->spicn);
2037             }
2038             else
2039             {
2040                 Ret = 0;
2041             }
2042 
2043             if (Ret == NewLong)
2044             {
2045                 /* It's a nop */
2046                 return Ret;
2047             }
2048 
2049             if (Ret && (Class->CSF_flags & CSF_CACHEDSMICON))
2050             {
2051                 /* We will change the small icon */
2052                 UserDereferenceObject(Class->spicnSm);
2053                 IntDestroyCurIconObject(Class->spicnSm);
2054                 Class->spicnSm = NULL;
2055                 Class->CSF_flags &= ~CSF_CACHEDSMICON;
2056             }
2057 
2058             if (NewLong && !Class->spicnSm)
2059             {
2060                 /* Create the new small icon from the new large(?) one */
2061                 HICON SmallIconHandle = NULL;
2062                 if((NewIcon->CURSORF_flags & (CURSORF_LRSHARED | CURSORF_FROMRESOURCE))
2063                         == (CURSORF_LRSHARED | CURSORF_FROMRESOURCE))
2064                 {
2065                     SmallIconHandle = co_IntCopyImage(
2066                         (HICON)NewLong,
2067                         IMAGE_ICON,
2068                         UserGetSystemMetrics( SM_CXSMICON ),
2069                         UserGetSystemMetrics( SM_CYSMICON ),
2070                         LR_COPYFROMRESOURCE);
2071                 }
2072                 if (!SmallIconHandle)
2073                 {
2074                     /* Retry without copying from resource */
2075                     SmallIconHandle = co_IntCopyImage(
2076                         (HICON)NewLong,
2077                         IMAGE_ICON,
2078                         UserGetSystemMetrics( SM_CXSMICON ),
2079                         UserGetSystemMetrics( SM_CYSMICON ),
2080                         0);
2081                 }
2082                 if (SmallIconHandle)
2083                 {
2084                     /* So use it */
2085                     NewSmallIcon = Class->spicnSm = UserGetCurIconObject(SmallIconHandle);
2086                     Class->CSF_flags |= CSF_CACHEDSMICON;
2087                 }
2088             }
2089 
2090             Class->spicn = NewIcon;
2091 
2092             /* Update the clones */
2093             Class = Class->pclsClone;
2094             while (Class != NULL)
2095             {
2096                 if (Class->spicn)
2097                     UserDereferenceObject(Class->spicn);
2098                 if (NewIcon)
2099                     UserReferenceObject(NewIcon);
2100                 Class->spicn = NewIcon;
2101                 if (NewSmallIcon)
2102                 {
2103                     if (Class->spicnSm)
2104                         UserDereferenceObject(Class->spicnSm);
2105                     UserReferenceObject(NewSmallIcon);
2106                     Class->spicnSm = NewSmallIcon;
2107                     Class->CSF_flags |= CSF_CACHEDSMICON;
2108                 }
2109                 Class = Class->pclsNext;
2110             }
2111             break;
2112         }
2113 
2114         case GCLP_HICONSM:
2115         {
2116             PCURICON_OBJECT NewSmallIcon = NULL;
2117             BOOLEAN NewIconFromCache = FALSE;
2118 
2119             if (NewLong)
2120             {
2121                 NewSmallIcon = UserGetCurIconObject((HCURSOR)NewLong);
2122                 if (!NewSmallIcon)
2123                 {
2124                     EngSetLastError(ERROR_INVALID_ICON_HANDLE);
2125                     return 0;
2126                 }
2127             }
2128             else
2129             {
2130                 /* Create the new small icon from the large one */
2131                 HICON SmallIconHandle = NULL;
2132                 if((Class->spicn->CURSORF_flags & (CURSORF_LRSHARED | CURSORF_FROMRESOURCE))
2133                         == (CURSORF_LRSHARED | CURSORF_FROMRESOURCE))
2134                 {
2135                     SmallIconHandle = co_IntCopyImage(
2136                         UserHMGetHandle(Class->spicn),
2137                         IMAGE_ICON,
2138                         UserGetSystemMetrics( SM_CXSMICON ),
2139                         UserGetSystemMetrics( SM_CYSMICON ),
2140                         LR_COPYFROMRESOURCE);
2141                 }
2142                 if (!SmallIconHandle)
2143                 {
2144                     /* Retry without copying from resource */
2145                     SmallIconHandle = co_IntCopyImage(
2146                         UserHMGetHandle(Class->spicn),
2147                         IMAGE_ICON,
2148                         UserGetSystemMetrics( SM_CXSMICON ),
2149                         UserGetSystemMetrics( SM_CYSMICON ),
2150                         0);
2151                 }
2152                 if (SmallIconHandle)
2153                 {
2154                     /* So use it */
2155                     NewSmallIcon = UserGetCurIconObject(SmallIconHandle);
2156                     NewIconFromCache = TRUE;
2157                 }
2158                 else
2159                 {
2160                     ERR("Failed getting a small icon for the class.\n");
2161                 }
2162             }
2163 
2164             if (Class->spicnSm)
2165             {
2166                 if (Class->CSF_flags & CSF_CACHEDSMICON)
2167                 {
2168                     /* We must destroy the icon if we own it */
2169                     IntDestroyCurIconObject(Class->spicnSm);
2170                     Ret = 0;
2171                 }
2172                 else
2173                 {
2174                     Ret = (ULONG_PTR)UserHMGetHandle(Class->spicnSm);
2175                 }
2176                 UserDereferenceObject(Class->spicnSm);
2177             }
2178             else
2179             {
2180                 Ret = 0;
2181             }
2182 
2183             if (NewIconFromCache)
2184                 Class->CSF_flags |= CSF_CACHEDSMICON;
2185             else
2186                 Class->CSF_flags &= ~CSF_CACHEDSMICON;
2187             Class->spicnSm = NewSmallIcon;
2188 
2189             /* Update the clones */
2190             Class = Class->pclsClone;
2191             while (Class != NULL)
2192             {
2193                 if (Class->spicnSm)
2194                     UserDereferenceObject(Class->spicnSm);
2195                 if (NewSmallIcon)
2196                     UserReferenceObject(NewSmallIcon);
2197                 if (NewIconFromCache)
2198                     Class->CSF_flags |= CSF_CACHEDSMICON;
2199                 else
2200                     Class->CSF_flags &= ~CSF_CACHEDSMICON;
2201                 Class->spicnSm = NewSmallIcon;
2202                 Class = Class->pclsNext;
2203             }
2204         }
2205         break;
2206 
2207         case GCLP_HMODULE:
2208             Ret = (ULONG_PTR)Class->hModule;
2209             Class->hModule = (HINSTANCE)NewLong;
2210 
2211            /* Update the clones */
2212             Class = Class->pclsClone;
2213             while (Class != NULL)
2214             {
2215                 Class->hModule = (HINSTANCE)NewLong;
2216                 Class = Class->pclsNext;
2217             }
2218             break;
2219 
2220         case GCLP_MENUNAME:
2221         {
2222             PUNICODE_STRING Value = (PUNICODE_STRING)NewLong;
2223 
2224             if (!IntSetClassMenuName(Class,
2225                                      Value))
2226             {
2227                 ERR("Setting the class menu name failed!\n");
2228             }
2229 
2230             /* FIXME: Really return NULL? Wine does so... */
2231             break;
2232         }
2233 
2234         case GCL_STYLE:
2235             Ret = (ULONG_PTR)Class->style;
2236             Class->style = (UINT)NewLong;
2237 
2238             /* FIXME: What if the CS_GLOBALCLASS style is changed? should we
2239                       move the class to the appropriate list? For now, we save
2240                       the original value in Class->Global, so we can always
2241                       locate the appropriate list */
2242 
2243            /* Update the clones */
2244             Class = Class->pclsClone;
2245             while (Class != NULL)
2246             {
2247                 Class->style = (UINT)NewLong;
2248                 Class = Class->pclsNext;
2249             }
2250             break;
2251 
2252         case GCLP_WNDPROC:
2253             Ret = (ULONG_PTR)IntSetClassWndProc(Class,
2254                                                 (WNDPROC)NewLong,
2255                                                 Ansi);
2256             break;
2257 
2258         case GCW_ATOM:
2259         {
2260             PUNICODE_STRING Value = (PUNICODE_STRING)NewLong;
2261 
2262             Ret = (ULONG_PTR)Class->atomNVClassName;
2263             if (!IntSetClassAtom(Class,
2264                                  Value))
2265             {
2266                 Ret = 0;
2267             }
2268             break;
2269         }
2270 
2271         default:
2272             EngSetLastError(ERROR_INVALID_INDEX);
2273             break;
2274     }
2275 
2276     return Ret;
2277 }
2278 
2279 static BOOL
UserGetClassInfo(IN PCLS Class,OUT PWNDCLASSEXW lpwcx,IN BOOL Ansi,HINSTANCE hInstance)2280 UserGetClassInfo(IN PCLS Class,
2281                  OUT PWNDCLASSEXW lpwcx,
2282                  IN BOOL Ansi,
2283                  HINSTANCE hInstance)
2284 {
2285     if (!Class) return FALSE;
2286 
2287     lpwcx->style = Class->style;
2288 
2289     // If fnId is set, clear the global bit. See wine class test check_style.
2290     if (Class->fnid)
2291        lpwcx->style &= ~CS_GLOBALCLASS;
2292 
2293     lpwcx->lpfnWndProc = IntGetClassWndProc(Class, Ansi);
2294 
2295     lpwcx->cbClsExtra = Class->cbclsExtra;
2296     lpwcx->cbWndExtra = Class->cbwndExtra;
2297     lpwcx->hIcon = Class->spicn ? UserHMGetHandle(Class->spicn) : NULL;
2298     lpwcx->hCursor = Class->spcur ? UserHMGetHandle(Class->spcur) : NULL;
2299     lpwcx->hIconSm = Class->spicnSm ? UserHMGetHandle(Class->spicnSm) : NULL;
2300     lpwcx->hbrBackground = Class->hbrBackground;
2301 
2302     /* Copy non-string to user first. */
2303     if (Ansi)
2304        ((PWNDCLASSEXA)lpwcx)->lpszMenuName = Class->lpszClientAnsiMenuName;
2305     else
2306        lpwcx->lpszMenuName = Class->lpszClientUnicodeMenuName;
2307 /*
2308  *  FIXME: CLSMENUNAME has the answers! Copy the already made buffers from there!
2309  *  Cls: lpszMenuName and lpszAnsiClassName should be used by kernel space.
2310  *  lpszClientXxxMenuName should already be mapped to user space.
2311  */
2312     /* Copy string ptr to user. */
2313     if ( Class->lpszClientUnicodeMenuName != NULL &&
2314          Class->MenuNameIsString)
2315     {
2316        lpwcx->lpszMenuName = UserHeapAddressToUser(Ansi ?
2317                                  (PVOID)Class->lpszClientAnsiMenuName :
2318                                  (PVOID)Class->lpszClientUnicodeMenuName);
2319     }
2320 
2321     if (hInstance == hModClient)
2322         lpwcx->hInstance = NULL;
2323     else
2324         lpwcx->hInstance = hInstance;
2325 
2326     /* FIXME: Return the string? Okay! This is performed in User32! */
2327     //lpwcx->lpszClassName = (LPCWSTR)((ULONG_PTR)Class->atomClassName);
2328 
2329     return TRUE;
2330 }
2331 
2332 //
2333 // Register System Classes....
2334 //
2335 BOOL
2336 FASTCALL
UserRegisterSystemClasses(VOID)2337 UserRegisterSystemClasses(VOID)
2338 {
2339     UINT i;
2340     UNICODE_STRING ClassName, MenuName;
2341     PPROCESSINFO ppi = GetW32ProcessInfo();
2342     WNDCLASSEXW wc;
2343     PCLS Class;
2344     BOOL Ret = TRUE;
2345     HBRUSH hBrush;
2346     DWORD Flags = 0;
2347 
2348     if (ppi->W32PF_flags & W32PF_CLASSESREGISTERED)
2349        return TRUE;
2350 
2351     if ( hModClient == NULL)
2352        return FALSE;
2353 
2354     RtlZeroMemory(&ClassName, sizeof(ClassName));
2355     RtlZeroMemory(&MenuName, sizeof(MenuName));
2356 
2357     for (i = 0; i != ARRAYSIZE(DefaultServerClasses); i++)
2358     {
2359         if (!IS_ATOM(DefaultServerClasses[i].ClassName))
2360         {
2361            RtlInitUnicodeString(&ClassName, DefaultServerClasses[i].ClassName);
2362         }
2363         else
2364         {
2365            ClassName.Buffer = DefaultServerClasses[i].ClassName;
2366            ClassName.Length = 0;
2367            ClassName.MaximumLength = 0;
2368         }
2369 
2370         wc.cbSize = sizeof(wc);
2371         wc.style = DefaultServerClasses[i].Style;
2372 
2373         Flags |= CSF_SERVERSIDEPROC;
2374 
2375         if (DefaultServerClasses[i].ProcW)
2376         {
2377            wc.lpfnWndProc = DefaultServerClasses[i].ProcW;
2378            wc.hInstance = hModuleWin;
2379         }
2380         else
2381         {
2382            wc.lpfnWndProc = GETPFNSERVER(DefaultServerClasses[i].fiId);
2383            wc.hInstance = hModClient;
2384         }
2385 
2386         wc.cbClsExtra = 0;
2387         wc.cbWndExtra = DefaultServerClasses[i].ExtraBytes;
2388         wc.hIcon = NULL;
2389 
2390         //// System Cursors should be initilized!!!
2391         wc.hCursor = NULL;
2392         if (DefaultServerClasses[i].hCursor == (HICON)OCR_NORMAL)
2393         {
2394             if (SYSTEMCUR(ARROW) == NULL)
2395             {
2396                 ERR("SYSTEMCUR(ARROW) == NULL, should not happen!!\n");
2397             }
2398             else
2399             {
2400                 wc.hCursor = UserHMGetHandle(SYSTEMCUR(ARROW));
2401             }
2402         }
2403 
2404         hBrush = DefaultServerClasses[i].hBrush;
2405         if (hBrush <= (HBRUSH)COLOR_MENUBAR)
2406         {
2407             hBrush = IntGetSysColorBrush(HandleToUlong(hBrush));
2408         }
2409         wc.hbrBackground = hBrush;
2410         wc.lpszMenuName = NULL;
2411         wc.lpszClassName = ClassName.Buffer;
2412         wc.hIconSm = NULL;
2413 
2414         Class = IntCreateClass( &wc,
2415                                 &ClassName,
2416                                 &ClassName,
2417                                 &MenuName,
2418                                  DefaultServerClasses[i].fiId,
2419                                  Flags,
2420                                  NULL,
2421                                  ppi);
2422         if (Class != NULL)
2423         {
2424             Class->pclsNext = ppi->pclsPublicList;
2425             (void)InterlockedExchangePointer((PVOID*)&ppi->pclsPublicList,
2426                                              Class);
2427 
2428             ppi->dwRegisteredClasses |= ICLASS_TO_MASK(DefaultServerClasses[i].iCls);
2429         }
2430         else
2431         {
2432             ERR("!!! Registering system class failed!\n");
2433             Ret = FALSE;
2434         }
2435     }
2436     if (Ret) ppi->W32PF_flags |= W32PF_CLASSESREGISTERED;
2437     return Ret;
2438 }
2439 
2440 /* SYSCALLS *****************************************************************/
2441 
2442 RTL_ATOM
2443 APIENTRY
NtUserRegisterClassExWOW(WNDCLASSEXW * lpwcx,PUNICODE_STRING ClassName,PUNICODE_STRING ClsVersion,PCLSMENUNAME pClassMenuName,DWORD fnID,DWORD Flags,LPDWORD pWow)2444 NtUserRegisterClassExWOW(
2445     WNDCLASSEXW* lpwcx,
2446     PUNICODE_STRING ClassName,
2447     PUNICODE_STRING ClsVersion,
2448     PCLSMENUNAME pClassMenuName,
2449     DWORD fnID,
2450     DWORD Flags,
2451     LPDWORD pWow)
2452 /*
2453  * FUNCTION:
2454  *   Registers a new class with the window manager
2455  * ARGUMENTS:
2456  *   lpwcx          = Win32 extended window class structure
2457  *   bUnicodeClass = Whether to send ANSI or unicode strings
2458  *                   to window procedures
2459  * RETURNS:
2460  *   Atom identifying the new class
2461  */
2462 {
2463     WNDCLASSEXW CapturedClassInfo = {0};
2464     UNICODE_STRING CapturedName = {0}, CapturedMenuName = {0}, CapturedVersion = {0};
2465     RTL_ATOM Ret = (RTL_ATOM)0;
2466     PPROCESSINFO ppi = GetW32ProcessInfo();
2467     BOOL Exception = FALSE;
2468 
2469     if (Flags & ~(CSF_ANSIPROC))
2470     {
2471         ERR("NtUserRegisterClassExWOW Bad Flags!\n");
2472         EngSetLastError(ERROR_INVALID_FLAGS);
2473         return Ret;
2474     }
2475 
2476     UserEnterExclusive();
2477 
2478     TRACE("NtUserRegisterClassExWOW ClsN %wZ\n",ClassName);
2479 
2480     if ( !(ppi->W32PF_flags & W32PF_CLASSESREGISTERED ))
2481     {
2482        UserRegisterSystemClasses();
2483     }
2484 
2485     _SEH2_TRY
2486     {
2487         /* Probe the parameters and basic parameter checks */
2488         if (ProbeForReadUint(&lpwcx->cbSize) != sizeof(WNDCLASSEXW))
2489         {
2490             ERR("NtUserRegisterClassExWOW Wrong cbSize!\n");
2491             goto InvalidParameter;
2492         }
2493 
2494         ProbeForRead(lpwcx,
2495                      sizeof(WNDCLASSEXW),
2496                      sizeof(ULONG));
2497         RtlCopyMemory(&CapturedClassInfo,
2498                       lpwcx,
2499                       sizeof(WNDCLASSEXW));
2500 
2501         CapturedName = ProbeForReadUnicodeString(ClassName);
2502         CapturedVersion = ProbeForReadUnicodeString(ClsVersion);
2503 
2504         ProbeForRead(pClassMenuName,
2505                      sizeof(CLSMENUNAME),
2506                      1);
2507 
2508         CapturedMenuName = ProbeForReadUnicodeString(pClassMenuName->pusMenuName);
2509 
2510         if ( (CapturedName.Length & 1) ||
2511              (CapturedMenuName.Length & 1) ||
2512              (CapturedClassInfo.cbClsExtra < 0) ||
2513              ((CapturedClassInfo.cbClsExtra + CapturedName.Length +
2514               CapturedMenuName.Length +  sizeof(CLS))
2515                 < (ULONG)CapturedClassInfo.cbClsExtra) ||
2516              (CapturedClassInfo.cbWndExtra < 0) ||
2517              (CapturedClassInfo.hInstance == NULL) )
2518         {
2519             ERR("NtUserRegisterClassExWOW Invalid Parameter Error!\n");
2520             goto InvalidParameter;
2521         }
2522 
2523         if (CapturedName.Length != 0)
2524         {
2525             ProbeForRead(CapturedName.Buffer,
2526                          CapturedName.Length,
2527                          sizeof(WCHAR));
2528         }
2529         else
2530         {
2531             if (!IS_ATOM(CapturedName.Buffer))
2532             {
2533                 ERR("NtUserRegisterClassExWOW ClassName Error!\n");
2534                 goto InvalidParameter;
2535             }
2536         }
2537 
2538         if (CapturedVersion.Length != 0)
2539         {
2540             ProbeForRead(CapturedVersion.Buffer,
2541                          CapturedVersion.Length,
2542                          sizeof(WCHAR));
2543         }
2544         else
2545         {
2546             if (!IS_ATOM(CapturedVersion.Buffer))
2547             {
2548                 ERR("NtUserRegisterClassExWOW ClassName Error!\n");
2549                 goto InvalidParameter;
2550             }
2551         }
2552 
2553         if (CapturedMenuName.Length != 0)
2554         {
2555             ProbeForRead(CapturedMenuName.Buffer,
2556                          CapturedMenuName.Length,
2557                          sizeof(WCHAR));
2558         }
2559         else if (CapturedMenuName.Buffer != NULL &&
2560                  !IS_INTRESOURCE(CapturedMenuName.Buffer))
2561         {
2562             ERR("NtUserRegisterClassExWOW MenuName Error!\n");
2563 InvalidParameter:
2564             EngSetLastError(ERROR_INVALID_PARAMETER);
2565             _SEH2_LEAVE;
2566         }
2567 
2568         if (IsCallProcHandle(lpwcx->lpfnWndProc))
2569         {  // Never seen this yet, but I'm sure it's a little haxxy trick!
2570            // If this pops up we know what todo!
2571            ERR("NtUserRegisterClassExWOW WndProc is CallProc!!\n");
2572         }
2573 
2574         TRACE("NtUserRegisterClassExWOW MnuN %wZ\n",&CapturedMenuName);
2575     }
2576     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2577     {
2578         ERR("NtUserRegisterClassExWOW Exception Error!\n");
2579         SetLastNtError(_SEH2_GetExceptionCode());
2580         Exception = TRUE;
2581     }
2582     _SEH2_END;
2583 
2584     if (!Exception)
2585     {
2586         /* Register the class */
2587         Ret = UserRegisterClass(&CapturedClassInfo,
2588                                 &CapturedName,
2589                                 &CapturedVersion,
2590                                 &CapturedMenuName,
2591                                 fnID,
2592                                 Flags);
2593     }
2594 
2595     if (!Ret)
2596     {
2597        TRACE("NtUserRegisterClassExWOW Null Return!\n");
2598     }
2599 
2600     UserLeave();
2601 
2602     return Ret;
2603 }
2604 
2605 ULONG_PTR APIENTRY
IntNtUserSetClassLongPtr(HWND hWnd,INT Offset,ULONG_PTR dwNewLong,BOOL Ansi,ULONG Size)2606 IntNtUserSetClassLongPtr(HWND hWnd,
2607                    INT Offset,
2608                    ULONG_PTR dwNewLong,
2609                    BOOL Ansi,
2610                    ULONG Size)
2611 {
2612     PPROCESSINFO pi;
2613     PWND Window;
2614     ULONG_PTR Ret = 0;
2615 
2616     UserEnterExclusive();
2617 
2618     pi = GetW32ProcessInfo();
2619 
2620     Window = UserGetWindowObject(hWnd);
2621     if (Window != NULL)
2622     {
2623         if (Window->head.pti->ppi != pi)
2624         {
2625             EngSetLastError(ERROR_ACCESS_DENIED);
2626             goto Cleanup;
2627         }
2628 
2629         _SEH2_TRY
2630         {
2631             UNICODE_STRING Value;
2632 
2633             /* Probe the parameters */
2634             if (Offset == GCW_ATOM || Offset == GCLP_MENUNAME)
2635             {
2636                 /* FIXME: Resource ID can be passed directly without UNICODE_STRING ? */
2637                 if (IS_ATOM(dwNewLong))
2638                 {
2639                     Value.MaximumLength = 0;
2640                     Value.Length = 0;
2641                     Value.Buffer = (PWSTR)dwNewLong;
2642                 }
2643                 else
2644                 {
2645                     Value = ProbeForReadUnicodeString((PUNICODE_STRING)dwNewLong);
2646                 }
2647 
2648                 if (Value.Length & 1)
2649                 {
2650                     goto InvalidParameter;
2651                 }
2652 
2653                 if (Value.Length != 0)
2654                 {
2655                     ProbeForRead(Value.Buffer,
2656                                  Value.Length,
2657                                  sizeof(WCHAR));
2658                 }
2659                 else
2660                 {
2661                     if (Offset == GCW_ATOM && !IS_ATOM(Value.Buffer))
2662                     {
2663                         goto InvalidParameter;
2664                     }
2665                     else if (Offset == GCLP_MENUNAME && !IS_INTRESOURCE(Value.Buffer))
2666                     {
2667 InvalidParameter:
2668                         EngSetLastError(ERROR_INVALID_PARAMETER);
2669                         _SEH2_LEAVE;
2670                     }
2671                 }
2672 
2673                 dwNewLong = (ULONG_PTR)&Value;
2674             }
2675 
2676             Ret = UserSetClassLongPtr(Window->pcls,
2677                                       Offset,
2678                                       dwNewLong,
2679                                       Ansi,
2680                                       Size);
2681             switch(Offset)
2682             {
2683                case GCLP_HICONSM:
2684                case GCLP_HICON:
2685                {
2686                   if (Ret && Ret != dwNewLong)
2687                      UserPaintCaption(Window, DC_ICON);
2688                }
2689             }
2690         }
2691         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2692         {
2693             SetLastNtError(_SEH2_GetExceptionCode());
2694         }
2695         _SEH2_END;
2696     }
2697 
2698 Cleanup:
2699     UserLeave();
2700 
2701     return Ret;
2702 }
2703 
2704 ULONG_PTR
2705 APIENTRY
NtUserSetClassLong(_In_ HWND hWnd,_In_ INT Offset,_In_ ULONG dwNewLong,_In_ BOOL Ansi)2706 NtUserSetClassLong(
2707     _In_ HWND hWnd,
2708     _In_ INT Offset,
2709     _In_ ULONG dwNewLong,
2710     _In_ BOOL Ansi)
2711 {
2712     return IntNtUserSetClassLongPtr(hWnd, Offset, dwNewLong, Ansi, sizeof(LONG));
2713 }
2714 
2715 #ifdef _WIN64
2716 
2717 ULONG_PTR
2718 APIENTRY
NtUserSetClassLongPtr(_In_ HWND hWnd,_In_ INT Offset,_In_ ULONG_PTR dwNewLong,_In_ BOOL Ansi)2719 NtUserSetClassLongPtr(
2720     _In_ HWND hWnd,
2721     _In_ INT Offset,
2722     _In_ ULONG_PTR dwNewLong,
2723     _In_ BOOL Ansi)
2724 {
2725     return IntNtUserSetClassLongPtr(hWnd, Offset, dwNewLong, Ansi, sizeof(LONG_PTR));
2726 }
2727 
2728 #endif // _WIN64
2729 
2730 WORD
2731 APIENTRY
NtUserSetClassWord(HWND hWnd,INT nIndex,WORD wNewWord)2732 NtUserSetClassWord(
2733   HWND hWnd,
2734   INT nIndex,
2735   WORD wNewWord)
2736 {
2737 /*
2738  * NOTE: Obsoleted in 32-bit windows
2739  */
2740    return(0);
2741 }
2742 
2743 BOOL
2744 APIENTRY
NtUserUnregisterClass(IN PUNICODE_STRING ClassNameOrAtom,IN HINSTANCE hInstance,OUT PCLSMENUNAME pClassMenuName)2745 NtUserUnregisterClass(
2746     IN PUNICODE_STRING ClassNameOrAtom,
2747     IN HINSTANCE hInstance,
2748     OUT PCLSMENUNAME pClassMenuName)
2749 {
2750     UNICODE_STRING SafeClassName;
2751     NTSTATUS Status;
2752     BOOL Ret;
2753 
2754     Status = ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName, ClassNameOrAtom);
2755     if (!NT_SUCCESS(Status))
2756     {
2757         ERR("Error capturing the class name\n");
2758         SetLastNtError(Status);
2759         return FALSE;
2760     }
2761 
2762     UserEnterExclusive();
2763 
2764     /* Unregister the class */
2765     Ret = UserUnregisterClass(&SafeClassName, hInstance, NULL); // Null for now~
2766 
2767     UserLeave();
2768 
2769     if (SafeClassName.Buffer && !IS_ATOM(SafeClassName.Buffer))
2770         ExFreePoolWithTag(SafeClassName.Buffer, TAG_STRING);
2771 
2772     return Ret;
2773 }
2774 
2775 
2776 /* NOTE: For system classes hInstance is not NULL here, but User32Instance */
2777 BOOL
2778 APIENTRY
NtUserGetClassInfo(HINSTANCE hInstance,PUNICODE_STRING ClassName,LPWNDCLASSEXW lpWndClassEx,LPWSTR * ppszMenuName,BOOL bAnsi)2779 NtUserGetClassInfo(
2780    HINSTANCE hInstance,
2781    PUNICODE_STRING ClassName,
2782    LPWNDCLASSEXW lpWndClassEx,
2783    LPWSTR *ppszMenuName,
2784    BOOL bAnsi)
2785 {
2786     UNICODE_STRING SafeClassName;
2787     WNDCLASSEXW Safewcexw;
2788     PCLS Class;
2789     RTL_ATOM ClassAtom = 0;
2790     PPROCESSINFO ppi;
2791     BOOL Ret = TRUE;
2792     NTSTATUS Status;
2793 
2794     _SEH2_TRY
2795     {
2796         ProbeForWrite( lpWndClassEx, sizeof(WNDCLASSEXW), sizeof(ULONG));
2797         RtlCopyMemory( &Safewcexw, lpWndClassEx, sizeof(WNDCLASSEXW));
2798         if (ppszMenuName)
2799         {
2800             ProbeForWrite(ppszMenuName, sizeof(*ppszMenuName), sizeof(PVOID));
2801         }
2802     }
2803     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2804     {
2805         SetLastNtError(_SEH2_GetExceptionCode());
2806         _SEH2_YIELD(return FALSE);
2807     }
2808     _SEH2_END;
2809 
2810     Status = ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName, ClassName);
2811     if (!NT_SUCCESS(Status))
2812     {
2813         ERR("Error capturing the class name\n");
2814         SetLastNtError(Status);
2815         return FALSE;
2816     }
2817 
2818     // If null instance use client.
2819     if (!hInstance) hInstance = hModClient;
2820 
2821     TRACE("GetClassInfo(%wZ, %p)\n", &SafeClassName, hInstance);
2822 
2823     /* NOTE: Need exclusive lock because getting the wndproc might require the
2824              creation of a call procedure handle */
2825     UserEnterExclusive();
2826 
2827     ppi = GetW32ProcessInfo();
2828     if (!(ppi->W32PF_flags & W32PF_CLASSESREGISTERED))
2829     {
2830         UserRegisterSystemClasses();
2831     }
2832 
2833     ClassAtom = IntGetClassAtom(&SafeClassName,
2834                                 hInstance,
2835                                 ppi,
2836                                 &Class,
2837                                 NULL);
2838     if (ClassAtom != (RTL_ATOM)0)
2839     {
2840         ClassAtom = Class->atomNVClassName;
2841         Ret = UserGetClassInfo(Class, &Safewcexw, bAnsi, hInstance);
2842     }
2843     else
2844     {
2845         EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST);
2846         Ret = FALSE;
2847     }
2848 
2849     UserLeave();
2850 
2851     if (Ret)
2852     {
2853         _SEH2_TRY
2854         {
2855             /* Emulate Function. */
2856             if (ppszMenuName) *ppszMenuName = (LPWSTR)Safewcexw.lpszMenuName;
2857 
2858             RtlCopyMemory(lpWndClassEx, &Safewcexw, sizeof(WNDCLASSEXW));
2859 
2860             // From Wine:
2861             /* We must return the atom of the class here instead of just TRUE. */
2862             /* Undocumented behavior! Return the class atom as a BOOL! */
2863             Ret = (BOOL)ClassAtom;
2864         }
2865         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2866         {
2867             EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST);
2868             Ret = FALSE;
2869         }
2870         _SEH2_END;
2871     }
2872 
2873     if (!IS_ATOM(SafeClassName.Buffer))
2874         ExFreePoolWithTag(SafeClassName.Buffer, TAG_STRING);
2875 
2876     return Ret;
2877 }
2878 
2879 
2880 INT APIENTRY
NtUserGetClassName(IN HWND hWnd,IN BOOL Real,OUT PUNICODE_STRING ClassName)2881 NtUserGetClassName (IN HWND hWnd,
2882                     IN BOOL Real,
2883                     OUT PUNICODE_STRING ClassName)
2884 {
2885     PWND Window;
2886     UNICODE_STRING CapturedClassName;
2887     INT iCls, Ret = 0;
2888     RTL_ATOM Atom = 0;
2889 
2890     UserEnterShared();
2891 
2892     Window = UserGetWindowObject(hWnd);
2893     if (Window != NULL)
2894     {
2895         if (Real && Window->fnid && !(Window->fnid & FNID_DESTROY))
2896         {
2897            if (LookupFnIdToiCls(Window->fnid, &iCls))
2898            {
2899               Atom = gpsi->atomSysClass[iCls];
2900            }
2901         }
2902 
2903         _SEH2_TRY
2904         {
2905             ProbeForWriteUnicodeString(ClassName);
2906             CapturedClassName = *ClassName;
2907             if (CapturedClassName.Length != 0)
2908             {
2909                 ProbeForRead(CapturedClassName.Buffer,
2910                              CapturedClassName.Length,
2911                              sizeof(WCHAR));
2912             }
2913 
2914             /* Get the class name */
2915             Ret = UserGetClassName(Window->pcls,
2916                                    &CapturedClassName,
2917                                    Atom,
2918                                    FALSE);
2919 
2920             if (Ret != 0)
2921             {
2922                 /* Update the Length field */
2923                 ClassName->Length = CapturedClassName.Length;
2924             }
2925         }
2926         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2927         {
2928             SetLastNtError(_SEH2_GetExceptionCode());
2929         }
2930         _SEH2_END;
2931     }
2932 
2933     UserLeave();
2934 
2935     return Ret;
2936 }
2937 
2938 /* Return Pointer to Class structure. */
2939 PCLS
2940 APIENTRY
NtUserGetWOWClass(HINSTANCE hInstance,PUNICODE_STRING ClassName)2941 NtUserGetWOWClass(
2942     HINSTANCE hInstance,
2943     PUNICODE_STRING ClassName)
2944 {
2945     UNICODE_STRING SafeClassName;
2946     PPROCESSINFO pi;
2947     PCLS Class = NULL;
2948     RTL_ATOM ClassAtom = 0;
2949     NTSTATUS Status;
2950 
2951     Status = ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName, ClassName);
2952     if (!NT_SUCCESS(Status))
2953     {
2954         ERR("Error capturing the class name\n");
2955         SetLastNtError(Status);
2956         return FALSE;
2957     }
2958 
2959     UserEnterExclusive();
2960 
2961     pi = GetW32ProcessInfo();
2962 
2963     ClassAtom = IntGetClassAtom(&SafeClassName,
2964                                 hInstance,
2965                                 pi,
2966                                 &Class,
2967                                 NULL);
2968     if (!ClassAtom)
2969     {
2970         EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST);
2971     }
2972 
2973 
2974     if (SafeClassName.Buffer && !IS_ATOM(SafeClassName.Buffer))
2975         ExFreePoolWithTag(SafeClassName.Buffer, TAG_STRING);
2976 
2977     UserLeave();
2978 //
2979 // Don't forget to use DesktopPtrToUser( ? ) with return pointer in user space.
2980 //
2981     return Class;
2982 }
2983 
2984 /* EOF */
2985