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 BOOL FASTCALL
IntClassDestroyIcon(HANDLE hCurIcon
);
13 static NTSTATUS
IntDeregisterClassAtom(IN RTL_ATOM Atom
);
15 REGISTER_SYSCLASS DefaultServerClasses
[] =
17 { ((PWSTR
)((ULONG_PTR
)(WORD
)(0x8001))),
18 CS_GLOBALCLASS
|CS_DBLCLKS
,
19 NULL
, // Use User32 procs
22 (HBRUSH
)(COLOR_BACKGROUND
),
26 { ((PWSTR
)((ULONG_PTR
)(WORD
)(0x8003))),
27 CS_VREDRAW
|CS_HREDRAW
|CS_SAVEBITS
,
28 NULL
, // Use User32 procs
35 { ((PWSTR
)((ULONG_PTR
)(WORD
)(0x8000))),
36 CS_DBLCLKS
|CS_SAVEBITS
,
37 NULL
, // Use User32 procs
40 (HBRUSH
)(COLOR_MENU
+ 1),
45 CS_DBLCLKS
|CS_VREDRAW
|CS_HREDRAW
|CS_PARENTDC
,
46 NULL
, // Use User32 procs
47 sizeof(SBWND
)-sizeof(WND
),
54 { ((PWSTR
)((ULONG_PTR
)(WORD
)(0x8006))), // Tooltips
55 CS_PARENTDC
|CS_DBLCLKS
,
56 NULL
, // Use User32 procs
64 { ((PWSTR
)((ULONG_PTR
)(WORD
)(0x8004))), // IconTitle is here for now...
66 NULL
, // Use User32 procs
75 NULL
, // Use User32 procs
89 { /* Function Ids to Class indexes. */
90 { FNID_SCROLLBAR
, ICLS_SCROLLBAR
},
91 { FNID_ICONTITLE
, ICLS_ICONTITLE
},
92 { FNID_MENU
, ICLS_MENU
},
93 { FNID_DESKTOP
, ICLS_DESKTOP
},
94 { FNID_SWITCH
, ICLS_SWITCH
},
95 { FNID_MESSAGEWND
, ICLS_HWNDMESSAGE
},
96 { FNID_BUTTON
, ICLS_BUTTON
},
97 { FNID_COMBOBOX
, ICLS_COMBOBOX
},
98 { FNID_COMBOLBOX
, ICLS_COMBOLBOX
},
99 { FNID_DIALOG
, ICLS_DIALOG
},
100 { FNID_EDIT
, ICLS_EDIT
},
101 { FNID_LISTBOX
, ICLS_LISTBOX
},
102 { FNID_MDICLIENT
, ICLS_MDICLIENT
},
103 { FNID_STATIC
, ICLS_STATIC
},
104 { FNID_IME
, ICLS_IME
},
105 { FNID_GHOST
, ICLS_GHOST
},
106 { FNID_TOOLTIPS
, ICLS_TOOLTIPS
}
111 LookupFnIdToiCls(int FnId
, int *iCls
)
115 for ( i
= 0; i
< ARRAYSIZE(FnidToiCls
); i
++)
117 if (FnidToiCls
[i
].FnId
== FnId
)
119 if (iCls
) *iCls
= FnidToiCls
[i
].ClsId
;
127 _Must_inspect_result_
130 ProbeAndCaptureUnicodeStringOrAtom(
131 _Out_
_When_(return>=0, _At_(pustrOut
->Buffer
, _Post_ _Notnull_
)) PUNICODE_STRING pustrOut
,
132 __in_data_source(USER_MODE
) _In_ PUNICODE_STRING pustrUnsafe
)
134 NTSTATUS Status
= STATUS_SUCCESS
;
136 /* Default to NULL */
137 pustrOut
->Buffer
= NULL
;
141 ProbeForRead(pustrUnsafe
, sizeof(UNICODE_STRING
), 1);
143 /* Validate the string */
144 if ((pustrUnsafe
->Length
& 1) || (pustrUnsafe
->Buffer
== NULL
))
146 /* This is not legal */
147 _SEH2_YIELD(return STATUS_INVALID_PARAMETER
);
150 /* Check if this is an atom */
151 if (IS_ATOM(pustrUnsafe
->Buffer
))
153 /* Copy the atom, length is 0 */
154 pustrOut
->MaximumLength
= pustrOut
->Length
= 0;
155 pustrOut
->Buffer
= pustrUnsafe
->Buffer
;
159 /* Get the length, maximum length includes zero termination */
160 pustrOut
->Length
= pustrUnsafe
->Length
;
161 pustrOut
->MaximumLength
= pustrOut
->Length
+ sizeof(WCHAR
);
163 /* Allocate a buffer */
164 pustrOut
->Buffer
= ExAllocatePoolWithTag(PagedPool
,
165 pustrOut
->MaximumLength
,
167 if (!pustrOut
->Buffer
)
169 _SEH2_YIELD(return STATUS_INSUFFICIENT_RESOURCES
);
172 /* Copy the string and zero terminate it */
173 ProbeForRead(pustrUnsafe
->Buffer
, pustrOut
->Length
, 1);
174 RtlCopyMemory(pustrOut
->Buffer
, pustrUnsafe
->Buffer
, pustrOut
->Length
);
175 pustrOut
->Buffer
[pustrOut
->Length
/ sizeof(WCHAR
)] = L
'\0';
178 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
180 /* Check if we already allocated a buffer */
181 if (pustrOut
->Buffer
)
183 /* Free the buffer */
184 ExFreePoolWithTag(pustrOut
->Buffer
, TAG_STRING
);
185 Status
= _SEH2_GetExceptionCode();
193 /* WINDOWCLASS ***************************************************************/
196 IntFreeClassMenuName(IN OUT PCLS Class
)
198 /* Free the menu name, if it was changed and allocated */
199 if (Class
->lpszClientUnicodeMenuName
!= NULL
&& Class
->MenuNameIsString
)
201 UserHeapFree(Class
->lpszClientUnicodeMenuName
);
202 Class
->lpszClientUnicodeMenuName
= NULL
;
203 Class
->lpszClientAnsiMenuName
= NULL
;
208 IntDestroyClass(IN OUT PCLS Class
)
212 /* There shouldn't be any clones anymore */
213 ASSERT(Class
->cWndReferenceCount
== 0);
214 ASSERT(Class
->pclsClone
== NULL
);
216 if (Class
->pclsBase
== Class
)
218 PCALLPROCDATA CallProc
, NextCallProc
;
220 /* Destroy allocated callproc handles */
221 CallProc
= Class
->spcpdFirst
;
222 while (CallProc
!= NULL
)
224 NextCallProc
= CallProc
->spcpdNext
;
226 CallProc
->spcpdNext
= NULL
;
227 DestroyCallProc(NULL
,
230 CallProc
= NextCallProc
;
233 // Fixes running the static test then run class test issue.
234 // Some applications do not use UnregisterClass before exiting.
235 // Keep from reusing the same atom with case insensitive
236 // comparisons, remove registration of the atom if not zeroed.
237 if (Class
->atomClassName
)
238 IntDeregisterClassAtom(Class
->atomClassName
);
242 DceFreeClassDCE(((PDCE
)Class
->pdce
)->hDC
);
246 IntFreeClassMenuName(Class
);
249 if (Class
->hIconSmIntern
)
250 IntClassDestroyIcon(Class
->hIconSmIntern
);
252 pDesk
= Class
->rpdeskParent
;
253 Class
->rpdeskParent
= NULL
;
255 /* Free the structure */
258 DesktopHeapFree(pDesk
, Class
);
267 /* Clean all process classes. all process windows must cleaned first!! */
268 void FASTCALL
DestroyProcessClasses(PPROCESSINFO Process
)
271 PPROCESSINFO pi
= (PPROCESSINFO
)Process
;
275 /* Free all local classes */
276 Class
= pi
->pclsPrivateList
;
277 while (Class
!= NULL
)
279 pi
->pclsPrivateList
= Class
->pclsNext
;
281 ASSERT(Class
->pclsBase
== Class
);
282 IntDestroyClass(Class
);
284 Class
= pi
->pclsPrivateList
;
287 /* Free all global classes */
288 Class
= pi
->pclsPublicList
;
289 while (Class
!= NULL
)
291 pi
->pclsPublicList
= Class
->pclsNext
;
293 ASSERT(Class
->pclsBase
== Class
);
294 IntDestroyClass(Class
);
296 Class
= pi
->pclsPublicList
;
302 IntRegisterClassAtom(IN PUNICODE_STRING ClassName
,
309 if (ClassName
->Length
!= 0)
311 /* FIXME: Don't limit to 64 characters! Use SEH when allocating memory! */
312 if (ClassName
->Length
/ sizeof(WCHAR
) >= sizeof(szBuf
) / sizeof(szBuf
[0]))
314 EngSetLastError(ERROR_INVALID_PARAMETER
);
321 szBuf
[ClassName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
325 AtomName
= ClassName
->Buffer
;
327 Status
= RtlAddAtomToAtomTable(gAtomTable
,
331 if (!NT_SUCCESS(Status
))
333 SetLastNtError(Status
);
341 IntDeregisterClassAtom(IN RTL_ATOM Atom
)
343 return RtlDeleteAtomFromAtomTable(gAtomTable
,
348 UserAddCallProcToClass(IN OUT PCLS Class
,
349 IN PCALLPROCDATA CallProc
)
353 ASSERT(CallProc
->spcpdNext
== NULL
);
355 BaseClass
= Class
->pclsBase
;
356 ASSERT(CallProc
->spcpdNext
== NULL
);
357 CallProc
->spcpdNext
= BaseClass
->spcpdFirst
;
358 BaseClass
->spcpdFirst
= CallProc
;
360 /* Update all clones */
361 Class
= Class
->pclsClone
;
362 while (Class
!= NULL
)
364 Class
->spcpdFirst
= BaseClass
->spcpdFirst
;
365 Class
= Class
->pclsNext
;
370 IntSetClassAtom(IN OUT PCLS Class
,
371 IN PUNICODE_STRING ClassName
)
373 RTL_ATOM Atom
= (RTL_ATOM
)0;
375 /* Update the base class first */
376 Class
= Class
->pclsBase
;
378 if (!IntRegisterClassAtom(ClassName
,
384 IntDeregisterClassAtom(Class
->atomClassName
);
386 Class
->atomClassName
= Atom
;
388 /* Update the clones */
389 Class
= Class
->pclsClone
;
390 while (Class
!= NULL
)
392 Class
->atomClassName
= Atom
;
394 Class
= Class
->pclsNext
;
401 // Same as User32:IntGetClsWndProc.
404 IntGetClassWndProc(PCLS Class
, BOOL Ansi
)
407 WNDPROC gcpd
= NULL
, Ret
= NULL
;
409 if (Class
->CSF_flags
& CSF_SERVERSIDEPROC
)
411 for ( i
= FNID_FIRST
; i
<= FNID_SWITCH
; i
++)
413 if (GETPFNSERVER(i
) == Class
->lpfnWndProc
)
416 Ret
= GETPFNCLIENTA(i
);
418 Ret
= GETPFNCLIENTW(i
);
423 Ret
= Class
->lpfnWndProc
;
425 if (Class
->fnid
<= FNID_GHOST
&& Class
->fnid
>= FNID_BUTTON
)
429 if (GETPFNCLIENTW(Class
->fnid
) == Class
->lpfnWndProc
)
430 Ret
= GETPFNCLIENTA(Class
->fnid
);
434 if (GETPFNCLIENTA(Class
->fnid
) == Class
->lpfnWndProc
)
435 Ret
= GETPFNCLIENTW(Class
->fnid
);
439 if ( Ret
!= Class
->lpfnWndProc
||
440 Ansi
== !!(Class
->CSF_flags
& CSF_ANSIPROC
) )
443 gcpd
= (WNDPROC
)UserGetCPD( Class
,
444 (Ansi
? UserGetCPDA2U
: UserGetCPDU2A
)|UserGetCPDClass
,
447 return (gcpd
? gcpd
: Ret
);
453 IntSetClassWndProc(IN OUT PCLS Class
,
459 WNDPROC Ret
, chWndProc
;
461 Ret
= IntGetClassWndProc(Class
, Ansi
);
463 // If Server Side, downgrade to Client Side.
464 if (Class
->CSF_flags
& CSF_SERVERSIDEPROC
)
466 if (Ansi
) Class
->CSF_flags
|= CSF_ANSIPROC
;
467 Class
->CSF_flags
&= ~CSF_SERVERSIDEPROC
;
468 Class
->Unicode
= !Ansi
;
471 if (!WndProc
) WndProc
= Class
->lpfnWndProc
;
475 // Check if CallProc handle and retrieve previous call proc address and set.
476 if (IsCallProcHandle(WndProc
))
478 pcpd
= UserGetObject(gHandleTable
, WndProc
, TYPE_CALLPROC
);
479 if (pcpd
) chWndProc
= pcpd
->pfnClientPrevious
;
482 Class
->lpfnWndProc
= chWndProc
;
487 // Switch from Client Side call to Server Side call if match. Ref: "deftest".
488 for ( i
= FNID_FIRST
; i
<= FNID_SWITCH
; i
++)
490 if (GETPFNCLIENTW(i
) == Class
->lpfnWndProc
)
492 chWndProc
= GETPFNSERVER(i
);
495 if (GETPFNCLIENTA(i
) == Class
->lpfnWndProc
)
497 chWndProc
= GETPFNSERVER(i
);
501 // If match, set/reset to Server Side and clear ansi.
504 Class
->lpfnWndProc
= chWndProc
;
505 Class
->Unicode
= TRUE
;
506 Class
->CSF_flags
&= ~CSF_ANSIPROC
;
507 Class
->CSF_flags
|= CSF_SERVERSIDEPROC
;
511 Class
->Unicode
= !Ansi
;
514 Class
->CSF_flags
|= CSF_ANSIPROC
;
516 Class
->CSF_flags
&= ~CSF_ANSIPROC
;
519 /* Update the clones */
520 chWndProc
= Class
->lpfnWndProc
;
522 Class
= Class
->pclsClone
;
523 while (Class
!= NULL
)
525 Class
->Unicode
= !Ansi
;
526 Class
->lpfnWndProc
= chWndProc
;
528 Class
= Class
->pclsNext
;
535 IntGetClassForDesktop(IN OUT PCLS BaseClass
,
536 IN OUT PCLS
*ClassLink
,
542 ASSERT(Desktop
!= NULL
);
543 ASSERT(BaseClass
->pclsBase
== BaseClass
);
545 if (BaseClass
->rpdeskParent
== Desktop
)
547 /* It is most likely that a window is created on the same
548 desktop as the window class. */
553 if (BaseClass
->rpdeskParent
== NULL
)
555 ASSERT(BaseClass
->cWndReferenceCount
== 0);
556 ASSERT(BaseClass
->pclsClone
== NULL
);
558 /* Classes are also located in the shared heap when the class
559 was created before the thread attached to a desktop. As soon
560 as a window is created for such a class located on the shared
561 heap, the class is cloned into the desktop heap on which the
562 window is created. */
567 /* The user is asking for a class object on a different desktop,
569 Class
= BaseClass
->pclsClone
;
570 while (Class
!= NULL
)
572 if (Class
->rpdeskParent
== Desktop
)
574 ASSERT(Class
->pclsBase
== BaseClass
);
575 ASSERT(Class
->pclsClone
== NULL
);
579 Class
= Class
->pclsNext
;
585 /* The window is created on a different desktop, we need to
586 clone the class object to the desktop heap of the window! */
587 ClassSize
= sizeof(*BaseClass
) + (SIZE_T
)BaseClass
->cbclsExtra
;
589 Class
= DesktopHeapAlloc(Desktop
,
593 /* Simply clone the class */
594 RtlCopyMemory( Class
, BaseClass
, ClassSize
);
596 TRACE("Clone Class 0x%p hM 0x%p\n %S\n",Class
, Class
->hModule
, Class
->lpszClientUnicodeMenuName
);
598 /* Restore module address if default user class Ref: Bug 4778 */
599 if ( Class
->hModule
!= hModClient
&&
600 Class
->fnid
<= FNID_GHOST
&&
601 Class
->fnid
>= FNID_BUTTON
)
603 Class
->hModule
= hModClient
;
604 TRACE("Clone Class 0x%p Reset hM 0x%p\n",Class
, Class
->hModule
);
607 /* Update some pointers and link the class */
608 Class
->rpdeskParent
= Desktop
;
609 Class
->cWndReferenceCount
= 0;
611 if (BaseClass
->rpdeskParent
== NULL
)
613 /* We don't really need the base class on the shared
614 heap anymore, delete it so the only class left is
615 the clone we just created, which now serves as the
617 ASSERT(BaseClass
->pclsClone
== NULL
);
618 ASSERT(Class
->pclsClone
== NULL
);
619 Class
->pclsBase
= Class
;
620 Class
->pclsNext
= BaseClass
->pclsNext
;
622 /* Replace the base class */
623 (void)InterlockedExchangePointer((PVOID
*)ClassLink
,
626 /* Destroy the obsolete copy on the shared heap */
627 BaseClass
->pclsBase
= NULL
;
628 BaseClass
->pclsClone
= NULL
;
629 IntDestroyClass(BaseClass
);
633 /* Link in the clone */
634 Class
->pclsClone
= NULL
;
635 Class
->pclsBase
= BaseClass
;
636 Class
->pclsNext
= BaseClass
->pclsClone
;
637 (void)InterlockedExchangePointer((PVOID
*)&BaseClass
->pclsClone
,
643 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
650 IntReferenceClass(IN OUT PCLS BaseClass
,
651 IN OUT PCLS
*ClassLink
,
655 ASSERT(BaseClass
->pclsBase
== BaseClass
);
659 Class
= IntGetClassForDesktop(BaseClass
,
670 Class
->cWndReferenceCount
++;
678 IntMakeCloneBaseClass(IN OUT PCLS Class
,
679 IN OUT PCLS
*BaseClassLink
,
680 IN OUT PCLS
*CloneLink
)
684 ASSERT(Class
->pclsBase
!= Class
);
685 ASSERT(Class
->pclsBase
->pclsClone
!= NULL
);
686 ASSERT(Class
->rpdeskParent
!= NULL
);
687 ASSERT(Class
->cWndReferenceCount
!= 0);
688 ASSERT(Class
->pclsBase
->rpdeskParent
!= NULL
);
689 ASSERT(Class
->pclsBase
->cWndReferenceCount
== 0);
691 /* Unlink the clone */
692 *CloneLink
= Class
->pclsNext
;
693 Class
->pclsClone
= Class
->pclsBase
->pclsClone
;
695 /* Update the class information to make it a base class */
696 Class
->pclsBase
= Class
;
697 Class
->pclsNext
= (*BaseClassLink
)->pclsNext
;
699 /* Update all clones */
700 Clone
= Class
->pclsClone
;
701 while (Clone
!= NULL
)
703 ASSERT(Clone
->pclsClone
== NULL
);
704 Clone
->pclsBase
= Class
;
706 Clone
= Clone
->pclsNext
;
709 /* Link in the new base class */
710 (void)InterlockedExchangePointer((PVOID
*)BaseClassLink
,
716 IntDereferenceClass(IN OUT PCLS Class
,
717 IN PDESKTOPINFO Desktop
,
720 PCLS
*PrevLink
, BaseClass
, CurrentClass
;
722 ASSERT(Class
->cWndReferenceCount
>= 1);
724 BaseClass
= Class
->pclsBase
;
726 if (--Class
->cWndReferenceCount
== 0)
728 if (BaseClass
== Class
)
730 ASSERT(Class
->pclsBase
== Class
);
732 TRACE("IntDereferenceClass 0x%p\n", Class
);
733 /* Check if there are clones of the class on other desktops,
734 link the first clone in if possible. If there are no clones
735 then leave the class on the desktop heap. It will get moved
736 to the shared heap when the thread detaches. */
737 if (BaseClass
->pclsClone
!= NULL
)
739 if (BaseClass
->Global
)
740 PrevLink
= &pi
->pclsPublicList
;
742 PrevLink
= &pi
->pclsPrivateList
;
744 CurrentClass
= *PrevLink
;
745 while (CurrentClass
!= BaseClass
)
747 ASSERT(CurrentClass
!= NULL
);
749 PrevLink
= &CurrentClass
->pclsNext
;
750 CurrentClass
= CurrentClass
->pclsNext
;
753 ASSERT(*PrevLink
== BaseClass
);
755 /* Make the first clone become the new base class */
756 IntMakeCloneBaseClass(BaseClass
->pclsClone
,
758 &BaseClass
->pclsClone
);
760 /* Destroy the class, there's still another clone of the class
761 that now serves as a base class. Make sure we don't destruct
762 resources shared by all classes (Base = NULL)! */
763 BaseClass
->pclsBase
= NULL
;
764 BaseClass
->pclsClone
= NULL
;
765 IntDestroyClass(BaseClass
);
770 TRACE("IntDereferenceClass1 0x%p\n", Class
);
772 /* Locate the cloned class and unlink it */
773 PrevLink
= &BaseClass
->pclsClone
;
774 CurrentClass
= BaseClass
->pclsClone
;
775 while (CurrentClass
!= Class
)
777 ASSERT(CurrentClass
!= NULL
);
779 PrevLink
= &CurrentClass
->pclsNext
;
780 CurrentClass
= CurrentClass
->pclsNext
;
783 ASSERT(CurrentClass
== Class
);
785 (void)InterlockedExchangePointer((PVOID
*)PrevLink
,
788 ASSERT(Class
->pclsBase
== BaseClass
);
789 ASSERT(Class
->pclsClone
== NULL
);
791 /* The class was just a clone, we don't need it anymore */
792 IntDestroyClass(Class
);
798 IntMoveClassToSharedHeap(IN OUT PCLS Class
,
799 IN OUT PCLS
**ClassLinkPtr
)
804 ASSERT(Class
->pclsBase
== Class
);
805 ASSERT(Class
->rpdeskParent
!= NULL
);
806 ASSERT(Class
->cWndReferenceCount
== 0);
807 ASSERT(Class
->pclsClone
== NULL
);
809 ClassSize
= sizeof(*Class
) + (SIZE_T
)Class
->cbclsExtra
;
811 /* Allocate the new base class on the shared heap */
812 NewClass
= UserHeapAlloc(ClassSize
);
813 if (NewClass
!= NULL
)
815 RtlCopyMemory(NewClass
,
819 NewClass
->rpdeskParent
= NULL
;
820 NewClass
->pclsBase
= NewClass
;
822 /* Replace the class in the list */
823 (void)InterlockedExchangePointer((PVOID
*)*ClassLinkPtr
,
825 *ClassLinkPtr
= &NewClass
->pclsNext
;
827 /* Free the obsolete class on the desktop heap */
828 Class
->pclsBase
= NULL
;
829 IntDestroyClass(Class
);
837 IntCheckDesktopClasses(IN PDESKTOP Desktop
,
838 IN OUT PCLS
*ClassList
,
839 IN BOOL FreeOnFailure
,
842 PCLS Class
, NextClass
, *Link
;
844 /* NOTE: We only need to check base classes! When classes are no longer needed
845 on a desktop, the clones will be freed automatically as soon as possible.
846 However, we need to move base classes to the shared heap, as soon as
847 the last desktop heap where a class is allocated on is about to be destroyed.
848 If we didn't move the class to the shared heap, the class would become
851 ASSERT(Desktop
!= NULL
);
855 while (Class
!= NULL
)
857 NextClass
= Class
->pclsNext
;
859 ASSERT(Class
->pclsBase
== Class
);
861 if (Class
->rpdeskParent
== Desktop
&&
862 Class
->cWndReferenceCount
== 0)
864 /* There shouldn't be any clones around anymore! */
865 ASSERT(Class
->pclsClone
== NULL
);
867 /* FIXME: If process is terminating, don't move the class but rather destroy it! */
868 /* FIXME: We could move the class to another desktop heap if there's still desktops
869 mapped into the process... */
871 /* Move the class to the shared heap */
872 if (IntMoveClassToSharedHeap(Class
,
875 ASSERT(*Link
== NextClass
);
879 ASSERT(NextClass
== Class
->pclsNext
);
883 /* Unlink the base class */
884 (void)InterlockedExchangePointer((PVOID
*)Link
,
887 /* We can free the old base class now */
888 Class
->pclsBase
= NULL
;
889 IntDestroyClass(Class
);
893 Link
= &Class
->pclsNext
;
899 Link
= &Class
->pclsNext
;
906 IntCheckProcessDesktopClasses(IN PDESKTOP Desktop
,
907 IN BOOL FreeOnFailure
)
912 pi
= GetW32ProcessInfo();
914 /* Check all local classes */
915 IntCheckDesktopClasses(Desktop
,
916 &pi
->pclsPrivateList
,
920 /* Check all global classes */
921 IntCheckDesktopClasses(Desktop
,
927 ERR("Failed to move process classes from desktop 0x%p to the shared heap!\n", Desktop
);
928 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
936 IntCreateClass(IN CONST WNDCLASSEXW
* lpwcx
,
937 IN PUNICODE_STRING ClassName
,
938 IN PUNICODE_STRING MenuName
,
948 PWSTR pszMenuName
= NULL
;
949 NTSTATUS Status
= STATUS_SUCCESS
;
951 TRACE("lpwcx=%p ClassName=%wZ MenuName=%wZ dwFlags=%08x Desktop=%p pi=%p\n",
952 lpwcx
, ClassName
, MenuName
, dwFlags
, Desktop
, pi
);
954 if (!IntRegisterClassAtom(ClassName
,
957 ERR("Failed to register class atom!\n");
961 ClassSize
= sizeof(*Class
) + lpwcx
->cbClsExtra
;
962 if (MenuName
->Length
!= 0)
964 pszMenuName
= UserHeapAlloc(MenuName
->Length
+ sizeof(UNICODE_NULL
) +
965 RtlUnicodeStringToAnsiSize(MenuName
));
966 if (pszMenuName
== NULL
)
972 Class
= DesktopHeapAlloc(Desktop
,
977 /* FIXME: The class was created before being connected
978 to a desktop. It is possible for the desktop window,
979 but should it be allowed for any other case? */
980 Class
= UserHeapAlloc(ClassSize
);
987 RtlZeroMemory(Class
, ClassSize
);
989 Class
->rpdeskParent
= Desktop
;
990 Class
->pclsBase
= Class
;
991 Class
->atomClassName
= Atom
;
993 Class
->CSF_flags
= dwFlags
;
995 if (LookupFnIdToiCls(Class
->fnid
, &iCls
))
997 gpsi
->atomSysClass
[iCls
] = Class
->atomClassName
;
1002 PWSTR pszMenuNameBuffer
= pszMenuName
;
1004 /* Need to protect with SEH since accessing the WNDCLASSEX structure
1005 and string buffers might raise an exception! We don't want to
1007 // What?! If the user interface was written correctly this would not be an issue!
1008 Class
->lpfnWndProc
= lpwcx
->lpfnWndProc
;
1009 Class
->style
= lpwcx
->style
;
1010 Class
->cbclsExtra
= lpwcx
->cbClsExtra
;
1011 Class
->cbwndExtra
= lpwcx
->cbWndExtra
;
1012 Class
->hModule
= lpwcx
->hInstance
;
1013 //// FIXME handles to pointers
1014 Class
->hIcon
= lpwcx
->hIcon
;
1015 Class
->hIconSm
= lpwcx
->hIconSm
;
1016 Class
->hCursor
= lpwcx
->hCursor
;
1018 Class
->hbrBackground
= lpwcx
->hbrBackground
;
1020 /* Make a copy of the string */
1021 if (pszMenuNameBuffer
!= NULL
)
1023 Class
->MenuNameIsString
= TRUE
;
1025 Class
->lpszClientUnicodeMenuName
= pszMenuNameBuffer
;
1026 RtlCopyMemory(Class
->lpszClientUnicodeMenuName
,
1029 Class
->lpszClientUnicodeMenuName
[MenuName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1031 pszMenuNameBuffer
+= (MenuName
->Length
/ sizeof(WCHAR
)) + 1;
1034 Class
->lpszClientUnicodeMenuName
= MenuName
->Buffer
;
1036 /* Save an ANSI copy of the string */
1037 if (pszMenuNameBuffer
!= NULL
)
1039 ANSI_STRING AnsiString
;
1041 Class
->lpszClientAnsiMenuName
= (PSTR
)pszMenuNameBuffer
;
1042 AnsiString
.MaximumLength
= (USHORT
)RtlUnicodeStringToAnsiSize(MenuName
);
1043 AnsiString
.Buffer
= Class
->lpszClientAnsiMenuName
;
1044 Status
= RtlUnicodeStringToAnsiString(&AnsiString
,
1047 if (!NT_SUCCESS(Status
))
1049 ERR("Failed to convert unicode menu name to ansi!\n");
1051 /* Life would've been much prettier if ntoskrnl exported RtlRaiseStatus()... */
1056 Class
->lpszClientAnsiMenuName
= (PSTR
)MenuName
->Buffer
;
1058 /* Save kernel use menu name and ansi class name */
1059 Class
->lpszMenuName
= Class
->lpszClientUnicodeMenuName
; // FIXME!
1060 //Class->lpszAnsiClassName = FIXME
1062 /* Server Side overrides class calling type (A/W)!
1063 User32 whine test_builtinproc: "deftest"
1064 built-in winproc - window A/W type automatically detected */
1065 if (!(Class
->CSF_flags
& CSF_SERVERSIDEPROC
))
1069 /* Due to the wine class "deftest" and most likely no FNID to reference
1070 from, sort through the Server Side list and compare proc addresses
1071 for match. This method will be used in related code.
1073 for ( i
= FNID_FIRST
; i
<= FNID_SWITCH
; i
++)
1074 { // Open ANSI or Unicode, just match, set and break.
1075 if (GETPFNCLIENTW(i
) == Class
->lpfnWndProc
)
1077 WndProc
= GETPFNSERVER(i
);
1080 if (GETPFNCLIENTA(i
) == Class
->lpfnWndProc
)
1082 WndProc
= GETPFNSERVER(i
);
1087 { // If a hit, we are Server Side so set the right flags and proc.
1088 Class
->CSF_flags
|= CSF_SERVERSIDEPROC
;
1089 Class
->CSF_flags
&= ~CSF_ANSIPROC
;
1090 Class
->lpfnWndProc
= WndProc
;
1094 if (!(Class
->CSF_flags
& CSF_ANSIPROC
))
1095 Class
->Unicode
= TRUE
;
1097 if (Class
->style
& CS_GLOBALCLASS
)
1098 Class
->Global
= TRUE
;
1100 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1102 Status
= _SEH2_GetExceptionCode();
1106 if (!NT_SUCCESS(Status
))
1108 ERR("Failed creating the class: 0x%x\n", Status
);
1110 SetLastNtError(Status
);
1112 if (pszMenuName
!= NULL
)
1113 UserHeapFree(pszMenuName
);
1115 DesktopHeapFree(Desktop
,
1119 IntDeregisterClassAtom(Atom
);
1125 ERR("Failed to allocate class on Desktop 0x%p\n", Desktop
);
1127 if (pszMenuName
!= NULL
)
1128 UserHeapFree(pszMenuName
);
1130 IntDeregisterClassAtom(Atom
);
1132 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1135 TRACE("Created class 0x%p with name %wZ and proc 0x%p for atom 0x%x and hInstance 0x%p, global %u\n",
1136 Class
, ClassName
, Class
->lpfnWndProc
, Atom
, Class
->hModule
, Class
->Global
);
1142 IntFindClass(IN RTL_ATOM Atom
,
1143 IN HINSTANCE hInstance
,
1145 OUT PCLS
**Link OPTIONAL
)
1147 PCLS Class
, *PrevLink
= ClassList
;
1150 while (Class
!= NULL
)
1152 if (Class
->atomClassName
== Atom
&&
1153 (hInstance
== NULL
|| Class
->hModule
== hInstance
) &&
1154 !(Class
->CSF_flags
& CSF_WOWDEFERDESTROY
))
1156 ASSERT(Class
->pclsBase
== Class
);
1163 PrevLink
= &Class
->pclsNext
;
1164 Class
= Class
->pclsNext
;
1172 IntGetAtomFromStringOrAtom(
1173 _In_ PUNICODE_STRING ClassName
,
1174 _Out_ RTL_ATOM
*Atom
)
1178 if (ClassName
->Length
!= 0)
1186 /* NOTE: Caller has to protect the call with SEH! */
1188 if (ClassName
->Length
!= 0)
1190 /* FIXME: Don't limit to 64 characters! use SEH when allocating memory! */
1191 if (ClassName
->Length
/ sizeof(WCHAR
) >= sizeof(szBuf
) / sizeof(szBuf
[0]))
1193 EngSetLastError(ERROR_INVALID_PARAMETER
);
1197 /* We need to make a local copy of the class name! The caller could
1198 modify the buffer and we could overflow in RtlLookupAtomInAtomTable.
1199 We're protected by SEH, but the ranges that might be accessed were
1201 RtlCopyMemory(szBuf
,
1204 szBuf
[ClassName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1208 AtomName
= ClassName
->Buffer
;
1210 /* Lookup the atom */
1211 Status
= RtlLookupAtomInAtomTable(gAtomTable
,
1214 if (NT_SUCCESS(Status
))
1220 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
1222 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
1226 SetLastNtError(Status
);
1232 ASSERT(IS_ATOM(ClassName
->Buffer
));
1233 *Atom
= (RTL_ATOM
)((ULONG_PTR
)ClassName
->Buffer
);
1242 _In_ PUNICODE_STRING ClassName
,
1243 IN HINSTANCE hInstance OPTIONAL
,
1244 IN PPROCESSINFO pi OPTIONAL
,
1245 OUT PCLS
*BaseClass OPTIONAL
,
1246 OUT PCLS
**Link OPTIONAL
)
1248 RTL_ATOM Atom
= (RTL_ATOM
)0;
1250 ASSERT(BaseClass
!= NULL
);
1252 if (IntGetAtomFromStringOrAtom(ClassName
,
1254 Atom
!= (RTL_ATOM
)0)
1258 /* Attempt to locate the class object */
1262 /* Step 1: Try to find an exact match of locally registered classes */
1263 Class
= IntFindClass(Atom
,
1265 &pi
->pclsPrivateList
,
1268 { TRACE("Step 1: 0x%p\n",Class
);
1272 /* Step 2: Try to find any globally registered class. The hInstance
1273 is not relevant for global classes */
1274 Class
= IntFindClass(Atom
,
1276 &pi
->pclsPublicList
,
1279 { TRACE("Step 2: 0x%p 0x%p\n",Class
, Class
->hModule
);
1283 /* Step 3: Try to find any local class registered by user32 */
1284 Class
= IntFindClass(Atom
,
1286 &pi
->pclsPrivateList
,
1289 { TRACE("Step 3: 0x%p\n",Class
);
1293 /* Step 4: Try to find any global class registered by user32 */
1294 Class
= IntFindClass(Atom
,
1296 &pi
->pclsPublicList
,
1300 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
1302 }else{TRACE("Step 4: 0x%p\n",Class
);}
1312 IntGetAndReferenceClass(PUNICODE_STRING ClassName
, HINSTANCE hInstance
, BOOL bDesktopThread
)
1314 PCLS
*ClassLink
, Class
= NULL
;
1319 pti
= gptiDesktopThread
;
1321 pti
= PsGetCurrentThreadWin32Thread();
1323 if ( !(pti
->ppi
->W32PF_flags
& W32PF_CLASSESREGISTERED
))
1325 UserRegisterSystemClasses();
1328 /* Check the class. */
1330 TRACE("Finding Class %wZ for hInstance 0x%p\n", ClassName
, hInstance
);
1332 ClassAtom
= IntGetClassAtom(ClassName
,
1338 if (ClassAtom
== (RTL_ATOM
)0)
1340 if (IS_ATOM(ClassName
->Buffer
))
1342 ERR("Class 0x%p not found\n", ClassName
->Buffer
);
1346 ERR("Class \"%wZ\" not found\n", ClassName
);
1349 EngSetLastError(ERROR_CANNOT_FIND_WND_CLASS
);
1353 TRACE("Referencing Class 0x%p with atom 0x%x\n", Class
, ClassAtom
);
1354 Class
= IntReferenceClass(Class
,
1359 ERR("Failed to reference window class!\n");
1367 UserRegisterClass(IN CONST WNDCLASSEXW
* lpwcx
,
1368 IN PUNICODE_STRING ClassName
,
1369 IN PUNICODE_STRING MenuName
,
1377 RTL_ATOM Ret
= (RTL_ATOM
)0;
1379 /* NOTE: Accessing the buffers in ClassName and MenuName may raise exceptions! */
1381 pti
= GetW32ThreadInfo();
1385 // Need only to test for two conditions not four....... Fix more whine tests....
1386 if ( IntGetAtomFromStringOrAtom( ClassName
, &ClassAtom
) &&
1387 ClassAtom
!= (RTL_ATOM
)0 &&
1388 !(dwFlags
& CSF_SERVERSIDEPROC
) ) // Bypass Server Sides
1390 Class
= IntFindClass( ClassAtom
,
1392 &pi
->pclsPrivateList
,
1395 if (Class
!= NULL
&& !Class
->Global
)
1397 // Local class already exists
1398 TRACE("Local Class 0x%x does already exist!\n", ClassAtom
);
1399 EngSetLastError(ERROR_CLASS_ALREADY_EXISTS
);
1403 if (lpwcx
->style
& CS_GLOBALCLASS
)
1405 Class
= IntFindClass( ClassAtom
,
1407 &pi
->pclsPublicList
,
1410 if (Class
!= NULL
&& Class
->Global
)
1412 TRACE("Global Class 0x%x does already exist!\n", ClassAtom
);
1413 EngSetLastError(ERROR_CLASS_ALREADY_EXISTS
);
1419 Class
= IntCreateClass(lpwcx
,
1431 /* Register the class */
1433 List
= &pi
->pclsPublicList
;
1435 List
= &pi
->pclsPrivateList
;
1437 Class
->pclsNext
= *List
;
1438 (void)InterlockedExchangePointer((PVOID
*)List
,
1441 Ret
= Class
->atomClassName
;
1445 ERR("UserRegisterClass: Yes, that is right, you have no Class!\n");
1452 UserUnregisterClass(IN PUNICODE_STRING ClassName
,
1453 IN HINSTANCE hInstance
,
1454 OUT PCLSMENUNAME pClassMenuName
)
1461 pi
= GetW32ProcessInfo();
1463 TRACE("UserUnregisterClass(%wZ, 0x%p)\n", ClassName
, hInstance
);
1465 /* NOTE: Accessing the buffer in ClassName may raise an exception! */
1466 ClassAtom
= IntGetClassAtom(ClassName
,
1471 if (ClassAtom
== (RTL_ATOM
)0)
1473 TRACE("UserUnregisterClass: No Class found.\n");
1477 ASSERT(Class
!= NULL
);
1479 if (Class
->cWndReferenceCount
!= 0 ||
1480 Class
->pclsClone
!= NULL
)
1482 TRACE("UserUnregisterClass: Class has a Window. Ct %u : Clone 0x%p\n", Class
->cWndReferenceCount
, Class
->pclsClone
);
1483 EngSetLastError(ERROR_CLASS_HAS_WINDOWS
);
1487 /* Must be a base class! */
1488 ASSERT(Class
->pclsBase
== Class
);
1490 /* Unlink the class */
1491 *Link
= Class
->pclsNext
;
1493 if (NT_SUCCESS(IntDeregisterClassAtom(Class
->atomClassName
)))
1495 TRACE("Class 0x%p\n", Class
);
1496 TRACE("UserUnregisterClass: Good Exit!\n");
1497 Class
->atomClassName
= 0; // Don't let it linger...
1498 /* Finally free the resources */
1499 IntDestroyClass(Class
);
1502 ERR("UserUnregisterClass: Can not deregister Class Atom.\n");
1507 UserGetClassName(IN PCLS Class
,
1508 IN OUT PUNICODE_STRING ClassName
,
1512 NTSTATUS Status
= STATUS_SUCCESS
;
1513 WCHAR szStaticTemp
[32];
1514 PWSTR szTemp
= NULL
;
1515 ULONG BufLen
= sizeof(szStaticTemp
);
1518 /* Note: Accessing the buffer in ClassName may raise an exception! */
1524 PANSI_STRING AnsiClassName
= (PANSI_STRING
)ClassName
;
1525 UNICODE_STRING UnicodeClassName
;
1527 /* Limit the size of the static buffer on the stack to the
1528 size of the buffer provided by the caller */
1529 if (BufLen
/ sizeof(WCHAR
) > AnsiClassName
->MaximumLength
)
1531 BufLen
= AnsiClassName
->MaximumLength
* sizeof(WCHAR
);
1534 /* Find out how big the buffer needs to be */
1535 Status
= RtlQueryAtomInAtomTable(gAtomTable
,
1536 Class
->atomClassName
,
1541 if (Status
== STATUS_BUFFER_TOO_SMALL
)
1543 if (BufLen
/ sizeof(WCHAR
) > AnsiClassName
->MaximumLength
)
1545 /* The buffer required exceeds the ansi buffer provided,
1546 pretend like we're using the ansi buffer and limit the
1547 size to the buffer size provided */
1548 BufLen
= AnsiClassName
->MaximumLength
* sizeof(WCHAR
);
1551 /* Allocate a temporary buffer that can hold the unicode class name */
1552 szTemp
= ExAllocatePoolWithTag(PagedPool
,
1557 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1561 /* Query the class name */
1562 Status
= RtlQueryAtomInAtomTable(gAtomTable
,
1563 Atom
? Atom
: Class
->atomClassName
,
1570 szTemp
= szStaticTemp
;
1572 if (NT_SUCCESS(Status
))
1574 /* Convert the atom name to ansi */
1576 RtlInitUnicodeString(&UnicodeClassName
,
1579 Status
= RtlUnicodeStringToAnsiString(AnsiClassName
,
1582 if (!NT_SUCCESS(Status
))
1584 SetLastNtError(Status
);
1589 Ret
= BufLen
/ sizeof(WCHAR
);
1593 BufLen
= ClassName
->MaximumLength
;
1595 /* Query the atom name */
1596 Status
= RtlQueryAtomInAtomTable(gAtomTable
,
1597 Atom
? Atom
: Class
->atomClassName
,
1603 if (!NT_SUCCESS(Status
))
1605 SetLastNtError(Status
);
1609 Ret
= BufLen
/ sizeof(WCHAR
);
1612 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1614 SetLastNtError(_SEH2_GetExceptionCode());
1618 if (Ansi
&& szTemp
!= NULL
&& szTemp
!= szStaticTemp
)
1620 ExFreePoolWithTag(szTemp
, USERTAG_CLASS
);
1627 IntSetClassMenuName(IN PCLS Class
,
1628 IN PUNICODE_STRING MenuName
)
1632 /* Change the base class first */
1633 Class
= Class
->pclsBase
;
1635 if (MenuName
->Length
!= 0)
1637 ANSI_STRING AnsiString
;
1640 AnsiString
.MaximumLength
= (USHORT
)RtlUnicodeStringToAnsiSize(MenuName
);
1642 strBufW
= UserHeapAlloc(MenuName
->Length
+ sizeof(UNICODE_NULL
) +
1643 AnsiString
.MaximumLength
);
1644 if (strBufW
!= NULL
)
1650 /* Copy the unicode string */
1651 RtlCopyMemory(strBufW
,
1654 strBufW
[MenuName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1656 /* Create an ANSI copy of the string */
1657 AnsiString
.Buffer
= (PSTR
)(strBufW
+ (MenuName
->Length
/ sizeof(WCHAR
)) + 1);
1658 Status
= RtlUnicodeStringToAnsiString(&AnsiString
,
1661 if (!NT_SUCCESS(Status
))
1663 SetLastNtError(Status
);
1669 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1671 SetLastNtError(_SEH2_GetExceptionCode());
1677 /* Update the base class */
1678 IntFreeClassMenuName(Class
);
1679 Class
->lpszClientUnicodeMenuName
= strBufW
;
1680 Class
->lpszClientAnsiMenuName
= AnsiString
.Buffer
;
1681 Class
->MenuNameIsString
= TRUE
;
1683 /* Update the clones */
1684 Class
= Class
->pclsClone
;
1685 while (Class
!= NULL
)
1687 Class
->lpszClientUnicodeMenuName
= strBufW
;
1688 Class
->lpszClientAnsiMenuName
= AnsiString
.Buffer
;
1689 Class
->MenuNameIsString
= TRUE
;
1691 Class
= Class
->pclsNext
;
1696 ERR("Failed to copy class menu name!\n");
1697 UserHeapFree(strBufW
);
1701 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1705 ASSERT(IS_INTRESOURCE(MenuName
->Buffer
));
1707 /* Update the base class */
1708 IntFreeClassMenuName(Class
);
1709 Class
->lpszClientUnicodeMenuName
= MenuName
->Buffer
;
1710 Class
->lpszClientAnsiMenuName
= (PSTR
)MenuName
->Buffer
;
1711 Class
->MenuNameIsString
= FALSE
;
1713 /* Update the clones */
1714 Class
= Class
->pclsClone
;
1715 while (Class
!= NULL
)
1717 Class
->lpszClientUnicodeMenuName
= MenuName
->Buffer
;
1718 Class
->lpszClientAnsiMenuName
= (PSTR
)MenuName
->Buffer
;
1719 Class
->MenuNameIsString
= FALSE
;
1721 Class
= Class
->pclsNext
;
1730 //// Do this for now in anticipation of new cursor icon code.
1731 #ifndef NEW_CURSORICON
1732 BOOLEAN FASTCALL
IntDestroyCurIconObject(PCURICON_OBJECT
, PPROCESSINFO
);
1734 BOOLEAN FASTCALL
IntDestroyCurIconObject(PCURICON_OBJECT
, BOOLEAN
);
1738 IntClassDestroyIcon(HANDLE hCurIcon
)
1740 PCURICON_OBJECT CurIcon
;
1743 if (!(CurIcon
= UserGetCurIconObject(hCurIcon
)))
1746 ERR("hCurIcon was not found!\n");
1749 #ifndef NEW_CURSORICON
1750 Ret
= IntDestroyCurIconObject(CurIcon
, PsGetCurrentProcessWin32Process());
1751 /* Note: IntDestroyCurIconObject will remove our reference for us! */
1753 /* Note: IntDestroyCurIconObject will remove our reference for us! */
1754 Ret
= IntDestroyCurIconObject(CurIcon
, TRUE
);
1758 ERR("hCurIcon was not Destroyed!\n");
1764 UserSetClassLongPtr(IN PCLS Class
,
1766 IN ULONG_PTR NewLong
,
1770 HANDLE hIconSmIntern
= NULL
;
1772 /* NOTE: For GCLP_MENUNAME and GCW_ATOM this function may raise an exception! */
1774 /* Change the information in the base class first, then update the clones */
1775 Class
= Class
->pclsBase
;
1781 TRACE("SetClassLong(%d, %x)\n", Index
, NewLong
);
1783 if (((ULONG
)Index
+ sizeof(ULONG_PTR
)) < (ULONG
)Index
||
1784 ((ULONG
)Index
+ sizeof(ULONG_PTR
)) > (ULONG
)Class
->cbclsExtra
)
1786 EngSetLastError(ERROR_INVALID_PARAMETER
);
1790 Data
= (PULONG_PTR
)((ULONG_PTR
)(Class
+ 1) + Index
);
1792 /* FIXME: Data might be a unaligned pointer! Might be a problem on
1793 certain architectures, maybe using RtlCopyMemory is a
1794 better choice for those architectures! */
1798 /* Update the clones */
1799 Class
= Class
->pclsClone
;
1800 while (Class
!= NULL
)
1802 *(PULONG_PTR
)((ULONG_PTR
)(Class
+ 1) + Index
) = NewLong
;
1803 Class
= Class
->pclsNext
;
1811 case GCL_CBWNDEXTRA
:
1812 Ret
= (ULONG_PTR
)Class
->cbwndExtra
;
1813 Class
->cbwndExtra
= (INT
)NewLong
;
1815 /* Update the clones */
1816 Class
= Class
->pclsClone
;
1817 while (Class
!= NULL
)
1819 Class
->cbwndExtra
= (INT
)NewLong
;
1820 Class
= Class
->pclsNext
;
1825 case GCL_CBCLSEXTRA
:
1826 EngSetLastError(ERROR_INVALID_PARAMETER
);
1829 case GCLP_HBRBACKGROUND
:
1830 Ret
= (ULONG_PTR
)Class
->hbrBackground
;
1831 Class
->hbrBackground
= (HBRUSH
)NewLong
;
1833 /* Update the clones */
1834 Class
= Class
->pclsClone
;
1835 while (Class
!= NULL
)
1837 Class
->hbrBackground
= (HBRUSH
)NewLong
;
1838 Class
= Class
->pclsNext
;
1843 /* FIXME: Get handle from pointer to CURSOR object */
1844 Ret
= (ULONG_PTR
)Class
->hCursor
;
1845 Class
->hCursor
= (HANDLE
)NewLong
;
1847 /* Update the clones */
1848 Class
= Class
->pclsClone
;
1849 while (Class
!= NULL
)
1851 Class
->hCursor
= (HANDLE
)NewLong
;
1852 Class
= Class
->pclsNext
;
1857 // hIconSm, A handle to a small icon that is associated with the window class.
1858 // If this member is NULL, the system searches the icon resource specified by
1859 // the hIcon member for an icon of the appropriate size to use as the small icon.
1862 /* FIXME: Get handle from pointer to ICON object */
1863 Ret
= (ULONG_PTR
)Class
->hIcon
;
1864 if (Class
->hIcon
== (HANDLE
)NewLong
) break;
1865 if (Ret
&& Class
->hIconSmIntern
)
1867 IntClassDestroyIcon(Class
->hIconSmIntern
);
1868 Class
->CSF_flags
&= ~CSF_CACHEDSMICON
;
1869 Class
->hIconSmIntern
= NULL
;
1871 if (NewLong
&& !Class
->hIconSm
)
1873 hIconSmIntern
= Class
->hIconSmIntern
= co_IntCopyImage( (HICON
)NewLong
, IMAGE_ICON
,
1874 UserGetSystemMetrics( SM_CXSMICON
),
1875 UserGetSystemMetrics( SM_CYSMICON
), 0 );
1876 Class
->CSF_flags
|= CSF_CACHEDSMICON
;
1878 Class
->hIcon
= (HANDLE
)NewLong
;
1880 /* Update the clones */
1881 Class
= Class
->pclsClone
;
1882 while (Class
!= NULL
)
1884 Class
->hIcon
= (HANDLE
)NewLong
;
1885 Class
->hIconSmIntern
= hIconSmIntern
;
1886 Class
= Class
->pclsNext
;
1891 /* FIXME: Get handle from pointer to ICON object */
1892 Ret
= (ULONG_PTR
)Class
->hIconSm
;
1893 if (Class
->hIconSm
== (HANDLE
)NewLong
) break;
1894 if (Class
->CSF_flags
& CSF_CACHEDSMICON
)
1896 if (Class
->hIconSmIntern
)
1898 IntClassDestroyIcon(Class
->hIconSmIntern
);
1899 Class
->hIconSmIntern
= NULL
;
1901 Class
->CSF_flags
&= ~CSF_CACHEDSMICON
;
1903 if (Class
->hIcon
&& !Class
->hIconSmIntern
)
1905 hIconSmIntern
= Class
->hIconSmIntern
= co_IntCopyImage( Class
->hIcon
, IMAGE_ICON
,
1906 UserGetSystemMetrics( SM_CXSMICON
),
1907 UserGetSystemMetrics( SM_CYSMICON
), 0 );
1909 if (hIconSmIntern
) Class
->CSF_flags
|= CSF_CACHEDSMICON
;
1910 //// FIXME: Very hacky here but it passes the tests....
1911 Ret
= 0; // Fixes 1009
1913 Class
->hIconSm
= (HANDLE
)NewLong
;
1915 /* Update the clones */
1916 Class
= Class
->pclsClone
;
1917 while (Class
!= NULL
)
1919 Class
->hIconSm
= (HANDLE
)NewLong
;
1920 Class
->hIconSmIntern
= hIconSmIntern
;
1921 Class
= Class
->pclsNext
;
1926 Ret
= (ULONG_PTR
)Class
->hModule
;
1927 Class
->hModule
= (HINSTANCE
)NewLong
;
1929 /* Update the clones */
1930 Class
= Class
->pclsClone
;
1931 while (Class
!= NULL
)
1933 Class
->hModule
= (HINSTANCE
)NewLong
;
1934 Class
= Class
->pclsNext
;
1940 PUNICODE_STRING Value
= (PUNICODE_STRING
)NewLong
;
1942 if (!IntSetClassMenuName(Class
,
1945 ERR("Setting the class menu name failed!\n");
1948 /* FIXME: Really return NULL? Wine does so... */
1953 Ret
= (ULONG_PTR
)Class
->style
;
1954 Class
->style
= (UINT
)NewLong
;
1956 /* FIXME: What if the CS_GLOBALCLASS style is changed? should we
1957 move the class to the appropriate list? For now, we save
1958 the original value in Class->Global, so we can always
1959 locate the appropriate list */
1961 /* Update the clones */
1962 Class
= Class
->pclsClone
;
1963 while (Class
!= NULL
)
1965 Class
->style
= (UINT
)NewLong
;
1966 Class
= Class
->pclsNext
;
1971 Ret
= (ULONG_PTR
)IntSetClassWndProc(Class
,
1978 PUNICODE_STRING Value
= (PUNICODE_STRING
)NewLong
;
1980 Ret
= (ULONG_PTR
)Class
->atomClassName
;
1981 if (!IntSetClassAtom(Class
,
1990 EngSetLastError(ERROR_INVALID_INDEX
);
1998 UserGetClassInfo(IN PCLS Class
,
1999 OUT PWNDCLASSEXW lpwcx
,
2001 HINSTANCE hInstance
)
2003 if (!Class
) return FALSE
;
2005 lpwcx
->style
= Class
->style
;
2007 // If fnId is set, clear the global bit. See wine class test check_style.
2009 lpwcx
->style
&= ~CS_GLOBALCLASS
;
2011 lpwcx
->lpfnWndProc
= IntGetClassWndProc(Class
, Ansi
);
2013 lpwcx
->cbClsExtra
= Class
->cbclsExtra
;
2014 lpwcx
->cbWndExtra
= Class
->cbwndExtra
;
2015 lpwcx
->hIcon
= Class
->hIcon
; /* FIXME: Get handle from pointer */
2016 lpwcx
->hCursor
= Class
->hCursor
; /* FIXME: Get handle from pointer */
2017 lpwcx
->hbrBackground
= Class
->hbrBackground
;
2019 /* Copy non-string to user first. */
2021 ((PWNDCLASSEXA
)lpwcx
)->lpszMenuName
= Class
->lpszClientAnsiMenuName
;
2023 lpwcx
->lpszMenuName
= Class
->lpszClientUnicodeMenuName
;
2025 * FIXME: CLSMENUNAME has the answers! Copy the already made buffers from there!
2026 * Cls: lpszMenuName and lpszAnsiClassName should be used by kernel space.
2027 * lpszClientXxxMenuName should already be mapped to user space.
2029 /* Copy string ptr to user. */
2030 if ( Class
->lpszClientUnicodeMenuName
!= NULL
&&
2031 Class
->MenuNameIsString
)
2033 lpwcx
->lpszMenuName
= UserHeapAddressToUser(Ansi
?
2034 (PVOID
)Class
->lpszClientAnsiMenuName
:
2035 (PVOID
)Class
->lpszClientUnicodeMenuName
);
2038 if (hInstance
== hModClient
)
2039 lpwcx
->hInstance
= NULL
;
2041 lpwcx
->hInstance
= hInstance
;
2043 /* FIXME: Return the string? Okay! This is performed in User32! */
2044 //lpwcx->lpszClassName = (LPCWSTR)((ULONG_PTR)Class->atomClassName);
2046 /* FIXME: Get handle from pointer */
2047 lpwcx
->hIconSm
= Class
->hIconSm
? Class
->hIconSm
: Class
->hIconSmIntern
;
2057 UserRegisterSystemClasses(VOID
)
2060 UNICODE_STRING ClassName
, MenuName
;
2061 PPROCESSINFO ppi
= GetW32ProcessInfo();
2068 if (ppi
->W32PF_flags
& W32PF_CLASSESREGISTERED
)
2071 if ( hModClient
== NULL
)
2074 RtlZeroMemory(&ClassName
, sizeof(ClassName
));
2075 RtlZeroMemory(&MenuName
, sizeof(MenuName
));
2077 for (i
= 0; i
!= ARRAYSIZE(DefaultServerClasses
); i
++)
2079 if (!IS_ATOM(DefaultServerClasses
[i
].ClassName
))
2081 RtlInitUnicodeString(&ClassName
, DefaultServerClasses
[i
].ClassName
);
2085 ClassName
.Buffer
= DefaultServerClasses
[i
].ClassName
;
2086 ClassName
.Length
= 0;
2087 ClassName
.MaximumLength
= 0;
2090 wc
.cbSize
= sizeof(wc
);
2091 wc
.style
= DefaultServerClasses
[i
].Style
;
2093 Flags
|= CSF_SERVERSIDEPROC
;
2095 if (DefaultServerClasses
[i
].ProcW
)
2097 wc
.lpfnWndProc
= DefaultServerClasses
[i
].ProcW
;
2098 wc
.hInstance
= hModuleWin
;
2102 wc
.lpfnWndProc
= GETPFNSERVER(DefaultServerClasses
[i
].fiId
);
2103 wc
.hInstance
= hModClient
;
2107 wc
.cbWndExtra
= DefaultServerClasses
[i
].ExtraBytes
;
2109 wc
.hCursor
= DefaultServerClasses
[i
].hCursor
;
2110 hBrush
= DefaultServerClasses
[i
].hBrush
;
2111 if (hBrush
<= (HBRUSH
)COLOR_MENUBAR
)
2113 hBrush
= IntGetSysColorBrush((INT
)hBrush
);
2115 wc
.hbrBackground
= hBrush
;
2116 wc
.lpszMenuName
= NULL
;
2117 wc
.lpszClassName
= ClassName
.Buffer
;
2120 Class
= IntCreateClass( &wc
,
2123 DefaultServerClasses
[i
].fiId
,
2129 Class
->pclsNext
= ppi
->pclsPublicList
;
2130 (void)InterlockedExchangePointer((PVOID
*)&ppi
->pclsPublicList
,
2133 ppi
->dwRegisteredClasses
|= ICLASS_TO_MASK(DefaultServerClasses
[i
].iCls
);
2137 ERR("!!! Registering system class failed!\n");
2141 if (Ret
) ppi
->W32PF_flags
|= W32PF_CLASSESREGISTERED
;
2145 /* SYSCALLS *****************************************************************/
2149 NtUserRegisterClassExWOW(
2151 PUNICODE_STRING ClassName
,
2152 PUNICODE_STRING ClsNVersion
,
2153 PCLSMENUNAME pClassMenuName
,
2159 * Registers a new class with the window manager
2161 * lpwcx = Win32 extended window class structure
2162 * bUnicodeClass = Whether to send ANSI or unicode strings
2163 * to window procedures
2165 * Atom identifying the new class
2168 WNDCLASSEXW CapturedClassInfo
= {0};
2169 UNICODE_STRING CapturedName
= {0}, CapturedMenuName
= {0};
2170 RTL_ATOM Ret
= (RTL_ATOM
)0;
2171 PPROCESSINFO ppi
= GetW32ProcessInfo();
2173 if (Flags
& ~(CSF_ANSIPROC
))
2175 ERR("NtUserRegisterClassExWOW Bad Flags!\n");
2176 EngSetLastError(ERROR_INVALID_FLAGS
);
2180 UserEnterExclusive();
2182 TRACE("NtUserRegisterClassExWOW ClsN %wZ\n",ClassName
);
2184 if ( !(ppi
->W32PF_flags
& W32PF_CLASSESREGISTERED
))
2186 UserRegisterSystemClasses();
2191 /* Probe the parameters and basic parameter checks */
2192 if (ProbeForReadUint(&lpwcx
->cbSize
) != sizeof(WNDCLASSEXW
))
2194 ERR("NtUserRegisterClassExWOW Wrong cbSize!\n");
2195 goto InvalidParameter
;
2199 sizeof(WNDCLASSEXW
),
2201 RtlCopyMemory(&CapturedClassInfo
,
2203 sizeof(WNDCLASSEXW
));
2205 CapturedName
= ProbeForReadUnicodeString(ClassName
);
2207 ProbeForRead(pClassMenuName
,
2208 sizeof(CLSMENUNAME
),
2211 CapturedMenuName
= ProbeForReadUnicodeString(pClassMenuName
->pusMenuName
);
2213 if ( (CapturedName
.Length
& 1) ||
2214 (CapturedMenuName
.Length
& 1) ||
2215 (CapturedClassInfo
.cbClsExtra
< 0) ||
2216 ((CapturedClassInfo
.cbClsExtra
+ CapturedName
.Length
+
2217 CapturedMenuName
.Length
+ sizeof(CLS
))
2218 < (ULONG
)CapturedClassInfo
.cbClsExtra
) ||
2219 (CapturedClassInfo
.cbWndExtra
< 0) ||
2220 (CapturedClassInfo
.hInstance
== NULL
) )
2222 ERR("NtUserRegisterClassExWOW Invalid Parameter Error!\n");
2223 goto InvalidParameter
;
2226 if (CapturedName
.Length
!= 0)
2228 ProbeForRead(CapturedName
.Buffer
,
2229 CapturedName
.Length
,
2234 if (!IS_ATOM(CapturedName
.Buffer
))
2236 ERR("NtUserRegisterClassExWOW ClassName Error!\n");
2237 goto InvalidParameter
;
2241 if (CapturedMenuName
.Length
!= 0)
2243 ProbeForRead(CapturedMenuName
.Buffer
,
2244 CapturedMenuName
.Length
,
2247 else if (CapturedMenuName
.Buffer
!= NULL
&&
2248 !IS_INTRESOURCE(CapturedMenuName
.Buffer
))
2250 ERR("NtUserRegisterClassExWOW MenuName Error!\n");
2252 EngSetLastError(ERROR_INVALID_PARAMETER
);
2256 if (IsCallProcHandle(lpwcx
->lpfnWndProc
))
2257 { // Never seen this yet, but I'm sure it's a little haxxy trick!
2258 // If this pops up we know what todo!
2259 ERR("NtUserRegisterClassExWOW WndProc is CallProc!!\n");
2262 TRACE("NtUserRegisterClassExWOW MnuN %wZ\n",&CapturedMenuName
);
2264 /* Register the class */
2265 Ret
= UserRegisterClass(&CapturedClassInfo
,
2271 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2273 ERR("NtUserRegisterClassExWOW Exception Error!\n");
2274 SetLastNtError(_SEH2_GetExceptionCode());
2280 ERR("NtUserRegisterClassExWOW Null Return!\n");
2289 NtUserSetClassLong(HWND hWnd
,
2291 ULONG_PTR dwNewLong
,
2298 UserEnterExclusive();
2300 pi
= GetW32ProcessInfo();
2302 Window
= UserGetWindowObject(hWnd
);
2305 if (Window
->head
.pti
->ppi
!= pi
)
2307 EngSetLastError(ERROR_ACCESS_DENIED
);
2313 UNICODE_STRING Value
;
2315 /* Probe the parameters */
2316 if (Offset
== GCW_ATOM
|| Offset
== GCLP_MENUNAME
)
2318 Value
= ProbeForReadUnicodeString((PUNICODE_STRING
)dwNewLong
);
2319 if (Value
.Length
& 1)
2321 goto InvalidParameter
;
2324 if (Value
.Length
!= 0)
2326 ProbeForRead(Value
.Buffer
,
2332 if (Offset
== GCW_ATOM
&& !IS_ATOM(Value
.Buffer
))
2334 goto InvalidParameter
;
2336 else if (Offset
== GCLP_MENUNAME
&& !IS_INTRESOURCE(Value
.Buffer
))
2339 EngSetLastError(ERROR_INVALID_PARAMETER
);
2344 dwNewLong
= (ULONG_PTR
)&Value
;
2347 Ret
= UserSetClassLongPtr(Window
->pcls
,
2352 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2354 SetLastNtError(_SEH2_GetExceptionCode());
2373 * NOTE: Obsoleted in 32-bit windows
2380 NtUserUnregisterClass(
2381 IN PUNICODE_STRING ClassNameOrAtom
,
2382 IN HINSTANCE hInstance
,
2383 OUT PCLSMENUNAME pClassMenuName
)
2385 UNICODE_STRING SafeClassName
;
2389 Status
= ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName
, ClassNameOrAtom
);
2390 if (!NT_SUCCESS(Status
))
2392 ERR("Error capturing the class name\n");
2393 SetLastNtError(Status
);
2397 UserEnterExclusive();
2399 /* Unregister the class */
2400 Ret
= UserUnregisterClass(&SafeClassName
, hInstance
, NULL
); // Null for now~
2404 if (SafeClassName
.Buffer
&& !IS_ATOM(SafeClassName
.Buffer
))
2405 ExFreePoolWithTag(SafeClassName
.Buffer
, TAG_STRING
);
2411 /* NOTE: For system classes hInstance is not NULL here, but User32Instance */
2415 HINSTANCE hInstance
,
2416 PUNICODE_STRING ClassName
,
2417 LPWNDCLASSEXW lpWndClassEx
,
2418 LPWSTR
*ppszMenuName
,
2421 UNICODE_STRING SafeClassName
;
2422 WNDCLASSEXW Safewcexw
;
2424 RTL_ATOM ClassAtom
= 0;
2431 ProbeForWrite( lpWndClassEx
, sizeof(WNDCLASSEXW
), sizeof(ULONG
));
2432 RtlCopyMemory( &Safewcexw
, lpWndClassEx
, sizeof(WNDCLASSEXW
));
2434 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2436 SetLastNtError(_SEH2_GetExceptionCode());
2437 _SEH2_YIELD(return FALSE
);
2441 Status
= ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName
, ClassName
);
2442 if (!NT_SUCCESS(Status
))
2444 ERR("Error capturing the class name\n");
2445 SetLastNtError(Status
);
2449 // If null instance use client.
2450 if (!hInstance
) hInstance
= hModClient
;
2452 TRACE("GetClassInfo(%wZ, %p)\n", SafeClassName
, hInstance
);
2454 /* NOTE: Need exclusive lock because getting the wndproc might require the
2455 creation of a call procedure handle */
2456 UserEnterExclusive();
2458 ppi
= GetW32ProcessInfo();
2459 if (!(ppi
->W32PF_flags
& W32PF_CLASSESREGISTERED
))
2461 UserRegisterSystemClasses();
2464 ClassAtom
= IntGetClassAtom(&SafeClassName
,
2469 if (ClassAtom
!= (RTL_ATOM
)0)
2471 Ret
= UserGetClassInfo(Class
, &Safewcexw
, bAnsi
, hInstance
);
2475 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
2485 /* Emulate Function. */
2486 if (ppszMenuName
) *ppszMenuName
= (LPWSTR
)Safewcexw
.lpszMenuName
;
2488 RtlCopyMemory(lpWndClassEx
, &Safewcexw
, sizeof(WNDCLASSEXW
));
2491 /* We must return the atom of the class here instead of just TRUE. */
2492 /* Undocumented behavior! Return the class atom as a BOOL! */
2493 Ret
= (BOOL
)ClassAtom
;
2495 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2497 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
2503 if (!IS_ATOM(SafeClassName
.Buffer
))
2504 ExFreePoolWithTag(SafeClassName
.Buffer
, TAG_STRING
);
2511 NtUserGetClassName (IN HWND hWnd
,
2513 OUT PUNICODE_STRING ClassName
)
2516 UNICODE_STRING CapturedClassName
;
2522 Window
= UserGetWindowObject(hWnd
);
2525 if (Real
&& Window
->fnid
&& !(Window
->fnid
& FNID_DESTROY
))
2527 if (LookupFnIdToiCls(Window
->fnid
, &iCls
))
2529 Atom
= gpsi
->atomSysClass
[iCls
];
2535 ProbeForWriteUnicodeString(ClassName
);
2536 CapturedClassName
= *ClassName
;
2538 /* Get the class name */
2539 Ret
= UserGetClassName(Window
->pcls
,
2546 /* Update the Length field */
2547 ClassName
->Length
= CapturedClassName
.Length
;
2550 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2552 SetLastNtError(_SEH2_GetExceptionCode());
2562 /* Return Pointer to Class structure. */
2566 HINSTANCE hInstance
,
2567 PUNICODE_STRING ClassName
)
2569 UNICODE_STRING SafeClassName
;
2572 RTL_ATOM ClassAtom
= 0;
2575 Status
= ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName
, ClassName
);
2576 if (!NT_SUCCESS(Status
))
2578 ERR("Error capturing the class name\n");
2579 SetLastNtError(Status
);
2583 UserEnterExclusive();
2585 pi
= GetW32ProcessInfo();
2587 ClassAtom
= IntGetClassAtom(&SafeClassName
,
2594 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
2598 if (SafeClassName
.Buffer
&& !IS_ATOM(SafeClassName
.Buffer
))
2599 ExFreePoolWithTag(SafeClassName
.Buffer
, TAG_STRING
);
2603 // Don't forget to use DesktopPtrToUser( ? ) with return pointer in user space.