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