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