The menu names are on the user heaps now
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / class.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998 - 2006 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id$
20 *
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * PURPOSE: Window classes
24 * FILE: subsys/win32k/ntuser/class.c
25 * PROGRAMER: Thomas Weidenmueller <w3seek@reactos.com>
26 * REVISION HISTORY:
27 * 06-06-2001 CSH Created
28 */
29 /* INCLUDES ******************************************************************/
30
31 #include <w32k.h>
32
33 #define NDEBUG
34 #include <debug.h>
35 #define TRACE DPRINT
36 #define WARN DPRINT1
37 #define ERR DPRINT1
38
39 /* WINDOWCLASS ***************************************************************/
40
41 static VOID
42 IntFreeClassMenuName(IN OUT PWINDOWCLASS Class)
43 {
44 /* free the menu name, if it was changed and allocated */
45 if (Class->MenuName != NULL && Class->MenuNameIsString)
46 {
47 UserHeapFree(Class->MenuName);
48 Class->MenuName = NULL;
49 Class->AnsiMenuName = NULL;
50 }
51 }
52
53 static VOID
54 IntDestroyClass(IN OUT PWINDOWCLASS Class)
55 {
56 /* there shouldn't be any clones anymore */
57 //ASSERT(Class->Windows == 0);
58 if (Class->Windows)
59 {
60 DPRINT1("FIXME: W3Seek's Class Patch is broken!\n");
61 Class->Windows = 0;
62 }
63 ASSERT(Class->Clone == NULL);
64
65 if (Class->Base == Class)
66 {
67 /* destruct resources shared with clones */
68 if (!Class->System && Class->CallProc != NULL)
69 {
70 DestroyCallProc(Class->GlobalCallProc ? NULL : Class->Desktop,
71 Class->CallProc);
72 }
73
74 if (Class->CallProc2 != NULL)
75 {
76 DestroyCallProc(Class->GlobalCallProc2 ? NULL : Class->Desktop,
77 Class->CallProc2);
78 }
79
80 IntFreeClassMenuName(Class);
81 }
82
83 /* free the structure */
84 if (Class->Desktop != NULL)
85 {
86 DesktopHeapFree(Class->Desktop,
87 Class);
88 }
89 else
90 {
91 UserHeapFree(Class);
92 }
93 }
94
95
96 /* clean all process classes. all process windows must cleaned first!! */
97 void FASTCALL DestroyProcessClasses(PW32PROCESS Process )
98 {
99 PWINDOWCLASS Class;
100 PW32PROCESSINFO pi = Process->ProcessInfo;
101
102 if (pi != NULL)
103 {
104 /* free all local classes */
105 Class = pi->LocalClassList;
106 while (Class != NULL)
107 {
108 pi->LocalClassList = Class->Next;
109
110 ASSERT(Class->Base == Class);
111 IntDestroyClass(Class);
112
113 Class = pi->LocalClassList;
114 }
115
116 /* free all global classes */
117 Class = pi->GlobalClassList;
118 while (Class != NULL)
119 {
120 pi->GlobalClassList = Class->Next;
121
122 ASSERT(Class->Base == Class);
123 IntDestroyClass(Class);
124
125 Class = pi->GlobalClassList;
126 }
127
128 /* free all system classes */
129 Class = pi->SystemClassList;
130 while (Class != NULL)
131 {
132 pi->SystemClassList = Class->Next;
133
134 ASSERT(Class->Base == Class);
135 IntDestroyClass(Class);
136
137 Class = pi->SystemClassList;
138 }
139 }
140 }
141
142 static BOOL
143 IntRegisterClassAtom(IN PUNICODE_STRING ClassName,
144 OUT RTL_ATOM *pAtom)
145 {
146 WCHAR szBuf[65];
147 PWSTR AtomName;
148 NTSTATUS Status;
149
150 if (ClassName->Length != 0)
151 {
152 /* FIXME - Don't limit to 64 characters! use SEH when allocating memory! */
153 if (ClassName->Length / sizeof(WCHAR) >= sizeof(szBuf) / sizeof(szBuf[0]))
154 {
155 SetLastWin32Error(ERROR_INVALID_PARAMETER);
156 return (RTL_ATOM)0;
157 }
158
159 RtlCopyMemory(szBuf,
160 ClassName->Buffer,
161 ClassName->Length);
162 szBuf[ClassName->Length / sizeof(WCHAR)] = UNICODE_NULL;
163 AtomName = szBuf;
164 }
165 else
166 AtomName = ClassName->Buffer;
167
168 Status = RtlAddAtomToAtomTable(gAtomTable,
169 AtomName,
170 pAtom);
171
172 if (!NT_SUCCESS(Status))
173 {
174 SetLastNtError(Status);
175 return FALSE;
176 }
177
178 return TRUE;
179 }
180
181 static NTSTATUS
182 IntDeregisterClassAtom(IN RTL_ATOM Atom)
183 {
184 return RtlDeleteAtomFromAtomTable(gAtomTable,
185 Atom);
186 }
187
188 static BOOL
189 IntSetClassAtom(IN OUT PWINDOWCLASS Class,
190 IN PUNICODE_STRING ClassName)
191 {
192 RTL_ATOM Atom = (RTL_ATOM)0;
193
194 /* update the base class first */
195 Class = Class->Base;
196
197 if (!IntRegisterClassAtom(ClassName,
198 &Atom))
199 {
200 return FALSE;
201 }
202
203 IntDeregisterClassAtom(Class->Atom);
204
205 Class->Atom = Atom;
206
207 /* update the clones */
208 Class = Class->Clone;
209 while (Class != NULL)
210 {
211 Class->Atom = Atom;
212
213 Class = Class->Next;
214 }
215
216 return TRUE;
217 }
218
219 static WNDPROC
220 IntGetClassWndProc(IN PWINDOWCLASS Class,
221 IN PW32PROCESSINFO pi,
222 IN BOOL Ansi,
223 IN BOOL UseCallProc2)
224 {
225 ASSERT(UserIsEnteredExclusive() == TRUE);
226
227 if (Class->System)
228 {
229 return (Ansi ? Class->WndProcExtra : Class->WndProc);
230 }
231 else
232 {
233 if (!Ansi == Class->Unicode)
234 {
235 return Class->WndProc;
236 }
237 else
238 {
239 PCALLPROC *CallProcPtr;
240 PWINDOWCLASS BaseClass;
241
242 /* make sure the call procedures are located on the desktop
243 of the base class! */
244 BaseClass = Class->Base;
245 Class = BaseClass;
246
247 CallProcPtr = (UseCallProc2 ? &Class->CallProc2 : &Class->CallProc);
248
249 if (*CallProcPtr != NULL)
250 {
251 return GetCallProcHandle(*CallProcPtr);
252 }
253 else
254 {
255 PCALLPROC NewCallProc;
256
257 if (pi == NULL)
258 return NULL;
259
260 NewCallProc = CreateCallProc(Class->Desktop,
261 Class->WndProc,
262 Class->Unicode,
263 pi);
264 if (NewCallProc == NULL)
265 {
266 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
267 return NULL;
268 }
269
270 *CallProcPtr = NewCallProc;
271
272 if (Class->Desktop == NULL)
273 {
274 if (UseCallProc2)
275 Class->GlobalCallProc2 = TRUE;
276 else
277 Class->GlobalCallProc = TRUE;
278 }
279
280 /* update the clones */
281 Class = Class->Clone;
282 while (Class != NULL)
283 {
284 if (UseCallProc2)
285 {
286 Class->CallProc2 = NewCallProc;
287 Class->GlobalCallProc2 = BaseClass->GlobalCallProc2;
288 }
289 else
290 {
291 Class->CallProc = NewCallProc;
292 Class->GlobalCallProc = BaseClass->GlobalCallProc;
293 }
294
295 Class = Class->Next;
296 }
297
298 return GetCallProcHandle(NewCallProc);
299 }
300 }
301 }
302 }
303
304 static WNDPROC
305 IntSetClassWndProc(IN OUT PWINDOWCLASS Class,
306 IN WNDPROC WndProc,
307 IN BOOL Ansi)
308 {
309 WNDPROC Ret;
310
311 if (Class->System)
312 {
313 DPRINT1("Attempted to change window procedure of system window class 0x%p!\n", Class->Atom);
314 SetLastWin32Error(ERROR_ACCESS_DENIED);
315 return NULL;
316 }
317
318 /* update the base class first */
319 Class = Class->Base;
320
321 /* resolve any callproc handle if possible */
322 if (IsCallProcHandle(WndProc))
323 {
324 WNDPROC_INFO wpInfo;
325
326 if (UserGetCallProcInfo((HANDLE)WndProc,
327 &wpInfo))
328 {
329 WndProc = wpInfo.WindowProc;
330 /* FIXME - what if wpInfo.IsUnicode doesn't match Ansi? */
331 }
332 }
333
334 Ret = IntGetClassWndProc(Class,
335 GetW32ProcessInfo(),
336 Ansi,
337 TRUE);
338 if (Ret == NULL)
339 {
340 return NULL;
341 }
342
343 /* update the class info */
344 Class->Unicode = !Ansi;
345 Class->WndProc = WndProc;
346 if (Class->CallProc != NULL)
347 {
348 Class->CallProc->WndProc = WndProc;
349 Class->CallProc->Unicode = !Ansi;
350 }
351
352 /* update the clones */
353 Class = Class->Clone;
354 while (Class != NULL)
355 {
356 Class->Unicode = !Ansi;
357 Class->WndProc = WndProc;
358
359 Class = Class->Next;
360 }
361
362 return Ret;
363 }
364
365 static PWINDOWCLASS
366 IntGetClassForDesktop(IN OUT PWINDOWCLASS BaseClass,
367 IN OUT PWINDOWCLASS *ClassLink,
368 IN PDESKTOP Desktop)
369 {
370 SIZE_T ClassSize;
371 PWINDOWCLASS Class;
372
373 ASSERT(Desktop != NULL);
374 ASSERT(BaseClass->Base == BaseClass);
375
376 if (BaseClass->Desktop == Desktop)
377 {
378 /* it is most likely that a window is created on the same
379 desktop as the window class. */
380
381 return BaseClass;
382 }
383
384 if (BaseClass->Desktop == NULL)
385 {
386 ASSERT(BaseClass->Windows == 0);
387 ASSERT(BaseClass->Clone == NULL);
388
389 /* Classes are also located in the shared heap when the class
390 was created before the thread attached to a desktop. As soon
391 as a window is created for such a class located on the shared
392 heap, the class is cloned into the desktop heap on which the
393 window is created. */
394 Class = NULL;
395 }
396 else
397 {
398 /* The user is asking for a class object on a different desktop,
399 try to find one! */
400 Class = BaseClass->Clone;
401 while (Class != NULL)
402 {
403 if (Class->Desktop == Desktop)
404 {
405 ASSERT(Class->Base == BaseClass);
406 ASSERT(Class->Clone == NULL);
407 break;
408 }
409
410 Class = Class->Next;
411 }
412 }
413
414 if (Class == NULL)
415 {
416 /* The window is created on a different desktop, we need to
417 clone the class object to the desktop heap of the window! */
418 ClassSize = sizeof(*BaseClass) + (SIZE_T)BaseClass->ClsExtra;
419
420 Class = DesktopHeapAlloc(Desktop,
421 ClassSize);
422 if (Class != NULL)
423 {
424 /* simply clone the class */
425 RtlCopyMemory(Class,
426 BaseClass,
427 ClassSize);
428
429 /* update some pointers and link the class */
430 Class->Desktop = Desktop;
431 Class->Windows = 0;
432
433 if (BaseClass->Desktop == NULL)
434 {
435 /* we don't really need the base class on the shared
436 heap anymore, delete it so the only class left is
437 the clone we just created, which now serves as the
438 new base class */
439 ASSERT(BaseClass->Clone == NULL);
440 ASSERT(Class->Clone == NULL);
441 Class->Base = Class;
442 Class->Next = BaseClass->Next;
443
444 if (!BaseClass->System && BaseClass->CallProc != NULL)
445 Class->GlobalCallProc = TRUE;
446 if (BaseClass->CallProc2 != NULL)
447 Class->GlobalCallProc2 = TRUE;
448
449 /* replace the base class */
450 (void)InterlockedExchangePointer(ClassLink,
451 Class);
452
453 /* destroy the obsolete copy on the shared heap */
454 BaseClass->Base = NULL;
455 BaseClass->Clone = NULL;
456 IntDestroyClass(BaseClass);
457 }
458 else
459 {
460 /* link in the clone */
461 Class->Clone = NULL;
462 Class->Base = BaseClass;
463 Class->Next = BaseClass->Clone;
464 (void)InterlockedExchangePointer(&BaseClass->Clone,
465 Class);
466 }
467 }
468 else
469 {
470 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
471 }
472 }
473
474 return Class;
475 }
476
477 PWINDOWCLASS
478 IntReferenceClass(IN OUT PWINDOWCLASS BaseClass,
479 IN OUT PWINDOWCLASS *ClassLink,
480 IN PDESKTOP Desktop)
481 {
482 PWINDOWCLASS Class;
483
484 ASSERT(BaseClass->Base == BaseClass);
485
486 Class = IntGetClassForDesktop(BaseClass,
487 ClassLink,
488 Desktop);
489 if (Class != NULL)
490 {
491 Class->Windows++;
492 }
493
494 return Class;
495 }
496
497 static VOID
498 IntMakeCloneBaseClass(IN OUT PWINDOWCLASS Class,
499 IN OUT PWINDOWCLASS *BaseClassLink,
500 IN OUT PWINDOWCLASS *CloneLink)
501 {
502 PWINDOWCLASS Clone, BaseClass;
503 PCALLPROC CallProc;
504
505 ASSERT(Class->Base != Class);
506 ASSERT(Class->Base->Clone != NULL);
507 ASSERT(Class->Desktop != NULL);
508 ASSERT(Class->Windows != 0);
509 ASSERT(Class->Base->Desktop != NULL);
510 ASSERT(Class->Base->Windows == 0);
511
512 /* unlink the clone */
513 *CloneLink = Class->Next;
514 Class->Clone = Class->Base->Clone;
515
516 BaseClass = Class->Base;
517
518 if (!BaseClass->System && BaseClass->CallProc != NULL &&
519 !BaseClass->GlobalCallProc)
520 {
521 /* we need to move the allocated call procedure */
522 CallProc = BaseClass->CallProc;
523 Class->CallProc = CloneCallProc(Class->Desktop,
524 CallProc);
525 DestroyCallProc(BaseClass->Desktop,
526 CallProc);
527 }
528
529 if (BaseClass->CallProc2 != NULL &&
530 !BaseClass->GlobalCallProc2)
531 {
532 /* we need to move the allocated call procedure */
533 CallProc = BaseClass->CallProc2;
534 Class->CallProc2 = CloneCallProc(Class->Desktop,
535 CallProc);
536 DestroyCallProc(BaseClass->Desktop,
537 CallProc);
538 }
539
540 /* update the class information to make it a base class */
541 Class->Base = Class;
542 Class->Next = (*BaseClassLink)->Next;
543
544 /* update all clones */
545 Clone = Class->Clone;
546 while (Clone != NULL)
547 {
548 ASSERT(Clone->Clone == NULL);
549 Clone->Base = Class;
550
551 if (!Class->System)
552 Clone->CallProc = Class->CallProc;
553 Clone->CallProc2 = Class->CallProc2;
554
555 Clone = Clone->Next;
556 }
557
558 /* link in the new base class */
559 (void)InterlockedExchangePointer(BaseClassLink,
560 Class);
561 }
562
563 VOID
564 IntDereferenceClass(IN OUT PWINDOWCLASS Class,
565 IN PDESKTOP Desktop,
566 IN PW32PROCESSINFO pi)
567 {
568 PWINDOWCLASS *PrevLink, BaseClass, CurrentClass;
569
570 BaseClass = Class->Base;
571
572 if (--Class->Windows == 0)
573 {
574 if (BaseClass == Class)
575 {
576 ASSERT(Class->Base == Class);
577
578 /* check if there are clones of the class on other desktops,
579 link the first clone in if possible. If there are no clones
580 then leave the class on the desktop heap. It will get moved
581 to the shared heap when the thread detaches. */
582 if (BaseClass->Clone != NULL)
583 {
584 if (BaseClass->System)
585 PrevLink = &pi->SystemClassList;
586 else if (BaseClass->Global)
587 PrevLink = &pi->GlobalClassList;
588 else
589 PrevLink = &pi->LocalClassList;
590
591 while (*PrevLink != BaseClass)
592 {
593 ASSERT(*PrevLink != NULL);
594 PrevLink = &BaseClass->Next;
595 }
596
597 ASSERT(*PrevLink == BaseClass);
598
599 /* make the first clone become the new base class */
600 IntMakeCloneBaseClass(BaseClass->Clone,
601 PrevLink,
602 &BaseClass->Clone);
603
604 /* destroy the class, there's still another clone of the class
605 that now serves as a base class. Make sure we don't destruct
606 resources shared by all classes (Base = NULL)! */
607 BaseClass->Base = NULL;
608 BaseClass->Clone = NULL;
609 IntDestroyClass(BaseClass);
610 }
611 }
612 else
613 {
614 /* locate the cloned class and unlink it */
615 PrevLink = &BaseClass->Clone;
616 CurrentClass = BaseClass->Clone;
617 while (CurrentClass != Class)
618 {
619 ASSERT(CurrentClass != NULL);
620
621 PrevLink = &CurrentClass->Next;
622 CurrentClass = CurrentClass->Next;
623 }
624
625 ASSERT(CurrentClass == Class);
626
627 (void)InterlockedExchangePointer(PrevLink,
628 Class->Next);
629
630 ASSERT(Class->Base == BaseClass);
631 ASSERT(Class->Clone == NULL);
632
633 /* the class was just a clone, we don't need it anymore */
634 IntDestroyClass(Class);
635 }
636 }
637 }
638
639 static BOOL
640 IntMoveClassToSharedHeap(IN OUT PWINDOWCLASS Class,
641 IN OUT PWINDOWCLASS **ClassLinkPtr)
642 {
643 PWINDOWCLASS NewClass;
644 PCALLPROC CallProc;
645 SIZE_T ClassSize;
646
647 ASSERT(Class->Base == Class);
648 ASSERT(Class->Desktop != NULL);
649 ASSERT(Class->Windows == 0);
650 ASSERT(Class->Clone == NULL);
651
652 ClassSize = sizeof(*Class) + (SIZE_T)Class->ClsExtra;
653
654 /* allocate the new base class on the shared heap */
655 NewClass = UserHeapAlloc(ClassSize);
656 if (NewClass != NULL)
657 {
658 RtlCopyMemory(NewClass,
659 Class,
660 ClassSize);
661
662 NewClass->Desktop = NULL;
663 NewClass->Base = NewClass;
664
665 if (!NewClass->System && NewClass->CallProc != NULL &&
666 !NewClass->GlobalCallProc)
667 {
668 /* we need to move the allocated call procedure to the shared heap */
669 CallProc = NewClass->CallProc;
670 NewClass->CallProc = CloneCallProc(NULL,
671 CallProc);
672 DestroyCallProc(Class->Desktop,
673 CallProc);
674
675 NewClass->GlobalCallProc = TRUE;
676 }
677
678 if (NewClass->CallProc2 != NULL &&
679 !NewClass->GlobalCallProc2)
680 {
681 /* we need to move the allocated call procedure to the shared heap */
682 CallProc = NewClass->CallProc2;
683 NewClass->CallProc2 = CloneCallProc(NULL,
684 CallProc);
685 DestroyCallProc(Class->Desktop,
686 CallProc);
687
688 NewClass->GlobalCallProc2 = TRUE;
689 }
690
691 /* replace the class in the list */
692 (void)InterlockedExchangePointer(*ClassLinkPtr,
693 NewClass);
694 *ClassLinkPtr = &NewClass->Next;
695
696 /* free the obsolete class on the desktop heap */
697 Class->Base = NULL;
698 IntDestroyClass(Class);
699 return TRUE;
700 }
701
702 return FALSE;
703 }
704
705 static VOID
706 IntCheckDesktopClasses(IN PDESKTOP Desktop,
707 IN OUT PWINDOWCLASS *ClassList,
708 IN BOOL FreeOnFailure,
709 OUT BOOL *Ret)
710 {
711 PWINDOWCLASS Class, NextClass, *Link;
712
713 /* NOTE: We only need to check base classes! When classes are no longer needed
714 on a desktop, the clones will be freed automatically as soon as possible.
715 However, we need to move base classes to the shared heap, as soon as
716 the last desktop heap where a class is allocated on is about to be destroyed.
717 If we didn't move the class to the shared heap, the class would become
718 inaccessible! */
719
720 ASSERT(Desktop != NULL);
721
722 Link = ClassList;
723 Class = *Link;
724 while (Class != NULL)
725 {
726 NextClass = Class->Next;
727
728 ASSERT(Class->Base == Class);
729
730 if (Class->Desktop == Desktop &&
731 Class->Windows == 0)
732 {
733 /* there shouldn't be any clones around anymore! */
734 ASSERT(Class->Clone == NULL);
735
736 /* FIXME - If process is terminating, don't move the class but rather destroy it! */
737 /* FIXME - We could move the class to another desktop heap if there's still desktops
738 mapped into the process... */
739
740 /* move the class to the shared heap */
741 if (IntMoveClassToSharedHeap(Class,
742 &Link))
743 {
744 ASSERT(*Link == NextClass);
745 }
746 else
747 {
748 ASSERT(NextClass == Class->Next);
749
750 if (FreeOnFailure)
751 {
752 /* unlink the base class */
753 (void)InterlockedExchangePointer(Link,
754 Class->Next);
755
756 /* we can free the old base class now */
757 Class->Base = NULL;
758 IntDestroyClass(Class);
759 }
760 else
761 {
762 Link = &Class->Next;
763 *Ret = FALSE;
764 }
765 }
766 }
767 else
768 Link = &Class->Next;
769
770 Class = NextClass;
771 }
772 }
773
774 BOOL
775 IntCheckProcessDesktopClasses(IN PDESKTOP Desktop,
776 IN BOOL FreeOnFailure)
777 {
778 PW32PROCESSINFO pi;
779 BOOL Ret = TRUE;
780
781 pi = GetW32ProcessInfo();
782 if (pi == NULL)
783 return TRUE;
784
785 /* check all local classes */
786 IntCheckDesktopClasses(Desktop,
787 &pi->LocalClassList,
788 FreeOnFailure,
789 &Ret);
790
791 /* check all global classes */
792 IntCheckDesktopClasses(Desktop,
793 &pi->GlobalClassList,
794 FreeOnFailure,
795 &Ret);
796
797 /* check all system classes */
798 IntCheckDesktopClasses(Desktop,
799 &pi->SystemClassList,
800 FreeOnFailure,
801 &Ret);
802
803 if (!Ret)
804 {
805 DPRINT1("Failed to move process classes from desktop 0x%p to the shared heap!\n", Desktop);
806 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
807 }
808
809 return Ret;
810 }
811
812 static PWINDOWCLASS
813 IntCreateClass(IN CONST WNDCLASSEXW* lpwcx,
814 IN PUNICODE_STRING ClassName,
815 IN PUNICODE_STRING MenuName,
816 IN WNDPROC wpExtra,
817 IN DWORD dwFlags,
818 IN PDESKTOP Desktop,
819 IN PW32PROCESSINFO pi)
820 {
821 SIZE_T ClassSize;
822 PWINDOWCLASS Class = NULL;
823 RTL_ATOM Atom;
824 PWSTR pszMenuName = NULL;
825 NTSTATUS Status = STATUS_SUCCESS;
826
827 TRACE("lpwcx=%p ClassName=%wZ MenuName=%wZ wpExtra=%p dwFlags=%08x Desktop=%p pi=%p\n",
828 lpwcx, ClassName, MenuName, wpExtra, dwFlags, Desktop, pi);
829
830 if (!IntRegisterClassAtom(ClassName,
831 &Atom))
832 {
833 DPRINT1("Failed to register class atom!\n");
834 return NULL;
835 }
836
837 ClassSize = sizeof(*Class) + lpwcx->cbClsExtra;
838 if (MenuName->Length != 0)
839 {
840 pszMenuName = UserHeapAlloc(MenuName->Length + sizeof(UNICODE_NULL) +
841 RtlUnicodeStringToAnsiSize(MenuName));
842 if (pszMenuName == NULL)
843 goto NoMem;
844 }
845
846 if (Desktop != NULL)
847 {
848 Class = DesktopHeapAlloc(Desktop,
849 ClassSize);
850 }
851 else
852 {
853 /* FIXME - the class was created before being connected
854 to a desktop. It is possible for the desktop window,
855 but should it be allowed for any other case? */
856 Class = UserHeapAlloc(ClassSize);
857 }
858
859 if (Class != NULL)
860 {
861 RtlZeroMemory(Class,
862 ClassSize);
863
864 Class->Desktop = Desktop;
865 Class->Base = Class;
866 Class->Atom = Atom;
867
868 if (dwFlags & REGISTERCLASS_SYSTEM)
869 {
870 dwFlags &= ~REGISTERCLASS_ANSI;
871 Class->WndProcExtra = wpExtra;
872 Class->System = TRUE;
873 }
874
875 _SEH_TRY
876 {
877 PWSTR pszMenuNameBuffer = pszMenuName;
878
879 /* need to protect with SEH since accessing the WNDCLASSEX structure
880 and string buffers might raise an exception! We don't want to
881 leak memory... */
882 Class->WndProc = lpwcx->lpfnWndProc;
883 Class->Style = lpwcx->style;
884 Class->ClsExtra = lpwcx->cbClsExtra;
885 Class->WndExtra = lpwcx->cbWndExtra;
886 Class->hInstance = lpwcx->hInstance;
887 Class->hIcon = lpwcx->hIcon; /* FIXME */
888 Class->hIconSm = lpwcx->hIconSm; /* FIXME */
889 Class->hCursor = lpwcx->hCursor; /* FIXME */
890 Class->hbrBackground = lpwcx->hbrBackground;
891
892 /* make a copy of the string */
893 if (pszMenuNameBuffer != NULL)
894 {
895 Class->MenuNameIsString = TRUE;
896
897 Class->MenuName = pszMenuNameBuffer;
898 RtlCopyMemory(Class->MenuName,
899 MenuName->Buffer,
900 MenuName->Length);
901 Class->MenuName[MenuName->Length / sizeof(WCHAR)] = UNICODE_NULL;
902
903 pszMenuNameBuffer += (MenuName->Length / sizeof(WCHAR)) + 1;
904 }
905 else
906 Class->MenuName = MenuName->Buffer;
907
908 /* save an ansi copy of the string */
909 if (pszMenuNameBuffer != NULL)
910 {
911 ANSI_STRING AnsiString;
912
913 Class->AnsiMenuName = (PSTR)pszMenuNameBuffer;
914 AnsiString.MaximumLength = RtlUnicodeStringToAnsiSize(MenuName);
915 AnsiString.Buffer = Class->AnsiMenuName;
916 Status = RtlUnicodeStringToAnsiString(&AnsiString,
917 MenuName,
918 FALSE);
919 if (!NT_SUCCESS(Status))
920 {
921 DPRINT1("Failed to convert unicode menu name to ansi!\n");
922
923 /* life would've been much prettier if ntoskrnl exported RtlRaiseStatus()... */
924 _SEH_LEAVE;
925 }
926 }
927 else
928 Class->AnsiMenuName = (PSTR)MenuName->Buffer;
929
930 if (!(dwFlags & REGISTERCLASS_ANSI))
931 Class->Unicode = TRUE;
932
933 if (Class->Style & CS_GLOBALCLASS)
934 Class->Global = TRUE;
935 }
936 _SEH_HANDLE
937 {
938 Status = _SEH_GetExceptionCode();
939 }
940 _SEH_END;
941
942 if (!NT_SUCCESS(Status))
943 {
944 DPRINT1("Failed creating the class: 0x%x\n", Status);
945
946 SetLastNtError(Status);
947
948 if (pszMenuName != NULL)
949 UserHeapFree(pszMenuName);
950
951 DesktopHeapFree(Desktop,
952 Class);
953 Class = NULL;
954
955 IntDeregisterClassAtom(Atom);
956 }
957 }
958 else
959 {
960 NoMem:
961 DPRINT1("Failed to allocate class on Desktop 0x%p\n", Desktop);
962
963 if (pszMenuName != NULL)
964 UserHeapFree(pszMenuName);
965
966 IntDeregisterClassAtom(Atom);
967
968 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
969 }
970
971 return Class;
972 }
973
974 static PWINDOWCLASS
975 IntFindClass(IN RTL_ATOM Atom,
976 IN HINSTANCE hInstance,
977 IN PWINDOWCLASS *ClassList,
978 OUT PWINDOWCLASS **Link OPTIONAL)
979 {
980 PWINDOWCLASS Class, *PrevLink = ClassList;
981
982 Class = *PrevLink;
983 while (Class != NULL)
984 {
985 if (Class->Atom == Atom &&
986 (hInstance == NULL || Class->hInstance == hInstance) &&
987 !Class->Destroying)
988 {
989 ASSERT(Class->Base == Class);
990
991 if (Link != NULL)
992 *Link = PrevLink;
993 break;
994 }
995
996 PrevLink = &Class->Next;
997 Class = Class->Next;
998 }
999
1000 return Class;
1001 }
1002
1003 BOOL
1004 IntGetAtomFromStringOrAtom(IN PUNICODE_STRING ClassName,
1005 OUT RTL_ATOM *Atom)
1006 {
1007 BOOL Ret = FALSE;
1008
1009 if (ClassName->Length != 0)
1010 {
1011 WCHAR szBuf[65];
1012 PWSTR AtomName;
1013 NTSTATUS Status;
1014
1015 /* NOTE: Caller has to protect the call with SEH! */
1016
1017 if (ClassName->Length != 0)
1018 {
1019 /* FIXME - Don't limit to 64 characters! use SEH when allocating memory! */
1020 if (ClassName->Length / sizeof(WCHAR) >= sizeof(szBuf) / sizeof(szBuf[0]))
1021 {
1022 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1023 return (RTL_ATOM)0;
1024 }
1025
1026 /* We need to make a local copy of the class name! The caller could
1027 modify the buffer and we could overflow in RtlLookupAtomInAtomTable.
1028 We're protected by SEH, but the ranges that might be accessed were
1029 not probed... */
1030 RtlCopyMemory(szBuf,
1031 ClassName->Buffer,
1032 ClassName->Length);
1033 szBuf[ClassName->Length / sizeof(WCHAR)] = UNICODE_NULL;
1034 AtomName = szBuf;
1035 }
1036 else
1037 AtomName = ClassName->Buffer;
1038
1039 /* lookup the atom */
1040 Status = RtlLookupAtomInAtomTable(gAtomTable,
1041 AtomName,
1042 Atom);
1043 if (NT_SUCCESS(Status))
1044 {
1045 Ret = TRUE;
1046 }
1047 else
1048 {
1049 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
1050 {
1051 SetLastWin32Error(ERROR_CANNOT_FIND_WND_CLASS);
1052 }
1053 else
1054 {
1055 SetLastNtError(Status);
1056 }
1057 }
1058 }
1059 else
1060 {
1061 ASSERT(IS_ATOM(ClassName->Buffer));
1062 *Atom = (RTL_ATOM)((ULONG_PTR)ClassName->Buffer);
1063 Ret = TRUE;
1064 }
1065
1066 return Ret;
1067 }
1068
1069 RTL_ATOM
1070 IntGetClassAtom(IN PUNICODE_STRING ClassName,
1071 IN HINSTANCE hInstance OPTIONAL,
1072 IN PW32PROCESSINFO pi OPTIONAL,
1073 OUT PWINDOWCLASS *BaseClass OPTIONAL,
1074 OUT PWINDOWCLASS **Link OPTIONAL)
1075 {
1076 RTL_ATOM Atom = (RTL_ATOM)0;
1077
1078 if (IntGetAtomFromStringOrAtom(ClassName,
1079 &Atom) &&
1080 BaseClass != NULL && Atom != (RTL_ATOM)0)
1081 {
1082 PWINDOWCLASS Class;
1083
1084 /* attempt to locate the class object */
1085
1086 ASSERT(pi != NULL);
1087
1088 /* Step 1: try to find an exact match of locally registered classes */
1089 Class = IntFindClass(Atom,
1090 hInstance,
1091 &pi->LocalClassList,
1092 Link);
1093 if (Class != NULL)
1094 {
1095 goto FoundClass;
1096 }
1097
1098 /* Step 2: try to find any globally registered class. The hInstance
1099 is not relevant for global classes */
1100 Class = IntFindClass(Atom,
1101 NULL,
1102 &pi->GlobalClassList,
1103 Link);
1104 if (Class != NULL)
1105 {
1106 goto FoundClass;
1107 }
1108
1109 /* Step 3: try to find any local class registered by user32 */
1110 Class = IntFindClass(Atom,
1111 pi->hModUser,
1112 &pi->LocalClassList,
1113 Link);
1114 if (Class != NULL)
1115 {
1116 goto FoundClass;
1117 }
1118
1119 /* Step 4: try to find any global class registered by user32 */
1120 Class = IntFindClass(Atom,
1121 pi->hModUser,
1122 &pi->GlobalClassList,
1123 Link);
1124 if (Class != NULL)
1125 {
1126 goto FoundClass;
1127 }
1128
1129 /* Step 5: try to find a system class */
1130 Class = IntFindClass(Atom,
1131 NULL,
1132 &pi->SystemClassList,
1133 Link);
1134
1135 if (Class == NULL)
1136 {
1137 SetLastWin32Error(ERROR_CLASS_DOES_NOT_EXIST);
1138 return (RTL_ATOM)0;
1139 }
1140
1141 FoundClass:
1142 *BaseClass = Class;
1143 }
1144
1145 return Atom;
1146 }
1147
1148 RTL_ATOM
1149 UserRegisterClass(IN CONST WNDCLASSEXW* lpwcx,
1150 IN PUNICODE_STRING ClassName,
1151 IN PUNICODE_STRING MenuName,
1152 IN HANDLE hMenu, /* FIXME */
1153 IN WNDPROC wpExtra,
1154 IN DWORD dwFlags)
1155 {
1156 PW32THREADINFO ti;
1157 PW32PROCESSINFO pi;
1158 PWINDOWCLASS Class;
1159 RTL_ATOM ClassAtom;
1160 RTL_ATOM Ret = (RTL_ATOM)0;
1161
1162 /* NOTE: Accessing the buffers in ClassName and MenuName may raise exceptions! */
1163
1164 ti = GetW32ThreadInfo();
1165 if (ti == NULL)
1166 {
1167 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
1168 return (RTL_ATOM)0;
1169 }
1170
1171 pi = ti->kpi;
1172
1173 /* try to find a previously registered class */
1174 ClassAtom = IntGetClassAtom(ClassName,
1175 lpwcx->hInstance,
1176 pi,
1177 &Class,
1178 NULL);
1179 if (ClassAtom != (RTL_ATOM)0)
1180 {
1181 if (lpwcx->style & CS_GLOBALCLASS)
1182 {
1183 // global classes shall not have same names as system classes
1184 if (Class->Global || Class->System)
1185 {
1186 DPRINT("Class 0x%p does already exist!\n", ClassAtom);
1187 SetLastWin32Error(ERROR_CLASS_ALREADY_EXISTS);
1188 return (RTL_ATOM)0;
1189 }
1190 }
1191 else if ( !Class->Global && !Class->System)
1192 {
1193 // local class already exists
1194 DPRINT("Class 0x%p does already exist!\n", ClassAtom);
1195 SetLastWin32Error(ERROR_CLASS_ALREADY_EXISTS);
1196 return (RTL_ATOM)0;
1197 }
1198 }
1199
1200 Class = IntCreateClass(lpwcx,
1201 ClassName,
1202 MenuName,
1203 wpExtra,
1204 dwFlags,
1205 ti->Desktop,
1206 pi);
1207
1208 if (Class != NULL)
1209 {
1210 PWINDOWCLASS *List;
1211
1212 /* FIXME - pass the PMENU pointer to IntCreateClass instead! */
1213 Class->hMenu = hMenu;
1214
1215 /* Register the class */
1216 if (Class->System)
1217 List = &pi->SystemClassList;
1218 else if (Class->Global)
1219 List = &pi->GlobalClassList;
1220 else
1221 List = &pi->LocalClassList;
1222
1223 Class->Next = *List;
1224 (void)InterlockedExchangePointer(List,
1225 Class);
1226
1227 Ret = Class->Atom;
1228 }
1229
1230 return Ret;
1231 }
1232
1233 BOOL
1234 UserUnregisterClass(IN PUNICODE_STRING ClassName,
1235 IN HINSTANCE hInstance)
1236 {
1237 PWINDOWCLASS *Link;
1238 PW32PROCESSINFO pi;
1239 RTL_ATOM ClassAtom;
1240 PWINDOWCLASS Class;
1241
1242 pi = GetW32ProcessInfo();
1243 if (pi == NULL)
1244 {
1245 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
1246 return FALSE;
1247 }
1248
1249 /* NOTE: Accessing the buffer in ClassName may raise an exception! */
1250 ClassAtom = IntGetClassAtom(ClassName,
1251 hInstance,
1252 pi,
1253 &Class,
1254 &Link);
1255 if (ClassAtom == (RTL_ATOM)0)
1256 {
1257 return FALSE;
1258 }
1259
1260 ASSERT(Class != NULL);
1261
1262 if (Class->Windows != 0 ||
1263 Class->Clone != NULL)
1264 {
1265 SetLastWin32Error(ERROR_CLASS_HAS_WINDOWS);
1266 return FALSE;
1267 }
1268
1269 /* must be a base class! */
1270 ASSERT(Class->Base == Class);
1271
1272 /* unlink the class */
1273 *Link = Class->Next;
1274
1275 if (NT_SUCCESS(IntDeregisterClassAtom(Class->Atom)))
1276 {
1277 /* finally free the resources */
1278 IntDestroyClass(Class);
1279 return TRUE;
1280 }
1281 return FALSE;
1282 }
1283
1284 INT
1285 UserGetClassName(IN PWINDOWCLASS Class,
1286 IN OUT PUNICODE_STRING ClassName,
1287 IN BOOL Ansi)
1288 {
1289 NTSTATUS Status = STATUS_SUCCESS;
1290 WCHAR szStaticTemp[32];
1291 PWSTR szTemp = NULL;
1292 ULONG BufLen = sizeof(szStaticTemp);
1293 INT Ret = 0;
1294
1295 /* Note: Accessing the buffer in ClassName may raise an exception! */
1296
1297 _SEH_TRY
1298 {
1299 if (Ansi)
1300 {
1301 PANSI_STRING AnsiClassName = (PANSI_STRING)ClassName;
1302 UNICODE_STRING UnicodeClassName;
1303
1304 /* limit the size of the static buffer on the stack to the
1305 size of the buffer provided by the caller */
1306 if (BufLen / sizeof(WCHAR) > AnsiClassName->MaximumLength)
1307 {
1308 BufLen = AnsiClassName->MaximumLength * sizeof(WCHAR);
1309 }
1310
1311 /* find out how big the buffer needs to be */
1312 Status = RtlQueryAtomInAtomTable(gAtomTable,
1313 Class->Atom,
1314 NULL,
1315 NULL,
1316 szStaticTemp,
1317 &BufLen);
1318 if (Status == STATUS_BUFFER_TOO_SMALL)
1319 {
1320 if (BufLen / sizeof(WCHAR) > AnsiClassName->MaximumLength)
1321 {
1322 /* the buffer required exceeds the ansi buffer provided,
1323 pretend like we're using the ansi buffer and limit the
1324 size to the buffer size provided */
1325 BufLen = AnsiClassName->MaximumLength * sizeof(WCHAR);
1326 }
1327
1328 /* allocate a temporary buffer that can hold the unicode class name */
1329 szTemp = ExAllocatePool(PagedPool,
1330 BufLen);
1331 if (szTemp == NULL)
1332 {
1333 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
1334 _SEH_LEAVE;
1335 }
1336
1337 /* query the class name */
1338 Status = RtlQueryAtomInAtomTable(gAtomTable,
1339 Class->Atom,
1340 NULL,
1341 NULL,
1342 szTemp,
1343 &BufLen);
1344 }
1345 else
1346 szTemp = szStaticTemp;
1347
1348 if (NT_SUCCESS(Status))
1349 {
1350 /* convert the atom name to ansi */
1351
1352 RtlInitUnicodeString(&UnicodeClassName,
1353 szTemp);
1354
1355 Status = RtlUnicodeStringToAnsiString(AnsiClassName,
1356 &UnicodeClassName,
1357 FALSE);
1358 if (!NT_SUCCESS(Status))
1359 {
1360 SetLastNtError(Status);
1361 _SEH_LEAVE;
1362 }
1363 }
1364
1365 Ret = BufLen / sizeof(WCHAR);
1366 }
1367 else /* !Ansi */
1368 {
1369 BufLen = ClassName->MaximumLength;
1370
1371 /* query the atom name */
1372 Status = RtlQueryAtomInAtomTable(gAtomTable,
1373 Class->Atom,
1374 NULL,
1375 NULL,
1376 ClassName->Buffer,
1377 &BufLen);
1378
1379 if (!NT_SUCCESS(Status))
1380 {
1381 SetLastNtError(Status);
1382 _SEH_LEAVE;
1383 }
1384
1385 Ret = BufLen / sizeof(WCHAR);
1386 }
1387 }
1388 _SEH_HANDLE
1389 {
1390 SetLastNtError(_SEH_GetExceptionCode());
1391 }
1392 _SEH_END;
1393
1394 if (Ansi && szTemp != NULL && szTemp != szStaticTemp)
1395 {
1396 ExFreePool(szTemp);
1397 }
1398
1399 return Ret;
1400 }
1401
1402 ULONG_PTR
1403 UserGetClassLongPtr(IN PWINDOWCLASS Class,
1404 IN INT Index,
1405 IN BOOL Ansi)
1406 {
1407 ULONG_PTR Ret = 0;
1408
1409 if (Index >= 0)
1410 {
1411 PULONG_PTR Data;
1412
1413 if (Index + sizeof(ULONG_PTR) < Index ||
1414 Index + sizeof(ULONG_PTR) > Class->ClsExtra)
1415 {
1416 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1417 return 0;
1418 }
1419
1420 Data = (PULONG_PTR)((ULONG_PTR)(Class + 1) + Index);
1421
1422 /* FIXME - Data might be a unaligned pointer! Might be a problem on
1423 certain architectures, maybe using RtlCopyMemory is a
1424 better choice for those architectures! */
1425 return *Data;
1426 }
1427
1428 switch (Index)
1429 {
1430 case GCL_CBWNDEXTRA:
1431 Ret = (ULONG_PTR)Class->WndExtra;
1432 break;
1433
1434 case GCL_CBCLSEXTRA:
1435 Ret = (ULONG_PTR)Class->ClsExtra;
1436 break;
1437
1438 case GCLP_HBRBACKGROUND:
1439 Ret = (ULONG_PTR)Class->hbrBackground;
1440 break;
1441
1442 case GCLP_HCURSOR:
1443 /* FIXME - get handle from pointer to CURSOR object */
1444 Ret = (ULONG_PTR)Class->hCursor;
1445 break;
1446
1447 case GCLP_HICON:
1448 /* FIXME - get handle from pointer to ICON object */
1449 Ret = (ULONG_PTR)Class->hIcon;
1450 break;
1451
1452 case GCLP_HICONSM:
1453 /* FIXME - get handle from pointer to ICON object */
1454 Ret = (ULONG_PTR)Class->hIconSm;
1455 break;
1456
1457 case GCLP_HMODULE:
1458 Ret = (ULONG_PTR)Class->hInstance;
1459 break;
1460
1461 case GCLP_MENUNAME:
1462 /* NOTE: Returns pointer in kernel heap! */
1463 if (Ansi)
1464 Ret = (ULONG_PTR)Class->AnsiMenuName;
1465 else
1466 Ret = (ULONG_PTR)Class->MenuName;
1467 break;
1468
1469 case GCL_STYLE:
1470 Ret = (ULONG_PTR)Class->Style;
1471 break;
1472
1473 case GCLP_WNDPROC:
1474 Ret = (ULONG_PTR)IntGetClassWndProc(Class,
1475 GetW32ProcessInfo(),
1476 Ansi,
1477 FALSE);
1478 break;
1479
1480 case GCW_ATOM:
1481 Ret = (ULONG_PTR)Class->Atom;
1482 break;
1483
1484 default:
1485 SetLastWin32Error(ERROR_INVALID_INDEX);
1486 break;
1487 }
1488
1489 return Ret;
1490 }
1491
1492 static BOOL
1493 IntSetClassMenuName(IN PWINDOWCLASS Class,
1494 IN PUNICODE_STRING MenuName)
1495 {
1496 BOOL Ret = FALSE;
1497
1498 /* change the base class first */
1499 Class = Class->Base;
1500
1501 if (MenuName->Length != 0)
1502 {
1503 ANSI_STRING AnsiString;
1504 PWSTR strBufW;
1505
1506 AnsiString.MaximumLength = RtlUnicodeStringToAnsiSize(MenuName);
1507
1508 strBufW = UserHeapAlloc(MenuName->Length + sizeof(UNICODE_NULL) +
1509 AnsiString.MaximumLength);
1510 if (strBufW != NULL)
1511 {
1512 _SEH_TRY
1513 {
1514 NTSTATUS Status;
1515
1516 /* copy the unicode string */
1517 RtlCopyMemory(strBufW,
1518 MenuName->Buffer,
1519 MenuName->Length);
1520 strBufW[MenuName->Length / sizeof(WCHAR)] = UNICODE_NULL;
1521
1522 /* create an ansi copy of the string */
1523 AnsiString.Buffer = (PSTR)(strBufW + (MenuName->Length / sizeof(WCHAR)) + 1);
1524 Status = RtlUnicodeStringToAnsiString(&AnsiString,
1525 MenuName,
1526 FALSE);
1527 if (!NT_SUCCESS(Status))
1528 {
1529 SetLastNtError(Status);
1530 _SEH_LEAVE;
1531 }
1532
1533 Ret = TRUE;
1534 }
1535 _SEH_HANDLE
1536 {
1537 SetLastNtError(_SEH_GetExceptionCode());
1538 }
1539 _SEH_END;
1540
1541 if (Ret)
1542 {
1543 /* update the base class */
1544 IntFreeClassMenuName(Class);
1545 Class->MenuName = strBufW;
1546 Class->AnsiMenuName = AnsiString.Buffer;
1547 Class->MenuNameIsString = TRUE;
1548
1549 /* update the clones */
1550 Class = Class->Clone;
1551 while (Class != NULL)
1552 {
1553 Class->MenuName = strBufW;
1554 Class->AnsiMenuName = AnsiString.Buffer;
1555 Class->MenuNameIsString = TRUE;
1556
1557 Class = Class->Next;
1558 }
1559 }
1560 else
1561 {
1562 DPRINT1("Failed to copy class menu name!\n");
1563 UserHeapFree(strBufW);
1564 }
1565 }
1566 else
1567 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
1568 }
1569 else
1570 {
1571 ASSERT(IS_INTRESOURCE(MenuName->Buffer));
1572
1573 /* update the base class */
1574 IntFreeClassMenuName(Class);
1575 Class->MenuName = MenuName->Buffer;
1576 Class->AnsiMenuName = (PSTR)MenuName->Buffer;
1577 Class->MenuNameIsString = FALSE;
1578
1579 /* update the clones */
1580 Class = Class->Clone;
1581 while (Class != NULL)
1582 {
1583 Class->MenuName = MenuName->Buffer;
1584 Class->AnsiMenuName = (PSTR)MenuName->Buffer;
1585 Class->MenuNameIsString = FALSE;
1586
1587 Class = Class->Next;
1588 }
1589
1590 Ret = TRUE;
1591 }
1592
1593 return Ret;
1594 }
1595
1596 ULONG_PTR
1597 UserSetClassLongPtr(IN PWINDOWCLASS Class,
1598 IN INT Index,
1599 IN ULONG_PTR NewLong,
1600 IN BOOL Ansi)
1601 {
1602 ULONG_PTR Ret = 0;
1603
1604 /* NOTE: For GCLP_MENUNAME and GCW_ATOM this function may raise an exception! */
1605
1606 /* change the information in the base class first, then update the clones */
1607 Class = Class->Base;
1608
1609 if (Index >= 0)
1610 {
1611 PULONG_PTR Data;
1612
1613 if (Index + sizeof(ULONG_PTR) < Index ||
1614 Index + sizeof(ULONG_PTR) > Class->ClsExtra)
1615 {
1616 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1617 return 0;
1618 }
1619
1620 Data = (PULONG_PTR)((ULONG_PTR)(Class + 1) + Index);
1621
1622 /* FIXME - Data might be a unaligned pointer! Might be a problem on
1623 certain architectures, maybe using RtlCopyMemory is a
1624 better choice for those architectures! */
1625 Ret = *Data;
1626 *Data = NewLong;
1627
1628 /* update the clones */
1629 Class = Class->Clone;
1630 while (Class != NULL)
1631 {
1632 *(PULONG_PTR)((ULONG_PTR)(Class + 1) + Index) = NewLong;
1633 Class = Class->Next;
1634 }
1635
1636 return Ret;
1637 }
1638
1639 switch (Index)
1640 {
1641 case GCL_CBWNDEXTRA:
1642 Ret = (ULONG_PTR)Class->WndExtra;
1643 Class->WndExtra = (INT)NewLong;
1644
1645 /* update the clones */
1646 Class = Class->Clone;
1647 while (Class != NULL)
1648 {
1649 Class->WndExtra = (INT)NewLong;
1650 Class = Class->Next;
1651 }
1652
1653 break;
1654
1655 case GCL_CBCLSEXTRA:
1656 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1657 break;
1658
1659 case GCLP_HBRBACKGROUND:
1660 Ret = (ULONG_PTR)Class->hbrBackground;
1661 Class->hbrBackground = (HBRUSH)NewLong;
1662
1663 /* update the clones */
1664 Class = Class->Clone;
1665 while (Class != NULL)
1666 {
1667 Class->hbrBackground = (HBRUSH)NewLong;
1668 Class = Class->Next;
1669 }
1670 break;
1671
1672 case GCLP_HCURSOR:
1673 /* FIXME - get handle from pointer to CURSOR object */
1674 Ret = (ULONG_PTR)Class->hCursor;
1675 Class->hCursor = (HANDLE)NewLong;
1676
1677 /* update the clones */
1678 Class = Class->Clone;
1679 while (Class != NULL)
1680 {
1681 Class->hCursor = (HANDLE)NewLong;
1682 Class = Class->Next;
1683 }
1684 break;
1685
1686 case GCLP_HICON:
1687 /* FIXME - get handle from pointer to ICON object */
1688 Ret = (ULONG_PTR)Class->hIcon;
1689 Class->hIcon = (HANDLE)NewLong;
1690
1691 /* update the clones */
1692 Class = Class->Clone;
1693 while (Class != NULL)
1694 {
1695 Class->hIcon = (HANDLE)NewLong;
1696 Class = Class->Next;
1697 }
1698 break;
1699
1700 case GCLP_HICONSM:
1701 /* FIXME - get handle from pointer to ICON object */
1702 Ret = (ULONG_PTR)Class->hIconSm;
1703 Class->hIconSm = (HANDLE)NewLong;
1704
1705 /* update the clones */
1706 Class = Class->Clone;
1707 while (Class != NULL)
1708 {
1709 Class->hIconSm = (HANDLE)NewLong;
1710 Class = Class->Next;
1711 }
1712 break;
1713
1714 case GCLP_HMODULE:
1715 Ret = (ULONG_PTR)Class->hInstance;
1716 Class->hInstance = (HINSTANCE)NewLong;
1717
1718 /* update the clones */
1719 Class = Class->Clone;
1720 while (Class != NULL)
1721 {
1722 Class->hInstance = (HINSTANCE)NewLong;
1723 Class = Class->Next;
1724 }
1725 break;
1726
1727 case GCLP_MENUNAME:
1728 {
1729 PUNICODE_STRING Value = (PUNICODE_STRING)NewLong;
1730
1731 if (!IntSetClassMenuName(Class,
1732 Value))
1733 {
1734 DPRINT("Setting the class menu name failed!\n");
1735 }
1736
1737 /* FIXME - really return NULL? Wine does so... */
1738 break;
1739 }
1740
1741 case GCL_STYLE:
1742 Ret = (ULONG_PTR)Class->Style;
1743 Class->Style = (UINT)NewLong;
1744
1745 /* FIXME - what if the CS_GLOBALCLASS style is changed? should we
1746 move the class to the appropriate list? For now, we save
1747 the original value in Class->Global, so we can always
1748 locate the appropriate list */
1749
1750 /* update the clones */
1751 Class = Class->Clone;
1752 while (Class != NULL)
1753 {
1754 Class->Style = (UINT)NewLong;
1755 Class = Class->Next;
1756 }
1757 break;
1758
1759 case GCLP_WNDPROC:
1760 Ret = (ULONG_PTR)IntSetClassWndProc(Class,
1761 (WNDPROC)NewLong,
1762 Ansi);
1763 break;
1764
1765 case GCW_ATOM:
1766 {
1767 PUNICODE_STRING Value = (PUNICODE_STRING)NewLong;
1768
1769 Ret = (ULONG_PTR)Class->Atom;
1770 if (!IntSetClassAtom(Class,
1771 Value))
1772 {
1773 Ret = 0;
1774 }
1775 break;
1776 }
1777
1778 default:
1779 SetLastWin32Error(ERROR_INVALID_INDEX);
1780 break;
1781 }
1782
1783 return Ret;
1784 }
1785
1786 static BOOL
1787 UserGetClassInfo(IN PWINDOWCLASS Class,
1788 OUT PWNDCLASSEXW lpwcx,
1789 IN BOOL Ansi,
1790 HINSTANCE hInstance)
1791 {
1792 PW32PROCESSINFO pi;
1793
1794 lpwcx->style = Class->Style;
1795
1796 pi = GetW32ProcessInfo();
1797 lpwcx->lpfnWndProc = IntGetClassWndProc(Class,
1798 pi,
1799 Ansi,
1800 FALSE);
1801
1802 lpwcx->cbClsExtra = Class->ClsExtra;
1803 lpwcx->cbWndExtra = Class->WndExtra;
1804 lpwcx->hIcon = Class->hIcon; /* FIXME - get handle from pointer */
1805 lpwcx->hCursor = Class->hCursor; /* FIXME - get handle from pointer */
1806 lpwcx->hbrBackground = Class->hbrBackground;
1807
1808 if (Ansi)
1809 ((PWNDCLASSEXA)lpwcx)->lpszMenuName = Class->AnsiMenuName;
1810 else
1811 lpwcx->lpszMenuName = Class->MenuName;
1812
1813 if (Class->hInstance == pi->hModUser)
1814 lpwcx->hInstance = NULL;
1815 else
1816 lpwcx->hInstance = Class->hInstance;
1817
1818 lpwcx->lpszClassName = (LPCWSTR)((ULONG_PTR)Class->Atom); /* FIXME - return the string? */
1819
1820 lpwcx->hIconSm = Class->hIconSm; /* FIXME - get handle from pointer */
1821
1822 return TRUE;
1823 }
1824
1825 /* SYSCALLS *****************************************************************/
1826
1827
1828 RTL_ATOM NTAPI
1829 NtUserRegisterClassEx(IN CONST WNDCLASSEXW* lpwcx,
1830 IN PUNICODE_STRING ClassName,
1831 IN PUNICODE_STRING MenuName,
1832 IN WNDPROC wpExtra,
1833 IN DWORD Flags,
1834 IN HMENU hMenu)
1835
1836 /*
1837 * FUNCTION:
1838 * Registers a new class with the window manager
1839 * ARGUMENTS:
1840 * lpwcx = Win32 extended window class structure
1841 * bUnicodeClass = Whether to send ANSI or unicode strings
1842 * to window procedures
1843 * wpExtra = Extra window procedure, if this is not null, its used for the second window procedure for standard controls.
1844 * RETURNS:
1845 * Atom identifying the new class
1846 */
1847 {
1848 WNDCLASSEXW CapturedClassInfo = {0};
1849 UNICODE_STRING CapturedName = {0}, CapturedMenuName = {0};
1850 RTL_ATOM Ret = (RTL_ATOM)0;
1851
1852 if (Flags & ~REGISTERCLASS_ALL)
1853 {
1854 SetLastWin32Error(ERROR_INVALID_FLAGS);
1855 return Ret;
1856 }
1857
1858 UserEnterExclusive();
1859
1860 _SEH_TRY
1861 {
1862 /* Probe the parameters and basic parameter checks */
1863 if (ProbeForReadUint(&lpwcx->cbSize) != sizeof(WNDCLASSEXW))
1864 {
1865 goto InvalidParameter;
1866 }
1867
1868 ProbeForRead(lpwcx,
1869 sizeof(WNDCLASSEXW),
1870 sizeof(ULONG));
1871 RtlCopyMemory(&CapturedClassInfo,
1872 lpwcx,
1873 sizeof(WNDCLASSEXW));
1874
1875 CapturedName = ProbeForReadUnicodeString(ClassName);
1876 CapturedMenuName = ProbeForReadUnicodeString(MenuName);
1877
1878 if (CapturedName.Length & 1 || CapturedMenuName.Length & 1 ||
1879 CapturedClassInfo.cbClsExtra < 0 ||
1880 CapturedClassInfo.cbClsExtra + CapturedName.Length +
1881 CapturedMenuName.Length + sizeof(WINDOWCLASS) < CapturedClassInfo.cbClsExtra ||
1882 CapturedClassInfo.cbWndExtra < 0 ||
1883 CapturedClassInfo.hInstance == NULL)
1884 {
1885 goto InvalidParameter;
1886 }
1887
1888 if (CapturedName.Length != 0)
1889 {
1890 ProbeForRead(CapturedName.Buffer,
1891 CapturedName.Length,
1892 sizeof(WCHAR));
1893 }
1894 else
1895 {
1896 if (!IS_ATOM(CapturedName.Buffer))
1897 {
1898 goto InvalidParameter;
1899 }
1900 }
1901
1902 if (CapturedMenuName.Length != 0)
1903 {
1904 ProbeForRead(CapturedMenuName.Buffer,
1905 CapturedMenuName.Length,
1906 sizeof(WCHAR));
1907 }
1908 else if (CapturedMenuName.Buffer != NULL &&
1909 !IS_INTRESOURCE(CapturedMenuName.Buffer))
1910 {
1911 InvalidParameter:
1912 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1913 _SEH_LEAVE;
1914 }
1915
1916 /* Register the class */
1917 Ret = UserRegisterClass(&CapturedClassInfo,
1918 &CapturedName,
1919 &CapturedMenuName,
1920 hMenu, /* FIXME - pass pointer */
1921 wpExtra,
1922 Flags);
1923
1924 }
1925 _SEH_HANDLE
1926 {
1927 SetLastNtError(_SEH_GetExceptionCode());
1928 }
1929 _SEH_END;
1930
1931 UserLeave();
1932
1933 return Ret;
1934 }
1935
1936
1937
1938 ULONG_PTR NTAPI
1939 NtUserGetClassLong(IN HWND hWnd,
1940 IN INT Offset,
1941 IN BOOL Ansi)
1942 {
1943 PWINDOW_OBJECT Window;
1944 ULONG_PTR Ret = 0;
1945
1946 if (Offset != GCLP_WNDPROC)
1947 {
1948 UserEnterShared();
1949 }
1950 else
1951 {
1952 UserEnterExclusive();
1953 }
1954
1955 Window = UserGetWindowObject(hWnd);
1956 if (Window != NULL)
1957 {
1958 Ret = UserGetClassLongPtr(Window->Class,
1959 Offset,
1960 Ansi);
1961
1962 if (Ret != 0 && Offset == GCLP_MENUNAME && Window->Class->MenuNameIsString)
1963 {
1964 Ret = (ULONG_PTR)UserHeapAddressToUser((PVOID)Ret);
1965 }
1966 }
1967
1968 UserLeave();
1969
1970 return Ret;
1971 }
1972
1973
1974
1975 ULONG_PTR STDCALL
1976 NtUserSetClassLong(HWND hWnd,
1977 INT Offset,
1978 ULONG_PTR dwNewLong,
1979 BOOL Ansi)
1980 {
1981 PW32PROCESSINFO pi;
1982 PWINDOW_OBJECT Window;
1983 ULONG_PTR Ret = 0;
1984
1985 UserEnterExclusive();
1986
1987 pi = GetW32ProcessInfo();
1988 if (pi == NULL)
1989 goto Cleanup;
1990
1991 Window = UserGetWindowObject(hWnd);
1992 if (Window != NULL)
1993 {
1994 if (Window->ti->kpi != pi)
1995 {
1996 SetLastWin32Error(ERROR_ACCESS_DENIED);
1997 goto Cleanup;
1998 }
1999
2000 _SEH_TRY
2001 {
2002 UNICODE_STRING Value;
2003
2004 /* probe the parameters */
2005 if (Offset == GCW_ATOM || Offset == GCLP_MENUNAME)
2006 {
2007 Value = ProbeForReadUnicodeString((PUNICODE_STRING)dwNewLong);
2008 if (Value.Length & 1)
2009 {
2010 goto InvalidParameter;
2011 }
2012
2013 if (Value.Length != 0)
2014 {
2015 ProbeForRead(Value.Buffer,
2016 Value.Length,
2017 sizeof(WCHAR));
2018 }
2019 else
2020 {
2021 if (Offset == GCW_ATOM && !IS_ATOM(Value.Buffer))
2022 {
2023 goto InvalidParameter;
2024 }
2025 else if (Offset == GCLP_MENUNAME && !IS_INTRESOURCE(Value.Buffer))
2026 {
2027 InvalidParameter:
2028 SetLastWin32Error(ERROR_INVALID_PARAMETER);
2029 _SEH_LEAVE;
2030 }
2031 }
2032
2033 dwNewLong = (ULONG_PTR)&Value;
2034 }
2035
2036 Ret = UserSetClassLongPtr(Window->Class,
2037 Offset,
2038 dwNewLong,
2039 Ansi);
2040 }
2041 _SEH_HANDLE
2042 {
2043 SetLastNtError(_SEH_GetExceptionCode());
2044 }
2045 _SEH_END;
2046 }
2047
2048 Cleanup:
2049 UserLeave();
2050
2051 return Ret;
2052 }
2053
2054 DWORD STDCALL
2055 NtUserSetClassWord(DWORD Unknown0,
2056 DWORD Unknown1,
2057 DWORD Unknown2)
2058 {
2059 return(0);
2060 }
2061
2062 BOOL NTAPI
2063 NtUserUnregisterClass(IN PUNICODE_STRING ClassNameOrAtom,
2064 IN HINSTANCE hInstance)
2065 {
2066 UNICODE_STRING CapturedClassName;
2067 BOOL Ret = FALSE;
2068
2069 UserEnterExclusive();
2070
2071 _SEH_TRY
2072 {
2073 /* probe the paramters */
2074 CapturedClassName = ProbeForReadUnicodeString(ClassNameOrAtom);
2075 if (CapturedClassName.Length & 1)
2076 {
2077 goto InvalidParameter;
2078 }
2079
2080 if (CapturedClassName.Length != 0)
2081 {
2082 ProbeForRead(CapturedClassName.Buffer,
2083 CapturedClassName.Length,
2084 sizeof(WCHAR));
2085 }
2086 else
2087 {
2088 if (!IS_ATOM(CapturedClassName.Buffer))
2089 {
2090 InvalidParameter:
2091 SetLastWin32Error(ERROR_INVALID_PARAMETER);
2092 _SEH_LEAVE;
2093 }
2094 }
2095
2096 /* unregister the class */
2097 Ret = UserUnregisterClass(&CapturedClassName,
2098 hInstance);
2099 }
2100 _SEH_HANDLE
2101 {
2102 SetLastNtError(_SEH_GetExceptionCode());
2103 }
2104 _SEH_END;
2105
2106 UserLeave();
2107
2108 return Ret;
2109 }
2110
2111 /* NOTE: for system classes hInstance is not NULL here, but User32Instance */
2112 BOOL STDCALL
2113 NtUserGetClassInfo(
2114 HINSTANCE hInstance,
2115 PUNICODE_STRING ClassName,
2116 LPWNDCLASSEXW lpWndClassEx,
2117 BOOL Ansi)
2118 {
2119 UNICODE_STRING CapturedClassName;
2120 PWINDOWCLASS Class;
2121 RTL_ATOM ClassAtom;
2122 PW32PROCESSINFO pi;
2123 BOOL Ret = FALSE;
2124
2125 /* NOTE: need exclusive lock because getting the wndproc might require the
2126 creation of a call procedure handle */
2127 UserEnterExclusive();
2128
2129 pi = GetW32ProcessInfo();
2130 if (pi == NULL)
2131 {
2132 ERR("GetW32ProcessInfo() returned NULL!\n");
2133 goto Cleanup;
2134 }
2135 _SEH_TRY
2136 {
2137 /* probe the paramters */
2138 CapturedClassName = ProbeForReadUnicodeString(ClassName);
2139
2140 if (CapturedClassName.Length == 0)
2141 TRACE("hInst %p atom %04X lpWndClassEx %p Ansi %d\n", hInstance, CapturedClassName.Buffer, lpWndClassEx, Ansi);
2142 else
2143 TRACE("hInst %p class %wZ lpWndClassEx %p Ansi %d\n", hInstance, &CapturedClassName, lpWndClassEx, Ansi);
2144
2145 if (CapturedClassName.Length & 1)
2146 {
2147 goto InvalidParameter;
2148 }
2149
2150 if (CapturedClassName.Length != 0)
2151 {
2152 ProbeForRead(CapturedClassName.Buffer,
2153 CapturedClassName.Length,
2154 sizeof(WCHAR));
2155 }
2156 else
2157 {
2158 if (!IS_ATOM(CapturedClassName.Buffer))
2159 {
2160 ERR("NtUserGetClassInfo() got ClassName instead of Atom!\n");
2161 goto InvalidParameter;
2162 }
2163 }
2164
2165 if (ProbeForReadUint(&lpWndClassEx->cbSize) != sizeof(WNDCLASSEXW))
2166 {
2167 InvalidParameter:
2168 SetLastWin32Error(ERROR_INVALID_PARAMETER);
2169 _SEH_LEAVE;
2170 }
2171
2172 ProbeForWrite(lpWndClassEx,
2173 sizeof(WNDCLASSEXW),
2174 sizeof(ULONG));
2175
2176 ClassAtom = IntGetClassAtom(&CapturedClassName,
2177 hInstance,
2178 pi,
2179 &Class,
2180 NULL);
2181 if (ClassAtom != (RTL_ATOM)0)
2182 {
2183 if (hInstance == NULL)
2184 hInstance = pi->hModUser;
2185
2186 Ret = UserGetClassInfo(Class,
2187 lpWndClassEx,
2188 Ansi,
2189 hInstance);
2190
2191 if (Ret)
2192 {
2193 lpWndClassEx->lpszClassName = CapturedClassName.Buffer;
2194 /* FIXME - handle Class->Desktop == NULL!!!!! */
2195
2196 if (Class->MenuName != NULL && Class->MenuNameIsString)
2197 {
2198 lpWndClassEx->lpszMenuName = UserHeapAddressToUser(Ansi ?
2199 (PVOID)Class->AnsiMenuName :
2200 (PVOID)Class->MenuName);
2201 }
2202
2203 /* Undocumented behavior! Return the class atom as a BOOL! */
2204 Ret = (BOOL)ClassAtom;
2205 }
2206 }
2207 else
2208 {
2209 if (CapturedClassName.Length == 0)
2210 WARN("Tried to get information of a non-existing class atom 0x%p\n", CapturedClassName.Buffer);
2211 else
2212 WARN("Tried to get information of a non-existing class \"%wZ\"\n", &CapturedClassName);
2213 SetLastWin32Error(ERROR_CLASS_DOES_NOT_EXIST);
2214 }
2215 }
2216 _SEH_HANDLE
2217 {
2218 SetLastWin32Error(ERROR_CLASS_DOES_NOT_EXIST);
2219 }
2220 _SEH_END;
2221
2222 Cleanup:
2223 UserLeave();
2224 return Ret;
2225 }
2226
2227
2228
2229 INT NTAPI
2230 NtUserGetClassName (IN HWND hWnd,
2231 OUT PUNICODE_STRING ClassName,
2232 IN BOOL Ansi)
2233 {
2234 PWINDOW_OBJECT Window;
2235 UNICODE_STRING CapturedClassName;
2236 INT Ret = 0;
2237
2238 UserEnterShared();
2239
2240 Window = UserGetWindowObject(hWnd);
2241 if (Window != NULL)
2242 {
2243 _SEH_TRY
2244 {
2245 ProbeForWriteUnicodeString(ClassName);
2246 CapturedClassName = *ClassName;
2247
2248 /* get the class name */
2249 Ret = UserGetClassName(Window->Class,
2250 &CapturedClassName,
2251 Ansi);
2252
2253 if (Ret != 0)
2254 {
2255 /* update the Length field */
2256 ClassName->Length = CapturedClassName.Length;
2257 }
2258 }
2259 _SEH_HANDLE
2260 {
2261 SetLastNtError(_SEH_GetExceptionCode());
2262 }
2263 _SEH_END;
2264 }
2265
2266 UserLeave();
2267
2268 return Ret;
2269 }
2270
2271 DWORD STDCALL
2272 NtUserGetWOWClass(DWORD Unknown0,
2273 DWORD Unknown1)
2274 {
2275 return(0);
2276 }
2277
2278 /* EOF */