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
,
16 NULL
, // Use User32 procs
19 (HBRUSH
)(COLOR_BACKGROUND
),
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
),
51 { ((PWSTR
)((ULONG_PTR
)(WORD
)(0x8006))), // Tooltips
52 CS_PARENTDC
|CS_DBLCLKS
,
53 NULL
, // Use User32 procs
61 { ((PWSTR
)((ULONG_PTR
)(WORD
)(0x8004))), // IconTitle is here for now...
63 NULL
, // Use User32 procs
72 NULL
, // Use User32 procs
86 { /* Function Ids to Class indexes. */
87 { FNID_SCROLLBAR
, ICLS_SCROLLBAR
},
88 { FNID_ICONTITLE
, ICLS_ICONTITLE
},
89 { FNID_MENU
, ICLS_MENU
},
90 { FNID_DESKTOP
, ICLS_DESKTOP
},
91 { FNID_SWITCH
, ICLS_SWITCH
},
92 { FNID_MESSAGEWND
, ICLS_HWNDMESSAGE
},
93 { FNID_BUTTON
, ICLS_BUTTON
},
94 { FNID_COMBOBOX
, ICLS_COMBOBOX
},
95 { FNID_COMBOLBOX
, ICLS_COMBOLBOX
},
96 { FNID_DIALOG
, ICLS_DIALOG
},
97 { FNID_EDIT
, ICLS_EDIT
},
98 { FNID_LISTBOX
, ICLS_LISTBOX
},
99 { FNID_MDICLIENT
, ICLS_MDICLIENT
},
100 { FNID_STATIC
, ICLS_STATIC
},
101 { FNID_IME
, ICLS_IME
},
102 { FNID_GHOST
, ICLS_GHOST
},
103 { FNID_TOOLTIPS
, ICLS_TOOLTIPS
}
108 LookupFnIdToiCls(int FnId
, int *iCls
)
112 for ( i
= 0; i
< ARRAYSIZE(FnidToiCls
); i
++)
114 if (FnidToiCls
[i
].FnId
== FnId
)
116 if (iCls
) *iCls
= FnidToiCls
[i
].ClsId
;
124 _Must_inspect_result_
127 CaptureUnicodeStringOrAtom(
128 _Out_ PUNICODE_STRING pustrOut
,
129 __in_data_source(USER_MODE
) _In_ PUNICODE_STRING pustrUnsafe
)
131 NTSTATUS Status
= STATUS_SUCCESS
;
133 /* Default to NULL */
134 pustrOut
->Buffer
= NULL
;
138 ProbeForRead(pustrUnsafe
, sizeof(UNICODE_STRING
), 1);
140 /* Validate the string */
141 if ((pustrUnsafe
->Length
& 1) || (pustrUnsafe
->Buffer
== NULL
))
143 /* This is not legal */
144 _SEH2_YIELD(return STATUS_INVALID_PARAMETER
);
147 /* Check if this is an atom */
148 if (IS_ATOM(pustrUnsafe
->Buffer
))
150 /* Copy the atom, length is 0 */
151 pustrOut
->MaximumLength
= pustrOut
->Length
= 0;
152 pustrOut
->Buffer
= pustrUnsafe
->Buffer
;
156 /* Get the length, maximum length includes zero termination */
157 pustrOut
->Length
= pustrUnsafe
->Length
;
158 pustrOut
->MaximumLength
= pustrOut
->Length
+ sizeof(WCHAR
);
160 /* Allocate a buffer */
161 pustrOut
->Buffer
= ExAllocatePoolWithTag(PagedPool
,
162 pustrOut
->MaximumLength
,
164 if (!pustrOut
->Buffer
)
166 _SEH2_YIELD(return STATUS_INSUFFICIENT_RESOURCES
);
169 /* Copy the string and zero terminate it */
170 ProbeForRead(pustrUnsafe
->Buffer
, pustrOut
->Length
, 1);
171 RtlCopyMemory(pustrOut
->Buffer
, pustrUnsafe
->Buffer
, pustrOut
->Length
);
172 pustrOut
->Buffer
[pustrOut
->Length
/ sizeof(WCHAR
)] = L
'\0';
175 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
177 /* Check if we already allocated a buffer */
178 if (pustrOut
->Buffer
)
180 /* Free the buffer */
181 ExFreePoolWithTag(pustrOut
->Buffer
, TAG_STRING
);
182 Status
= _SEH2_GetExceptionCode();
190 /* WINDOWCLASS ***************************************************************/
193 IntFreeClassMenuName(IN OUT PCLS Class
)
195 /* Free the menu name, if it was changed and allocated */
196 if (Class
->lpszClientUnicodeMenuName
!= NULL
&& Class
->MenuNameIsString
)
198 UserHeapFree(Class
->lpszClientUnicodeMenuName
);
199 Class
->lpszClientUnicodeMenuName
= NULL
;
200 Class
->lpszClientAnsiMenuName
= NULL
;
205 IntDestroyClass(IN OUT PCLS Class
)
209 /* There shouldn't be any clones anymore */
210 ASSERT(Class
->cWndReferenceCount
== 0);
211 ASSERT(Class
->pclsClone
== NULL
);
213 if (Class
->pclsBase
== Class
)
215 PCALLPROCDATA CallProc
, NextCallProc
;
217 /* Destroy allocated callproc handles */
218 CallProc
= Class
->spcpdFirst
;
219 while (CallProc
!= NULL
)
221 NextCallProc
= CallProc
->spcpdNext
;
223 CallProc
->spcpdNext
= NULL
;
224 DestroyCallProc(NULL
,
227 CallProc
= NextCallProc
;
232 DceFreeClassDCE(((PDCE
)Class
->pdce
)->hDC
);
236 IntFreeClassMenuName(Class
);
239 pDesk
= Class
->rpdeskParent
;
240 Class
->rpdeskParent
= NULL
;
242 /* Free the structure */
245 DesktopHeapFree(pDesk
, Class
);
254 /* Clean all process classes. all process windows must cleaned first!! */
255 void FASTCALL
DestroyProcessClasses(PPROCESSINFO Process
)
258 PPROCESSINFO pi
= (PPROCESSINFO
)Process
;
262 /* Free all local classes */
263 Class
= pi
->pclsPrivateList
;
264 while (Class
!= NULL
)
266 pi
->pclsPrivateList
= Class
->pclsNext
;
268 ASSERT(Class
->pclsBase
== Class
);
269 IntDestroyClass(Class
);
271 Class
= pi
->pclsPrivateList
;
274 /* Free all global classes */
275 Class
= pi
->pclsPublicList
;
276 while (Class
!= NULL
)
278 pi
->pclsPublicList
= Class
->pclsNext
;
280 ASSERT(Class
->pclsBase
== Class
);
281 IntDestroyClass(Class
);
283 Class
= pi
->pclsPublicList
;
289 IntRegisterClassAtom(IN PUNICODE_STRING ClassName
,
296 if (ClassName
->Length
!= 0)
298 /* FIXME: Don't limit to 64 characters! Use SEH when allocating memory! */
299 if (ClassName
->Length
/ sizeof(WCHAR
) >= sizeof(szBuf
) / sizeof(szBuf
[0]))
301 EngSetLastError(ERROR_INVALID_PARAMETER
);
308 szBuf
[ClassName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
312 AtomName
= ClassName
->Buffer
;
314 Status
= RtlAddAtomToAtomTable(gAtomTable
,
318 if (!NT_SUCCESS(Status
))
320 SetLastNtError(Status
);
328 IntDeregisterClassAtom(IN RTL_ATOM Atom
)
330 return RtlDeleteAtomFromAtomTable(gAtomTable
,
335 UserAddCallProcToClass(IN OUT PCLS Class
,
336 IN PCALLPROCDATA CallProc
)
340 ASSERT(CallProc
->spcpdNext
== NULL
);
342 BaseClass
= Class
->pclsBase
;
343 ASSERT(CallProc
->spcpdNext
== NULL
);
344 CallProc
->spcpdNext
= BaseClass
->spcpdFirst
;
345 BaseClass
->spcpdFirst
= CallProc
;
347 /* Update all clones */
348 Class
= Class
->pclsClone
;
349 while (Class
!= NULL
)
351 Class
->spcpdFirst
= BaseClass
->spcpdFirst
;
352 Class
= Class
->pclsNext
;
357 IntSetClassAtom(IN OUT PCLS Class
,
358 IN PUNICODE_STRING ClassName
)
360 RTL_ATOM Atom
= (RTL_ATOM
)0;
362 /* Update the base class first */
363 Class
= Class
->pclsBase
;
365 if (!IntRegisterClassAtom(ClassName
,
371 IntDeregisterClassAtom(Class
->atomClassName
);
373 Class
->atomClassName
= Atom
;
375 /* Update the clones */
376 Class
= Class
->pclsClone
;
377 while (Class
!= NULL
)
379 Class
->atomClassName
= Atom
;
381 Class
= Class
->pclsNext
;
388 // Same as User32:IntGetClsWndProc.
391 IntGetClassWndProc(PCLS Class
, BOOL Ansi
)
394 WNDPROC gcpd
= NULL
, Ret
= NULL
;
396 if (Class
->CSF_flags
& CSF_SERVERSIDEPROC
)
398 for ( i
= FNID_FIRST
; i
<= FNID_SWITCH
; i
++)
400 if (GETPFNSERVER(i
) == Class
->lpfnWndProc
)
403 Ret
= GETPFNCLIENTA(i
);
405 Ret
= GETPFNCLIENTW(i
);
410 Ret
= Class
->lpfnWndProc
;
412 if (Class
->fnid
<= FNID_GHOST
&& Class
->fnid
>= FNID_BUTTON
)
416 if (GETPFNCLIENTW(Class
->fnid
) == Class
->lpfnWndProc
)
417 Ret
= GETPFNCLIENTA(Class
->fnid
);
421 if (GETPFNCLIENTA(Class
->fnid
) == Class
->lpfnWndProc
)
422 Ret
= GETPFNCLIENTW(Class
->fnid
);
426 if ( Ret
!= Class
->lpfnWndProc
||
427 Ansi
== !!(Class
->CSF_flags
& CSF_ANSIPROC
) )
430 gcpd
= (WNDPROC
)UserGetCPD( Class
,
431 (Ansi
? UserGetCPDA2U
: UserGetCPDU2A
)|UserGetCPDClass
,
434 return (gcpd
? gcpd
: Ret
);
440 IntSetClassWndProc(IN OUT PCLS Class
,
446 WNDPROC Ret
, chWndProc
;
448 Ret
= IntGetClassWndProc(Class
, Ansi
);
450 // If Server Side, downgrade to Client Side.
451 if (Class
->CSF_flags
& CSF_SERVERSIDEPROC
)
453 if (Ansi
) Class
->CSF_flags
|= CSF_ANSIPROC
;
454 Class
->CSF_flags
&= ~CSF_SERVERSIDEPROC
;
455 Class
->Unicode
= !Ansi
;
458 if (!WndProc
) WndProc
= Class
->lpfnWndProc
;
462 // Check if CallProc handle and retrieve previous call proc address and set.
463 if (IsCallProcHandle(WndProc
))
465 pcpd
= UserGetObject(gHandleTable
, WndProc
, otCallProc
);
466 if (pcpd
) chWndProc
= pcpd
->pfnClientPrevious
;
469 Class
->lpfnWndProc
= chWndProc
;
474 // Switch from Client Side call to Server Side call if match. Ref: "deftest".
475 for ( i
= FNID_FIRST
; i
<= FNID_SWITCH
; i
++)
477 if (GETPFNCLIENTW(i
) == Class
->lpfnWndProc
)
479 chWndProc
= GETPFNSERVER(i
);
482 if (GETPFNCLIENTA(i
) == Class
->lpfnWndProc
)
484 chWndProc
= GETPFNSERVER(i
);
488 // If match, set/reset to Server Side and clear ansi.
491 Class
->lpfnWndProc
= chWndProc
;
492 Class
->Unicode
= TRUE
;
493 Class
->CSF_flags
&= ~CSF_ANSIPROC
;
494 Class
->CSF_flags
|= CSF_SERVERSIDEPROC
;
498 Class
->Unicode
= !Ansi
;
501 Class
->CSF_flags
|= CSF_ANSIPROC
;
503 Class
->CSF_flags
&= ~CSF_ANSIPROC
;
506 /* Update the clones */
507 chWndProc
= Class
->lpfnWndProc
;
509 Class
= Class
->pclsClone
;
510 while (Class
!= NULL
)
512 Class
->Unicode
= !Ansi
;
513 Class
->lpfnWndProc
= chWndProc
;
515 Class
= Class
->pclsNext
;
522 IntGetClassForDesktop(IN OUT PCLS BaseClass
,
523 IN OUT PCLS
*ClassLink
,
529 ASSERT(Desktop
!= NULL
);
530 ASSERT(BaseClass
->pclsBase
== BaseClass
);
532 if (BaseClass
->rpdeskParent
== Desktop
)
534 /* It is most likely that a window is created on the same
535 desktop as the window class. */
540 if (BaseClass
->rpdeskParent
== NULL
)
542 ASSERT(BaseClass
->cWndReferenceCount
== 0);
543 ASSERT(BaseClass
->pclsClone
== NULL
);
545 /* Classes are also located in the shared heap when the class
546 was created before the thread attached to a desktop. As soon
547 as a window is created for such a class located on the shared
548 heap, the class is cloned into the desktop heap on which the
549 window is created. */
554 /* The user is asking for a class object on a different desktop,
556 Class
= BaseClass
->pclsClone
;
557 while (Class
!= NULL
)
559 if (Class
->rpdeskParent
== Desktop
)
561 ASSERT(Class
->pclsBase
== BaseClass
);
562 ASSERT(Class
->pclsClone
== NULL
);
566 Class
= Class
->pclsNext
;
572 /* The window is created on a different desktop, we need to
573 clone the class object to the desktop heap of the window! */
574 ClassSize
= sizeof(*BaseClass
) + (SIZE_T
)BaseClass
->cbclsExtra
;
576 Class
= DesktopHeapAlloc(Desktop
,
580 /* Simply clone the class */
581 RtlCopyMemory( Class
, BaseClass
, ClassSize
);
583 TRACE("Clone Class 0x%p hM 0x%p\n %S\n",Class
, Class
->hModule
, Class
->lpszClientUnicodeMenuName
);
585 /* Restore module address if default user class Ref: Bug 4778 */
586 if ( Class
->hModule
!= hModClient
&&
587 Class
->fnid
<= FNID_GHOST
&&
588 Class
->fnid
>= FNID_BUTTON
)
590 Class
->hModule
= hModClient
;
591 TRACE("Clone Class 0x%p Reset hM 0x%p\n",Class
, Class
->hModule
);
594 /* Update some pointers and link the class */
595 Class
->rpdeskParent
= Desktop
;
596 Class
->cWndReferenceCount
= 0;
598 if (BaseClass
->rpdeskParent
== NULL
)
600 /* We don't really need the base class on the shared
601 heap anymore, delete it so the only class left is
602 the clone we just created, which now serves as the
604 ASSERT(BaseClass
->pclsClone
== NULL
);
605 ASSERT(Class
->pclsClone
== NULL
);
606 Class
->pclsBase
= Class
;
607 Class
->pclsNext
= BaseClass
->pclsNext
;
609 /* Replace the base class */
610 (void)InterlockedExchangePointer((PVOID
*)ClassLink
,
613 /* Destroy the obsolete copy on the shared heap */
614 BaseClass
->pclsBase
= NULL
;
615 BaseClass
->pclsClone
= NULL
;
616 IntDestroyClass(BaseClass
);
620 /* Link in the clone */
621 Class
->pclsClone
= NULL
;
622 Class
->pclsBase
= BaseClass
;
623 Class
->pclsNext
= BaseClass
->pclsClone
;
624 (void)InterlockedExchangePointer((PVOID
*)&BaseClass
->pclsClone
,
630 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
637 IntReferenceClass(IN OUT PCLS BaseClass
,
638 IN OUT PCLS
*ClassLink
,
642 ASSERT(BaseClass
->pclsBase
== BaseClass
);
644 Class
= IntGetClassForDesktop(BaseClass
,
649 Class
->cWndReferenceCount
++;
657 IntMakeCloneBaseClass(IN OUT PCLS Class
,
658 IN OUT PCLS
*BaseClassLink
,
659 IN OUT PCLS
*CloneLink
)
663 ASSERT(Class
->pclsBase
!= Class
);
664 ASSERT(Class
->pclsBase
->pclsClone
!= NULL
);
665 ASSERT(Class
->rpdeskParent
!= NULL
);
666 ASSERT(Class
->cWndReferenceCount
!= 0);
667 ASSERT(Class
->pclsBase
->rpdeskParent
!= NULL
);
668 ASSERT(Class
->pclsBase
->cWndReferenceCount
== 0);
670 /* Unlink the clone */
671 *CloneLink
= Class
->pclsNext
;
672 Class
->pclsClone
= Class
->pclsBase
->pclsClone
;
674 /* Update the class information to make it a base class */
675 Class
->pclsBase
= Class
;
676 Class
->pclsNext
= (*BaseClassLink
)->pclsNext
;
678 /* Update all clones */
679 Clone
= Class
->pclsClone
;
680 while (Clone
!= NULL
)
682 ASSERT(Clone
->pclsClone
== NULL
);
683 Clone
->pclsBase
= Class
;
685 Clone
= Clone
->pclsNext
;
688 /* Link in the new base class */
689 (void)InterlockedExchangePointer((PVOID
*)BaseClassLink
,
695 IntDereferenceClass(IN OUT PCLS Class
,
696 IN PDESKTOPINFO Desktop
,
699 PCLS
*PrevLink
, BaseClass
, CurrentClass
;
701 ASSERT(Class
->cWndReferenceCount
>= 1);
703 BaseClass
= Class
->pclsBase
;
705 if (--Class
->cWndReferenceCount
== 0)
707 if (BaseClass
== Class
)
709 ASSERT(Class
->pclsBase
== Class
);
711 TRACE("IntDereferenceClass 0x%p\n", Class
);
712 /* Check if there are clones of the class on other desktops,
713 link the first clone in if possible. If there are no clones
714 then leave the class on the desktop heap. It will get moved
715 to the shared heap when the thread detaches. */
716 if (BaseClass
->pclsClone
!= NULL
)
718 if (BaseClass
->Global
)
719 PrevLink
= &pi
->pclsPublicList
;
721 PrevLink
= &pi
->pclsPrivateList
;
723 CurrentClass
= *PrevLink
;
724 while (CurrentClass
!= BaseClass
)
726 ASSERT(CurrentClass
!= NULL
);
728 PrevLink
= &CurrentClass
->pclsNext
;
729 CurrentClass
= CurrentClass
->pclsNext
;
732 ASSERT(*PrevLink
== BaseClass
);
734 /* Make the first clone become the new base class */
735 IntMakeCloneBaseClass(BaseClass
->pclsClone
,
737 &BaseClass
->pclsClone
);
739 /* Destroy the class, there's still another clone of the class
740 that now serves as a base class. Make sure we don't destruct
741 resources shared by all classes (Base = NULL)! */
742 BaseClass
->pclsBase
= NULL
;
743 BaseClass
->pclsClone
= NULL
;
744 IntDestroyClass(BaseClass
);
749 TRACE("IntDereferenceClass1 0x%p\n", Class
);
751 /* Locate the cloned class and unlink it */
752 PrevLink
= &BaseClass
->pclsClone
;
753 CurrentClass
= BaseClass
->pclsClone
;
754 while (CurrentClass
!= Class
)
756 ASSERT(CurrentClass
!= NULL
);
758 PrevLink
= &CurrentClass
->pclsNext
;
759 CurrentClass
= CurrentClass
->pclsNext
;
762 ASSERT(CurrentClass
== Class
);
764 (void)InterlockedExchangePointer((PVOID
*)PrevLink
,
767 ASSERT(Class
->pclsBase
== BaseClass
);
768 ASSERT(Class
->pclsClone
== NULL
);
770 /* The class was just a clone, we don't need it anymore */
771 IntDestroyClass(Class
);
777 IntMoveClassToSharedHeap(IN OUT PCLS Class
,
778 IN OUT PCLS
**ClassLinkPtr
)
783 ASSERT(Class
->pclsBase
== Class
);
784 ASSERT(Class
->rpdeskParent
!= NULL
);
785 ASSERT(Class
->cWndReferenceCount
== 0);
786 ASSERT(Class
->pclsClone
== NULL
);
788 ClassSize
= sizeof(*Class
) + (SIZE_T
)Class
->cbclsExtra
;
790 /* Allocate the new base class on the shared heap */
791 NewClass
= UserHeapAlloc(ClassSize
);
792 if (NewClass
!= NULL
)
794 RtlCopyMemory(NewClass
,
798 NewClass
->rpdeskParent
= NULL
;
799 NewClass
->pclsBase
= NewClass
;
801 /* Replace the class in the list */
802 (void)InterlockedExchangePointer((PVOID
*)*ClassLinkPtr
,
804 *ClassLinkPtr
= &NewClass
->pclsNext
;
806 /* Free the obsolete class on the desktop heap */
807 Class
->pclsBase
= NULL
;
808 IntDestroyClass(Class
);
816 IntCheckDesktopClasses(IN PDESKTOP Desktop
,
817 IN OUT PCLS
*ClassList
,
818 IN BOOL FreeOnFailure
,
821 PCLS Class
, NextClass
, *Link
;
823 /* NOTE: We only need to check base classes! When classes are no longer needed
824 on a desktop, the clones will be freed automatically as soon as possible.
825 However, we need to move base classes to the shared heap, as soon as
826 the last desktop heap where a class is allocated on is about to be destroyed.
827 If we didn't move the class to the shared heap, the class would become
830 ASSERT(Desktop
!= NULL
);
834 while (Class
!= NULL
)
836 NextClass
= Class
->pclsNext
;
838 ASSERT(Class
->pclsBase
== Class
);
840 if (Class
->rpdeskParent
== Desktop
&&
841 Class
->cWndReferenceCount
== 0)
843 /* There shouldn't be any clones around anymore! */
844 ASSERT(Class
->pclsClone
== NULL
);
846 /* FIXME: If process is terminating, don't move the class but rather destroy it! */
847 /* FIXME: We could move the class to another desktop heap if there's still desktops
848 mapped into the process... */
850 /* Move the class to the shared heap */
851 if (IntMoveClassToSharedHeap(Class
,
854 ASSERT(*Link
== NextClass
);
858 ASSERT(NextClass
== Class
->pclsNext
);
862 /* Unlink the base class */
863 (void)InterlockedExchangePointer((PVOID
*)Link
,
866 /* We can free the old base class now */
867 Class
->pclsBase
= NULL
;
868 IntDestroyClass(Class
);
872 Link
= &Class
->pclsNext
;
878 Link
= &Class
->pclsNext
;
885 IntCheckProcessDesktopClasses(IN PDESKTOP Desktop
,
886 IN BOOL FreeOnFailure
)
891 pi
= GetW32ProcessInfo();
893 /* Check all local classes */
894 IntCheckDesktopClasses(Desktop
,
895 &pi
->pclsPrivateList
,
899 /* Check all global classes */
900 IntCheckDesktopClasses(Desktop
,
906 ERR("Failed to move process classes from desktop 0x%p to the shared heap!\n", Desktop
);
907 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
915 IntCreateClass(IN CONST WNDCLASSEXW
* lpwcx
,
916 IN PUNICODE_STRING ClassName
,
917 IN PUNICODE_STRING MenuName
,
927 PWSTR pszMenuName
= NULL
;
928 NTSTATUS Status
= STATUS_SUCCESS
;
930 TRACE("lpwcx=%p ClassName=%wZ MenuName=%wZ dwFlags=%08x Desktop=%p pi=%p\n",
931 lpwcx
, ClassName
, MenuName
, dwFlags
, Desktop
, pi
);
933 if (!IntRegisterClassAtom(ClassName
,
936 ERR("Failed to register class atom!\n");
940 ClassSize
= sizeof(*Class
) + lpwcx
->cbClsExtra
;
941 if (MenuName
->Length
!= 0)
943 pszMenuName
= UserHeapAlloc(MenuName
->Length
+ sizeof(UNICODE_NULL
) +
944 RtlUnicodeStringToAnsiSize(MenuName
));
945 if (pszMenuName
== NULL
)
951 Class
= DesktopHeapAlloc(Desktop
,
956 /* FIXME: The class was created before being connected
957 to a desktop. It is possible for the desktop window,
958 but should it be allowed for any other case? */
959 Class
= UserHeapAlloc(ClassSize
);
966 RtlZeroMemory(Class
, ClassSize
);
968 Class
->rpdeskParent
= Desktop
;
969 Class
->pclsBase
= Class
;
970 Class
->atomClassName
= Atom
;
972 Class
->CSF_flags
= dwFlags
;
974 if (LookupFnIdToiCls(Class
->fnid
, &iCls
))
976 gpsi
->atomSysClass
[iCls
] = Class
->atomClassName
;
981 PWSTR pszMenuNameBuffer
= pszMenuName
;
983 /* Need to protect with SEH since accessing the WNDCLASSEX structure
984 and string buffers might raise an exception! We don't want to
986 // What?! If the user interface was written correctly this would not be an issue!
987 Class
->lpfnWndProc
= lpwcx
->lpfnWndProc
;
988 Class
->style
= lpwcx
->style
;
989 Class
->cbclsExtra
= lpwcx
->cbClsExtra
;
990 Class
->cbwndExtra
= lpwcx
->cbWndExtra
;
991 Class
->hModule
= lpwcx
->hInstance
;
992 Class
->hIcon
= lpwcx
->hIcon
; /* FIXME */
993 Class
->hIconSm
= lpwcx
->hIconSm
; /* FIXME */
994 Class
->hCursor
= lpwcx
->hCursor
; /* FIXME */
995 Class
->hbrBackground
= lpwcx
->hbrBackground
;
997 /* Make a copy of the string */
998 if (pszMenuNameBuffer
!= NULL
)
1000 Class
->MenuNameIsString
= TRUE
;
1002 Class
->lpszClientUnicodeMenuName
= pszMenuNameBuffer
;
1003 RtlCopyMemory(Class
->lpszClientUnicodeMenuName
,
1006 Class
->lpszClientUnicodeMenuName
[MenuName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1008 pszMenuNameBuffer
+= (MenuName
->Length
/ sizeof(WCHAR
)) + 1;
1011 Class
->lpszClientUnicodeMenuName
= MenuName
->Buffer
;
1013 /* Save an ANSI copy of the string */
1014 if (pszMenuNameBuffer
!= NULL
)
1016 ANSI_STRING AnsiString
;
1018 Class
->lpszClientAnsiMenuName
= (PSTR
)pszMenuNameBuffer
;
1019 AnsiString
.MaximumLength
= (USHORT
)RtlUnicodeStringToAnsiSize(MenuName
);
1020 AnsiString
.Buffer
= Class
->lpszClientAnsiMenuName
;
1021 Status
= RtlUnicodeStringToAnsiString(&AnsiString
,
1024 if (!NT_SUCCESS(Status
))
1026 ERR("Failed to convert unicode menu name to ansi!\n");
1028 /* Life would've been much prettier if ntoskrnl exported RtlRaiseStatus()... */
1033 Class
->lpszClientAnsiMenuName
= (PSTR
)MenuName
->Buffer
;
1035 /* Save kernel use menu name and ansi class name */
1036 Class
->lpszMenuName
= Class
->lpszClientUnicodeMenuName
; // FIXME!
1037 //Class->lpszAnsiClassName = FIXME
1039 /* Server Side overrides class calling type (A/W)!
1040 User32 whine test_builtinproc: "deftest"
1041 built-in winproc - window A/W type automatically detected */
1042 if (!(Class
->CSF_flags
& CSF_SERVERSIDEPROC
))
1046 /* Due to the wine class "deftest" and most likely no FNID to reference
1047 from, sort through the Server Side list and compare proc addresses
1048 for match. This method will be used in related code.
1050 for ( i
= FNID_FIRST
; i
<= FNID_SWITCH
; i
++)
1051 { // Open ANSI or Unicode, just match, set and break.
1052 if (GETPFNCLIENTW(i
) == Class
->lpfnWndProc
)
1054 WndProc
= GETPFNSERVER(i
);
1057 if (GETPFNCLIENTA(i
) == Class
->lpfnWndProc
)
1059 WndProc
= GETPFNSERVER(i
);
1064 { // If a hit, we are Server Side so set the right flags and proc.
1065 Class
->CSF_flags
|= CSF_SERVERSIDEPROC
;
1066 Class
->CSF_flags
&= ~CSF_ANSIPROC
;
1067 Class
->lpfnWndProc
= WndProc
;
1071 if (!(Class
->CSF_flags
& CSF_ANSIPROC
))
1072 Class
->Unicode
= TRUE
;
1074 if (Class
->style
& CS_GLOBALCLASS
)
1075 Class
->Global
= TRUE
;
1077 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1079 Status
= _SEH2_GetExceptionCode();
1083 if (!NT_SUCCESS(Status
))
1085 ERR("Failed creating the class: 0x%x\n", Status
);
1087 SetLastNtError(Status
);
1089 if (pszMenuName
!= NULL
)
1090 UserHeapFree(pszMenuName
);
1092 DesktopHeapFree(Desktop
,
1096 IntDeregisterClassAtom(Atom
);
1102 ERR("Failed to allocate class on Desktop 0x%p\n", Desktop
);
1104 if (pszMenuName
!= NULL
)
1105 UserHeapFree(pszMenuName
);
1107 IntDeregisterClassAtom(Atom
);
1109 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1112 TRACE("Created class 0x%p with name %wZ and proc 0x%p for atom 0x%x and hInstance 0x%p, global %u\n",
1113 Class
, ClassName
, Class
->lpfnWndProc
, Atom
, Class
->hModule
, Class
->Global
);
1119 IntFindClass(IN RTL_ATOM Atom
,
1120 IN HINSTANCE hInstance
,
1122 OUT PCLS
**Link OPTIONAL
)
1124 PCLS Class
, *PrevLink
= ClassList
;
1127 while (Class
!= NULL
)
1129 if (Class
->atomClassName
== Atom
&&
1130 (hInstance
== NULL
|| Class
->hModule
== hInstance
) &&
1131 !(Class
->CSF_flags
& CSF_WOWDEFERDESTROY
))
1133 ASSERT(Class
->pclsBase
== Class
);
1140 PrevLink
= &Class
->pclsNext
;
1141 Class
= Class
->pclsNext
;
1149 IntGetAtomFromStringOrAtom(
1150 _In_ PUNICODE_STRING ClassName
,
1151 _Out_ RTL_ATOM
*Atom
)
1155 if (ClassName
->Length
!= 0)
1163 /* NOTE: Caller has to protect the call with SEH! */
1165 if (ClassName
->Length
!= 0)
1167 /* FIXME: Don't limit to 64 characters! use SEH when allocating memory! */
1168 if (ClassName
->Length
/ sizeof(WCHAR
) >= sizeof(szBuf
) / sizeof(szBuf
[0]))
1170 EngSetLastError(ERROR_INVALID_PARAMETER
);
1174 /* We need to make a local copy of the class name! The caller could
1175 modify the buffer and we could overflow in RtlLookupAtomInAtomTable.
1176 We're protected by SEH, but the ranges that might be accessed were
1178 RtlCopyMemory(szBuf
,
1181 szBuf
[ClassName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1185 AtomName
= ClassName
->Buffer
;
1187 /* Lookup the atom */
1188 Status
= RtlLookupAtomInAtomTable(gAtomTable
,
1191 if (NT_SUCCESS(Status
))
1197 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
1199 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
1203 SetLastNtError(Status
);
1209 ASSERT(IS_ATOM(ClassName
->Buffer
));
1210 *Atom
= (RTL_ATOM
)((ULONG_PTR
)ClassName
->Buffer
);
1219 _In_ PUNICODE_STRING ClassName
,
1220 IN HINSTANCE hInstance OPTIONAL
,
1221 IN PPROCESSINFO pi OPTIONAL
,
1222 OUT PCLS
*BaseClass OPTIONAL
,
1223 OUT PCLS
**Link OPTIONAL
)
1225 RTL_ATOM Atom
= (RTL_ATOM
)0;
1227 ASSERT(BaseClass
!= NULL
);
1229 if (IntGetAtomFromStringOrAtom(ClassName
,
1231 Atom
!= (RTL_ATOM
)0)
1235 /* Attempt to locate the class object */
1239 /* Step 1: Try to find an exact match of locally registered classes */
1240 Class
= IntFindClass(Atom
,
1242 &pi
->pclsPrivateList
,
1245 { TRACE("Step 1: 0x%p\n",Class
);
1249 /* Step 2: Try to find any globally registered class. The hInstance
1250 is not relevant for global classes */
1251 Class
= IntFindClass(Atom
,
1253 &pi
->pclsPublicList
,
1256 { TRACE("Step 2: 0x%p 0x%p\n",Class
, Class
->hModule
);
1260 /* Step 3: Try to find any local class registered by user32 */
1261 Class
= IntFindClass(Atom
,
1263 &pi
->pclsPrivateList
,
1266 { TRACE("Step 3: 0x%p\n",Class
);
1270 /* Step 4: Try to find any global class registered by user32 */
1271 Class
= IntFindClass(Atom
,
1273 &pi
->pclsPublicList
,
1277 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
1279 }else{TRACE("Step 4: 0x%p\n",Class
);}
1289 IntGetAndReferenceClass(PUNICODE_STRING ClassName
, HINSTANCE hInstance
)
1291 PCLS
*ClassLink
, Class
= NULL
;
1295 pti
= PsGetCurrentThreadWin32Thread();
1297 if ( !(pti
->ppi
->W32PF_flags
& W32PF_CLASSESREGISTERED
))
1299 UserRegisterSystemClasses();
1302 /* Check the class. */
1304 TRACE("Finding Class %wZ for hInstance 0x%p\n", ClassName
, hInstance
);
1306 ClassAtom
= IntGetClassAtom(ClassName
,
1312 if (ClassAtom
== (RTL_ATOM
)0)
1314 if (IS_ATOM(ClassName
->Buffer
))
1316 ERR("Class 0x%p not found\n", ClassName
->Buffer
);
1320 ERR("Class \"%wZ\" not found\n", ClassName
);
1323 EngSetLastError(ERROR_CANNOT_FIND_WND_CLASS
);
1327 TRACE("Referencing Class 0x%p with atom 0x%x\n", Class
, ClassAtom
);
1328 Class
= IntReferenceClass(Class
,
1333 ERR("Failed to reference window class!\n");
1341 UserRegisterClass(IN CONST WNDCLASSEXW
* lpwcx
,
1342 IN PUNICODE_STRING ClassName
,
1343 IN PUNICODE_STRING MenuName
,
1351 RTL_ATOM Ret
= (RTL_ATOM
)0;
1353 /* NOTE: Accessing the buffers in ClassName and MenuName may raise exceptions! */
1355 pti
= GetW32ThreadInfo();
1359 // Need only to test for two conditions not four....... Fix more whine tests....
1360 if ( IntGetAtomFromStringOrAtom( ClassName
, &ClassAtom
) &&
1361 ClassAtom
!= (RTL_ATOM
)0 &&
1362 !(dwFlags
& CSF_SERVERSIDEPROC
) ) // Bypass Server Sides
1364 Class
= IntFindClass( ClassAtom
,
1366 &pi
->pclsPrivateList
,
1369 if (Class
!= NULL
&& !Class
->Global
)
1371 // Local class already exists
1372 TRACE("Local Class 0x%x does already exist!\n", ClassAtom
);
1373 EngSetLastError(ERROR_CLASS_ALREADY_EXISTS
);
1377 if (lpwcx
->style
& CS_GLOBALCLASS
)
1379 Class
= IntFindClass( ClassAtom
,
1381 &pi
->pclsPublicList
,
1384 if (Class
!= NULL
&& Class
->Global
)
1386 TRACE("Global Class 0x%x does already exist!\n", ClassAtom
);
1387 EngSetLastError(ERROR_CLASS_ALREADY_EXISTS
);
1393 Class
= IntCreateClass(lpwcx
,
1405 /* Register the class */
1407 List
= &pi
->pclsPublicList
;
1409 List
= &pi
->pclsPrivateList
;
1411 Class
->pclsNext
= *List
;
1412 (void)InterlockedExchangePointer((PVOID
*)List
,
1415 Ret
= Class
->atomClassName
;
1419 ERR("UserRegisterClass: Yes, that is right, you have no Class!\n");
1426 UserUnregisterClass(IN PUNICODE_STRING ClassName
,
1427 IN HINSTANCE hInstance
,
1428 OUT PCLSMENUNAME pClassMenuName
)
1435 pi
= GetW32ProcessInfo();
1437 TRACE("UserUnregisterClass(%wZ, 0x%p)\n", ClassName
, hInstance
);
1439 /* NOTE: Accessing the buffer in ClassName may raise an exception! */
1440 ClassAtom
= IntGetClassAtom(ClassName
,
1445 if (ClassAtom
== (RTL_ATOM
)0)
1447 TRACE("UserUnregisterClass: No Class found.\n");
1451 ASSERT(Class
!= NULL
);
1453 if (Class
->cWndReferenceCount
!= 0 ||
1454 Class
->pclsClone
!= NULL
)
1456 TRACE("UserUnregisterClass: Class has a Window. Ct %u : Clone 0x%p\n", Class
->cWndReferenceCount
, Class
->pclsClone
);
1457 EngSetLastError(ERROR_CLASS_HAS_WINDOWS
);
1461 /* Must be a base class! */
1462 ASSERT(Class
->pclsBase
== Class
);
1464 /* Unlink the class */
1465 *Link
= Class
->pclsNext
;
1467 if (NT_SUCCESS(IntDeregisterClassAtom(Class
->atomClassName
)))
1469 TRACE("Class 0x%p\n", Class
);
1470 TRACE("UserUnregisterClass: Good Exit!\n");
1471 /* Finally free the resources */
1472 IntDestroyClass(Class
);
1475 ERR("UserUnregisterClass: Can not deregister Class Atom.\n");
1480 UserGetClassName(IN PCLS Class
,
1481 IN OUT PUNICODE_STRING ClassName
,
1485 NTSTATUS Status
= STATUS_SUCCESS
;
1486 WCHAR szStaticTemp
[32];
1487 PWSTR szTemp
= NULL
;
1488 ULONG BufLen
= sizeof(szStaticTemp
);
1491 /* Note: Accessing the buffer in ClassName may raise an exception! */
1497 PANSI_STRING AnsiClassName
= (PANSI_STRING
)ClassName
;
1498 UNICODE_STRING UnicodeClassName
;
1500 /* Limit the size of the static buffer on the stack to the
1501 size of the buffer provided by the caller */
1502 if (BufLen
/ sizeof(WCHAR
) > AnsiClassName
->MaximumLength
)
1504 BufLen
= AnsiClassName
->MaximumLength
* sizeof(WCHAR
);
1507 /* Find out how big the buffer needs to be */
1508 Status
= RtlQueryAtomInAtomTable(gAtomTable
,
1509 Class
->atomClassName
,
1514 if (Status
== STATUS_BUFFER_TOO_SMALL
)
1516 if (BufLen
/ sizeof(WCHAR
) > AnsiClassName
->MaximumLength
)
1518 /* The buffer required exceeds the ansi buffer provided,
1519 pretend like we're using the ansi buffer and limit the
1520 size to the buffer size provided */
1521 BufLen
= AnsiClassName
->MaximumLength
* sizeof(WCHAR
);
1524 /* Allocate a temporary buffer that can hold the unicode class name */
1525 szTemp
= ExAllocatePoolWithTag(PagedPool
,
1530 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1534 /* Query the class name */
1535 Status
= RtlQueryAtomInAtomTable(gAtomTable
,
1536 Atom
? Atom
: Class
->atomClassName
,
1543 szTemp
= szStaticTemp
;
1545 if (NT_SUCCESS(Status
))
1547 /* Convert the atom name to ansi */
1549 RtlInitUnicodeString(&UnicodeClassName
,
1552 Status
= RtlUnicodeStringToAnsiString(AnsiClassName
,
1555 if (!NT_SUCCESS(Status
))
1557 SetLastNtError(Status
);
1562 Ret
= BufLen
/ sizeof(WCHAR
);
1566 BufLen
= ClassName
->MaximumLength
;
1568 /* Query the atom name */
1569 Status
= RtlQueryAtomInAtomTable(gAtomTable
,
1570 Atom
? Atom
: Class
->atomClassName
,
1576 if (!NT_SUCCESS(Status
))
1578 SetLastNtError(Status
);
1582 Ret
= BufLen
/ sizeof(WCHAR
);
1585 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1587 SetLastNtError(_SEH2_GetExceptionCode());
1591 if (Ansi
&& szTemp
!= NULL
&& szTemp
!= szStaticTemp
)
1593 ExFreePoolWithTag(szTemp
, USERTAG_CLASS
);
1600 IntSetClassMenuName(IN PCLS Class
,
1601 IN PUNICODE_STRING MenuName
)
1605 /* Change the base class first */
1606 Class
= Class
->pclsBase
;
1608 if (MenuName
->Length
!= 0)
1610 ANSI_STRING AnsiString
;
1613 AnsiString
.MaximumLength
= (USHORT
)RtlUnicodeStringToAnsiSize(MenuName
);
1615 strBufW
= UserHeapAlloc(MenuName
->Length
+ sizeof(UNICODE_NULL
) +
1616 AnsiString
.MaximumLength
);
1617 if (strBufW
!= NULL
)
1623 /* Copy the unicode string */
1624 RtlCopyMemory(strBufW
,
1627 strBufW
[MenuName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1629 /* Create an ANSI copy of the string */
1630 AnsiString
.Buffer
= (PSTR
)(strBufW
+ (MenuName
->Length
/ sizeof(WCHAR
)) + 1);
1631 Status
= RtlUnicodeStringToAnsiString(&AnsiString
,
1634 if (!NT_SUCCESS(Status
))
1636 SetLastNtError(Status
);
1642 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1644 SetLastNtError(_SEH2_GetExceptionCode());
1650 /* Update the base class */
1651 IntFreeClassMenuName(Class
);
1652 Class
->lpszClientUnicodeMenuName
= strBufW
;
1653 Class
->lpszClientAnsiMenuName
= AnsiString
.Buffer
;
1654 Class
->MenuNameIsString
= TRUE
;
1656 /* Update the clones */
1657 Class
= Class
->pclsClone
;
1658 while (Class
!= NULL
)
1660 Class
->lpszClientUnicodeMenuName
= strBufW
;
1661 Class
->lpszClientAnsiMenuName
= AnsiString
.Buffer
;
1662 Class
->MenuNameIsString
= TRUE
;
1664 Class
= Class
->pclsNext
;
1669 ERR("Failed to copy class menu name!\n");
1670 UserHeapFree(strBufW
);
1674 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1678 ASSERT(IS_INTRESOURCE(MenuName
->Buffer
));
1680 /* Update the base class */
1681 IntFreeClassMenuName(Class
);
1682 Class
->lpszClientUnicodeMenuName
= MenuName
->Buffer
;
1683 Class
->lpszClientAnsiMenuName
= (PSTR
)MenuName
->Buffer
;
1684 Class
->MenuNameIsString
= FALSE
;
1686 /* Update the clones */
1687 Class
= Class
->pclsClone
;
1688 while (Class
!= NULL
)
1690 Class
->lpszClientUnicodeMenuName
= MenuName
->Buffer
;
1691 Class
->lpszClientAnsiMenuName
= (PSTR
)MenuName
->Buffer
;
1692 Class
->MenuNameIsString
= FALSE
;
1694 Class
= Class
->pclsNext
;
1704 UserSetClassLongPtr(IN PCLS Class
,
1706 IN ULONG_PTR NewLong
,
1711 /* NOTE: For GCLP_MENUNAME and GCW_ATOM this function may raise an exception! */
1713 /* Change the information in the base class first, then update the clones */
1714 Class
= Class
->pclsBase
;
1720 TRACE("SetClassLong(%d, %x)\n", Index
, NewLong
);
1722 if (((ULONG
)Index
+ sizeof(ULONG_PTR
)) < (ULONG
)Index
||
1723 ((ULONG
)Index
+ sizeof(ULONG_PTR
)) > (ULONG
)Class
->cbclsExtra
)
1725 EngSetLastError(ERROR_INVALID_PARAMETER
);
1729 Data
= (PULONG_PTR
)((ULONG_PTR
)(Class
+ 1) + Index
);
1731 /* FIXME: Data might be a unaligned pointer! Might be a problem on
1732 certain architectures, maybe using RtlCopyMemory is a
1733 better choice for those architectures! */
1737 /* Update the clones */
1738 Class
= Class
->pclsClone
;
1739 while (Class
!= NULL
)
1741 *(PULONG_PTR
)((ULONG_PTR
)(Class
+ 1) + Index
) = NewLong
;
1742 Class
= Class
->pclsNext
;
1750 case GCL_CBWNDEXTRA
:
1751 Ret
= (ULONG_PTR
)Class
->cbwndExtra
;
1752 Class
->cbwndExtra
= (INT
)NewLong
;
1754 /* Update the clones */
1755 Class
= Class
->pclsClone
;
1756 while (Class
!= NULL
)
1758 Class
->cbwndExtra
= (INT
)NewLong
;
1759 Class
= Class
->pclsNext
;
1764 case GCL_CBCLSEXTRA
:
1765 EngSetLastError(ERROR_INVALID_PARAMETER
);
1768 case GCLP_HBRBACKGROUND
:
1769 Ret
= (ULONG_PTR
)Class
->hbrBackground
;
1770 Class
->hbrBackground
= (HBRUSH
)NewLong
;
1772 /* Update the clones */
1773 Class
= Class
->pclsClone
;
1774 while (Class
!= NULL
)
1776 Class
->hbrBackground
= (HBRUSH
)NewLong
;
1777 Class
= Class
->pclsNext
;
1782 /* FIXME: Get handle from pointer to CURSOR object */
1783 Ret
= (ULONG_PTR
)Class
->hCursor
;
1784 Class
->hCursor
= (HANDLE
)NewLong
;
1786 /* Update the clones */
1787 Class
= Class
->pclsClone
;
1788 while (Class
!= NULL
)
1790 Class
->hCursor
= (HANDLE
)NewLong
;
1791 Class
= Class
->pclsNext
;
1796 /* FIXME: Get handle from pointer to ICON object */
1797 Ret
= (ULONG_PTR
)Class
->hIcon
;
1798 Class
->hIcon
= (HANDLE
)NewLong
;
1800 /* Update the clones */
1801 Class
= Class
->pclsClone
;
1802 while (Class
!= NULL
)
1804 Class
->hIcon
= (HANDLE
)NewLong
;
1805 Class
= Class
->pclsNext
;
1810 /* FIXME: Get handle from pointer to ICON object */
1811 Ret
= (ULONG_PTR
)Class
->hIconSm
;
1812 Class
->hIconSm
= (HANDLE
)NewLong
;
1814 /* Update the clones */
1815 Class
= Class
->pclsClone
;
1816 while (Class
!= NULL
)
1818 Class
->hIconSm
= (HANDLE
)NewLong
;
1819 Class
= Class
->pclsNext
;
1824 Ret
= (ULONG_PTR
)Class
->hModule
;
1825 Class
->hModule
= (HINSTANCE
)NewLong
;
1827 /* Update the clones */
1828 Class
= Class
->pclsClone
;
1829 while (Class
!= NULL
)
1831 Class
->hModule
= (HINSTANCE
)NewLong
;
1832 Class
= Class
->pclsNext
;
1838 PUNICODE_STRING Value
= (PUNICODE_STRING
)NewLong
;
1840 if (!IntSetClassMenuName(Class
,
1843 ERR("Setting the class menu name failed!\n");
1846 /* FIXME: Really return NULL? Wine does so... */
1851 Ret
= (ULONG_PTR
)Class
->style
;
1852 Class
->style
= (UINT
)NewLong
;
1854 /* FIXME: What if the CS_GLOBALCLASS style is changed? should we
1855 move the class to the appropriate list? For now, we save
1856 the original value in Class->Global, so we can always
1857 locate the appropriate list */
1859 /* Update the clones */
1860 Class
= Class
->pclsClone
;
1861 while (Class
!= NULL
)
1863 Class
->style
= (UINT
)NewLong
;
1864 Class
= Class
->pclsNext
;
1869 Ret
= (ULONG_PTR
)IntSetClassWndProc(Class
,
1876 PUNICODE_STRING Value
= (PUNICODE_STRING
)NewLong
;
1878 Ret
= (ULONG_PTR
)Class
->atomClassName
;
1879 if (!IntSetClassAtom(Class
,
1888 EngSetLastError(ERROR_INVALID_INDEX
);
1896 UserGetClassInfo(IN PCLS Class
,
1897 OUT PWNDCLASSEXW lpwcx
,
1899 HINSTANCE hInstance
)
1901 if (!Class
) return FALSE
;
1903 lpwcx
->style
= Class
->style
;
1905 // If fnId is set, clear the global bit. See wine class test check_style.
1907 lpwcx
->style
&= ~CS_GLOBALCLASS
;
1909 lpwcx
->lpfnWndProc
= IntGetClassWndProc(Class
, Ansi
);
1911 lpwcx
->cbClsExtra
= Class
->cbclsExtra
;
1912 lpwcx
->cbWndExtra
= Class
->cbwndExtra
;
1913 lpwcx
->hIcon
= Class
->hIcon
; /* FIXME: Get handle from pointer */
1914 lpwcx
->hCursor
= Class
->hCursor
; /* FIXME: Get handle from pointer */
1915 lpwcx
->hbrBackground
= Class
->hbrBackground
;
1917 /* Copy non-string to user first. */
1919 ((PWNDCLASSEXA
)lpwcx
)->lpszMenuName
= Class
->lpszClientAnsiMenuName
;
1921 lpwcx
->lpszMenuName
= Class
->lpszClientUnicodeMenuName
;
1923 * FIXME: CLSMENUNAME has the answers! Copy the already made buffers from there!
1924 * Cls: lpszMenuName and lpszAnsiClassName should be used by kernel space.
1925 * lpszClientXxxMenuName should already be mapped to user space.
1927 /* Copy string ptr to user. */
1928 if ( Class
->lpszClientUnicodeMenuName
!= NULL
&&
1929 Class
->MenuNameIsString
)
1931 lpwcx
->lpszMenuName
= UserHeapAddressToUser(Ansi
?
1932 (PVOID
)Class
->lpszClientAnsiMenuName
:
1933 (PVOID
)Class
->lpszClientUnicodeMenuName
);
1936 if (hInstance
== hModClient
)
1937 lpwcx
->hInstance
= NULL
;
1939 lpwcx
->hInstance
= hInstance
;
1941 /* FIXME: Return the string? Okay! This is performed in User32! */
1942 //lpwcx->lpszClassName = (LPCWSTR)((ULONG_PTR)Class->atomClassName);
1944 lpwcx
->hIconSm
= Class
->hIconSm
; /* FIXME: Get handle from pointer */
1954 UserRegisterSystemClasses(VOID
)
1957 UNICODE_STRING ClassName
, MenuName
;
1958 PPROCESSINFO ppi
= GetW32ProcessInfo();
1965 if (ppi
->W32PF_flags
& W32PF_CLASSESREGISTERED
)
1968 if ( hModClient
== NULL
)
1971 RtlZeroMemory(&ClassName
, sizeof(ClassName
));
1972 RtlZeroMemory(&MenuName
, sizeof(MenuName
));
1974 for (i
= 0; i
!= ARRAYSIZE(DefaultServerClasses
); i
++)
1976 if (!IS_ATOM(DefaultServerClasses
[i
].ClassName
))
1978 RtlInitUnicodeString(&ClassName
, DefaultServerClasses
[i
].ClassName
);
1982 ClassName
.Buffer
= DefaultServerClasses
[i
].ClassName
;
1983 ClassName
.Length
= 0;
1984 ClassName
.MaximumLength
= 0;
1987 wc
.cbSize
= sizeof(wc
);
1988 wc
.style
= DefaultServerClasses
[i
].Style
;
1990 Flags
|= CSF_SERVERSIDEPROC
;
1992 if (DefaultServerClasses
[i
].ProcW
)
1994 wc
.lpfnWndProc
= DefaultServerClasses
[i
].ProcW
;
1995 wc
.hInstance
= hModuleWin
;
1999 wc
.lpfnWndProc
= GETPFNSERVER(DefaultServerClasses
[i
].fiId
);
2000 wc
.hInstance
= hModClient
;
2004 wc
.cbWndExtra
= DefaultServerClasses
[i
].ExtraBytes
;
2006 wc
.hCursor
= DefaultServerClasses
[i
].hCursor
;
2007 hBrush
= DefaultServerClasses
[i
].hBrush
;
2008 if (hBrush
<= (HBRUSH
)COLOR_MENUBAR
)
2010 hBrush
= IntGetSysColorBrush((INT
)hBrush
);
2012 wc
.hbrBackground
= hBrush
;
2013 wc
.lpszMenuName
= NULL
;
2014 wc
.lpszClassName
= ClassName
.Buffer
;
2017 Class
= IntCreateClass( &wc
,
2020 DefaultServerClasses
[i
].fiId
,
2026 Class
->pclsNext
= ppi
->pclsPublicList
;
2027 (void)InterlockedExchangePointer((PVOID
*)&ppi
->pclsPublicList
,
2030 ppi
->dwRegisteredClasses
|= ICLASS_TO_MASK(DefaultServerClasses
[i
].iCls
);
2034 ERR("!!! Registering system class failed!\n");
2038 if (Ret
) ppi
->W32PF_flags
|= W32PF_CLASSESREGISTERED
;
2042 /* SYSCALLS *****************************************************************/
2046 NtUserRegisterClassExWOW(
2048 PUNICODE_STRING ClassName
,
2049 PUNICODE_STRING ClsNVersion
,
2050 PCLSMENUNAME pClassMenuName
,
2056 * Registers a new class with the window manager
2058 * lpwcx = Win32 extended window class structure
2059 * bUnicodeClass = Whether to send ANSI or unicode strings
2060 * to window procedures
2062 * Atom identifying the new class
2065 WNDCLASSEXW CapturedClassInfo
= {0};
2066 UNICODE_STRING CapturedName
= {0}, CapturedMenuName
= {0};
2067 RTL_ATOM Ret
= (RTL_ATOM
)0;
2068 PPROCESSINFO ppi
= GetW32ProcessInfo();
2070 if (Flags
& ~(CSF_ANSIPROC
))
2072 ERR("NtUserRegisterClassExWOW Bad Flags!\n");
2073 EngSetLastError(ERROR_INVALID_FLAGS
);
2077 UserEnterExclusive();
2079 TRACE("NtUserRegisterClassExWOW ClsN %wZ\n",ClassName
);
2081 if ( !(ppi
->W32PF_flags
& W32PF_CLASSESREGISTERED
))
2083 UserRegisterSystemClasses();
2088 /* Probe the parameters and basic parameter checks */
2089 if (ProbeForReadUint(&lpwcx
->cbSize
) != sizeof(WNDCLASSEXW
))
2091 ERR("NtUserRegisterClassExWOW Wrong cbSize!\n");
2092 goto InvalidParameter
;
2096 sizeof(WNDCLASSEXW
),
2098 RtlCopyMemory(&CapturedClassInfo
,
2100 sizeof(WNDCLASSEXW
));
2102 CapturedName
= ProbeForReadUnicodeString(ClassName
);
2104 ProbeForRead(pClassMenuName
,
2105 sizeof(CLSMENUNAME
),
2108 CapturedMenuName
= ProbeForReadUnicodeString(pClassMenuName
->pusMenuName
);
2110 if ( (CapturedName
.Length
& 1) ||
2111 (CapturedMenuName
.Length
& 1) ||
2112 (CapturedClassInfo
.cbClsExtra
< 0) ||
2113 ((CapturedClassInfo
.cbClsExtra
+ CapturedName
.Length
+
2114 CapturedMenuName
.Length
+ sizeof(CLS
))
2115 < (ULONG
)CapturedClassInfo
.cbClsExtra
) ||
2116 (CapturedClassInfo
.cbWndExtra
< 0) ||
2117 (CapturedClassInfo
.hInstance
== NULL
) )
2119 ERR("NtUserRegisterClassExWOW Invalid Parameter Error!\n");
2120 goto InvalidParameter
;
2123 if (CapturedName
.Length
!= 0)
2125 ProbeForRead(CapturedName
.Buffer
,
2126 CapturedName
.Length
,
2131 if (!IS_ATOM(CapturedName
.Buffer
))
2133 ERR("NtUserRegisterClassExWOW ClassName Error!\n");
2134 goto InvalidParameter
;
2138 if (CapturedMenuName
.Length
!= 0)
2140 ProbeForRead(CapturedMenuName
.Buffer
,
2141 CapturedMenuName
.Length
,
2144 else if (CapturedMenuName
.Buffer
!= NULL
&&
2145 !IS_INTRESOURCE(CapturedMenuName
.Buffer
))
2147 ERR("NtUserRegisterClassExWOW MenuName Error!\n");
2149 EngSetLastError(ERROR_INVALID_PARAMETER
);
2153 if (IsCallProcHandle(lpwcx
->lpfnWndProc
))
2154 { // Never seen this yet, but I'm sure it's a little haxxy trick!
2155 // If this pops up we know what todo!
2156 ERR("NtUserRegisterClassExWOW WndProc is CallProc!!\n");
2159 TRACE("NtUserRegisterClassExWOW MnuN %wZ\n",&CapturedMenuName
);
2161 /* Register the class */
2162 Ret
= UserRegisterClass(&CapturedClassInfo
,
2168 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2170 ERR("NtUserRegisterClassExWOW Exception Error!\n");
2171 SetLastNtError(_SEH2_GetExceptionCode());
2177 ERR("NtUserRegisterClassExWOW Null Return!\n");
2186 NtUserSetClassLong(HWND hWnd
,
2188 ULONG_PTR dwNewLong
,
2195 UserEnterExclusive();
2197 pi
= GetW32ProcessInfo();
2199 Window
= UserGetWindowObject(hWnd
);
2202 if (Window
->head
.pti
->ppi
!= pi
)
2204 EngSetLastError(ERROR_ACCESS_DENIED
);
2210 UNICODE_STRING Value
;
2212 /* Probe the parameters */
2213 if (Offset
== GCW_ATOM
|| Offset
== GCLP_MENUNAME
)
2215 Value
= ProbeForReadUnicodeString((PUNICODE_STRING
)dwNewLong
);
2216 if (Value
.Length
& 1)
2218 goto InvalidParameter
;
2221 if (Value
.Length
!= 0)
2223 ProbeForRead(Value
.Buffer
,
2229 if (Offset
== GCW_ATOM
&& !IS_ATOM(Value
.Buffer
))
2231 goto InvalidParameter
;
2233 else if (Offset
== GCLP_MENUNAME
&& !IS_INTRESOURCE(Value
.Buffer
))
2236 EngSetLastError(ERROR_INVALID_PARAMETER
);
2241 dwNewLong
= (ULONG_PTR
)&Value
;
2244 Ret
= UserSetClassLongPtr(Window
->pcls
,
2249 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2251 SetLastNtError(_SEH2_GetExceptionCode());
2270 * NOTE: Obsoleted in 32-bit windows
2277 NtUserUnregisterClass(
2278 IN PUNICODE_STRING ClassNameOrAtom
,
2279 IN HINSTANCE hInstance
,
2280 OUT PCLSMENUNAME pClassMenuName
)
2282 UNICODE_STRING SafeClassName
;
2286 Status
= CaptureUnicodeStringOrAtom(&SafeClassName
, ClassNameOrAtom
);
2287 if (!NT_SUCCESS(Status
))
2289 ERR("Error capturing the class name\n");
2290 SetLastNtError(Status
);
2294 UserEnterExclusive();
2296 /* Unregister the class */
2297 Ret
= UserUnregisterClass(&SafeClassName
, hInstance
, NULL
); // Null for now~
2301 if (SafeClassName
.Buffer
&& !IS_ATOM(SafeClassName
.Buffer
))
2302 ExFreePoolWithTag(SafeClassName
.Buffer
, TAG_STRING
);
2308 /* NOTE: For system classes hInstance is not NULL here, but User32Instance */
2312 HINSTANCE hInstance
,
2313 PUNICODE_STRING ClassName
,
2314 LPWNDCLASSEXW lpWndClassEx
,
2315 LPWSTR
*ppszMenuName
,
2318 UNICODE_STRING SafeClassName
;
2319 WNDCLASSEXW Safewcexw
;
2321 RTL_ATOM ClassAtom
= 0;
2328 ProbeForWrite( lpWndClassEx
, sizeof(WNDCLASSEXW
), sizeof(ULONG
));
2329 RtlCopyMemory( &Safewcexw
, lpWndClassEx
, sizeof(WNDCLASSEXW
));
2331 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2333 SetLastNtError(_SEH2_GetExceptionCode());
2334 _SEH2_YIELD(return FALSE
);
2338 Status
= CaptureUnicodeStringOrAtom(&SafeClassName
, ClassName
);
2339 if (!NT_SUCCESS(Status
))
2341 ERR("Error capturing the class name\n");
2342 SetLastNtError(Status
);
2346 // If null instance use client.
2347 if (!hInstance
) hInstance
= hModClient
;
2349 TRACE("GetClassInfo(%wZ, 0x%x)\n", SafeClassName
, hInstance
);
2351 /* NOTE: Need exclusive lock because getting the wndproc might require the
2352 creation of a call procedure handle */
2353 UserEnterExclusive();
2355 ppi
= GetW32ProcessInfo();
2356 if (!(ppi
->W32PF_flags
& W32PF_CLASSESREGISTERED
))
2358 UserRegisterSystemClasses();
2361 ClassAtom
= IntGetClassAtom(&SafeClassName
,
2366 if (ClassAtom
!= (RTL_ATOM
)0)
2368 Ret
= UserGetClassInfo(Class
, &Safewcexw
, bAnsi
, hInstance
);
2372 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
2382 /* Emulate Function. */
2383 if (ppszMenuName
) *ppszMenuName
= (LPWSTR
)Safewcexw
.lpszMenuName
;
2385 RtlCopyMemory(lpWndClassEx
, &Safewcexw
, sizeof(WNDCLASSEXW
));
2388 /* We must return the atom of the class here instead of just TRUE. */
2389 /* Undocumented behavior! Return the class atom as a BOOL! */
2390 Ret
= (BOOL
)ClassAtom
;
2392 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2394 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
2400 if (!IS_ATOM(SafeClassName
.Buffer
))
2401 ExFreePoolWithTag(SafeClassName
.Buffer
, TAG_STRING
);
2408 NtUserGetClassName (IN HWND hWnd
,
2410 OUT PUNICODE_STRING ClassName
)
2413 UNICODE_STRING CapturedClassName
;
2419 Window
= UserGetWindowObject(hWnd
);
2422 if (Real
&& Window
->fnid
&& !(Window
->fnid
& FNID_DESTROY
))
2424 if (LookupFnIdToiCls(Window
->fnid
, &iCls
))
2426 Atom
= gpsi
->atomSysClass
[iCls
];
2432 ProbeForWriteUnicodeString(ClassName
);
2433 CapturedClassName
= *ClassName
;
2435 /* Get the class name */
2436 Ret
= UserGetClassName(Window
->pcls
,
2443 /* Update the Length field */
2444 ClassName
->Length
= CapturedClassName
.Length
;
2447 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2449 SetLastNtError(_SEH2_GetExceptionCode());
2459 /* Return Pointer to Class structure. */
2463 HINSTANCE hInstance
,
2464 PUNICODE_STRING ClassName
)
2466 UNICODE_STRING SafeClassName
;
2469 RTL_ATOM ClassAtom
= 0;
2472 Status
= CaptureUnicodeStringOrAtom(&SafeClassName
, ClassName
);
2473 if (!NT_SUCCESS(Status
))
2475 ERR("Error capturing the class name\n");
2476 SetLastNtError(Status
);
2480 UserEnterExclusive();
2482 pi
= GetW32ProcessInfo();
2484 ClassAtom
= IntGetClassAtom(&SafeClassName
,
2491 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
2495 if (SafeClassName
.Buffer
&& !IS_ATOM(SafeClassName
.Buffer
))
2496 ExFreePoolWithTag(SafeClassName
.Buffer
, TAG_STRING
);
2500 // Don't forget to use DesktopPtrToUser( ? ) with return pointer in user space.