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 ProbeAndCaptureUnicodeStringOrAtom(
128 _Out_
_When_(return>=0, _At_(pustrOut
->Buffer
, _Post_ _Notnull_
)) 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
, TYPE_CALLPROC
);
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
);
646 Class
= IntGetClassForDesktop(BaseClass
,
657 Class
->cWndReferenceCount
++;
665 IntMakeCloneBaseClass(IN OUT PCLS Class
,
666 IN OUT PCLS
*BaseClassLink
,
667 IN OUT PCLS
*CloneLink
)
671 ASSERT(Class
->pclsBase
!= Class
);
672 ASSERT(Class
->pclsBase
->pclsClone
!= NULL
);
673 ASSERT(Class
->rpdeskParent
!= NULL
);
674 ASSERT(Class
->cWndReferenceCount
!= 0);
675 ASSERT(Class
->pclsBase
->rpdeskParent
!= NULL
);
676 ASSERT(Class
->pclsBase
->cWndReferenceCount
== 0);
678 /* Unlink the clone */
679 *CloneLink
= Class
->pclsNext
;
680 Class
->pclsClone
= Class
->pclsBase
->pclsClone
;
682 /* Update the class information to make it a base class */
683 Class
->pclsBase
= Class
;
684 Class
->pclsNext
= (*BaseClassLink
)->pclsNext
;
686 /* Update all clones */
687 Clone
= Class
->pclsClone
;
688 while (Clone
!= NULL
)
690 ASSERT(Clone
->pclsClone
== NULL
);
691 Clone
->pclsBase
= Class
;
693 Clone
= Clone
->pclsNext
;
696 /* Link in the new base class */
697 (void)InterlockedExchangePointer((PVOID
*)BaseClassLink
,
703 IntDereferenceClass(IN OUT PCLS Class
,
704 IN PDESKTOPINFO Desktop
,
707 PCLS
*PrevLink
, BaseClass
, CurrentClass
;
709 ASSERT(Class
->cWndReferenceCount
>= 1);
711 BaseClass
= Class
->pclsBase
;
713 if (--Class
->cWndReferenceCount
== 0)
715 if (BaseClass
== Class
)
717 ASSERT(Class
->pclsBase
== Class
);
719 TRACE("IntDereferenceClass 0x%p\n", Class
);
720 /* Check if there are clones of the class on other desktops,
721 link the first clone in if possible. If there are no clones
722 then leave the class on the desktop heap. It will get moved
723 to the shared heap when the thread detaches. */
724 if (BaseClass
->pclsClone
!= NULL
)
726 if (BaseClass
->Global
)
727 PrevLink
= &pi
->pclsPublicList
;
729 PrevLink
= &pi
->pclsPrivateList
;
731 CurrentClass
= *PrevLink
;
732 while (CurrentClass
!= BaseClass
)
734 ASSERT(CurrentClass
!= NULL
);
736 PrevLink
= &CurrentClass
->pclsNext
;
737 CurrentClass
= CurrentClass
->pclsNext
;
740 ASSERT(*PrevLink
== BaseClass
);
742 /* Make the first clone become the new base class */
743 IntMakeCloneBaseClass(BaseClass
->pclsClone
,
745 &BaseClass
->pclsClone
);
747 /* Destroy the class, there's still another clone of the class
748 that now serves as a base class. Make sure we don't destruct
749 resources shared by all classes (Base = NULL)! */
750 BaseClass
->pclsBase
= NULL
;
751 BaseClass
->pclsClone
= NULL
;
752 IntDestroyClass(BaseClass
);
757 TRACE("IntDereferenceClass1 0x%p\n", Class
);
759 /* Locate the cloned class and unlink it */
760 PrevLink
= &BaseClass
->pclsClone
;
761 CurrentClass
= BaseClass
->pclsClone
;
762 while (CurrentClass
!= Class
)
764 ASSERT(CurrentClass
!= NULL
);
766 PrevLink
= &CurrentClass
->pclsNext
;
767 CurrentClass
= CurrentClass
->pclsNext
;
770 ASSERT(CurrentClass
== Class
);
772 (void)InterlockedExchangePointer((PVOID
*)PrevLink
,
775 ASSERT(Class
->pclsBase
== BaseClass
);
776 ASSERT(Class
->pclsClone
== NULL
);
778 /* The class was just a clone, we don't need it anymore */
779 IntDestroyClass(Class
);
785 IntMoveClassToSharedHeap(IN OUT PCLS Class
,
786 IN OUT PCLS
**ClassLinkPtr
)
791 ASSERT(Class
->pclsBase
== Class
);
792 ASSERT(Class
->rpdeskParent
!= NULL
);
793 ASSERT(Class
->cWndReferenceCount
== 0);
794 ASSERT(Class
->pclsClone
== NULL
);
796 ClassSize
= sizeof(*Class
) + (SIZE_T
)Class
->cbclsExtra
;
798 /* Allocate the new base class on the shared heap */
799 NewClass
= UserHeapAlloc(ClassSize
);
800 if (NewClass
!= NULL
)
802 RtlCopyMemory(NewClass
,
806 NewClass
->rpdeskParent
= NULL
;
807 NewClass
->pclsBase
= NewClass
;
809 /* Replace the class in the list */
810 (void)InterlockedExchangePointer((PVOID
*)*ClassLinkPtr
,
812 *ClassLinkPtr
= &NewClass
->pclsNext
;
814 /* Free the obsolete class on the desktop heap */
815 Class
->pclsBase
= NULL
;
816 IntDestroyClass(Class
);
824 IntCheckDesktopClasses(IN PDESKTOP Desktop
,
825 IN OUT PCLS
*ClassList
,
826 IN BOOL FreeOnFailure
,
829 PCLS Class
, NextClass
, *Link
;
831 /* NOTE: We only need to check base classes! When classes are no longer needed
832 on a desktop, the clones will be freed automatically as soon as possible.
833 However, we need to move base classes to the shared heap, as soon as
834 the last desktop heap where a class is allocated on is about to be destroyed.
835 If we didn't move the class to the shared heap, the class would become
838 ASSERT(Desktop
!= NULL
);
842 while (Class
!= NULL
)
844 NextClass
= Class
->pclsNext
;
846 ASSERT(Class
->pclsBase
== Class
);
848 if (Class
->rpdeskParent
== Desktop
&&
849 Class
->cWndReferenceCount
== 0)
851 /* There shouldn't be any clones around anymore! */
852 ASSERT(Class
->pclsClone
== NULL
);
854 /* FIXME: If process is terminating, don't move the class but rather destroy it! */
855 /* FIXME: We could move the class to another desktop heap if there's still desktops
856 mapped into the process... */
858 /* Move the class to the shared heap */
859 if (IntMoveClassToSharedHeap(Class
,
862 ASSERT(*Link
== NextClass
);
866 ASSERT(NextClass
== Class
->pclsNext
);
870 /* Unlink the base class */
871 (void)InterlockedExchangePointer((PVOID
*)Link
,
874 /* We can free the old base class now */
875 Class
->pclsBase
= NULL
;
876 IntDestroyClass(Class
);
880 Link
= &Class
->pclsNext
;
886 Link
= &Class
->pclsNext
;
893 IntCheckProcessDesktopClasses(IN PDESKTOP Desktop
,
894 IN BOOL FreeOnFailure
)
899 pi
= GetW32ProcessInfo();
901 /* Check all local classes */
902 IntCheckDesktopClasses(Desktop
,
903 &pi
->pclsPrivateList
,
907 /* Check all global classes */
908 IntCheckDesktopClasses(Desktop
,
914 ERR("Failed to move process classes from desktop 0x%p to the shared heap!\n", Desktop
);
915 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
923 IntCreateClass(IN CONST WNDCLASSEXW
* lpwcx
,
924 IN PUNICODE_STRING ClassName
,
925 IN PUNICODE_STRING MenuName
,
935 PWSTR pszMenuName
= NULL
;
936 NTSTATUS Status
= STATUS_SUCCESS
;
938 TRACE("lpwcx=%p ClassName=%wZ MenuName=%wZ dwFlags=%08x Desktop=%p pi=%p\n",
939 lpwcx
, ClassName
, MenuName
, dwFlags
, Desktop
, pi
);
941 if (!IntRegisterClassAtom(ClassName
,
944 ERR("Failed to register class atom!\n");
948 ClassSize
= sizeof(*Class
) + lpwcx
->cbClsExtra
;
949 if (MenuName
->Length
!= 0)
951 pszMenuName
= UserHeapAlloc(MenuName
->Length
+ sizeof(UNICODE_NULL
) +
952 RtlUnicodeStringToAnsiSize(MenuName
));
953 if (pszMenuName
== NULL
)
959 Class
= DesktopHeapAlloc(Desktop
,
964 /* FIXME: The class was created before being connected
965 to a desktop. It is possible for the desktop window,
966 but should it be allowed for any other case? */
967 Class
= UserHeapAlloc(ClassSize
);
974 RtlZeroMemory(Class
, ClassSize
);
976 Class
->rpdeskParent
= Desktop
;
977 Class
->pclsBase
= Class
;
978 Class
->atomClassName
= Atom
;
980 Class
->CSF_flags
= dwFlags
;
982 if (LookupFnIdToiCls(Class
->fnid
, &iCls
))
984 gpsi
->atomSysClass
[iCls
] = Class
->atomClassName
;
989 PWSTR pszMenuNameBuffer
= pszMenuName
;
991 /* Need to protect with SEH since accessing the WNDCLASSEX structure
992 and string buffers might raise an exception! We don't want to
994 // What?! If the user interface was written correctly this would not be an issue!
995 Class
->lpfnWndProc
= lpwcx
->lpfnWndProc
;
996 Class
->style
= lpwcx
->style
;
997 Class
->cbclsExtra
= lpwcx
->cbClsExtra
;
998 Class
->cbwndExtra
= lpwcx
->cbWndExtra
;
999 Class
->hModule
= lpwcx
->hInstance
;
1000 Class
->hIcon
= lpwcx
->hIcon
; /* FIXME */
1001 Class
->hIconSm
= lpwcx
->hIconSm
; /* FIXME */
1002 Class
->hCursor
= lpwcx
->hCursor
; /* FIXME */
1003 Class
->hbrBackground
= lpwcx
->hbrBackground
;
1005 /* Make a copy of the string */
1006 if (pszMenuNameBuffer
!= NULL
)
1008 Class
->MenuNameIsString
= TRUE
;
1010 Class
->lpszClientUnicodeMenuName
= pszMenuNameBuffer
;
1011 RtlCopyMemory(Class
->lpszClientUnicodeMenuName
,
1014 Class
->lpszClientUnicodeMenuName
[MenuName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1016 pszMenuNameBuffer
+= (MenuName
->Length
/ sizeof(WCHAR
)) + 1;
1019 Class
->lpszClientUnicodeMenuName
= MenuName
->Buffer
;
1021 /* Save an ANSI copy of the string */
1022 if (pszMenuNameBuffer
!= NULL
)
1024 ANSI_STRING AnsiString
;
1026 Class
->lpszClientAnsiMenuName
= (PSTR
)pszMenuNameBuffer
;
1027 AnsiString
.MaximumLength
= (USHORT
)RtlUnicodeStringToAnsiSize(MenuName
);
1028 AnsiString
.Buffer
= Class
->lpszClientAnsiMenuName
;
1029 Status
= RtlUnicodeStringToAnsiString(&AnsiString
,
1032 if (!NT_SUCCESS(Status
))
1034 ERR("Failed to convert unicode menu name to ansi!\n");
1036 /* Life would've been much prettier if ntoskrnl exported RtlRaiseStatus()... */
1041 Class
->lpszClientAnsiMenuName
= (PSTR
)MenuName
->Buffer
;
1043 /* Save kernel use menu name and ansi class name */
1044 Class
->lpszMenuName
= Class
->lpszClientUnicodeMenuName
; // FIXME!
1045 //Class->lpszAnsiClassName = FIXME
1047 /* Server Side overrides class calling type (A/W)!
1048 User32 whine test_builtinproc: "deftest"
1049 built-in winproc - window A/W type automatically detected */
1050 if (!(Class
->CSF_flags
& CSF_SERVERSIDEPROC
))
1054 /* Due to the wine class "deftest" and most likely no FNID to reference
1055 from, sort through the Server Side list and compare proc addresses
1056 for match. This method will be used in related code.
1058 for ( i
= FNID_FIRST
; i
<= FNID_SWITCH
; i
++)
1059 { // Open ANSI or Unicode, just match, set and break.
1060 if (GETPFNCLIENTW(i
) == Class
->lpfnWndProc
)
1062 WndProc
= GETPFNSERVER(i
);
1065 if (GETPFNCLIENTA(i
) == Class
->lpfnWndProc
)
1067 WndProc
= GETPFNSERVER(i
);
1072 { // If a hit, we are Server Side so set the right flags and proc.
1073 Class
->CSF_flags
|= CSF_SERVERSIDEPROC
;
1074 Class
->CSF_flags
&= ~CSF_ANSIPROC
;
1075 Class
->lpfnWndProc
= WndProc
;
1079 if (!(Class
->CSF_flags
& CSF_ANSIPROC
))
1080 Class
->Unicode
= TRUE
;
1082 if (Class
->style
& CS_GLOBALCLASS
)
1083 Class
->Global
= TRUE
;
1085 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1087 Status
= _SEH2_GetExceptionCode();
1091 if (!NT_SUCCESS(Status
))
1093 ERR("Failed creating the class: 0x%x\n", Status
);
1095 SetLastNtError(Status
);
1097 if (pszMenuName
!= NULL
)
1098 UserHeapFree(pszMenuName
);
1100 DesktopHeapFree(Desktop
,
1104 IntDeregisterClassAtom(Atom
);
1110 ERR("Failed to allocate class on Desktop 0x%p\n", Desktop
);
1112 if (pszMenuName
!= NULL
)
1113 UserHeapFree(pszMenuName
);
1115 IntDeregisterClassAtom(Atom
);
1117 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1120 TRACE("Created class 0x%p with name %wZ and proc 0x%p for atom 0x%x and hInstance 0x%p, global %u\n",
1121 Class
, ClassName
, Class
->lpfnWndProc
, Atom
, Class
->hModule
, Class
->Global
);
1127 IntFindClass(IN RTL_ATOM Atom
,
1128 IN HINSTANCE hInstance
,
1130 OUT PCLS
**Link OPTIONAL
)
1132 PCLS Class
, *PrevLink
= ClassList
;
1135 while (Class
!= NULL
)
1137 if (Class
->atomClassName
== Atom
&&
1138 (hInstance
== NULL
|| Class
->hModule
== hInstance
) &&
1139 !(Class
->CSF_flags
& CSF_WOWDEFERDESTROY
))
1141 ASSERT(Class
->pclsBase
== Class
);
1148 PrevLink
= &Class
->pclsNext
;
1149 Class
= Class
->pclsNext
;
1157 IntGetAtomFromStringOrAtom(
1158 _In_ PUNICODE_STRING ClassName
,
1159 _Out_ RTL_ATOM
*Atom
)
1163 if (ClassName
->Length
!= 0)
1171 /* NOTE: Caller has to protect the call with SEH! */
1173 if (ClassName
->Length
!= 0)
1175 /* FIXME: Don't limit to 64 characters! use SEH when allocating memory! */
1176 if (ClassName
->Length
/ sizeof(WCHAR
) >= sizeof(szBuf
) / sizeof(szBuf
[0]))
1178 EngSetLastError(ERROR_INVALID_PARAMETER
);
1182 /* We need to make a local copy of the class name! The caller could
1183 modify the buffer and we could overflow in RtlLookupAtomInAtomTable.
1184 We're protected by SEH, but the ranges that might be accessed were
1186 RtlCopyMemory(szBuf
,
1189 szBuf
[ClassName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1193 AtomName
= ClassName
->Buffer
;
1195 /* Lookup the atom */
1196 Status
= RtlLookupAtomInAtomTable(gAtomTable
,
1199 if (NT_SUCCESS(Status
))
1205 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
1207 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
1211 SetLastNtError(Status
);
1217 ASSERT(IS_ATOM(ClassName
->Buffer
));
1218 *Atom
= (RTL_ATOM
)((ULONG_PTR
)ClassName
->Buffer
);
1227 _In_ PUNICODE_STRING ClassName
,
1228 IN HINSTANCE hInstance OPTIONAL
,
1229 IN PPROCESSINFO pi OPTIONAL
,
1230 OUT PCLS
*BaseClass OPTIONAL
,
1231 OUT PCLS
**Link OPTIONAL
)
1233 RTL_ATOM Atom
= (RTL_ATOM
)0;
1235 ASSERT(BaseClass
!= NULL
);
1237 if (IntGetAtomFromStringOrAtom(ClassName
,
1239 Atom
!= (RTL_ATOM
)0)
1243 /* Attempt to locate the class object */
1247 /* Step 1: Try to find an exact match of locally registered classes */
1248 Class
= IntFindClass(Atom
,
1250 &pi
->pclsPrivateList
,
1253 { TRACE("Step 1: 0x%p\n",Class
);
1257 /* Step 2: Try to find any globally registered class. The hInstance
1258 is not relevant for global classes */
1259 Class
= IntFindClass(Atom
,
1261 &pi
->pclsPublicList
,
1264 { TRACE("Step 2: 0x%p 0x%p\n",Class
, Class
->hModule
);
1268 /* Step 3: Try to find any local class registered by user32 */
1269 Class
= IntFindClass(Atom
,
1271 &pi
->pclsPrivateList
,
1274 { TRACE("Step 3: 0x%p\n",Class
);
1278 /* Step 4: Try to find any global class registered by user32 */
1279 Class
= IntFindClass(Atom
,
1281 &pi
->pclsPublicList
,
1285 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
1287 }else{TRACE("Step 4: 0x%p\n",Class
);}
1297 IntGetAndReferenceClass(PUNICODE_STRING ClassName
, HINSTANCE hInstance
, BOOL bDesktopThread
)
1299 PCLS
*ClassLink
, Class
= NULL
;
1304 pti
= gptiDesktopThread
;
1306 pti
= PsGetCurrentThreadWin32Thread();
1308 if ( !(pti
->ppi
->W32PF_flags
& W32PF_CLASSESREGISTERED
))
1310 UserRegisterSystemClasses();
1313 /* Check the class. */
1315 TRACE("Finding Class %wZ for hInstance 0x%p\n", ClassName
, hInstance
);
1317 ClassAtom
= IntGetClassAtom(ClassName
,
1323 if (ClassAtom
== (RTL_ATOM
)0)
1325 if (IS_ATOM(ClassName
->Buffer
))
1327 ERR("Class 0x%p not found\n", ClassName
->Buffer
);
1331 ERR("Class \"%wZ\" not found\n", ClassName
);
1334 EngSetLastError(ERROR_CANNOT_FIND_WND_CLASS
);
1338 TRACE("Referencing Class 0x%p with atom 0x%x\n", Class
, ClassAtom
);
1339 Class
= IntReferenceClass(Class
,
1344 ERR("Failed to reference window class!\n");
1352 UserRegisterClass(IN CONST WNDCLASSEXW
* lpwcx
,
1353 IN PUNICODE_STRING ClassName
,
1354 IN PUNICODE_STRING MenuName
,
1362 RTL_ATOM Ret
= (RTL_ATOM
)0;
1364 /* NOTE: Accessing the buffers in ClassName and MenuName may raise exceptions! */
1366 pti
= GetW32ThreadInfo();
1370 // Need only to test for two conditions not four....... Fix more whine tests....
1371 if ( IntGetAtomFromStringOrAtom( ClassName
, &ClassAtom
) &&
1372 ClassAtom
!= (RTL_ATOM
)0 &&
1373 !(dwFlags
& CSF_SERVERSIDEPROC
) ) // Bypass Server Sides
1375 Class
= IntFindClass( ClassAtom
,
1377 &pi
->pclsPrivateList
,
1380 if (Class
!= NULL
&& !Class
->Global
)
1382 // Local class already exists
1383 TRACE("Local Class 0x%x does already exist!\n", ClassAtom
);
1384 EngSetLastError(ERROR_CLASS_ALREADY_EXISTS
);
1388 if (lpwcx
->style
& CS_GLOBALCLASS
)
1390 Class
= IntFindClass( ClassAtom
,
1392 &pi
->pclsPublicList
,
1395 if (Class
!= NULL
&& Class
->Global
)
1397 TRACE("Global Class 0x%x does already exist!\n", ClassAtom
);
1398 EngSetLastError(ERROR_CLASS_ALREADY_EXISTS
);
1404 Class
= IntCreateClass(lpwcx
,
1416 /* Register the class */
1418 List
= &pi
->pclsPublicList
;
1420 List
= &pi
->pclsPrivateList
;
1422 Class
->pclsNext
= *List
;
1423 (void)InterlockedExchangePointer((PVOID
*)List
,
1426 Ret
= Class
->atomClassName
;
1430 ERR("UserRegisterClass: Yes, that is right, you have no Class!\n");
1437 UserUnregisterClass(IN PUNICODE_STRING ClassName
,
1438 IN HINSTANCE hInstance
,
1439 OUT PCLSMENUNAME pClassMenuName
)
1446 pi
= GetW32ProcessInfo();
1448 TRACE("UserUnregisterClass(%wZ, 0x%p)\n", ClassName
, hInstance
);
1450 /* NOTE: Accessing the buffer in ClassName may raise an exception! */
1451 ClassAtom
= IntGetClassAtom(ClassName
,
1456 if (ClassAtom
== (RTL_ATOM
)0)
1458 TRACE("UserUnregisterClass: No Class found.\n");
1462 ASSERT(Class
!= NULL
);
1464 if (Class
->cWndReferenceCount
!= 0 ||
1465 Class
->pclsClone
!= NULL
)
1467 TRACE("UserUnregisterClass: Class has a Window. Ct %u : Clone 0x%p\n", Class
->cWndReferenceCount
, Class
->pclsClone
);
1468 EngSetLastError(ERROR_CLASS_HAS_WINDOWS
);
1472 /* Must be a base class! */
1473 ASSERT(Class
->pclsBase
== Class
);
1475 /* Unlink the class */
1476 *Link
= Class
->pclsNext
;
1478 if (NT_SUCCESS(IntDeregisterClassAtom(Class
->atomClassName
)))
1480 TRACE("Class 0x%p\n", Class
);
1481 TRACE("UserUnregisterClass: Good Exit!\n");
1482 /* Finally free the resources */
1483 IntDestroyClass(Class
);
1486 ERR("UserUnregisterClass: Can not deregister Class Atom.\n");
1491 UserGetClassName(IN PCLS Class
,
1492 IN OUT PUNICODE_STRING ClassName
,
1496 NTSTATUS Status
= STATUS_SUCCESS
;
1497 WCHAR szStaticTemp
[32];
1498 PWSTR szTemp
= NULL
;
1499 ULONG BufLen
= sizeof(szStaticTemp
);
1502 /* Note: Accessing the buffer in ClassName may raise an exception! */
1508 PANSI_STRING AnsiClassName
= (PANSI_STRING
)ClassName
;
1509 UNICODE_STRING UnicodeClassName
;
1511 /* Limit the size of the static buffer on the stack to the
1512 size of the buffer provided by the caller */
1513 if (BufLen
/ sizeof(WCHAR
) > AnsiClassName
->MaximumLength
)
1515 BufLen
= AnsiClassName
->MaximumLength
* sizeof(WCHAR
);
1518 /* Find out how big the buffer needs to be */
1519 Status
= RtlQueryAtomInAtomTable(gAtomTable
,
1520 Class
->atomClassName
,
1525 if (Status
== STATUS_BUFFER_TOO_SMALL
)
1527 if (BufLen
/ sizeof(WCHAR
) > AnsiClassName
->MaximumLength
)
1529 /* The buffer required exceeds the ansi buffer provided,
1530 pretend like we're using the ansi buffer and limit the
1531 size to the buffer size provided */
1532 BufLen
= AnsiClassName
->MaximumLength
* sizeof(WCHAR
);
1535 /* Allocate a temporary buffer that can hold the unicode class name */
1536 szTemp
= ExAllocatePoolWithTag(PagedPool
,
1541 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1545 /* Query the class name */
1546 Status
= RtlQueryAtomInAtomTable(gAtomTable
,
1547 Atom
? Atom
: Class
->atomClassName
,
1554 szTemp
= szStaticTemp
;
1556 if (NT_SUCCESS(Status
))
1558 /* Convert the atom name to ansi */
1560 RtlInitUnicodeString(&UnicodeClassName
,
1563 Status
= RtlUnicodeStringToAnsiString(AnsiClassName
,
1566 if (!NT_SUCCESS(Status
))
1568 SetLastNtError(Status
);
1573 Ret
= BufLen
/ sizeof(WCHAR
);
1577 BufLen
= ClassName
->MaximumLength
;
1579 /* Query the atom name */
1580 Status
= RtlQueryAtomInAtomTable(gAtomTable
,
1581 Atom
? Atom
: Class
->atomClassName
,
1587 if (!NT_SUCCESS(Status
))
1589 SetLastNtError(Status
);
1593 Ret
= BufLen
/ sizeof(WCHAR
);
1596 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1598 SetLastNtError(_SEH2_GetExceptionCode());
1602 if (Ansi
&& szTemp
!= NULL
&& szTemp
!= szStaticTemp
)
1604 ExFreePoolWithTag(szTemp
, USERTAG_CLASS
);
1611 IntSetClassMenuName(IN PCLS Class
,
1612 IN PUNICODE_STRING MenuName
)
1616 /* Change the base class first */
1617 Class
= Class
->pclsBase
;
1619 if (MenuName
->Length
!= 0)
1621 ANSI_STRING AnsiString
;
1624 AnsiString
.MaximumLength
= (USHORT
)RtlUnicodeStringToAnsiSize(MenuName
);
1626 strBufW
= UserHeapAlloc(MenuName
->Length
+ sizeof(UNICODE_NULL
) +
1627 AnsiString
.MaximumLength
);
1628 if (strBufW
!= NULL
)
1634 /* Copy the unicode string */
1635 RtlCopyMemory(strBufW
,
1638 strBufW
[MenuName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1640 /* Create an ANSI copy of the string */
1641 AnsiString
.Buffer
= (PSTR
)(strBufW
+ (MenuName
->Length
/ sizeof(WCHAR
)) + 1);
1642 Status
= RtlUnicodeStringToAnsiString(&AnsiString
,
1645 if (!NT_SUCCESS(Status
))
1647 SetLastNtError(Status
);
1653 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1655 SetLastNtError(_SEH2_GetExceptionCode());
1661 /* Update the base class */
1662 IntFreeClassMenuName(Class
);
1663 Class
->lpszClientUnicodeMenuName
= strBufW
;
1664 Class
->lpszClientAnsiMenuName
= AnsiString
.Buffer
;
1665 Class
->MenuNameIsString
= TRUE
;
1667 /* Update the clones */
1668 Class
= Class
->pclsClone
;
1669 while (Class
!= NULL
)
1671 Class
->lpszClientUnicodeMenuName
= strBufW
;
1672 Class
->lpszClientAnsiMenuName
= AnsiString
.Buffer
;
1673 Class
->MenuNameIsString
= TRUE
;
1675 Class
= Class
->pclsNext
;
1680 ERR("Failed to copy class menu name!\n");
1681 UserHeapFree(strBufW
);
1685 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1689 ASSERT(IS_INTRESOURCE(MenuName
->Buffer
));
1691 /* Update the base class */
1692 IntFreeClassMenuName(Class
);
1693 Class
->lpszClientUnicodeMenuName
= MenuName
->Buffer
;
1694 Class
->lpszClientAnsiMenuName
= (PSTR
)MenuName
->Buffer
;
1695 Class
->MenuNameIsString
= FALSE
;
1697 /* Update the clones */
1698 Class
= Class
->pclsClone
;
1699 while (Class
!= NULL
)
1701 Class
->lpszClientUnicodeMenuName
= MenuName
->Buffer
;
1702 Class
->lpszClientAnsiMenuName
= (PSTR
)MenuName
->Buffer
;
1703 Class
->MenuNameIsString
= FALSE
;
1705 Class
= Class
->pclsNext
;
1715 UserSetClassLongPtr(IN PCLS Class
,
1717 IN ULONG_PTR NewLong
,
1722 /* NOTE: For GCLP_MENUNAME and GCW_ATOM this function may raise an exception! */
1724 /* Change the information in the base class first, then update the clones */
1725 Class
= Class
->pclsBase
;
1731 TRACE("SetClassLong(%d, %x)\n", Index
, NewLong
);
1733 if (((ULONG
)Index
+ sizeof(ULONG_PTR
)) < (ULONG
)Index
||
1734 ((ULONG
)Index
+ sizeof(ULONG_PTR
)) > (ULONG
)Class
->cbclsExtra
)
1736 EngSetLastError(ERROR_INVALID_PARAMETER
);
1740 Data
= (PULONG_PTR
)((ULONG_PTR
)(Class
+ 1) + Index
);
1742 /* FIXME: Data might be a unaligned pointer! Might be a problem on
1743 certain architectures, maybe using RtlCopyMemory is a
1744 better choice for those architectures! */
1748 /* Update the clones */
1749 Class
= Class
->pclsClone
;
1750 while (Class
!= NULL
)
1752 *(PULONG_PTR
)((ULONG_PTR
)(Class
+ 1) + Index
) = NewLong
;
1753 Class
= Class
->pclsNext
;
1761 case GCL_CBWNDEXTRA
:
1762 Ret
= (ULONG_PTR
)Class
->cbwndExtra
;
1763 Class
->cbwndExtra
= (INT
)NewLong
;
1765 /* Update the clones */
1766 Class
= Class
->pclsClone
;
1767 while (Class
!= NULL
)
1769 Class
->cbwndExtra
= (INT
)NewLong
;
1770 Class
= Class
->pclsNext
;
1775 case GCL_CBCLSEXTRA
:
1776 EngSetLastError(ERROR_INVALID_PARAMETER
);
1779 case GCLP_HBRBACKGROUND
:
1780 Ret
= (ULONG_PTR
)Class
->hbrBackground
;
1781 Class
->hbrBackground
= (HBRUSH
)NewLong
;
1783 /* Update the clones */
1784 Class
= Class
->pclsClone
;
1785 while (Class
!= NULL
)
1787 Class
->hbrBackground
= (HBRUSH
)NewLong
;
1788 Class
= Class
->pclsNext
;
1793 /* FIXME: Get handle from pointer to CURSOR object */
1794 Ret
= (ULONG_PTR
)Class
->hCursor
;
1795 Class
->hCursor
= (HANDLE
)NewLong
;
1797 /* Update the clones */
1798 Class
= Class
->pclsClone
;
1799 while (Class
!= NULL
)
1801 Class
->hCursor
= (HANDLE
)NewLong
;
1802 Class
= Class
->pclsNext
;
1807 /* FIXME: Get handle from pointer to ICON object */
1808 Ret
= (ULONG_PTR
)Class
->hIcon
;
1809 Class
->hIcon
= (HANDLE
)NewLong
;
1811 /* Update the clones */
1812 Class
= Class
->pclsClone
;
1813 while (Class
!= NULL
)
1815 Class
->hIcon
= (HANDLE
)NewLong
;
1816 Class
= Class
->pclsNext
;
1821 /* FIXME: Get handle from pointer to ICON object */
1822 Ret
= (ULONG_PTR
)Class
->hIconSm
;
1823 Class
->hIconSm
= (HANDLE
)NewLong
;
1825 /* Update the clones */
1826 Class
= Class
->pclsClone
;
1827 while (Class
!= NULL
)
1829 Class
->hIconSm
= (HANDLE
)NewLong
;
1830 Class
= Class
->pclsNext
;
1835 Ret
= (ULONG_PTR
)Class
->hModule
;
1836 Class
->hModule
= (HINSTANCE
)NewLong
;
1838 /* Update the clones */
1839 Class
= Class
->pclsClone
;
1840 while (Class
!= NULL
)
1842 Class
->hModule
= (HINSTANCE
)NewLong
;
1843 Class
= Class
->pclsNext
;
1849 PUNICODE_STRING Value
= (PUNICODE_STRING
)NewLong
;
1851 if (!IntSetClassMenuName(Class
,
1854 ERR("Setting the class menu name failed!\n");
1857 /* FIXME: Really return NULL? Wine does so... */
1862 Ret
= (ULONG_PTR
)Class
->style
;
1863 Class
->style
= (UINT
)NewLong
;
1865 /* FIXME: What if the CS_GLOBALCLASS style is changed? should we
1866 move the class to the appropriate list? For now, we save
1867 the original value in Class->Global, so we can always
1868 locate the appropriate list */
1870 /* Update the clones */
1871 Class
= Class
->pclsClone
;
1872 while (Class
!= NULL
)
1874 Class
->style
= (UINT
)NewLong
;
1875 Class
= Class
->pclsNext
;
1880 Ret
= (ULONG_PTR
)IntSetClassWndProc(Class
,
1887 PUNICODE_STRING Value
= (PUNICODE_STRING
)NewLong
;
1889 Ret
= (ULONG_PTR
)Class
->atomClassName
;
1890 if (!IntSetClassAtom(Class
,
1899 EngSetLastError(ERROR_INVALID_INDEX
);
1907 UserGetClassInfo(IN PCLS Class
,
1908 OUT PWNDCLASSEXW lpwcx
,
1910 HINSTANCE hInstance
)
1912 if (!Class
) return FALSE
;
1914 lpwcx
->style
= Class
->style
;
1916 // If fnId is set, clear the global bit. See wine class test check_style.
1918 lpwcx
->style
&= ~CS_GLOBALCLASS
;
1920 lpwcx
->lpfnWndProc
= IntGetClassWndProc(Class
, Ansi
);
1922 lpwcx
->cbClsExtra
= Class
->cbclsExtra
;
1923 lpwcx
->cbWndExtra
= Class
->cbwndExtra
;
1924 lpwcx
->hIcon
= Class
->hIcon
; /* FIXME: Get handle from pointer */
1925 lpwcx
->hCursor
= Class
->hCursor
; /* FIXME: Get handle from pointer */
1926 lpwcx
->hbrBackground
= Class
->hbrBackground
;
1928 /* Copy non-string to user first. */
1930 ((PWNDCLASSEXA
)lpwcx
)->lpszMenuName
= Class
->lpszClientAnsiMenuName
;
1932 lpwcx
->lpszMenuName
= Class
->lpszClientUnicodeMenuName
;
1934 * FIXME: CLSMENUNAME has the answers! Copy the already made buffers from there!
1935 * Cls: lpszMenuName and lpszAnsiClassName should be used by kernel space.
1936 * lpszClientXxxMenuName should already be mapped to user space.
1938 /* Copy string ptr to user. */
1939 if ( Class
->lpszClientUnicodeMenuName
!= NULL
&&
1940 Class
->MenuNameIsString
)
1942 lpwcx
->lpszMenuName
= UserHeapAddressToUser(Ansi
?
1943 (PVOID
)Class
->lpszClientAnsiMenuName
:
1944 (PVOID
)Class
->lpszClientUnicodeMenuName
);
1947 if (hInstance
== hModClient
)
1948 lpwcx
->hInstance
= NULL
;
1950 lpwcx
->hInstance
= hInstance
;
1952 /* FIXME: Return the string? Okay! This is performed in User32! */
1953 //lpwcx->lpszClassName = (LPCWSTR)((ULONG_PTR)Class->atomClassName);
1955 lpwcx
->hIconSm
= Class
->hIconSm
; /* FIXME: Get handle from pointer */
1965 UserRegisterSystemClasses(VOID
)
1968 UNICODE_STRING ClassName
, MenuName
;
1969 PPROCESSINFO ppi
= GetW32ProcessInfo();
1976 if (ppi
->W32PF_flags
& W32PF_CLASSESREGISTERED
)
1979 if ( hModClient
== NULL
)
1982 RtlZeroMemory(&ClassName
, sizeof(ClassName
));
1983 RtlZeroMemory(&MenuName
, sizeof(MenuName
));
1985 for (i
= 0; i
!= ARRAYSIZE(DefaultServerClasses
); i
++)
1987 if (!IS_ATOM(DefaultServerClasses
[i
].ClassName
))
1989 RtlInitUnicodeString(&ClassName
, DefaultServerClasses
[i
].ClassName
);
1993 ClassName
.Buffer
= DefaultServerClasses
[i
].ClassName
;
1994 ClassName
.Length
= 0;
1995 ClassName
.MaximumLength
= 0;
1998 wc
.cbSize
= sizeof(wc
);
1999 wc
.style
= DefaultServerClasses
[i
].Style
;
2001 Flags
|= CSF_SERVERSIDEPROC
;
2003 if (DefaultServerClasses
[i
].ProcW
)
2005 wc
.lpfnWndProc
= DefaultServerClasses
[i
].ProcW
;
2006 wc
.hInstance
= hModuleWin
;
2010 wc
.lpfnWndProc
= GETPFNSERVER(DefaultServerClasses
[i
].fiId
);
2011 wc
.hInstance
= hModClient
;
2015 wc
.cbWndExtra
= DefaultServerClasses
[i
].ExtraBytes
;
2017 wc
.hCursor
= DefaultServerClasses
[i
].hCursor
;
2018 hBrush
= DefaultServerClasses
[i
].hBrush
;
2019 if (hBrush
<= (HBRUSH
)COLOR_MENUBAR
)
2021 hBrush
= IntGetSysColorBrush((INT
)hBrush
);
2023 wc
.hbrBackground
= hBrush
;
2024 wc
.lpszMenuName
= NULL
;
2025 wc
.lpszClassName
= ClassName
.Buffer
;
2028 Class
= IntCreateClass( &wc
,
2031 DefaultServerClasses
[i
].fiId
,
2037 Class
->pclsNext
= ppi
->pclsPublicList
;
2038 (void)InterlockedExchangePointer((PVOID
*)&ppi
->pclsPublicList
,
2041 ppi
->dwRegisteredClasses
|= ICLASS_TO_MASK(DefaultServerClasses
[i
].iCls
);
2045 ERR("!!! Registering system class failed!\n");
2049 if (Ret
) ppi
->W32PF_flags
|= W32PF_CLASSESREGISTERED
;
2053 /* SYSCALLS *****************************************************************/
2057 NtUserRegisterClassExWOW(
2059 PUNICODE_STRING ClassName
,
2060 PUNICODE_STRING ClsNVersion
,
2061 PCLSMENUNAME pClassMenuName
,
2067 * Registers a new class with the window manager
2069 * lpwcx = Win32 extended window class structure
2070 * bUnicodeClass = Whether to send ANSI or unicode strings
2071 * to window procedures
2073 * Atom identifying the new class
2076 WNDCLASSEXW CapturedClassInfo
= {0};
2077 UNICODE_STRING CapturedName
= {0}, CapturedMenuName
= {0};
2078 RTL_ATOM Ret
= (RTL_ATOM
)0;
2079 PPROCESSINFO ppi
= GetW32ProcessInfo();
2081 if (Flags
& ~(CSF_ANSIPROC
))
2083 ERR("NtUserRegisterClassExWOW Bad Flags!\n");
2084 EngSetLastError(ERROR_INVALID_FLAGS
);
2088 UserEnterExclusive();
2090 TRACE("NtUserRegisterClassExWOW ClsN %wZ\n",ClassName
);
2092 if ( !(ppi
->W32PF_flags
& W32PF_CLASSESREGISTERED
))
2094 UserRegisterSystemClasses();
2099 /* Probe the parameters and basic parameter checks */
2100 if (ProbeForReadUint(&lpwcx
->cbSize
) != sizeof(WNDCLASSEXW
))
2102 ERR("NtUserRegisterClassExWOW Wrong cbSize!\n");
2103 goto InvalidParameter
;
2107 sizeof(WNDCLASSEXW
),
2109 RtlCopyMemory(&CapturedClassInfo
,
2111 sizeof(WNDCLASSEXW
));
2113 CapturedName
= ProbeForReadUnicodeString(ClassName
);
2115 ProbeForRead(pClassMenuName
,
2116 sizeof(CLSMENUNAME
),
2119 CapturedMenuName
= ProbeForReadUnicodeString(pClassMenuName
->pusMenuName
);
2121 if ( (CapturedName
.Length
& 1) ||
2122 (CapturedMenuName
.Length
& 1) ||
2123 (CapturedClassInfo
.cbClsExtra
< 0) ||
2124 ((CapturedClassInfo
.cbClsExtra
+ CapturedName
.Length
+
2125 CapturedMenuName
.Length
+ sizeof(CLS
))
2126 < (ULONG
)CapturedClassInfo
.cbClsExtra
) ||
2127 (CapturedClassInfo
.cbWndExtra
< 0) ||
2128 (CapturedClassInfo
.hInstance
== NULL
) )
2130 ERR("NtUserRegisterClassExWOW Invalid Parameter Error!\n");
2131 goto InvalidParameter
;
2134 if (CapturedName
.Length
!= 0)
2136 ProbeForRead(CapturedName
.Buffer
,
2137 CapturedName
.Length
,
2142 if (!IS_ATOM(CapturedName
.Buffer
))
2144 ERR("NtUserRegisterClassExWOW ClassName Error!\n");
2145 goto InvalidParameter
;
2149 if (CapturedMenuName
.Length
!= 0)
2151 ProbeForRead(CapturedMenuName
.Buffer
,
2152 CapturedMenuName
.Length
,
2155 else if (CapturedMenuName
.Buffer
!= NULL
&&
2156 !IS_INTRESOURCE(CapturedMenuName
.Buffer
))
2158 ERR("NtUserRegisterClassExWOW MenuName Error!\n");
2160 EngSetLastError(ERROR_INVALID_PARAMETER
);
2164 if (IsCallProcHandle(lpwcx
->lpfnWndProc
))
2165 { // Never seen this yet, but I'm sure it's a little haxxy trick!
2166 // If this pops up we know what todo!
2167 ERR("NtUserRegisterClassExWOW WndProc is CallProc!!\n");
2170 TRACE("NtUserRegisterClassExWOW MnuN %wZ\n",&CapturedMenuName
);
2172 /* Register the class */
2173 Ret
= UserRegisterClass(&CapturedClassInfo
,
2179 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2181 ERR("NtUserRegisterClassExWOW Exception Error!\n");
2182 SetLastNtError(_SEH2_GetExceptionCode());
2188 ERR("NtUserRegisterClassExWOW Null Return!\n");
2197 NtUserSetClassLong(HWND hWnd
,
2199 ULONG_PTR dwNewLong
,
2206 UserEnterExclusive();
2208 pi
= GetW32ProcessInfo();
2210 Window
= UserGetWindowObject(hWnd
);
2213 if (Window
->head
.pti
->ppi
!= pi
)
2215 EngSetLastError(ERROR_ACCESS_DENIED
);
2221 UNICODE_STRING Value
;
2223 /* Probe the parameters */
2224 if (Offset
== GCW_ATOM
|| Offset
== GCLP_MENUNAME
)
2226 Value
= ProbeForReadUnicodeString((PUNICODE_STRING
)dwNewLong
);
2227 if (Value
.Length
& 1)
2229 goto InvalidParameter
;
2232 if (Value
.Length
!= 0)
2234 ProbeForRead(Value
.Buffer
,
2240 if (Offset
== GCW_ATOM
&& !IS_ATOM(Value
.Buffer
))
2242 goto InvalidParameter
;
2244 else if (Offset
== GCLP_MENUNAME
&& !IS_INTRESOURCE(Value
.Buffer
))
2247 EngSetLastError(ERROR_INVALID_PARAMETER
);
2252 dwNewLong
= (ULONG_PTR
)&Value
;
2255 Ret
= UserSetClassLongPtr(Window
->pcls
,
2260 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2262 SetLastNtError(_SEH2_GetExceptionCode());
2281 * NOTE: Obsoleted in 32-bit windows
2288 NtUserUnregisterClass(
2289 IN PUNICODE_STRING ClassNameOrAtom
,
2290 IN HINSTANCE hInstance
,
2291 OUT PCLSMENUNAME pClassMenuName
)
2293 UNICODE_STRING SafeClassName
;
2297 Status
= ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName
, ClassNameOrAtom
);
2298 if (!NT_SUCCESS(Status
))
2300 ERR("Error capturing the class name\n");
2301 SetLastNtError(Status
);
2305 UserEnterExclusive();
2307 /* Unregister the class */
2308 Ret
= UserUnregisterClass(&SafeClassName
, hInstance
, NULL
); // Null for now~
2312 if (SafeClassName
.Buffer
&& !IS_ATOM(SafeClassName
.Buffer
))
2313 ExFreePoolWithTag(SafeClassName
.Buffer
, TAG_STRING
);
2319 /* NOTE: For system classes hInstance is not NULL here, but User32Instance */
2323 HINSTANCE hInstance
,
2324 PUNICODE_STRING ClassName
,
2325 LPWNDCLASSEXW lpWndClassEx
,
2326 LPWSTR
*ppszMenuName
,
2329 UNICODE_STRING SafeClassName
;
2330 WNDCLASSEXW Safewcexw
;
2332 RTL_ATOM ClassAtom
= 0;
2339 ProbeForWrite( lpWndClassEx
, sizeof(WNDCLASSEXW
), sizeof(ULONG
));
2340 RtlCopyMemory( &Safewcexw
, lpWndClassEx
, sizeof(WNDCLASSEXW
));
2342 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2344 SetLastNtError(_SEH2_GetExceptionCode());
2345 _SEH2_YIELD(return FALSE
);
2349 Status
= ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName
, ClassName
);
2350 if (!NT_SUCCESS(Status
))
2352 ERR("Error capturing the class name\n");
2353 SetLastNtError(Status
);
2357 // If null instance use client.
2358 if (!hInstance
) hInstance
= hModClient
;
2360 TRACE("GetClassInfo(%wZ, 0x%x)\n", SafeClassName
, hInstance
);
2362 /* NOTE: Need exclusive lock because getting the wndproc might require the
2363 creation of a call procedure handle */
2364 UserEnterExclusive();
2366 ppi
= GetW32ProcessInfo();
2367 if (!(ppi
->W32PF_flags
& W32PF_CLASSESREGISTERED
))
2369 UserRegisterSystemClasses();
2372 ClassAtom
= IntGetClassAtom(&SafeClassName
,
2377 if (ClassAtom
!= (RTL_ATOM
)0)
2379 Ret
= UserGetClassInfo(Class
, &Safewcexw
, bAnsi
, hInstance
);
2383 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
2393 /* Emulate Function. */
2394 if (ppszMenuName
) *ppszMenuName
= (LPWSTR
)Safewcexw
.lpszMenuName
;
2396 RtlCopyMemory(lpWndClassEx
, &Safewcexw
, sizeof(WNDCLASSEXW
));
2399 /* We must return the atom of the class here instead of just TRUE. */
2400 /* Undocumented behavior! Return the class atom as a BOOL! */
2401 Ret
= (BOOL
)ClassAtom
;
2403 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2405 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
2411 if (!IS_ATOM(SafeClassName
.Buffer
))
2412 ExFreePoolWithTag(SafeClassName
.Buffer
, TAG_STRING
);
2419 NtUserGetClassName (IN HWND hWnd
,
2421 OUT PUNICODE_STRING ClassName
)
2424 UNICODE_STRING CapturedClassName
;
2430 Window
= UserGetWindowObject(hWnd
);
2433 if (Real
&& Window
->fnid
&& !(Window
->fnid
& FNID_DESTROY
))
2435 if (LookupFnIdToiCls(Window
->fnid
, &iCls
))
2437 Atom
= gpsi
->atomSysClass
[iCls
];
2443 ProbeForWriteUnicodeString(ClassName
);
2444 CapturedClassName
= *ClassName
;
2446 /* Get the class name */
2447 Ret
= UserGetClassName(Window
->pcls
,
2454 /* Update the Length field */
2455 ClassName
->Length
= CapturedClassName
.Length
;
2458 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2460 SetLastNtError(_SEH2_GetExceptionCode());
2470 /* Return Pointer to Class structure. */
2474 HINSTANCE hInstance
,
2475 PUNICODE_STRING ClassName
)
2477 UNICODE_STRING SafeClassName
;
2480 RTL_ATOM ClassAtom
= 0;
2483 Status
= ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName
, ClassName
);
2484 if (!NT_SUCCESS(Status
))
2486 ERR("Error capturing the class name\n");
2487 SetLastNtError(Status
);
2491 UserEnterExclusive();
2493 pi
= GetW32ProcessInfo();
2495 ClassAtom
= IntGetClassAtom(&SafeClassName
,
2502 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
2506 if (SafeClassName
.Buffer
&& !IS_ATOM(SafeClassName
.Buffer
))
2507 ExFreePoolWithTag(SafeClassName
.Buffer
, TAG_STRING
);
2511 // Don't forget to use DesktopPtrToUser( ? ) with return pointer in user space.