2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
4 * PURPOSE: Window classes
5 * FILE: subsystems/win32/win32k/ntuser/class.c
6 * PROGRAMER: Thomas Weidenmueller <w3seek@reactos.com>
10 DBG_DEFAULT_CHANNEL(UserClass
);
12 REGISTER_SYSCLASS DefaultServerClasses
[] =
14 { ((PWSTR
)((ULONG_PTR
)(WORD
)(0x8001))),
15 CS_GLOBALCLASS
|CS_DBLCLKS
,
19 (HBRUSH
)(COLOR_BACKGROUND
+ 1),
23 { ((PWSTR
)((ULONG_PTR
)(WORD
)(0x8003))),
24 CS_VREDRAW
|CS_HREDRAW
|CS_SAVEBITS
,
25 NULL
, // Use User32 procs
32 { ((PWSTR
)((ULONG_PTR
)(WORD
)(0x8000))),
33 CS_DBLCLKS
|CS_SAVEBITS
,
34 NULL
, // Use User32 procs
37 (HBRUSH
)(COLOR_MENU
+ 1),
42 CS_DBLCLKS
|CS_VREDRAW
|CS_HREDRAW
|CS_PARENTDC
,
43 NULL
, // Use User32 procs
44 sizeof(SBWND
)-sizeof(WND
),
50 { ((PWSTR
)((ULONG_PTR
)(WORD
)(0x8004))), // IconTitle is here for now...
52 NULL
, // Use User32 procs
61 NULL
, // Use User32 procs
75 { /* Function Ids to Class indexes. */
76 { FNID_SCROLLBAR
, ICLS_SCROLLBAR
},
77 { FNID_ICONTITLE
, ICLS_ICONTITLE
},
78 { FNID_MENU
, ICLS_MENU
},
79 { FNID_DESKTOP
, ICLS_DESKTOP
},
80 { FNID_SWITCH
, ICLS_SWITCH
},
81 { FNID_MESSAGEWND
, ICLS_HWNDMESSAGE
},
82 { FNID_BUTTON
, ICLS_BUTTON
},
83 { FNID_COMBOBOX
, ICLS_COMBOBOX
},
84 { FNID_COMBOLBOX
, ICLS_COMBOLBOX
},
85 { FNID_DIALOG
, ICLS_DIALOG
},
86 { FNID_EDIT
, ICLS_EDIT
},
87 { FNID_LISTBOX
, ICLS_LISTBOX
},
88 { FNID_MDICLIENT
, ICLS_MDICLIENT
},
89 { FNID_STATIC
, ICLS_STATIC
},
90 { FNID_IME
, ICLS_IME
},
91 { FNID_GHOST
, ICLS_GHOST
},
92 { FNID_TOOLTIPS
, ICLS_TOOLTIPS
}
97 LookupFnIdToiCls(int FnId
, int *iCls
)
101 for ( i
= 0; i
< ARRAYSIZE(FnidToiCls
); i
++)
103 if (FnidToiCls
[i
].FnId
== FnId
)
105 if (iCls
) *iCls
= FnidToiCls
[i
].ClsId
;
113 /* WINDOWCLASS ***************************************************************/
116 IntFreeClassMenuName(IN OUT PCLS Class
)
118 /* Free the menu name, if it was changed and allocated */
119 if (Class
->lpszClientUnicodeMenuName
!= NULL
&& Class
->MenuNameIsString
)
121 UserHeapFree(Class
->lpszClientUnicodeMenuName
);
122 Class
->lpszClientUnicodeMenuName
= NULL
;
123 Class
->lpszClientAnsiMenuName
= NULL
;
128 IntDestroyClass(IN OUT PCLS Class
)
131 /* There shouldn't be any clones anymore */
132 ASSERT(Class
->cWndReferenceCount
== 0);
133 ASSERT(Class
->pclsClone
== NULL
);
135 if (Class
->pclsBase
== Class
)
137 PCALLPROCDATA CallProc
, NextCallProc
;
139 /* Destroy allocated callproc handles */
140 CallProc
= Class
->spcpdFirst
;
141 while (CallProc
!= NULL
)
143 NextCallProc
= CallProc
->spcpdNext
;
145 CallProc
->spcpdNext
= NULL
;
146 DestroyCallProc(NULL
,
149 CallProc
= NextCallProc
;
154 DceFreeClassDCE(((PDCE
)Class
->pdce
)->hDC
);
158 IntFreeClassMenuName(Class
);
161 pDesk
= Class
->rpdeskParent
;
162 Class
->rpdeskParent
= NULL
;
164 /* Free the structure */
167 DesktopHeapFree(pDesk
, Class
);
176 /* Clean all process classes. all process windows must cleaned first!! */
177 void FASTCALL
DestroyProcessClasses(PPROCESSINFO Process
)
180 PPROCESSINFO pi
= (PPROCESSINFO
)Process
;
184 /* Free all local classes */
185 Class
= pi
->pclsPrivateList
;
186 while (Class
!= NULL
)
188 pi
->pclsPrivateList
= Class
->pclsNext
;
190 ASSERT(Class
->pclsBase
== Class
);
191 IntDestroyClass(Class
);
193 Class
= pi
->pclsPrivateList
;
196 /* Free all global classes */
197 Class
= pi
->pclsPublicList
;
198 while (Class
!= NULL
)
200 pi
->pclsPublicList
= Class
->pclsNext
;
202 ASSERT(Class
->pclsBase
== Class
);
203 IntDestroyClass(Class
);
205 Class
= pi
->pclsPublicList
;
211 IntRegisterClassAtom(IN PUNICODE_STRING ClassName
,
218 if (ClassName
->Length
!= 0)
220 /* FIXME: Don't limit to 64 characters! Use SEH when allocating memory! */
221 if (ClassName
->Length
/ sizeof(WCHAR
) >= sizeof(szBuf
) / sizeof(szBuf
[0]))
223 EngSetLastError(ERROR_INVALID_PARAMETER
);
230 szBuf
[ClassName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
234 AtomName
= ClassName
->Buffer
;
236 Status
= RtlAddAtomToAtomTable(gAtomTable
,
240 if (!NT_SUCCESS(Status
))
242 SetLastNtError(Status
);
250 IntDeregisterClassAtom(IN RTL_ATOM Atom
)
252 return RtlDeleteAtomFromAtomTable(gAtomTable
,
257 UserAddCallProcToClass(IN OUT PCLS Class
,
258 IN PCALLPROCDATA CallProc
)
262 ASSERT(CallProc
->spcpdNext
== NULL
);
264 BaseClass
= Class
->pclsBase
;
265 ASSERT(CallProc
->spcpdNext
== NULL
);
266 CallProc
->spcpdNext
= BaseClass
->spcpdFirst
;
267 BaseClass
->spcpdFirst
= CallProc
;
269 /* Update all clones */
270 Class
= Class
->pclsClone
;
271 while (Class
!= NULL
)
273 Class
->spcpdFirst
= BaseClass
->spcpdFirst
;
274 Class
= Class
->pclsNext
;
279 IntSetClassAtom(IN OUT PCLS Class
,
280 IN PUNICODE_STRING ClassName
)
282 RTL_ATOM Atom
= (RTL_ATOM
)0;
284 /* Update the base class first */
285 Class
= Class
->pclsBase
;
287 if (!IntRegisterClassAtom(ClassName
,
293 IntDeregisterClassAtom(Class
->atomClassName
);
295 Class
->atomClassName
= Atom
;
297 /* Update the clones */
298 Class
= Class
->pclsClone
;
299 while (Class
!= NULL
)
301 Class
->atomClassName
= Atom
;
303 Class
= Class
->pclsNext
;
310 // Same as User32:IntGetClsWndProc.
313 IntGetClassWndProc(PCLS Class
, BOOL Ansi
)
316 WNDPROC gcpd
= NULL
, Ret
= NULL
;
318 if (Class
->CSF_flags
& CSF_SERVERSIDEPROC
)
320 for ( i
= FNID_FIRST
; i
<= FNID_SWITCH
; i
++)
322 if (GETPFNSERVER(i
) == Class
->lpfnWndProc
)
325 Ret
= GETPFNCLIENTA(i
);
327 Ret
= GETPFNCLIENTW(i
);
332 Ret
= Class
->lpfnWndProc
;
334 if (Class
->fnid
<= FNID_GHOST
&& Class
->fnid
>= FNID_BUTTON
)
338 if (GETPFNCLIENTW(Class
->fnid
) == Class
->lpfnWndProc
)
339 Ret
= GETPFNCLIENTA(Class
->fnid
);
343 if (GETPFNCLIENTA(Class
->fnid
) == Class
->lpfnWndProc
)
344 Ret
= GETPFNCLIENTW(Class
->fnid
);
348 if ( Ret
!= Class
->lpfnWndProc
||
349 Ansi
== !!(Class
->CSF_flags
& CSF_ANSIPROC
) )
352 gcpd
= (WNDPROC
)UserGetCPD( Class
,
353 (Ansi
? UserGetCPDA2U
: UserGetCPDU2A
)|UserGetCPDClass
,
356 return (gcpd
? gcpd
: Ret
);
362 IntSetClassWndProc(IN OUT PCLS Class
,
368 WNDPROC Ret
, chWndProc
;
370 Ret
= IntGetClassWndProc(Class
, Ansi
);
372 // If Server Side, downgrade to Client Side.
373 if (Class
->CSF_flags
& CSF_SERVERSIDEPROC
)
375 if (Ansi
) Class
->CSF_flags
|= CSF_ANSIPROC
;
376 Class
->CSF_flags
&= ~CSF_SERVERSIDEPROC
;
377 Class
->Unicode
= !Ansi
;
380 if (!WndProc
) WndProc
= Class
->lpfnWndProc
;
384 // Check if CallProc handle and retrieve previous call proc address and set.
385 if (IsCallProcHandle(WndProc
))
387 pcpd
= UserGetObject(gHandleTable
, WndProc
, otCallProc
);
388 if (pcpd
) chWndProc
= pcpd
->pfnClientPrevious
;
391 Class
->lpfnWndProc
= chWndProc
;
396 // Switch from Client Side call to Server Side call if match. Ref: "deftest".
397 for ( i
= FNID_FIRST
; i
<= FNID_SWITCH
; i
++)
399 if (GETPFNCLIENTW(i
) == Class
->lpfnWndProc
)
401 chWndProc
= GETPFNSERVER(i
);
404 if (GETPFNCLIENTA(i
) == Class
->lpfnWndProc
)
406 chWndProc
= GETPFNSERVER(i
);
410 // If match, set/reset to Server Side and clear ansi.
413 Class
->lpfnWndProc
= chWndProc
;
414 Class
->Unicode
= TRUE
;
415 Class
->CSF_flags
&= ~CSF_ANSIPROC
;
416 Class
->CSF_flags
|= CSF_SERVERSIDEPROC
;
420 Class
->Unicode
= !Ansi
;
423 Class
->CSF_flags
|= CSF_ANSIPROC
;
425 Class
->CSF_flags
&= ~CSF_ANSIPROC
;
428 /* Update the clones */
429 chWndProc
= Class
->lpfnWndProc
;
431 Class
= Class
->pclsClone
;
432 while (Class
!= NULL
)
434 Class
->Unicode
= !Ansi
;
435 Class
->lpfnWndProc
= chWndProc
;
437 Class
= Class
->pclsNext
;
444 IntGetClassForDesktop(IN OUT PCLS BaseClass
,
445 IN OUT PCLS
*ClassLink
,
451 ASSERT(Desktop
!= NULL
);
452 ASSERT(BaseClass
->pclsBase
== BaseClass
);
454 if (BaseClass
->rpdeskParent
== Desktop
)
456 /* It is most likely that a window is created on the same
457 desktop as the window class. */
462 if (BaseClass
->rpdeskParent
== NULL
)
464 ASSERT(BaseClass
->cWndReferenceCount
== 0);
465 ASSERT(BaseClass
->pclsClone
== NULL
);
467 /* Classes are also located in the shared heap when the class
468 was created before the thread attached to a desktop. As soon
469 as a window is created for such a class located on the shared
470 heap, the class is cloned into the desktop heap on which the
471 window is created. */
476 /* The user is asking for a class object on a different desktop,
478 Class
= BaseClass
->pclsClone
;
479 while (Class
!= NULL
)
481 if (Class
->rpdeskParent
== Desktop
)
483 ASSERT(Class
->pclsBase
== BaseClass
);
484 ASSERT(Class
->pclsClone
== NULL
);
488 Class
= Class
->pclsNext
;
494 /* The window is created on a different desktop, we need to
495 clone the class object to the desktop heap of the window! */
496 ClassSize
= sizeof(*BaseClass
) + (SIZE_T
)BaseClass
->cbclsExtra
;
498 Class
= DesktopHeapAlloc(Desktop
,
502 /* Simply clone the class */
503 RtlCopyMemory( Class
, BaseClass
, ClassSize
);
505 TRACE("Clone Class 0x%x hM 0x%x\n %S\n",Class
, Class
->hModule
, Class
->lpszClientUnicodeMenuName
);
507 /* Restore module address if default user class Ref: Bug 4778 */
508 if ( Class
->hModule
!= hModClient
&&
509 Class
->fnid
<= FNID_GHOST
&&
510 Class
->fnid
>= FNID_BUTTON
)
512 Class
->hModule
= hModClient
;
513 TRACE("Clone Class 0x%x Reset hM 0x%x\n",Class
, Class
->hModule
);
516 /* Update some pointers and link the class */
517 Class
->rpdeskParent
= Desktop
;
518 Class
->cWndReferenceCount
= 0;
520 if (BaseClass
->rpdeskParent
== NULL
)
522 /* We don't really need the base class on the shared
523 heap anymore, delete it so the only class left is
524 the clone we just created, which now serves as the
526 ASSERT(BaseClass
->pclsClone
== NULL
);
527 ASSERT(Class
->pclsClone
== NULL
);
528 Class
->pclsBase
= Class
;
529 Class
->pclsNext
= BaseClass
->pclsNext
;
531 /* Replace the base class */
532 (void)InterlockedExchangePointer((PVOID
*)ClassLink
,
535 /* Destroy the obsolete copy on the shared heap */
536 BaseClass
->pclsBase
= NULL
;
537 BaseClass
->pclsClone
= NULL
;
538 IntDestroyClass(BaseClass
);
542 /* Link in the clone */
543 Class
->pclsClone
= NULL
;
544 Class
->pclsBase
= BaseClass
;
545 Class
->pclsNext
= BaseClass
->pclsClone
;
546 (void)InterlockedExchangePointer((PVOID
*)&BaseClass
->pclsClone
,
552 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
559 IntReferenceClass(IN OUT PCLS BaseClass
,
560 IN OUT PCLS
*ClassLink
,
564 ASSERT(BaseClass
->pclsBase
== BaseClass
);
566 Class
= IntGetClassForDesktop(BaseClass
,
571 Class
->cWndReferenceCount
++;
579 IntMakeCloneBaseClass(IN OUT PCLS Class
,
580 IN OUT PCLS
*BaseClassLink
,
581 IN OUT PCLS
*CloneLink
)
585 ASSERT(Class
->pclsBase
!= Class
);
586 ASSERT(Class
->pclsBase
->pclsClone
!= NULL
);
587 ASSERT(Class
->rpdeskParent
!= NULL
);
588 ASSERT(Class
->cWndReferenceCount
!= 0);
589 ASSERT(Class
->pclsBase
->rpdeskParent
!= NULL
);
590 ASSERT(Class
->pclsBase
->cWndReferenceCount
== 0);
592 /* Unlink the clone */
593 *CloneLink
= Class
->pclsNext
;
594 Class
->pclsClone
= Class
->pclsBase
->pclsClone
;
596 /* Update the class information to make it a base class */
597 Class
->pclsBase
= Class
;
598 Class
->pclsNext
= (*BaseClassLink
)->pclsNext
;
600 /* Update all clones */
601 Clone
= Class
->pclsClone
;
602 while (Clone
!= NULL
)
604 ASSERT(Clone
->pclsClone
== NULL
);
605 Clone
->pclsBase
= Class
;
607 Clone
= Clone
->pclsNext
;
610 /* Link in the new base class */
611 (void)InterlockedExchangePointer((PVOID
*)BaseClassLink
,
617 IntDereferenceClass(IN OUT PCLS Class
,
618 IN PDESKTOPINFO Desktop
,
621 PCLS
*PrevLink
, BaseClass
, CurrentClass
;
623 BaseClass
= Class
->pclsBase
;
625 if (--Class
->cWndReferenceCount
<= 0)
627 if (BaseClass
== Class
)
629 ASSERT(Class
->pclsBase
== Class
);
631 TRACE("IntDereferenceClass 0x%x\n", Class
);
632 /* Check if there are clones of the class on other desktops,
633 link the first clone in if possible. If there are no clones
634 then leave the class on the desktop heap. It will get moved
635 to the shared heap when the thread detaches. */
636 if (BaseClass
->pclsClone
!= NULL
)
638 if (BaseClass
->Global
)
639 PrevLink
= &pi
->pclsPublicList
;
641 PrevLink
= &pi
->pclsPrivateList
;
643 CurrentClass
= *PrevLink
;
644 while (CurrentClass
!= BaseClass
)
646 ASSERT(CurrentClass
!= NULL
);
648 PrevLink
= &CurrentClass
->pclsNext
;
649 CurrentClass
= CurrentClass
->pclsNext
;
652 ASSERT(*PrevLink
== BaseClass
);
654 /* Make the first clone become the new base class */
655 IntMakeCloneBaseClass(BaseClass
->pclsClone
,
657 &BaseClass
->pclsClone
);
659 /* Destroy the class, there's still another clone of the class
660 that now serves as a base class. Make sure we don't destruct
661 resources shared by all classes (Base = NULL)! */
662 BaseClass
->pclsBase
= NULL
;
663 BaseClass
->pclsClone
= NULL
;
664 IntDestroyClass(BaseClass
);
669 TRACE("IntDereferenceClass1 0x%x\n", Class
);
671 /* Locate the cloned class and unlink it */
672 PrevLink
= &BaseClass
->pclsClone
;
673 CurrentClass
= BaseClass
->pclsClone
;
674 while (CurrentClass
!= Class
)
676 ASSERT(CurrentClass
!= NULL
);
678 PrevLink
= &CurrentClass
->pclsNext
;
679 CurrentClass
= CurrentClass
->pclsNext
;
682 ASSERT(CurrentClass
== Class
);
684 (void)InterlockedExchangePointer((PVOID
*)PrevLink
,
687 ASSERT(Class
->pclsBase
== BaseClass
);
688 ASSERT(Class
->pclsClone
== NULL
);
690 /* The class was just a clone, we don't need it anymore */
691 IntDestroyClass(Class
);
697 IntMoveClassToSharedHeap(IN OUT PCLS Class
,
698 IN OUT PCLS
**ClassLinkPtr
)
703 ASSERT(Class
->pclsBase
== Class
);
704 ASSERT(Class
->rpdeskParent
!= NULL
);
705 ASSERT(Class
->cWndReferenceCount
== 0);
706 ASSERT(Class
->pclsClone
== NULL
);
708 ClassSize
= sizeof(*Class
) + (SIZE_T
)Class
->cbclsExtra
;
710 /* Allocate the new base class on the shared heap */
711 NewClass
= UserHeapAlloc(ClassSize
);
712 if (NewClass
!= NULL
)
714 RtlCopyMemory(NewClass
,
718 NewClass
->rpdeskParent
= NULL
;
719 NewClass
->pclsBase
= NewClass
;
721 /* Replace the class in the list */
722 (void)InterlockedExchangePointer((PVOID
*)*ClassLinkPtr
,
724 *ClassLinkPtr
= &NewClass
->pclsNext
;
726 /* Free the obsolete class on the desktop heap */
727 Class
->pclsBase
= NULL
;
728 IntDestroyClass(Class
);
736 IntCheckDesktopClasses(IN PDESKTOP Desktop
,
737 IN OUT PCLS
*ClassList
,
738 IN BOOL FreeOnFailure
,
741 PCLS Class
, NextClass
, *Link
;
743 /* NOTE: We only need to check base classes! When classes are no longer needed
744 on a desktop, the clones will be freed automatically as soon as possible.
745 However, we need to move base classes to the shared heap, as soon as
746 the last desktop heap where a class is allocated on is about to be destroyed.
747 If we didn't move the class to the shared heap, the class would become
750 ASSERT(Desktop
!= NULL
);
754 while (Class
!= NULL
)
756 NextClass
= Class
->pclsNext
;
758 ASSERT(Class
->pclsBase
== Class
);
760 if (Class
->rpdeskParent
== Desktop
&&
761 Class
->cWndReferenceCount
== 0)
763 /* There shouldn't be any clones around anymore! */
764 ASSERT(Class
->pclsClone
== NULL
);
766 /* FIXME: If process is terminating, don't move the class but rather destroy it! */
767 /* FIXME: We could move the class to another desktop heap if there's still desktops
768 mapped into the process... */
770 /* Move the class to the shared heap */
771 if (IntMoveClassToSharedHeap(Class
,
774 ASSERT(*Link
== NextClass
);
778 ASSERT(NextClass
== Class
->pclsNext
);
782 /* Unlink the base class */
783 (void)InterlockedExchangePointer((PVOID
*)Link
,
786 /* We can free the old base class now */
787 Class
->pclsBase
= NULL
;
788 IntDestroyClass(Class
);
792 Link
= &Class
->pclsNext
;
798 Link
= &Class
->pclsNext
;
805 IntCheckProcessDesktopClasses(IN PDESKTOP Desktop
,
806 IN BOOL FreeOnFailure
)
811 pi
= GetW32ProcessInfo();
813 /* Check all local classes */
814 IntCheckDesktopClasses(Desktop
,
815 &pi
->pclsPrivateList
,
819 /* Check all global classes */
820 IntCheckDesktopClasses(Desktop
,
826 ERR("Failed to move process classes from desktop 0x%p to the shared heap!\n", Desktop
);
827 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
835 IntCreateClass(IN CONST WNDCLASSEXW
* lpwcx
,
836 IN PUNICODE_STRING ClassName
,
837 IN PUNICODE_STRING MenuName
,
847 PWSTR pszMenuName
= NULL
;
848 NTSTATUS Status
= STATUS_SUCCESS
;
850 TRACE("lpwcx=%p ClassName=%wZ MenuName=%wZ dwFlags=%08x Desktop=%p pi=%p\n",
851 lpwcx
, ClassName
, MenuName
, dwFlags
, Desktop
, pi
);
853 if (!IntRegisterClassAtom(ClassName
,
856 ERR("Failed to register class atom!\n");
860 ClassSize
= sizeof(*Class
) + lpwcx
->cbClsExtra
;
861 if (MenuName
->Length
!= 0)
863 pszMenuName
= UserHeapAlloc(MenuName
->Length
+ sizeof(UNICODE_NULL
) +
864 RtlUnicodeStringToAnsiSize(MenuName
));
865 if (pszMenuName
== NULL
)
871 Class
= DesktopHeapAlloc(Desktop
,
876 /* FIXME: The class was created before being connected
877 to a desktop. It is possible for the desktop window,
878 but should it be allowed for any other case? */
879 Class
= UserHeapAlloc(ClassSize
);
886 RtlZeroMemory(Class
, ClassSize
);
888 Class
->rpdeskParent
= Desktop
;
889 Class
->pclsBase
= Class
;
890 Class
->atomClassName
= Atom
;
892 Class
->CSF_flags
= dwFlags
;
894 if (LookupFnIdToiCls(Class
->fnid
, &iCls
))
896 gpsi
->atomSysClass
[iCls
] = Class
->atomClassName
;
901 PWSTR pszMenuNameBuffer
= pszMenuName
;
903 /* Need to protect with SEH since accessing the WNDCLASSEX structure
904 and string buffers might raise an exception! We don't want to
906 // What?! If the user interface was written correctly this would not be an issue!
907 Class
->lpfnWndProc
= lpwcx
->lpfnWndProc
;
908 Class
->style
= lpwcx
->style
;
909 Class
->cbclsExtra
= lpwcx
->cbClsExtra
;
910 Class
->cbwndExtra
= lpwcx
->cbWndExtra
;
911 Class
->hModule
= lpwcx
->hInstance
;
912 Class
->hIcon
= lpwcx
->hIcon
; /* FIXME */
913 Class
->hIconSm
= lpwcx
->hIconSm
; /* FIXME */
914 Class
->hCursor
= lpwcx
->hCursor
; /* FIXME */
915 Class
->hbrBackground
= lpwcx
->hbrBackground
;
917 /* Make a copy of the string */
918 if (pszMenuNameBuffer
!= NULL
)
920 Class
->MenuNameIsString
= TRUE
;
922 Class
->lpszClientUnicodeMenuName
= pszMenuNameBuffer
;
923 RtlCopyMemory(Class
->lpszClientUnicodeMenuName
,
926 Class
->lpszClientUnicodeMenuName
[MenuName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
928 pszMenuNameBuffer
+= (MenuName
->Length
/ sizeof(WCHAR
)) + 1;
931 Class
->lpszClientUnicodeMenuName
= MenuName
->Buffer
;
933 /* Save an ANSI copy of the string */
934 if (pszMenuNameBuffer
!= NULL
)
936 ANSI_STRING AnsiString
;
938 Class
->lpszClientAnsiMenuName
= (PSTR
)pszMenuNameBuffer
;
939 AnsiString
.MaximumLength
= (USHORT
)RtlUnicodeStringToAnsiSize(MenuName
);
940 AnsiString
.Buffer
= Class
->lpszClientAnsiMenuName
;
941 Status
= RtlUnicodeStringToAnsiString(&AnsiString
,
944 if (!NT_SUCCESS(Status
))
946 ERR("Failed to convert unicode menu name to ansi!\n");
948 /* Life would've been much prettier if ntoskrnl exported RtlRaiseStatus()... */
953 Class
->lpszClientAnsiMenuName
= (PSTR
)MenuName
->Buffer
;
955 /* Save kernel use menu name and ansi class name */
956 Class
->lpszMenuName
= Class
->lpszClientUnicodeMenuName
; // FIXME!
957 //Class->lpszAnsiClassName = FIXME
959 /* Server Side overrides class calling type (A/W)!
960 User32 whine test_builtinproc: "deftest"
961 built-in winproc - window A/W type automatically detected */
962 if (!(Class
->CSF_flags
& CSF_SERVERSIDEPROC
))
966 /* Due to the wine class "deftest" and most likely no FNID to reference
967 from, sort through the Server Side list and compare proc addresses
968 for match. This method will be used in related code.
970 for ( i
= FNID_FIRST
; i
<= FNID_SWITCH
; i
++)
971 { // Open ANSI or Unicode, just match, set and break.
972 if (GETPFNCLIENTW(i
) == Class
->lpfnWndProc
)
974 WndProc
= GETPFNSERVER(i
);
977 if (GETPFNCLIENTA(i
) == Class
->lpfnWndProc
)
979 WndProc
= GETPFNSERVER(i
);
984 { // If a hit, we are Server Side so set the right flags and proc.
985 Class
->CSF_flags
|= CSF_SERVERSIDEPROC
;
986 Class
->CSF_flags
&= ~CSF_ANSIPROC
;
987 Class
->lpfnWndProc
= WndProc
;
991 if (!(Class
->CSF_flags
& CSF_ANSIPROC
))
992 Class
->Unicode
= TRUE
;
994 if (Class
->style
& CS_GLOBALCLASS
)
995 Class
->Global
= TRUE
;
997 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
999 Status
= _SEH2_GetExceptionCode();
1003 if (!NT_SUCCESS(Status
))
1005 ERR("Failed creating the class: 0x%x\n", Status
);
1007 SetLastNtError(Status
);
1009 if (pszMenuName
!= NULL
)
1010 UserHeapFree(pszMenuName
);
1012 DesktopHeapFree(Desktop
,
1016 IntDeregisterClassAtom(Atom
);
1022 ERR("Failed to allocate class on Desktop 0x%p\n", Desktop
);
1024 if (pszMenuName
!= NULL
)
1025 UserHeapFree(pszMenuName
);
1027 IntDeregisterClassAtom(Atom
);
1029 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1032 TRACE("Created class 0x%x with name %wZ and proc 0x%x for atom 0x%x and hInstance 0x%x, global %d\n",
1033 Class
, ClassName
, Class
->lpfnWndProc
, Atom
, Class
->hModule
, Class
->Global
);
1039 IntFindClass(IN RTL_ATOM Atom
,
1040 IN HINSTANCE hInstance
,
1042 OUT PCLS
**Link OPTIONAL
)
1044 PCLS Class
, *PrevLink
= ClassList
;
1047 while (Class
!= NULL
)
1049 if (Class
->atomClassName
== Atom
&&
1050 (hInstance
== NULL
|| Class
->hModule
== hInstance
) &&
1051 !(Class
->CSF_flags
& CSF_WOWDEFERDESTROY
))
1053 ASSERT(Class
->pclsBase
== Class
);
1060 PrevLink
= &Class
->pclsNext
;
1061 Class
= Class
->pclsNext
;
1068 IntGetAtomFromStringOrAtom(IN PUNICODE_STRING ClassName
,
1073 if (ClassName
->Length
!= 0)
1079 /* NOTE: Caller has to protect the call with SEH! */
1081 if (ClassName
->Length
!= 0)
1083 /* FIXME: Don't limit to 64 characters! use SEH when allocating memory! */
1084 if (ClassName
->Length
/ sizeof(WCHAR
) >= sizeof(szBuf
) / sizeof(szBuf
[0]))
1086 EngSetLastError(ERROR_INVALID_PARAMETER
);
1090 /* We need to make a local copy of the class name! The caller could
1091 modify the buffer and we could overflow in RtlLookupAtomInAtomTable.
1092 We're protected by SEH, but the ranges that might be accessed were
1094 RtlCopyMemory(szBuf
,
1097 szBuf
[ClassName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1101 AtomName
= ClassName
->Buffer
;
1103 /* Lookup the atom */
1104 Status
= RtlLookupAtomInAtomTable(gAtomTable
,
1107 if (NT_SUCCESS(Status
))
1113 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
1115 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
1119 SetLastNtError(Status
);
1125 ASSERT(IS_ATOM(ClassName
->Buffer
));
1126 *Atom
= (RTL_ATOM
)((ULONG_PTR
)ClassName
->Buffer
);
1134 IntGetClassAtom(IN PUNICODE_STRING ClassName
,
1135 IN HINSTANCE hInstance OPTIONAL
,
1136 IN PPROCESSINFO pi OPTIONAL
,
1137 OUT PCLS
*BaseClass OPTIONAL
,
1138 OUT PCLS
**Link OPTIONAL
)
1140 RTL_ATOM Atom
= (RTL_ATOM
)0;
1142 ASSERT(BaseClass
!= NULL
);
1144 if (IntGetAtomFromStringOrAtom(ClassName
,
1146 Atom
!= (RTL_ATOM
)0)
1150 /* Attempt to locate the class object */
1154 /* Step 1: Try to find an exact match of locally registered classes */
1155 Class
= IntFindClass(Atom
,
1157 &pi
->pclsPrivateList
,
1160 { TRACE("Step 1: 0x%x\n",Class
);
1164 /* Step 2: Try to find any globally registered class. The hInstance
1165 is not relevant for global classes */
1166 Class
= IntFindClass(Atom
,
1168 &pi
->pclsPublicList
,
1171 { TRACE("Step 2: 0x%x 0x%x\n",Class
, Class
->hModule
);
1175 /* Step 3: Try to find any local class registered by user32 */
1176 Class
= IntFindClass(Atom
,
1178 &pi
->pclsPrivateList
,
1181 { TRACE("Step 3: 0x%x\n",Class
);
1185 /* Step 4: Try to find any global class registered by user32 */
1186 Class
= IntFindClass(Atom
,
1188 &pi
->pclsPublicList
,
1192 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
1194 }else{TRACE("Step 4: 0x%x\n",Class
);}
1204 IntGetAndReferenceClass(PUNICODE_STRING ClassName
, HINSTANCE hInstance
)
1206 PCLS
*ClassLink
, Class
= NULL
;
1210 pti
= PsGetCurrentThreadWin32Thread();
1212 if ( !(pti
->ppi
->W32PF_flags
& W32PF_CLASSESREGISTERED
))
1214 UserRegisterSystemClasses();
1217 /* Check the class. */
1219 TRACE("Finding Class %wZ for hInstance 0x%x\n", ClassName
, hInstance
);
1221 ClassAtom
= IntGetClassAtom(ClassName
,
1227 if (ClassAtom
== (RTL_ATOM
)0)
1229 if (IS_ATOM(ClassName
->Buffer
))
1231 ERR("Class 0x%p not found\n", (DWORD_PTR
) ClassName
->Buffer
);
1235 ERR("Class \"%wZ\" not found\n", ClassName
);
1238 EngSetLastError(ERROR_CANNOT_FIND_WND_CLASS
);
1242 TRACE("Referencing Class 0x%x with atom 0x%x\n", Class
, ClassAtom
);
1243 Class
= IntReferenceClass(Class
,
1248 ERR("Failed to reference window class!\n");
1256 UserRegisterClass(IN CONST WNDCLASSEXW
* lpwcx
,
1257 IN PUNICODE_STRING ClassName
,
1258 IN PUNICODE_STRING MenuName
,
1266 RTL_ATOM Ret
= (RTL_ATOM
)0;
1268 /* NOTE: Accessing the buffers in ClassName and MenuName may raise exceptions! */
1270 pti
= GetW32ThreadInfo();
1274 // Need only to test for two conditions not four....... Fix more whine tests....
1275 if ( IntGetAtomFromStringOrAtom( ClassName
, &ClassAtom
) &&
1276 ClassAtom
!= (RTL_ATOM
)0 &&
1277 !(dwFlags
& CSF_SERVERSIDEPROC
) ) // Bypass Server Sides
1279 Class
= IntFindClass( ClassAtom
,
1281 &pi
->pclsPrivateList
,
1284 if (Class
!= NULL
&& !Class
->Global
)
1286 // Local class already exists
1287 TRACE("Local Class 0x%p does already exist!\n", ClassAtom
);
1288 EngSetLastError(ERROR_CLASS_ALREADY_EXISTS
);
1292 if (lpwcx
->style
& CS_GLOBALCLASS
)
1294 Class
= IntFindClass( ClassAtom
,
1296 &pi
->pclsPublicList
,
1299 if (Class
!= NULL
&& Class
->Global
)
1301 TRACE("Global Class 0x%p does already exist!\n", ClassAtom
);
1302 EngSetLastError(ERROR_CLASS_ALREADY_EXISTS
);
1308 Class
= IntCreateClass(lpwcx
,
1320 /* Register the class */
1322 List
= &pi
->pclsPublicList
;
1324 List
= &pi
->pclsPrivateList
;
1326 Class
->pclsNext
= *List
;
1327 (void)InterlockedExchangePointer((PVOID
*)List
,
1330 Ret
= Class
->atomClassName
;
1334 ERR("UserRegisterClass: Yes, that is right, you have no Class!\n");
1341 UserUnregisterClass(IN PUNICODE_STRING ClassName
,
1342 IN HINSTANCE hInstance
,
1343 OUT PCLSMENUNAME pClassMenuName
)
1350 pi
= GetW32ProcessInfo();
1352 TRACE("UserUnregisterClass(%wZ, 0x%x)\n", ClassName
, hInstance
);
1354 /* NOTE: Accessing the buffer in ClassName may raise an exception! */
1355 ClassAtom
= IntGetClassAtom(ClassName
,
1360 if (ClassAtom
== (RTL_ATOM
)0)
1362 TRACE("UserUnregisterClass: No Class found.\n");
1366 ASSERT(Class
!= NULL
);
1368 if (Class
->cWndReferenceCount
!= 0 ||
1369 Class
->pclsClone
!= NULL
)
1371 TRACE("UserUnregisterClass: Class has a Window. Ct %d : Clone 0x%x\n", Class
->cWndReferenceCount
, Class
->pclsClone
);
1372 EngSetLastError(ERROR_CLASS_HAS_WINDOWS
);
1376 /* Must be a base class! */
1377 ASSERT(Class
->pclsBase
== Class
);
1379 /* Unlink the class */
1380 *Link
= Class
->pclsNext
;
1382 if (NT_SUCCESS(IntDeregisterClassAtom(Class
->atomClassName
)))
1384 TRACE("Class 0x%x\n", Class
);
1385 TRACE("UserUnregisterClass: Good Exit!\n");
1386 /* Finally free the resources */
1387 IntDestroyClass(Class
);
1390 ERR("UserUnregisterClass: Can not deregister Class Atom.\n");
1395 UserGetClassName(IN PCLS Class
,
1396 IN OUT PUNICODE_STRING ClassName
,
1400 NTSTATUS Status
= STATUS_SUCCESS
;
1401 WCHAR szStaticTemp
[32];
1402 PWSTR szTemp
= NULL
;
1403 ULONG BufLen
= sizeof(szStaticTemp
);
1406 /* Note: Accessing the buffer in ClassName may raise an exception! */
1412 PANSI_STRING AnsiClassName
= (PANSI_STRING
)ClassName
;
1413 UNICODE_STRING UnicodeClassName
;
1415 /* Limit the size of the static buffer on the stack to the
1416 size of the buffer provided by the caller */
1417 if (BufLen
/ sizeof(WCHAR
) > AnsiClassName
->MaximumLength
)
1419 BufLen
= AnsiClassName
->MaximumLength
* sizeof(WCHAR
);
1422 /* Find out how big the buffer needs to be */
1423 Status
= RtlQueryAtomInAtomTable(gAtomTable
,
1424 Class
->atomClassName
,
1429 if (Status
== STATUS_BUFFER_TOO_SMALL
)
1431 if (BufLen
/ sizeof(WCHAR
) > AnsiClassName
->MaximumLength
)
1433 /* The buffer required exceeds the ansi buffer provided,
1434 pretend like we're using the ansi buffer and limit the
1435 size to the buffer size provided */
1436 BufLen
= AnsiClassName
->MaximumLength
* sizeof(WCHAR
);
1439 /* Allocate a temporary buffer that can hold the unicode class name */
1440 szTemp
= ExAllocatePoolWithTag(PagedPool
,
1445 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1449 /* Query the class name */
1450 Status
= RtlQueryAtomInAtomTable(gAtomTable
,
1451 Atom
? Atom
: Class
->atomClassName
,
1458 szTemp
= szStaticTemp
;
1460 if (NT_SUCCESS(Status
))
1462 /* Convert the atom name to ansi */
1464 RtlInitUnicodeString(&UnicodeClassName
,
1467 Status
= RtlUnicodeStringToAnsiString(AnsiClassName
,
1470 if (!NT_SUCCESS(Status
))
1472 SetLastNtError(Status
);
1477 Ret
= BufLen
/ sizeof(WCHAR
);
1481 BufLen
= ClassName
->MaximumLength
;
1483 /* Query the atom name */
1484 Status
= RtlQueryAtomInAtomTable(gAtomTable
,
1485 Atom
? Atom
: Class
->atomClassName
,
1491 if (!NT_SUCCESS(Status
))
1493 SetLastNtError(Status
);
1497 Ret
= BufLen
/ sizeof(WCHAR
);
1500 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1502 SetLastNtError(_SEH2_GetExceptionCode());
1506 if (Ansi
&& szTemp
!= NULL
&& szTemp
!= szStaticTemp
)
1515 IntSetClassMenuName(IN PCLS Class
,
1516 IN PUNICODE_STRING MenuName
)
1520 /* Change the base class first */
1521 Class
= Class
->pclsBase
;
1523 if (MenuName
->Length
!= 0)
1525 ANSI_STRING AnsiString
;
1528 AnsiString
.MaximumLength
= (USHORT
)RtlUnicodeStringToAnsiSize(MenuName
);
1530 strBufW
= UserHeapAlloc(MenuName
->Length
+ sizeof(UNICODE_NULL
) +
1531 AnsiString
.MaximumLength
);
1532 if (strBufW
!= NULL
)
1538 /* Copy the unicode string */
1539 RtlCopyMemory(strBufW
,
1542 strBufW
[MenuName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1544 /* Create an ANSI copy of the string */
1545 AnsiString
.Buffer
= (PSTR
)(strBufW
+ (MenuName
->Length
/ sizeof(WCHAR
)) + 1);
1546 Status
= RtlUnicodeStringToAnsiString(&AnsiString
,
1549 if (!NT_SUCCESS(Status
))
1551 SetLastNtError(Status
);
1557 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1559 SetLastNtError(_SEH2_GetExceptionCode());
1565 /* Update the base class */
1566 IntFreeClassMenuName(Class
);
1567 Class
->lpszClientUnicodeMenuName
= strBufW
;
1568 Class
->lpszClientAnsiMenuName
= AnsiString
.Buffer
;
1569 Class
->MenuNameIsString
= TRUE
;
1571 /* Update the clones */
1572 Class
= Class
->pclsClone
;
1573 while (Class
!= NULL
)
1575 Class
->lpszClientUnicodeMenuName
= strBufW
;
1576 Class
->lpszClientAnsiMenuName
= AnsiString
.Buffer
;
1577 Class
->MenuNameIsString
= TRUE
;
1579 Class
= Class
->pclsNext
;
1584 ERR("Failed to copy class menu name!\n");
1585 UserHeapFree(strBufW
);
1589 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1593 ASSERT(IS_INTRESOURCE(MenuName
->Buffer
));
1595 /* Update the base class */
1596 IntFreeClassMenuName(Class
);
1597 Class
->lpszClientUnicodeMenuName
= MenuName
->Buffer
;
1598 Class
->lpszClientAnsiMenuName
= (PSTR
)MenuName
->Buffer
;
1599 Class
->MenuNameIsString
= FALSE
;
1601 /* Update the clones */
1602 Class
= Class
->pclsClone
;
1603 while (Class
!= NULL
)
1605 Class
->lpszClientUnicodeMenuName
= MenuName
->Buffer
;
1606 Class
->lpszClientAnsiMenuName
= (PSTR
)MenuName
->Buffer
;
1607 Class
->MenuNameIsString
= FALSE
;
1609 Class
= Class
->pclsNext
;
1619 UserSetClassLongPtr(IN PCLS Class
,
1621 IN ULONG_PTR NewLong
,
1626 /* NOTE: For GCLP_MENUNAME and GCW_ATOM this function may raise an exception! */
1628 /* Change the information in the base class first, then update the clones */
1629 Class
= Class
->pclsBase
;
1635 TRACE("SetClassLong(%d, %x)\n", Index
, NewLong
);
1637 if (((ULONG
)Index
+ sizeof(ULONG_PTR
)) < (ULONG
)Index
||
1638 ((ULONG
)Index
+ sizeof(ULONG_PTR
)) > (ULONG
)Class
->cbclsExtra
)
1640 EngSetLastError(ERROR_INVALID_PARAMETER
);
1644 Data
= (PULONG_PTR
)((ULONG_PTR
)(Class
+ 1) + Index
);
1646 /* FIXME: Data might be a unaligned pointer! Might be a problem on
1647 certain architectures, maybe using RtlCopyMemory is a
1648 better choice for those architectures! */
1652 /* Update the clones */
1653 Class
= Class
->pclsClone
;
1654 while (Class
!= NULL
)
1656 *(PULONG_PTR
)((ULONG_PTR
)(Class
+ 1) + Index
) = NewLong
;
1657 Class
= Class
->pclsNext
;
1665 case GCL_CBWNDEXTRA
:
1666 Ret
= (ULONG_PTR
)Class
->cbwndExtra
;
1667 Class
->cbwndExtra
= (INT
)NewLong
;
1669 /* Update the clones */
1670 Class
= Class
->pclsClone
;
1671 while (Class
!= NULL
)
1673 Class
->cbwndExtra
= (INT
)NewLong
;
1674 Class
= Class
->pclsNext
;
1679 case GCL_CBCLSEXTRA
:
1680 EngSetLastError(ERROR_INVALID_PARAMETER
);
1683 case GCLP_HBRBACKGROUND
:
1684 Ret
= (ULONG_PTR
)Class
->hbrBackground
;
1685 Class
->hbrBackground
= (HBRUSH
)NewLong
;
1687 /* Update the clones */
1688 Class
= Class
->pclsClone
;
1689 while (Class
!= NULL
)
1691 Class
->hbrBackground
= (HBRUSH
)NewLong
;
1692 Class
= Class
->pclsNext
;
1697 /* FIXME: Get handle from pointer to CURSOR object */
1698 Ret
= (ULONG_PTR
)Class
->hCursor
;
1699 Class
->hCursor
= (HANDLE
)NewLong
;
1701 /* Update the clones */
1702 Class
= Class
->pclsClone
;
1703 while (Class
!= NULL
)
1705 Class
->hCursor
= (HANDLE
)NewLong
;
1706 Class
= Class
->pclsNext
;
1711 /* FIXME: Get handle from pointer to ICON object */
1712 Ret
= (ULONG_PTR
)Class
->hIcon
;
1713 Class
->hIcon
= (HANDLE
)NewLong
;
1715 /* Update the clones */
1716 Class
= Class
->pclsClone
;
1717 while (Class
!= NULL
)
1719 Class
->hIcon
= (HANDLE
)NewLong
;
1720 Class
= Class
->pclsNext
;
1725 /* FIXME: Get handle from pointer to ICON object */
1726 Ret
= (ULONG_PTR
)Class
->hIconSm
;
1727 Class
->hIconSm
= (HANDLE
)NewLong
;
1729 /* Update the clones */
1730 Class
= Class
->pclsClone
;
1731 while (Class
!= NULL
)
1733 Class
->hIconSm
= (HANDLE
)NewLong
;
1734 Class
= Class
->pclsNext
;
1739 Ret
= (ULONG_PTR
)Class
->hModule
;
1740 Class
->hModule
= (HINSTANCE
)NewLong
;
1742 /* Update the clones */
1743 Class
= Class
->pclsClone
;
1744 while (Class
!= NULL
)
1746 Class
->hModule
= (HINSTANCE
)NewLong
;
1747 Class
= Class
->pclsNext
;
1753 PUNICODE_STRING Value
= (PUNICODE_STRING
)NewLong
;
1755 if (!IntSetClassMenuName(Class
,
1758 ERR("Setting the class menu name failed!\n");
1761 /* FIXME: Really return NULL? Wine does so... */
1766 Ret
= (ULONG_PTR
)Class
->style
;
1767 Class
->style
= (UINT
)NewLong
;
1769 /* FIXME: What if the CS_GLOBALCLASS style is changed? should we
1770 move the class to the appropriate list? For now, we save
1771 the original value in Class->Global, so we can always
1772 locate the appropriate list */
1774 /* Update the clones */
1775 Class
= Class
->pclsClone
;
1776 while (Class
!= NULL
)
1778 Class
->style
= (UINT
)NewLong
;
1779 Class
= Class
->pclsNext
;
1784 Ret
= (ULONG_PTR
)IntSetClassWndProc(Class
,
1791 PUNICODE_STRING Value
= (PUNICODE_STRING
)NewLong
;
1793 Ret
= (ULONG_PTR
)Class
->atomClassName
;
1794 if (!IntSetClassAtom(Class
,
1803 EngSetLastError(ERROR_INVALID_INDEX
);
1811 UserGetClassInfo(IN PCLS Class
,
1812 OUT PWNDCLASSEXW lpwcx
,
1814 HINSTANCE hInstance
)
1816 if (!Class
) return FALSE
;
1818 lpwcx
->style
= Class
->style
;
1820 // If fnId is set, clear the global bit. See wine class test check_style.
1822 lpwcx
->style
&= ~CS_GLOBALCLASS
;
1824 lpwcx
->lpfnWndProc
= IntGetClassWndProc(Class
, Ansi
);
1826 lpwcx
->cbClsExtra
= Class
->cbclsExtra
;
1827 lpwcx
->cbWndExtra
= Class
->cbwndExtra
;
1828 lpwcx
->hIcon
= Class
->hIcon
; /* FIXME: Get handle from pointer */
1829 lpwcx
->hCursor
= Class
->hCursor
; /* FIXME: Get handle from pointer */
1830 lpwcx
->hbrBackground
= Class
->hbrBackground
;
1832 /* Copy non-string to user first. */
1834 ((PWNDCLASSEXA
)lpwcx
)->lpszMenuName
= Class
->lpszClientAnsiMenuName
;
1836 lpwcx
->lpszMenuName
= Class
->lpszClientUnicodeMenuName
;
1838 * FIXME: CLSMENUNAME has the answers! Copy the already made buffers from there!
1839 * Cls: lpszMenuName and lpszAnsiClassName should be used by kernel space.
1840 * lpszClientXxxMenuName should already be mapped to user space.
1842 /* Copy string ptr to user. */
1843 if ( Class
->lpszClientUnicodeMenuName
!= NULL
&&
1844 Class
->MenuNameIsString
)
1846 lpwcx
->lpszMenuName
= UserHeapAddressToUser(Ansi
?
1847 (PVOID
)Class
->lpszClientAnsiMenuName
:
1848 (PVOID
)Class
->lpszClientUnicodeMenuName
);
1851 if (hInstance
== hModClient
)
1852 lpwcx
->hInstance
= NULL
;
1854 lpwcx
->hInstance
= hInstance
;
1856 /* FIXME: Return the string? Okay! This is performed in User32! */
1857 //lpwcx->lpszClassName = (LPCWSTR)((ULONG_PTR)Class->atomClassName);
1859 lpwcx
->hIconSm
= Class
->hIconSm
; /* FIXME: Get handle from pointer */
1869 UserRegisterSystemClasses(VOID
)
1872 UNICODE_STRING ClassName
, MenuName
;
1873 PPROCESSINFO ppi
= GetW32ProcessInfo();
1879 if (ppi
->W32PF_flags
& W32PF_CLASSESREGISTERED
)
1882 if ( hModClient
== NULL
)
1885 RtlZeroMemory(&ClassName
, sizeof(ClassName
));
1886 RtlZeroMemory(&MenuName
, sizeof(MenuName
));
1888 for (i
= 0; i
!= ARRAYSIZE(DefaultServerClasses
); i
++)
1890 if (!IS_ATOM(DefaultServerClasses
[i
].ClassName
))
1892 RtlInitUnicodeString(&ClassName
, DefaultServerClasses
[i
].ClassName
);
1896 ClassName
.Buffer
= DefaultServerClasses
[i
].ClassName
;
1897 ClassName
.Length
= 0;
1898 ClassName
.MaximumLength
= 0;
1901 wc
.cbSize
= sizeof(wc
);
1902 wc
.style
= DefaultServerClasses
[i
].Style
;
1904 Flags
|= CSF_SERVERSIDEPROC
;
1906 if (DefaultServerClasses
[i
].ProcW
)
1908 wc
.lpfnWndProc
= DefaultServerClasses
[i
].ProcW
;
1909 wc
.hInstance
= hModuleWin
;
1913 wc
.lpfnWndProc
= GETPFNSERVER(DefaultServerClasses
[i
].fiId
);
1914 wc
.hInstance
= hModClient
;
1918 wc
.cbWndExtra
= DefaultServerClasses
[i
].ExtraBytes
;
1920 wc
.hCursor
= DefaultServerClasses
[i
].hCursor
;
1921 wc
.hbrBackground
= DefaultServerClasses
[i
].hBrush
;
1922 wc
.lpszMenuName
= NULL
;
1923 wc
.lpszClassName
= ClassName
.Buffer
;
1926 Class
= IntCreateClass( &wc
,
1929 DefaultServerClasses
[i
].fiId
,
1935 Class
->pclsNext
= ppi
->pclsPublicList
;
1936 (void)InterlockedExchangePointer((PVOID
*)&ppi
->pclsPublicList
,
1939 ppi
->dwRegisteredClasses
|= ICLASS_TO_MASK(DefaultServerClasses
[i
].iCls
);
1943 ERR("!!! Registering system class failed!\n");
1947 if (Ret
) ppi
->W32PF_flags
|= W32PF_CLASSESREGISTERED
;
1951 /* SYSCALLS *****************************************************************/
1955 NtUserRegisterClassExWOW(
1957 PUNICODE_STRING ClassName
,
1958 PUNICODE_STRING ClsNVersion
,
1959 PCLSMENUNAME pClassMenuName
,
1965 * Registers a new class with the window manager
1967 * lpwcx = Win32 extended window class structure
1968 * bUnicodeClass = Whether to send ANSI or unicode strings
1969 * to window procedures
1971 * Atom identifying the new class
1974 WNDCLASSEXW CapturedClassInfo
= {0};
1975 UNICODE_STRING CapturedName
= {0}, CapturedMenuName
= {0};
1976 RTL_ATOM Ret
= (RTL_ATOM
)0;
1977 PPROCESSINFO ppi
= GetW32ProcessInfo();
1979 if (Flags
& ~(CSF_ANSIPROC
))
1981 ERR("NtUserRegisterClassExWOW Bad Flags!\n");
1982 EngSetLastError(ERROR_INVALID_FLAGS
);
1986 UserEnterExclusive();
1988 TRACE("NtUserRegisterClassExWOW ClsN %wZ\n",ClassName
);
1990 if ( !(ppi
->W32PF_flags
& W32PF_CLASSESREGISTERED
))
1992 UserRegisterSystemClasses();
1997 /* Probe the parameters and basic parameter checks */
1998 if (ProbeForReadUint(&lpwcx
->cbSize
) != sizeof(WNDCLASSEXW
))
2000 ERR("NtUserRegisterClassExWOW Wrong cbSize!\n");
2001 goto InvalidParameter
;
2005 sizeof(WNDCLASSEXW
),
2007 RtlCopyMemory(&CapturedClassInfo
,
2009 sizeof(WNDCLASSEXW
));
2011 CapturedName
= ProbeForReadUnicodeString(ClassName
);
2013 ProbeForRead(pClassMenuName
,
2014 sizeof(CLSMENUNAME
),
2017 CapturedMenuName
= ProbeForReadUnicodeString(pClassMenuName
->pusMenuName
);
2019 if ( (CapturedName
.Length
& 1) ||
2020 (CapturedMenuName
.Length
& 1) ||
2021 (CapturedClassInfo
.cbClsExtra
< 0) ||
2022 ((CapturedClassInfo
.cbClsExtra
+ CapturedName
.Length
+
2023 CapturedMenuName
.Length
+ sizeof(CLS
))
2024 < (ULONG
)CapturedClassInfo
.cbClsExtra
) ||
2025 (CapturedClassInfo
.cbWndExtra
< 0) ||
2026 (CapturedClassInfo
.hInstance
== NULL
) )
2028 ERR("NtUserRegisterClassExWOW Invalid Parameter Error!\n");
2029 goto InvalidParameter
;
2032 if (CapturedName
.Length
!= 0)
2034 ProbeForRead(CapturedName
.Buffer
,
2035 CapturedName
.Length
,
2040 if (!IS_ATOM(CapturedName
.Buffer
))
2042 ERR("NtUserRegisterClassExWOW ClassName Error!\n");
2043 goto InvalidParameter
;
2047 if (CapturedMenuName
.Length
!= 0)
2049 ProbeForRead(CapturedMenuName
.Buffer
,
2050 CapturedMenuName
.Length
,
2053 else if (CapturedMenuName
.Buffer
!= NULL
&&
2054 !IS_INTRESOURCE(CapturedMenuName
.Buffer
))
2056 ERR("NtUserRegisterClassExWOW MenuName Error!\n");
2058 EngSetLastError(ERROR_INVALID_PARAMETER
);
2062 if (IsCallProcHandle(lpwcx
->lpfnWndProc
))
2063 { // Never seen this yet, but I'm sure it's a little haxxy trick!
2064 // If this pops up we know what todo!
2065 ERR("NtUserRegisterClassExWOW WndProc is CallProc!!\n");
2068 TRACE("NtUserRegisterClassExWOW MnuN %wZ\n",&CapturedMenuName
);
2070 /* Register the class */
2071 Ret
= UserRegisterClass(&CapturedClassInfo
,
2077 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2079 ERR("NtUserRegisterClassExWOW Exception Error!\n");
2080 SetLastNtError(_SEH2_GetExceptionCode());
2086 ERR("NtUserRegisterClassExWOW Null Return!\n");
2095 NtUserSetClassLong(HWND hWnd
,
2097 ULONG_PTR dwNewLong
,
2104 UserEnterExclusive();
2106 pi
= GetW32ProcessInfo();
2108 Window
= UserGetWindowObject(hWnd
);
2111 if (Window
->head
.pti
->ppi
!= pi
)
2113 EngSetLastError(ERROR_ACCESS_DENIED
);
2119 UNICODE_STRING Value
;
2121 /* Probe the parameters */
2122 if (Offset
== GCW_ATOM
|| Offset
== GCLP_MENUNAME
)
2124 Value
= ProbeForReadUnicodeString((PUNICODE_STRING
)dwNewLong
);
2125 if (Value
.Length
& 1)
2127 goto InvalidParameter
;
2130 if (Value
.Length
!= 0)
2132 ProbeForRead(Value
.Buffer
,
2138 if (Offset
== GCW_ATOM
&& !IS_ATOM(Value
.Buffer
))
2140 goto InvalidParameter
;
2142 else if (Offset
== GCLP_MENUNAME
&& !IS_INTRESOURCE(Value
.Buffer
))
2145 EngSetLastError(ERROR_INVALID_PARAMETER
);
2150 dwNewLong
= (ULONG_PTR
)&Value
;
2153 Ret
= UserSetClassLongPtr(Window
->pcls
,
2158 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2160 SetLastNtError(_SEH2_GetExceptionCode());
2179 * NOTE: Obsoleted in 32-bit windows
2185 NtUserUnregisterClass(IN PUNICODE_STRING ClassNameOrAtom
,
2186 IN HINSTANCE hInstance
,
2187 OUT PCLSMENUNAME pClassMenuName
)
2189 UNICODE_STRING CapturedClassName
;
2192 UserEnterExclusive();
2196 /* Probe the paramters */
2197 CapturedClassName
= ProbeForReadUnicodeString(ClassNameOrAtom
);
2198 if (CapturedClassName
.Length
& 1)
2200 goto InvalidParameter
;
2203 if (CapturedClassName
.Length
!= 0)
2205 ProbeForRead(CapturedClassName
.Buffer
,
2206 CapturedClassName
.Length
,
2211 if (!IS_ATOM(CapturedClassName
.Buffer
))
2214 EngSetLastError(ERROR_INVALID_PARAMETER
);
2219 /* Unregister the class */
2220 Ret
= UserUnregisterClass(&CapturedClassName
,
2222 NULL
); // Null for now~
2224 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2226 SetLastNtError(_SEH2_GetExceptionCode());
2234 /* NOTE: For system classes hInstance is not NULL here, but User32Instance */
2237 HINSTANCE hInstance
,
2238 PUNICODE_STRING ClassName
,
2239 LPWNDCLASSEXW lpWndClassEx
,
2240 LPWSTR
*ppszMenuName
,
2243 UNICODE_STRING CapturedClassName
, SafeClassName
;
2244 WNDCLASSEXW Safewcexw
;
2246 RTL_ATOM ClassAtom
= 0;
2250 /* NOTE: Need exclusive lock because getting the wndproc might require the
2251 creation of a call procedure handle */
2252 UserEnterExclusive();
2254 ppi
= GetW32ProcessInfo();
2256 if ( !(ppi
->W32PF_flags
& W32PF_CLASSESREGISTERED
))
2258 UserRegisterSystemClasses();
2263 /* Probe the paramters */
2264 CapturedClassName
= ProbeForReadUnicodeString(ClassName
);
2266 if (CapturedClassName
.Length
== 0)
2268 TRACE("hInst %p atom %04X lpWndClassEx %p Ansi %d\n", hInstance
, CapturedClassName
.Buffer
, lpWndClassEx
, Ansi
);
2272 TRACE("hInst %p class %wZ lpWndClassEx %p Ansi %d\n", hInstance
, &CapturedClassName
, lpWndClassEx
, Ansi
);
2275 if (CapturedClassName
.Length
& 1)
2277 EngSetLastError(ERROR_INVALID_PARAMETER
);
2282 if (CapturedClassName
.Length
!= 0)
2284 ProbeForRead( CapturedClassName
.Buffer
,
2285 CapturedClassName
.Length
,
2288 RtlInitUnicodeString( &SafeClassName
, CapturedClassName
.Buffer
);
2290 SafeClassName
.Buffer
= ExAllocatePoolWithTag( PagedPool
,
2291 SafeClassName
.MaximumLength
,
2293 RtlCopyMemory( SafeClassName
.Buffer
,
2294 CapturedClassName
.Buffer
,
2295 SafeClassName
.MaximumLength
);
2299 if (!IS_ATOM(CapturedClassName
.Buffer
))
2301 ERR("NtUserGetClassInfo() got ClassName instead of Atom!\n");
2302 EngSetLastError(ERROR_INVALID_PARAMETER
);
2307 SafeClassName
.Buffer
= CapturedClassName
.Buffer
;
2308 SafeClassName
.Length
= 0;
2309 SafeClassName
.MaximumLength
= 0;
2312 ProbeForWrite( lpWndClassEx
, sizeof(WNDCLASSEXW
), sizeof(ULONG
));
2314 RtlCopyMemory( &Safewcexw
, lpWndClassEx
, sizeof(WNDCLASSEXW
));
2316 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2318 SetLastNtError(_SEH2_GetExceptionCode());
2323 // If null instance use client.
2324 if (!hInstance
) hInstance
= hModClient
;
2328 TRACE("GetClassInfo(%wZ, 0x%x)\n", SafeClassName
, hInstance
);
2329 ClassAtom
= IntGetClassAtom( &SafeClassName
,
2334 if (ClassAtom
!= (RTL_ATOM
)0)
2336 if (hInstance
== NULL
) hInstance
= hModClient
;
2338 Ret
= UserGetClassInfo( Class
,
2345 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
2353 /* Emulate Function. */
2354 if (ppszMenuName
) *ppszMenuName
= (LPWSTR
)Safewcexw
.lpszMenuName
;
2356 RtlCopyMemory(lpWndClassEx
, &Safewcexw
, sizeof(WNDCLASSEXW
));
2359 /* We must return the atom of the class here instead of just TRUE. */
2360 /* Undocumented behavior! Return the class atom as a BOOL! */
2361 Ret
= (BOOL
)ClassAtom
;
2364 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2366 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
2371 if (SafeClassName
.Length
) ExFreePoolWithTag(SafeClassName
.Buffer
, TAG_STRING
);
2378 NtUserGetClassName (IN HWND hWnd
,
2380 OUT PUNICODE_STRING ClassName
)
2383 UNICODE_STRING CapturedClassName
;
2389 Window
= UserGetWindowObject(hWnd
);
2392 if (Real
&& Window
->fnid
&& !(Window
->fnid
& FNID_DESTROY
))
2394 if (LookupFnIdToiCls(Window
->fnid
, &iCls
))
2396 Atom
= gpsi
->atomSysClass
[iCls
];
2402 ProbeForWriteUnicodeString(ClassName
);
2403 CapturedClassName
= *ClassName
;
2405 /* Get the class name */
2406 Ret
= UserGetClassName(Window
->pcls
,
2413 /* Update the Length field */
2414 ClassName
->Length
= CapturedClassName
.Length
;
2417 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2419 SetLastNtError(_SEH2_GetExceptionCode());
2429 /* Return Pointer to Class structure. */
2431 NtUserGetWOWClass(HINSTANCE hInstance
,
2432 PUNICODE_STRING ClassName
)
2434 UNICODE_STRING SafeClassName
;
2437 RTL_ATOM ClassAtom
= 0;
2440 UserEnterExclusive();
2442 pi
= GetW32ProcessInfo();
2446 if (ClassName
->Length
!= 0)
2448 ProbeForRead( ClassName
->Buffer
,
2452 RtlInitUnicodeString( &SafeClassName
, ClassName
->Buffer
);
2454 SafeClassName
.Buffer
= ExAllocatePoolWithTag( PagedPool
,
2455 SafeClassName
.MaximumLength
,
2457 RtlCopyMemory( SafeClassName
.Buffer
,
2459 SafeClassName
.MaximumLength
);
2463 if (!IS_ATOM(ClassName
->Buffer
))
2465 ERR("NtUserGetWOWClass() got ClassName instead of Atom!\n");
2470 SafeClassName
.Buffer
= ClassName
->Buffer
;
2471 SafeClassName
.Length
= 0;
2472 SafeClassName
.MaximumLength
= 0;
2476 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2478 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
2485 ClassAtom
= IntGetClassAtom( &SafeClassName
,
2492 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
2496 if (SafeClassName
.Length
) ExFreePoolWithTag(SafeClassName
.Buffer
, TAG_STRING
);
2499 // Don't forget to use DesktopPtrToUser( ? ) with return pointer in user space.