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