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