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