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