xref: /reactos/win32ss/user/ntuser/class.c (revision 4514e91d)
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         ASSERT(IS_ATOM(ClassName->Buffer));
1359         *Atom = (RTL_ATOM)((ULONG_PTR)ClassName->Buffer);
1360         Ret = TRUE;
1361     }
1362 
1363     return Ret;
1364 }
1365 
1366 RTL_ATOM
1367 IntGetClassAtom(
1368     _In_ PUNICODE_STRING ClassName,
1369     IN HINSTANCE hInstance  OPTIONAL,
1370     IN PPROCESSINFO pi  OPTIONAL,
1371     OUT PCLS *BaseClass  OPTIONAL,
1372     OUT PCLS **Link  OPTIONAL)
1373 {
1374     RTL_ATOM Atom = (RTL_ATOM)0;
1375 
1376     ASSERT(BaseClass != NULL);
1377 
1378     if (IntGetAtomFromStringOrAtom(ClassName, &Atom) &&
1379         Atom != (RTL_ATOM)0)
1380     {
1381         PCLS Class;
1382 
1383         /* Attempt to locate the class object */
1384 
1385         ASSERT(pi != NULL);
1386 
1387         /* Step 1: Try to find an exact match of locally registered classes */
1388         Class = IntFindClass(Atom,
1389                              hInstance,
1390                              &pi->pclsPrivateList,
1391                              Link);
1392         if (Class != NULL)
1393         {  TRACE("Step 1: 0x%p\n",Class );
1394             goto FoundClass;
1395         }
1396 
1397         /* Step 2: Try to find any globally registered class. The hInstance
1398                    is not relevant for global classes */
1399         Class = IntFindClass(Atom,
1400                              NULL,
1401                              &pi->pclsPublicList,
1402                              Link);
1403         if (Class != NULL)
1404         { TRACE("Step 2: 0x%p 0x%p\n",Class, Class->hModule);
1405             goto FoundClass;
1406         }
1407 
1408         /* Step 3: Try to find any local class registered by user32 */
1409         Class = IntFindClass(Atom,
1410                              hModClient,
1411                              &pi->pclsPrivateList,
1412                              Link);
1413         if (Class != NULL)
1414         { TRACE("Step 3: 0x%p\n",Class );
1415             goto FoundClass;
1416         }
1417 
1418         /* Step 4: Try to find any global class registered by user32 */
1419         Class = IntFindClass(Atom,
1420                              hModClient,
1421                              &pi->pclsPublicList,
1422                              Link);
1423         if (Class == NULL)
1424         {
1425             return (RTL_ATOM)0;
1426         }else{TRACE("Step 4: 0x%p\n",Class );}
1427 
1428 FoundClass:
1429         *BaseClass = Class;
1430     }
1431     else
1432     {
1433         Atom = 0;
1434     }
1435 
1436     return Atom;
1437 }
1438 
1439 PCLS
1440 IntGetAndReferenceClass(PUNICODE_STRING ClassName, HINSTANCE hInstance, BOOL bDesktopThread)
1441 {
1442    PCLS *ClassLink, Class = NULL;
1443    RTL_ATOM ClassAtom;
1444    PTHREADINFO pti;
1445 
1446    if (bDesktopThread)
1447        pti = gptiDesktopThread;
1448    else
1449        pti = PsGetCurrentThreadWin32Thread();
1450 
1451    if ( !(pti->ppi->W32PF_flags & W32PF_CLASSESREGISTERED ))
1452    {
1453       UserRegisterSystemClasses();
1454    }
1455 
1456    /* Check the class. */
1457 
1458    TRACE("Finding Class %wZ for hInstance 0x%p\n", ClassName, hInstance);
1459 
1460    ClassAtom = IntGetClassAtom(ClassName,
1461                                hInstance,
1462                                pti->ppi,
1463                                &Class,
1464                                &ClassLink);
1465 
1466    if (ClassAtom == (RTL_ATOM)0)
1467    {
1468       if (IS_ATOM(ClassName->Buffer))
1469       {
1470          ERR("Class 0x%p not found\n", ClassName->Buffer);
1471       }
1472       else
1473       {
1474          ERR("Class \"%wZ\" not found\n", ClassName);
1475       }
1476 
1477       return NULL;
1478    }
1479 
1480    TRACE("Referencing Class 0x%p with atom 0x%x\n", Class, ClassAtom);
1481    Class = IntReferenceClass(Class,
1482                              ClassLink,
1483                              pti->rpdesk);
1484    if (Class == NULL)
1485    {
1486        ERR("Failed to reference window class!\n");
1487        return NULL;
1488    }
1489 
1490    return Class;
1491 }
1492 
1493 RTL_ATOM
1494 UserRegisterClass(IN CONST WNDCLASSEXW* lpwcx,
1495                   IN PUNICODE_STRING ClassName,
1496                   IN PUNICODE_STRING ClassVersion,
1497                   IN PUNICODE_STRING MenuName,
1498                   IN DWORD fnID,
1499                   IN DWORD dwFlags)
1500 {
1501     PTHREADINFO pti;
1502     PPROCESSINFO pi;
1503     PCLS Class;
1504     RTL_ATOM ClassAtom;
1505     RTL_ATOM Ret = (RTL_ATOM)0;
1506 
1507     /* NOTE: Accessing the buffers in ClassName and MenuName may raise exceptions! */
1508 
1509     pti = GetW32ThreadInfo();
1510 
1511     pi = pti->ppi;
1512 
1513     // Need only to test for two conditions not four....... Fix more whine tests....
1514     if ( IntGetAtomFromStringOrAtom( ClassVersion, &ClassAtom) &&
1515                                      ClassAtom != (RTL_ATOM)0 &&
1516                                     !(dwFlags & CSF_SERVERSIDEPROC) ) // Bypass Server Sides
1517     {
1518        Class = IntFindClass( ClassAtom,
1519                              lpwcx->hInstance,
1520                             &pi->pclsPrivateList,
1521                              NULL);
1522 
1523        if (Class != NULL && !Class->Global)
1524        {
1525           // Local class already exists
1526           TRACE("Local Class 0x%x does already exist!\n", ClassAtom);
1527           EngSetLastError(ERROR_CLASS_ALREADY_EXISTS);
1528           return (RTL_ATOM)0;
1529        }
1530 
1531        if (lpwcx->style & CS_GLOBALCLASS)
1532        {
1533           Class = IntFindClass( ClassAtom,
1534                                 NULL,
1535                                &pi->pclsPublicList,
1536                                 NULL);
1537 
1538           if (Class != NULL && Class->Global)
1539           {
1540              TRACE("Global Class 0x%x does already exist!\n", ClassAtom);
1541              EngSetLastError(ERROR_CLASS_ALREADY_EXISTS);
1542              return (RTL_ATOM)0;
1543           }
1544        }
1545     }
1546 
1547     Class = IntCreateClass(lpwcx,
1548                            ClassName,
1549                            ClassVersion,
1550                            MenuName,
1551                            fnID,
1552                            dwFlags,
1553                            pti->rpdesk,
1554                            pi);
1555 
1556     if (Class != NULL)
1557     {
1558         PCLS *List;
1559 
1560         /* Register the class */
1561         if (Class->Global)
1562             List = &pi->pclsPublicList;
1563         else
1564             List = &pi->pclsPrivateList;
1565 
1566         Class->pclsNext = *List;
1567         (void)InterlockedExchangePointer((PVOID*)List,
1568                                          Class);
1569 
1570         Ret = Class->atomNVClassName;
1571     }
1572     else
1573     {
1574        ERR("UserRegisterClass: Yes, that is right, you have no Class!\n");
1575     }
1576 
1577     return Ret;
1578 }
1579 
1580 BOOL
1581 UserUnregisterClass(IN PUNICODE_STRING ClassName,
1582                     IN HINSTANCE hInstance,
1583                     OUT PCLSMENUNAME pClassMenuName)
1584 {
1585     PCLS *Link;
1586     PPROCESSINFO pi;
1587     RTL_ATOM ClassAtom;
1588     PCLS Class;
1589 
1590     pi = GetW32ProcessInfo();
1591 
1592     TRACE("UserUnregisterClass(%wZ, 0x%p)\n", ClassName, hInstance);
1593 
1594     /* NOTE: Accessing the buffer in ClassName may raise an exception! */
1595     ClassAtom = IntGetClassAtom(ClassName,
1596                                 hInstance,
1597                                 pi,
1598                                 &Class,
1599                                 &Link);
1600     if (ClassAtom == (RTL_ATOM)0)
1601     {
1602         EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST);
1603         TRACE("UserUnregisterClass: No Class found.\n");
1604         return FALSE;
1605     }
1606 
1607     ASSERT(Class != NULL);
1608 
1609     if (Class->cWndReferenceCount != 0 ||
1610         Class->pclsClone != NULL)
1611     {
1612         TRACE("UserUnregisterClass: Class has a Window. Ct %u : Clone 0x%p\n", Class->cWndReferenceCount, Class->pclsClone);
1613         EngSetLastError(ERROR_CLASS_HAS_WINDOWS);
1614         return FALSE;
1615     }
1616 
1617     /* Must be a base class! */
1618     ASSERT(Class->pclsBase == Class);
1619 
1620     /* Unlink the class */
1621     *Link = Class->pclsNext;
1622 
1623     if (NT_SUCCESS(IntDeregisterClassAtom(Class->atomClassName)))
1624     {
1625         TRACE("Class 0x%p\n", Class);
1626         TRACE("UserUnregisterClass: Good Exit!\n");
1627         Class->atomClassName = 0; // Don't let it linger...
1628         /* Finally free the resources */
1629         IntDestroyClass(Class);
1630         return TRUE;
1631     }
1632     ERR("UserUnregisterClass: Can not deregister Class Atom.\n");
1633     return FALSE;
1634 }
1635 
1636 INT
1637 UserGetClassName(IN PCLS Class,
1638                  IN OUT PUNICODE_STRING ClassName,
1639                  IN RTL_ATOM Atom,
1640                  IN BOOL Ansi)
1641 {
1642     NTSTATUS Status = STATUS_SUCCESS;
1643     WCHAR szStaticTemp[32];
1644     PWSTR szTemp = NULL;
1645     ULONG BufLen = sizeof(szStaticTemp);
1646     INT Ret = 0;
1647 
1648     /* Note: Accessing the buffer in ClassName may raise an exception! */
1649 
1650     _SEH2_TRY
1651     {
1652         if (Ansi)
1653         {
1654             PANSI_STRING AnsiClassName = (PANSI_STRING)ClassName;
1655             UNICODE_STRING UnicodeClassName;
1656 
1657             /* Limit the size of the static buffer on the stack to the
1658                size of the buffer provided by the caller */
1659             if (BufLen / sizeof(WCHAR) > AnsiClassName->MaximumLength)
1660             {
1661                 BufLen = AnsiClassName->MaximumLength * sizeof(WCHAR);
1662             }
1663 
1664             /* Find out how big the buffer needs to be */
1665             Status = RtlQueryAtomInAtomTable(gAtomTable,
1666                                              Class->atomClassName,
1667                                              NULL,
1668                                              NULL,
1669                                              szStaticTemp,
1670                                              &BufLen);
1671             if (Status == STATUS_BUFFER_TOO_SMALL)
1672             {
1673                 if (BufLen / sizeof(WCHAR) > AnsiClassName->MaximumLength)
1674                 {
1675                     /* The buffer required exceeds the ansi buffer provided,
1676                        pretend like we're using the ansi buffer and limit the
1677                        size to the buffer size provided */
1678                     BufLen = AnsiClassName->MaximumLength * sizeof(WCHAR);
1679                 }
1680 
1681                 /* Allocate a temporary buffer that can hold the unicode class name */
1682                 szTemp = ExAllocatePoolWithTag(PagedPool,
1683                                                BufLen,
1684                                                USERTAG_CLASS);
1685                 if (szTemp == NULL)
1686                 {
1687                     EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1688                     _SEH2_LEAVE;
1689                 }
1690 
1691                 /* Query the class name */
1692                 Status = RtlQueryAtomInAtomTable(gAtomTable,
1693                                                  Atom ? Atom : Class->atomNVClassName,
1694                                                  NULL,
1695                                                  NULL,
1696                                                  szTemp,
1697                                                  &BufLen);
1698             }
1699             else
1700                 szTemp = szStaticTemp;
1701 
1702             if (NT_SUCCESS(Status))
1703             {
1704                 /* Convert the atom name to ansi */
1705 
1706                 RtlInitUnicodeString(&UnicodeClassName,
1707                                      szTemp);
1708 
1709                 Status = RtlUnicodeStringToAnsiString(AnsiClassName,
1710                                                       &UnicodeClassName,
1711                                                       FALSE);
1712                 if (!NT_SUCCESS(Status))
1713                 {
1714                     SetLastNtError(Status);
1715                     _SEH2_LEAVE;
1716                 }
1717             }
1718 
1719             Ret = BufLen / sizeof(WCHAR);
1720         }
1721         else /* !ANSI */
1722         {
1723             BufLen = ClassName->MaximumLength;
1724 
1725             /* Query the atom name */
1726             Status = RtlQueryAtomInAtomTable(gAtomTable,
1727                                              Atom ? Atom : Class->atomNVClassName,
1728                                              NULL,
1729                                              NULL,
1730                                              ClassName->Buffer,
1731                                              &BufLen);
1732 
1733             if (!NT_SUCCESS(Status))
1734             {
1735                 SetLastNtError(Status);
1736                 _SEH2_LEAVE;
1737             }
1738 
1739             Ret = BufLen / sizeof(WCHAR);
1740         }
1741     }
1742     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1743     {
1744         SetLastNtError(_SEH2_GetExceptionCode());
1745     }
1746     _SEH2_END;
1747 
1748     if (Ansi && szTemp != NULL && szTemp != szStaticTemp)
1749     {
1750         ExFreePoolWithTag(szTemp, USERTAG_CLASS);
1751     }
1752 
1753     return Ret;
1754 }
1755 
1756 static BOOL
1757 IntSetClassMenuName(IN PCLS Class,
1758                     IN PUNICODE_STRING MenuName)
1759 {
1760     BOOL Ret = FALSE;
1761 
1762     /* Change the base class first */
1763     Class = Class->pclsBase;
1764 
1765     if (MenuName->Length != 0)
1766     {
1767         ANSI_STRING AnsiString;
1768         PWSTR strBufW;
1769 
1770         AnsiString.MaximumLength = (USHORT)RtlUnicodeStringToAnsiSize(MenuName);
1771 
1772         strBufW = UserHeapAlloc(MenuName->Length + sizeof(UNICODE_NULL) +
1773                                 AnsiString.MaximumLength);
1774         if (strBufW != NULL)
1775         {
1776             _SEH2_TRY
1777             {
1778                 NTSTATUS Status;
1779 
1780                 /* Copy the unicode string */
1781                 RtlCopyMemory(strBufW,
1782                               MenuName->Buffer,
1783                               MenuName->Length);
1784                 strBufW[MenuName->Length / sizeof(WCHAR)] = UNICODE_NULL;
1785 
1786                 /* Create an ANSI copy of the string */
1787                 AnsiString.Buffer = (PSTR)(strBufW + (MenuName->Length / sizeof(WCHAR)) + 1);
1788                 Status = RtlUnicodeStringToAnsiString(&AnsiString,
1789                                                       MenuName,
1790                                                       FALSE);
1791                 if (!NT_SUCCESS(Status))
1792                 {
1793                     SetLastNtError(Status);
1794                     _SEH2_LEAVE;
1795                 }
1796 
1797                 Ret = TRUE;
1798             }
1799             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1800             {
1801                 SetLastNtError(_SEH2_GetExceptionCode());
1802             }
1803             _SEH2_END;
1804 
1805             if (Ret)
1806             {
1807                 /* Update the base class */
1808                 IntFreeClassMenuName(Class);
1809                 Class->lpszClientUnicodeMenuName = strBufW;
1810                 Class->lpszClientAnsiMenuName = AnsiString.Buffer;
1811                 Class->MenuNameIsString = TRUE;
1812 
1813                 /* Update the clones */
1814                 Class = Class->pclsClone;
1815                 while (Class != NULL)
1816                 {
1817                     Class->lpszClientUnicodeMenuName = strBufW;
1818                     Class->lpszClientAnsiMenuName = AnsiString.Buffer;
1819                     Class->MenuNameIsString = TRUE;
1820 
1821                     Class = Class->pclsNext;
1822                 }
1823             }
1824             else
1825             {
1826                 ERR("Failed to copy class menu name!\n");
1827                 UserHeapFree(strBufW);
1828             }
1829         }
1830         else
1831             EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1832     }
1833     else
1834     {
1835         ASSERT(IS_INTRESOURCE(MenuName->Buffer));
1836 
1837         /* Update the base class */
1838         IntFreeClassMenuName(Class);
1839         Class->lpszClientUnicodeMenuName = MenuName->Buffer;
1840         Class->lpszClientAnsiMenuName = (PSTR)MenuName->Buffer;
1841         Class->MenuNameIsString = FALSE;
1842 
1843         /* Update the clones */
1844         Class = Class->pclsClone;
1845         while (Class != NULL)
1846         {
1847             Class->lpszClientUnicodeMenuName = MenuName->Buffer;
1848             Class->lpszClientAnsiMenuName = (PSTR)MenuName->Buffer;
1849             Class->MenuNameIsString = FALSE;
1850 
1851             Class = Class->pclsNext;
1852         }
1853 
1854         Ret = TRUE;
1855     }
1856 
1857     return Ret;
1858 }
1859 
1860 ULONG_PTR
1861 UserSetClassLongPtr(IN PCLS Class,
1862                     IN INT Index,
1863                     IN ULONG_PTR NewLong,
1864                     IN BOOL Ansi)
1865 {
1866     ULONG_PTR Ret = 0;
1867 
1868     /* NOTE: For GCLP_MENUNAME and GCW_ATOM this function may raise an exception! */
1869 
1870     /* Change the information in the base class first, then update the clones */
1871     Class = Class->pclsBase;
1872 
1873     if (Index >= 0)
1874     {
1875         PULONG_PTR Data;
1876 
1877         TRACE("SetClassLong(%d, %x)\n", Index, NewLong);
1878 
1879         if (((ULONG)Index + sizeof(ULONG_PTR)) < (ULONG)Index ||
1880             ((ULONG)Index + sizeof(ULONG_PTR)) > (ULONG)Class->cbclsExtra)
1881         {
1882             EngSetLastError(ERROR_INVALID_PARAMETER);
1883             return 0;
1884         }
1885 
1886         Data = (PULONG_PTR)((ULONG_PTR)(Class + 1) + Index);
1887 
1888         /* FIXME: Data might be a unaligned pointer! Might be a problem on
1889                   certain architectures, maybe using RtlCopyMemory is a
1890                   better choice for those architectures! */
1891         Ret = *Data;
1892         *Data = NewLong;
1893 
1894         /* Update the clones */
1895         Class = Class->pclsClone;
1896         while (Class != NULL)
1897         {
1898             *(PULONG_PTR)((ULONG_PTR)(Class + 1) + Index) = NewLong;
1899             Class = Class->pclsNext;
1900         }
1901 
1902         return Ret;
1903     }
1904 
1905     switch (Index)
1906     {
1907         case GCL_CBWNDEXTRA:
1908             Ret = (ULONG_PTR)Class->cbwndExtra;
1909             Class->cbwndExtra = (INT)NewLong;
1910 
1911             /* Update the clones */
1912             Class = Class->pclsClone;
1913             while (Class != NULL)
1914             {
1915                 Class->cbwndExtra = (INT)NewLong;
1916                 Class = Class->pclsNext;
1917             }
1918 
1919             break;
1920 
1921         case GCL_CBCLSEXTRA:
1922             EngSetLastError(ERROR_INVALID_PARAMETER);
1923             break;
1924 
1925         case GCLP_HBRBACKGROUND:
1926             Ret = (ULONG_PTR)Class->hbrBackground;
1927             Class->hbrBackground = (HBRUSH)NewLong;
1928 
1929             /* Update the clones */
1930             Class = Class->pclsClone;
1931             while (Class != NULL)
1932             {
1933                 Class->hbrBackground = (HBRUSH)NewLong;
1934                 Class = Class->pclsNext;
1935             }
1936             break;
1937 
1938         case GCLP_HCURSOR:
1939         {
1940             PCURICON_OBJECT NewCursor = NULL;
1941 
1942             if (NewLong)
1943             {
1944                 NewCursor = UserGetCurIconObject((HCURSOR)NewLong);
1945                 if (!NewCursor)
1946                 {
1947                     EngSetLastError(ERROR_INVALID_CURSOR_HANDLE);
1948                     return 0;
1949                 }
1950             }
1951 
1952             if (Class->spcur)
1953             {
1954                 Ret = (ULONG_PTR)UserHMGetHandle(Class->spcur);
1955                 UserDereferenceObject(Class->spcur);
1956             }
1957             else
1958             {
1959                 Ret = 0;
1960             }
1961 
1962             if (Ret == NewLong)
1963             {
1964                 /* It's a nop */
1965                 return Ret;
1966             }
1967 
1968             Class->spcur = NewCursor;
1969 
1970             /* Update the clones */
1971             Class = Class->pclsClone;
1972             while (Class != NULL)
1973             {
1974                 if (Class->spcur)
1975                     UserDereferenceObject(Class->spcur);
1976                 if (NewCursor)
1977                     UserReferenceObject(NewCursor);
1978                 Class->spcur = NewCursor;
1979                 Class = Class->pclsNext;
1980             }
1981 
1982             break;
1983         }
1984 
1985         // MSDN:
1986         // hIconSm, A handle to a small icon that is associated with the window class.
1987         // If this member is NULL, the system searches the icon resource specified by
1988         // the hIcon member for an icon of the appropriate size to use as the small icon.
1989         //
1990         case GCLP_HICON:
1991         {
1992             PCURICON_OBJECT NewIcon = NULL;
1993             PCURICON_OBJECT NewSmallIcon = NULL;
1994 
1995             if (NewLong)
1996             {
1997                 NewIcon = UserGetCurIconObject((HCURSOR)NewLong);
1998                 if (!NewIcon)
1999                 {
2000                     EngSetLastError(ERROR_INVALID_ICON_HANDLE);
2001                     return 0;
2002                 }
2003             }
2004 
2005             if (Class->spicn)
2006             {
2007                 Ret = (ULONG_PTR)UserHMGetHandle(Class->spicn);
2008                 UserDereferenceObject(Class->spicn);
2009             }
2010             else
2011             {
2012                 Ret = 0;
2013             }
2014 
2015             if (Ret == NewLong)
2016             {
2017                 /* It's a nop */
2018                 return Ret;
2019             }
2020 
2021             if (Ret && (Class->CSF_flags & CSF_CACHEDSMICON))
2022             {
2023                 /* We will change the small icon */
2024                 UserDereferenceObject(Class->spicnSm);
2025                 IntDestroyCurIconObject(Class->spicnSm);
2026                 Class->spicnSm = NULL;
2027                 Class->CSF_flags &= ~CSF_CACHEDSMICON;
2028             }
2029 
2030             if (NewLong && !Class->spicnSm)
2031             {
2032                 /* Create the new small icon from the new large(?) one */
2033                 HICON SmallIconHandle = NULL;
2034                 if((NewIcon->CURSORF_flags & (CURSORF_LRSHARED | CURSORF_FROMRESOURCE))
2035                         == (CURSORF_LRSHARED | CURSORF_FROMRESOURCE))
2036                 {
2037                     SmallIconHandle = co_IntCopyImage(
2038                         (HICON)NewLong,
2039                         IMAGE_ICON,
2040                         UserGetSystemMetrics( SM_CXSMICON ),
2041                         UserGetSystemMetrics( SM_CYSMICON ),
2042                         LR_COPYFROMRESOURCE);
2043                 }
2044                 if (!SmallIconHandle)
2045                 {
2046                     /* Retry without copying from resource */
2047                     SmallIconHandle = co_IntCopyImage(
2048                         (HICON)NewLong,
2049                         IMAGE_ICON,
2050                         UserGetSystemMetrics( SM_CXSMICON ),
2051                         UserGetSystemMetrics( SM_CYSMICON ),
2052                         0);
2053                 }
2054                 if (SmallIconHandle)
2055                 {
2056                     /* So use it */
2057                     NewSmallIcon = Class->spicnSm = UserGetCurIconObject(SmallIconHandle);
2058                     Class->CSF_flags |= CSF_CACHEDSMICON;
2059                 }
2060             }
2061 
2062             Class->spicn = NewIcon;
2063 
2064             /* Update the clones */
2065             Class = Class->pclsClone;
2066             while (Class != NULL)
2067             {
2068                 if (Class->spicn)
2069                     UserDereferenceObject(Class->spicn);
2070                 if (NewIcon)
2071                     UserReferenceObject(NewIcon);
2072                 Class->spicn = NewIcon;
2073                 if (NewSmallIcon)
2074                 {
2075                     if (Class->spicnSm)
2076                         UserDereferenceObject(Class->spicnSm);
2077                     UserReferenceObject(NewSmallIcon);
2078                     Class->spicnSm = NewSmallIcon;
2079                     Class->CSF_flags |= CSF_CACHEDSMICON;
2080                 }
2081                 Class = Class->pclsNext;
2082             }
2083             break;
2084         }
2085 
2086         case GCLP_HICONSM:
2087         {
2088             PCURICON_OBJECT NewSmallIcon = NULL;
2089             BOOLEAN NewIconFromCache = FALSE;
2090 
2091             if (NewLong)
2092             {
2093                 NewSmallIcon = UserGetCurIconObject((HCURSOR)NewLong);
2094                 if (!NewSmallIcon)
2095                 {
2096                     EngSetLastError(ERROR_INVALID_ICON_HANDLE);
2097                     return 0;
2098                 }
2099             }
2100             else
2101             {
2102                 /* Create the new small icon from the large one */
2103                 HICON SmallIconHandle = NULL;
2104                 if((Class->spicn->CURSORF_flags & (CURSORF_LRSHARED | CURSORF_FROMRESOURCE))
2105                         == (CURSORF_LRSHARED | CURSORF_FROMRESOURCE))
2106                 {
2107                     SmallIconHandle = co_IntCopyImage(
2108                         UserHMGetHandle(Class->spicn),
2109                         IMAGE_ICON,
2110                         UserGetSystemMetrics( SM_CXSMICON ),
2111                         UserGetSystemMetrics( SM_CYSMICON ),
2112                         LR_COPYFROMRESOURCE);
2113                 }
2114                 if (!SmallIconHandle)
2115                 {
2116                     /* Retry without copying from resource */
2117                     SmallIconHandle = co_IntCopyImage(
2118                         UserHMGetHandle(Class->spicn),
2119                         IMAGE_ICON,
2120                         UserGetSystemMetrics( SM_CXSMICON ),
2121                         UserGetSystemMetrics( SM_CYSMICON ),
2122                         0);
2123                 }
2124                 if (SmallIconHandle)
2125                 {
2126                     /* So use it */
2127                     NewSmallIcon = UserGetCurIconObject(SmallIconHandle);
2128                     NewIconFromCache = TRUE;
2129                 }
2130                 else
2131                 {
2132                     ERR("Failed getting a small icon for the class.\n");
2133                 }
2134             }
2135 
2136             if (Class->spicnSm)
2137             {
2138                 if (Class->CSF_flags & CSF_CACHEDSMICON)
2139                 {
2140                     /* We must destroy the icon if we own it */
2141                     IntDestroyCurIconObject(Class->spicnSm);
2142                     Ret = 0;
2143                 }
2144                 else
2145                 {
2146                     Ret = (ULONG_PTR)UserHMGetHandle(Class->spicnSm);
2147                 }
2148                 UserDereferenceObject(Class->spicnSm);
2149             }
2150             else
2151             {
2152                 Ret = 0;
2153             }
2154 
2155             if (NewIconFromCache)
2156                 Class->CSF_flags |= CSF_CACHEDSMICON;
2157             else
2158                 Class->CSF_flags &= ~CSF_CACHEDSMICON;
2159             Class->spicnSm = NewSmallIcon;
2160 
2161             /* Update the clones */
2162             Class = Class->pclsClone;
2163             while (Class != NULL)
2164             {
2165                 if (Class->spicnSm)
2166                     UserDereferenceObject(Class->spicnSm);
2167                 if (NewSmallIcon)
2168                     UserReferenceObject(NewSmallIcon);
2169                 if (NewIconFromCache)
2170                     Class->CSF_flags |= CSF_CACHEDSMICON;
2171                 else
2172                     Class->CSF_flags &= ~CSF_CACHEDSMICON;
2173                 Class->spicnSm = NewSmallIcon;
2174                 Class = Class->pclsNext;
2175             }
2176         }
2177         break;
2178 
2179         case GCLP_HMODULE:
2180             Ret = (ULONG_PTR)Class->hModule;
2181             Class->hModule = (HINSTANCE)NewLong;
2182 
2183            /* Update the clones */
2184             Class = Class->pclsClone;
2185             while (Class != NULL)
2186             {
2187                 Class->hModule = (HINSTANCE)NewLong;
2188                 Class = Class->pclsNext;
2189             }
2190             break;
2191 
2192         case GCLP_MENUNAME:
2193         {
2194             PUNICODE_STRING Value = (PUNICODE_STRING)NewLong;
2195 
2196             if (!IntSetClassMenuName(Class,
2197                                      Value))
2198             {
2199                 ERR("Setting the class menu name failed!\n");
2200             }
2201 
2202             /* FIXME: Really return NULL? Wine does so... */
2203             break;
2204         }
2205 
2206         case GCL_STYLE:
2207             Ret = (ULONG_PTR)Class->style;
2208             Class->style = (UINT)NewLong;
2209 
2210             /* FIXME: What if the CS_GLOBALCLASS style is changed? should we
2211                       move the class to the appropriate list? For now, we save
2212                       the original value in Class->Global, so we can always
2213                       locate the appropriate list */
2214 
2215            /* Update the clones */
2216             Class = Class->pclsClone;
2217             while (Class != NULL)
2218             {
2219                 Class->style = (UINT)NewLong;
2220                 Class = Class->pclsNext;
2221             }
2222             break;
2223 
2224         case GCLP_WNDPROC:
2225             Ret = (ULONG_PTR)IntSetClassWndProc(Class,
2226                                                 (WNDPROC)NewLong,
2227                                                 Ansi);
2228             break;
2229 
2230         case GCW_ATOM:
2231         {
2232             PUNICODE_STRING Value = (PUNICODE_STRING)NewLong;
2233 
2234             Ret = (ULONG_PTR)Class->atomNVClassName;
2235             if (!IntSetClassAtom(Class,
2236                                  Value))
2237             {
2238                 Ret = 0;
2239             }
2240             break;
2241         }
2242 
2243         default:
2244             EngSetLastError(ERROR_INVALID_INDEX);
2245             break;
2246     }
2247 
2248     return Ret;
2249 }
2250 
2251 static BOOL
2252 UserGetClassInfo(IN PCLS Class,
2253                  OUT PWNDCLASSEXW lpwcx,
2254                  IN BOOL Ansi,
2255                  HINSTANCE hInstance)
2256 {
2257     if (!Class) return FALSE;
2258 
2259     lpwcx->style = Class->style;
2260 
2261     // If fnId is set, clear the global bit. See wine class test check_style.
2262     if (Class->fnid)
2263        lpwcx->style &= ~CS_GLOBALCLASS;
2264 
2265     lpwcx->lpfnWndProc = IntGetClassWndProc(Class, Ansi);
2266 
2267     lpwcx->cbClsExtra = Class->cbclsExtra;
2268     lpwcx->cbWndExtra = Class->cbwndExtra;
2269     lpwcx->hIcon = Class->spicn ? UserHMGetHandle(Class->spicn) : NULL;
2270     lpwcx->hCursor = Class->spcur ? UserHMGetHandle(Class->spcur) : NULL;
2271     lpwcx->hIconSm = Class->spicnSm ? UserHMGetHandle(Class->spicnSm) : NULL;
2272     lpwcx->hbrBackground = Class->hbrBackground;
2273 
2274     /* Copy non-string to user first. */
2275     if (Ansi)
2276        ((PWNDCLASSEXA)lpwcx)->lpszMenuName = Class->lpszClientAnsiMenuName;
2277     else
2278        lpwcx->lpszMenuName = Class->lpszClientUnicodeMenuName;
2279 /*
2280  *  FIXME: CLSMENUNAME has the answers! Copy the already made buffers from there!
2281  *  Cls: lpszMenuName and lpszAnsiClassName should be used by kernel space.
2282  *  lpszClientXxxMenuName should already be mapped to user space.
2283  */
2284     /* Copy string ptr to user. */
2285     if ( Class->lpszClientUnicodeMenuName != NULL &&
2286          Class->MenuNameIsString)
2287     {
2288        lpwcx->lpszMenuName = UserHeapAddressToUser(Ansi ?
2289                                  (PVOID)Class->lpszClientAnsiMenuName :
2290                                  (PVOID)Class->lpszClientUnicodeMenuName);
2291     }
2292 
2293     if (hInstance == hModClient)
2294         lpwcx->hInstance = NULL;
2295     else
2296         lpwcx->hInstance = hInstance;
2297 
2298     /* FIXME: Return the string? Okay! This is performed in User32! */
2299     //lpwcx->lpszClassName = (LPCWSTR)((ULONG_PTR)Class->atomClassName);
2300 
2301     return TRUE;
2302 }
2303 
2304 //
2305 // Register System Classes....
2306 //
2307 BOOL
2308 FASTCALL
2309 UserRegisterSystemClasses(VOID)
2310 {
2311     UINT i;
2312     UNICODE_STRING ClassName, MenuName;
2313     PPROCESSINFO ppi = GetW32ProcessInfo();
2314     WNDCLASSEXW wc;
2315     PCLS Class;
2316     BOOL Ret = TRUE;
2317     HBRUSH hBrush;
2318     DWORD Flags = 0;
2319 
2320     if (ppi->W32PF_flags & W32PF_CLASSESREGISTERED)
2321        return TRUE;
2322 
2323     if ( hModClient == NULL)
2324        return FALSE;
2325 
2326     RtlZeroMemory(&ClassName, sizeof(ClassName));
2327     RtlZeroMemory(&MenuName, sizeof(MenuName));
2328 
2329     for (i = 0; i != ARRAYSIZE(DefaultServerClasses); i++)
2330     {
2331         if (!IS_ATOM(DefaultServerClasses[i].ClassName))
2332         {
2333            RtlInitUnicodeString(&ClassName, DefaultServerClasses[i].ClassName);
2334         }
2335         else
2336         {
2337            ClassName.Buffer = DefaultServerClasses[i].ClassName;
2338            ClassName.Length = 0;
2339            ClassName.MaximumLength = 0;
2340         }
2341 
2342         wc.cbSize = sizeof(wc);
2343         wc.style = DefaultServerClasses[i].Style;
2344 
2345         Flags |= CSF_SERVERSIDEPROC;
2346 
2347         if (DefaultServerClasses[i].ProcW)
2348         {
2349            wc.lpfnWndProc = DefaultServerClasses[i].ProcW;
2350            wc.hInstance = hModuleWin;
2351         }
2352         else
2353         {
2354            wc.lpfnWndProc = GETPFNSERVER(DefaultServerClasses[i].fiId);
2355            wc.hInstance = hModClient;
2356         }
2357 
2358         wc.cbClsExtra = 0;
2359         wc.cbWndExtra = DefaultServerClasses[i].ExtraBytes;
2360         wc.hIcon = NULL;
2361 
2362         //// System Cursors should be initilized!!!
2363         wc.hCursor = NULL;
2364         if (DefaultServerClasses[i].hCursor == (HICON)OCR_NORMAL)
2365         {
2366             if (SYSTEMCUR(ARROW) == NULL)
2367             {
2368                 ERR("SYSTEMCUR(ARROW) == NULL, should not happen!!\n");
2369             }
2370             else
2371             {
2372                 wc.hCursor = UserHMGetHandle(SYSTEMCUR(ARROW));
2373             }
2374         }
2375 
2376         hBrush = DefaultServerClasses[i].hBrush;
2377         if (hBrush <= (HBRUSH)COLOR_MENUBAR)
2378         {
2379             hBrush = IntGetSysColorBrush(HandleToUlong(hBrush));
2380         }
2381         wc.hbrBackground = hBrush;
2382         wc.lpszMenuName = NULL;
2383         wc.lpszClassName = ClassName.Buffer;
2384         wc.hIconSm = NULL;
2385 
2386         Class = IntCreateClass( &wc,
2387                                 &ClassName,
2388                                 &ClassName,
2389                                 &MenuName,
2390                                  DefaultServerClasses[i].fiId,
2391                                  Flags,
2392                                  NULL,
2393                                  ppi);
2394         if (Class != NULL)
2395         {
2396             Class->pclsNext = ppi->pclsPublicList;
2397             (void)InterlockedExchangePointer((PVOID*)&ppi->pclsPublicList,
2398                                              Class);
2399 
2400             ppi->dwRegisteredClasses |= ICLASS_TO_MASK(DefaultServerClasses[i].iCls);
2401         }
2402         else
2403         {
2404             ERR("!!! Registering system class failed!\n");
2405             Ret = FALSE;
2406         }
2407     }
2408     if (Ret) ppi->W32PF_flags |= W32PF_CLASSESREGISTERED;
2409     return Ret;
2410 }
2411 
2412 /* SYSCALLS *****************************************************************/
2413 
2414 RTL_ATOM
2415 APIENTRY
2416 NtUserRegisterClassExWOW(
2417     WNDCLASSEXW* lpwcx,
2418     PUNICODE_STRING ClassName,
2419     PUNICODE_STRING ClsVersion,
2420     PCLSMENUNAME pClassMenuName,
2421     DWORD fnID,
2422     DWORD Flags,
2423     LPDWORD pWow)
2424 /*
2425  * FUNCTION:
2426  *   Registers a new class with the window manager
2427  * ARGUMENTS:
2428  *   lpwcx          = Win32 extended window class structure
2429  *   bUnicodeClass = Whether to send ANSI or unicode strings
2430  *                   to window procedures
2431  * RETURNS:
2432  *   Atom identifying the new class
2433  */
2434 {
2435     WNDCLASSEXW CapturedClassInfo = {0};
2436     UNICODE_STRING CapturedName = {0}, CapturedMenuName = {0}, CapturedVersion = {0};
2437     RTL_ATOM Ret = (RTL_ATOM)0;
2438     PPROCESSINFO ppi = GetW32ProcessInfo();
2439     BOOL Exception = FALSE;
2440 
2441     if (Flags & ~(CSF_ANSIPROC))
2442     {
2443         ERR("NtUserRegisterClassExWOW Bad Flags!\n");
2444         EngSetLastError(ERROR_INVALID_FLAGS);
2445         return Ret;
2446     }
2447 
2448     UserEnterExclusive();
2449 
2450     TRACE("NtUserRegisterClassExWOW ClsN %wZ\n",ClassName);
2451 
2452     if ( !(ppi->W32PF_flags & W32PF_CLASSESREGISTERED ))
2453     {
2454        UserRegisterSystemClasses();
2455     }
2456 
2457     _SEH2_TRY
2458     {
2459         /* Probe the parameters and basic parameter checks */
2460         if (ProbeForReadUint(&lpwcx->cbSize) != sizeof(WNDCLASSEXW))
2461         {
2462             ERR("NtUserRegisterClassExWOW Wrong cbSize!\n");
2463             goto InvalidParameter;
2464         }
2465 
2466         ProbeForRead(lpwcx,
2467                      sizeof(WNDCLASSEXW),
2468                      sizeof(ULONG));
2469         RtlCopyMemory(&CapturedClassInfo,
2470                       lpwcx,
2471                       sizeof(WNDCLASSEXW));
2472 
2473         CapturedName = ProbeForReadUnicodeString(ClassName);
2474         CapturedVersion = ProbeForReadUnicodeString(ClsVersion);
2475 
2476         ProbeForRead(pClassMenuName,
2477                      sizeof(CLSMENUNAME),
2478                      1);
2479 
2480         CapturedMenuName = ProbeForReadUnicodeString(pClassMenuName->pusMenuName);
2481 
2482         if ( (CapturedName.Length & 1) ||
2483              (CapturedMenuName.Length & 1) ||
2484              (CapturedClassInfo.cbClsExtra < 0) ||
2485              ((CapturedClassInfo.cbClsExtra + CapturedName.Length +
2486               CapturedMenuName.Length +  sizeof(CLS))
2487                 < (ULONG)CapturedClassInfo.cbClsExtra) ||
2488              (CapturedClassInfo.cbWndExtra < 0) ||
2489              (CapturedClassInfo.hInstance == NULL) )
2490         {
2491             ERR("NtUserRegisterClassExWOW Invalid Parameter Error!\n");
2492             goto InvalidParameter;
2493         }
2494 
2495         if (CapturedName.Length != 0)
2496         {
2497             ProbeForRead(CapturedName.Buffer,
2498                          CapturedName.Length,
2499                          sizeof(WCHAR));
2500         }
2501         else
2502         {
2503             if (!IS_ATOM(CapturedName.Buffer))
2504             {
2505                 ERR("NtUserRegisterClassExWOW ClassName Error!\n");
2506                 goto InvalidParameter;
2507             }
2508         }
2509 
2510         if (CapturedVersion.Length != 0)
2511         {
2512             ProbeForRead(CapturedVersion.Buffer,
2513                          CapturedVersion.Length,
2514                          sizeof(WCHAR));
2515         }
2516         else
2517         {
2518             if (!IS_ATOM(CapturedVersion.Buffer))
2519             {
2520                 ERR("NtUserRegisterClassExWOW ClassName Error!\n");
2521                 goto InvalidParameter;
2522             }
2523         }
2524 
2525         if (CapturedMenuName.Length != 0)
2526         {
2527             ProbeForRead(CapturedMenuName.Buffer,
2528                          CapturedMenuName.Length,
2529                          sizeof(WCHAR));
2530         }
2531         else if (CapturedMenuName.Buffer != NULL &&
2532                  !IS_INTRESOURCE(CapturedMenuName.Buffer))
2533         {
2534             ERR("NtUserRegisterClassExWOW MenuName Error!\n");
2535 InvalidParameter:
2536             EngSetLastError(ERROR_INVALID_PARAMETER);
2537             _SEH2_LEAVE;
2538         }
2539 
2540         if (IsCallProcHandle(lpwcx->lpfnWndProc))
2541         {  // Never seen this yet, but I'm sure it's a little haxxy trick!
2542            // If this pops up we know what todo!
2543            ERR("NtUserRegisterClassExWOW WndProc is CallProc!!\n");
2544         }
2545 
2546         TRACE("NtUserRegisterClassExWOW MnuN %wZ\n",&CapturedMenuName);
2547     }
2548     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2549     {
2550         ERR("NtUserRegisterClassExWOW Exception Error!\n");
2551         SetLastNtError(_SEH2_GetExceptionCode());
2552         Exception = TRUE;
2553     }
2554     _SEH2_END;
2555 
2556     if (!Exception)
2557     {
2558         /* Register the class */
2559         Ret = UserRegisterClass(&CapturedClassInfo,
2560                                 &CapturedName,
2561                                 &CapturedVersion,
2562                                 &CapturedMenuName,
2563                                 fnID,
2564                                 Flags);
2565     }
2566 
2567     if (!Ret)
2568     {
2569        TRACE("NtUserRegisterClassExWOW Null Return!\n");
2570     }
2571 
2572     UserLeave();
2573 
2574     return Ret;
2575 }
2576 
2577 ULONG_PTR APIENTRY
2578 NtUserSetClassLong(HWND hWnd,
2579                    INT Offset,
2580                    ULONG_PTR dwNewLong,
2581                    BOOL Ansi)
2582 {
2583     PPROCESSINFO pi;
2584     PWND Window;
2585     ULONG_PTR Ret = 0;
2586 
2587     UserEnterExclusive();
2588 
2589     pi = GetW32ProcessInfo();
2590 
2591     Window = UserGetWindowObject(hWnd);
2592     if (Window != NULL)
2593     {
2594         if (Window->head.pti->ppi != pi)
2595         {
2596             EngSetLastError(ERROR_ACCESS_DENIED);
2597             goto Cleanup;
2598         }
2599 
2600         _SEH2_TRY
2601         {
2602             UNICODE_STRING Value;
2603 
2604             /* Probe the parameters */
2605             if (Offset == GCW_ATOM || Offset == GCLP_MENUNAME)
2606             {
2607                 /* FIXME: Resource ID can be passed directly without UNICODE_STRING ? */
2608                 if (IS_ATOM(dwNewLong))
2609                 {
2610                     Value.MaximumLength = 0;
2611                     Value.Length = 0;
2612                     Value.Buffer = (PWSTR)dwNewLong;
2613                 }
2614                 else
2615                 {
2616                     Value = ProbeForReadUnicodeString((PUNICODE_STRING)dwNewLong);
2617                 }
2618 
2619                 if (Value.Length & 1)
2620                 {
2621                     goto InvalidParameter;
2622                 }
2623 
2624                 if (Value.Length != 0)
2625                 {
2626                     ProbeForRead(Value.Buffer,
2627                                  Value.Length,
2628                                  sizeof(WCHAR));
2629                 }
2630                 else
2631                 {
2632                     if (Offset == GCW_ATOM && !IS_ATOM(Value.Buffer))
2633                     {
2634                         goto InvalidParameter;
2635                     }
2636                     else if (Offset == GCLP_MENUNAME && !IS_INTRESOURCE(Value.Buffer))
2637                     {
2638 InvalidParameter:
2639                         EngSetLastError(ERROR_INVALID_PARAMETER);
2640                         _SEH2_LEAVE;
2641                     }
2642                 }
2643 
2644                 dwNewLong = (ULONG_PTR)&Value;
2645             }
2646 
2647             Ret = UserSetClassLongPtr(Window->pcls,
2648                                       Offset,
2649                                       dwNewLong,
2650                                       Ansi);
2651             switch(Offset)
2652             {
2653                case GCLP_HICONSM:
2654                case GCLP_HICON:
2655                {
2656                   if (Ret && Ret != dwNewLong)
2657                      UserPaintCaption(Window, DC_ICON);
2658                }
2659             }
2660         }
2661         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2662         {
2663             SetLastNtError(_SEH2_GetExceptionCode());
2664         }
2665         _SEH2_END;
2666     }
2667 
2668 Cleanup:
2669     UserLeave();
2670 
2671     return Ret;
2672 }
2673 
2674 WORD
2675 APIENTRY
2676 NtUserSetClassWord(
2677   HWND hWnd,
2678   INT nIndex,
2679   WORD wNewWord)
2680 {
2681 /*
2682  * NOTE: Obsoleted in 32-bit windows
2683  */
2684    return(0);
2685 }
2686 
2687 BOOL
2688 APIENTRY
2689 NtUserUnregisterClass(
2690     IN PUNICODE_STRING ClassNameOrAtom,
2691     IN HINSTANCE hInstance,
2692     OUT PCLSMENUNAME pClassMenuName)
2693 {
2694     UNICODE_STRING SafeClassName;
2695     NTSTATUS Status;
2696     BOOL Ret;
2697 
2698     Status = ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName, ClassNameOrAtom);
2699     if (!NT_SUCCESS(Status))
2700     {
2701         ERR("Error capturing the class name\n");
2702         SetLastNtError(Status);
2703         return FALSE;
2704     }
2705 
2706     UserEnterExclusive();
2707 
2708     /* Unregister the class */
2709     Ret = UserUnregisterClass(&SafeClassName, hInstance, NULL); // Null for now~
2710 
2711     UserLeave();
2712 
2713     if (SafeClassName.Buffer && !IS_ATOM(SafeClassName.Buffer))
2714         ExFreePoolWithTag(SafeClassName.Buffer, TAG_STRING);
2715 
2716     return Ret;
2717 }
2718 
2719 
2720 /* NOTE: For system classes hInstance is not NULL here, but User32Instance */
2721 BOOL
2722 APIENTRY
2723 NtUserGetClassInfo(
2724    HINSTANCE hInstance,
2725    PUNICODE_STRING ClassName,
2726    LPWNDCLASSEXW lpWndClassEx,
2727    LPWSTR *ppszMenuName,
2728    BOOL bAnsi)
2729 {
2730     UNICODE_STRING SafeClassName;
2731     WNDCLASSEXW Safewcexw;
2732     PCLS Class;
2733     RTL_ATOM ClassAtom = 0;
2734     PPROCESSINFO ppi;
2735     BOOL Ret = TRUE;
2736     NTSTATUS Status;
2737 
2738     _SEH2_TRY
2739     {
2740         ProbeForWrite( lpWndClassEx, sizeof(WNDCLASSEXW), sizeof(ULONG));
2741         RtlCopyMemory( &Safewcexw, lpWndClassEx, sizeof(WNDCLASSEXW));
2742         if (ppszMenuName)
2743         {
2744             ProbeForWrite(ppszMenuName, sizeof(*ppszMenuName), sizeof(PVOID));
2745         }
2746     }
2747     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2748     {
2749         SetLastNtError(_SEH2_GetExceptionCode());
2750         _SEH2_YIELD(return FALSE);
2751     }
2752     _SEH2_END;
2753 
2754     Status = ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName, ClassName);
2755     if (!NT_SUCCESS(Status))
2756     {
2757         ERR("Error capturing the class name\n");
2758         SetLastNtError(Status);
2759         return FALSE;
2760     }
2761 
2762     // If null instance use client.
2763     if (!hInstance) hInstance = hModClient;
2764 
2765     TRACE("GetClassInfo(%wZ, %p)\n", &SafeClassName, hInstance);
2766 
2767     /* NOTE: Need exclusive lock because getting the wndproc might require the
2768              creation of a call procedure handle */
2769     UserEnterExclusive();
2770 
2771     ppi = GetW32ProcessInfo();
2772     if (!(ppi->W32PF_flags & W32PF_CLASSESREGISTERED))
2773     {
2774         UserRegisterSystemClasses();
2775     }
2776 
2777     ClassAtom = IntGetClassAtom(&SafeClassName,
2778                                 hInstance,
2779                                 ppi,
2780                                 &Class,
2781                                 NULL);
2782     if (ClassAtom != (RTL_ATOM)0)
2783     {
2784         ClassAtom = Class->atomNVClassName;
2785         Ret = UserGetClassInfo(Class, &Safewcexw, bAnsi, hInstance);
2786     }
2787     else
2788     {
2789         EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST);
2790         Ret = FALSE;
2791     }
2792 
2793     UserLeave();
2794 
2795     if (Ret)
2796     {
2797         _SEH2_TRY
2798         {
2799             /* Emulate Function. */
2800             if (ppszMenuName) *ppszMenuName = (LPWSTR)Safewcexw.lpszMenuName;
2801 
2802             RtlCopyMemory(lpWndClassEx, &Safewcexw, sizeof(WNDCLASSEXW));
2803 
2804             // From Wine:
2805             /* We must return the atom of the class here instead of just TRUE. */
2806             /* Undocumented behavior! Return the class atom as a BOOL! */
2807             Ret = (BOOL)ClassAtom;
2808         }
2809         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2810         {
2811             EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST);
2812             Ret = FALSE;
2813         }
2814         _SEH2_END;
2815     }
2816 
2817     if (!IS_ATOM(SafeClassName.Buffer))
2818         ExFreePoolWithTag(SafeClassName.Buffer, TAG_STRING);
2819 
2820     return Ret;
2821 }
2822 
2823 
2824 INT APIENTRY
2825 NtUserGetClassName (IN HWND hWnd,
2826                     IN BOOL Real,
2827                     OUT PUNICODE_STRING ClassName)
2828 {
2829     PWND Window;
2830     UNICODE_STRING CapturedClassName;
2831     INT iCls, Ret = 0;
2832     RTL_ATOM Atom = 0;
2833 
2834     UserEnterShared();
2835 
2836     Window = UserGetWindowObject(hWnd);
2837     if (Window != NULL)
2838     {
2839         if (Real && Window->fnid && !(Window->fnid & FNID_DESTROY))
2840         {
2841            if (LookupFnIdToiCls(Window->fnid, &iCls))
2842            {
2843               Atom = gpsi->atomSysClass[iCls];
2844            }
2845         }
2846 
2847         _SEH2_TRY
2848         {
2849             ProbeForWriteUnicodeString(ClassName);
2850             CapturedClassName = *ClassName;
2851             if (CapturedClassName.Length != 0)
2852             {
2853                 ProbeForRead(CapturedClassName.Buffer,
2854                              CapturedClassName.Length,
2855                              sizeof(WCHAR));
2856             }
2857 
2858             /* Get the class name */
2859             Ret = UserGetClassName(Window->pcls,
2860                                    &CapturedClassName,
2861                                    Atom,
2862                                    FALSE);
2863 
2864             if (Ret != 0)
2865             {
2866                 /* Update the Length field */
2867                 ClassName->Length = CapturedClassName.Length;
2868             }
2869         }
2870         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2871         {
2872             SetLastNtError(_SEH2_GetExceptionCode());
2873         }
2874         _SEH2_END;
2875     }
2876 
2877     UserLeave();
2878 
2879     return Ret;
2880 }
2881 
2882 /* Return Pointer to Class structure. */
2883 PCLS
2884 APIENTRY
2885 NtUserGetWOWClass(
2886     HINSTANCE hInstance,
2887     PUNICODE_STRING ClassName)
2888 {
2889     UNICODE_STRING SafeClassName;
2890     PPROCESSINFO pi;
2891     PCLS Class = NULL;
2892     RTL_ATOM ClassAtom = 0;
2893     NTSTATUS Status;
2894 
2895     Status = ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName, ClassName);
2896     if (!NT_SUCCESS(Status))
2897     {
2898         ERR("Error capturing the class name\n");
2899         SetLastNtError(Status);
2900         return FALSE;
2901     }
2902 
2903     UserEnterExclusive();
2904 
2905     pi = GetW32ProcessInfo();
2906 
2907     ClassAtom = IntGetClassAtom(&SafeClassName,
2908                                 hInstance,
2909                                 pi,
2910                                 &Class,
2911                                 NULL);
2912     if (!ClassAtom)
2913     {
2914         EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST);
2915     }
2916 
2917 
2918     if (SafeClassName.Buffer && !IS_ATOM(SafeClassName.Buffer))
2919         ExFreePoolWithTag(SafeClassName.Buffer, TAG_STRING);
2920 
2921     UserLeave();
2922 //
2923 // Don't forget to use DesktopPtrToUser( ? ) with return pointer in user space.
2924 //
2925     return Class;
2926 }
2927 
2928 /* EOF */
2929