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