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