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