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