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