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