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