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