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 //// FIXME handles to pointers
1001 Class
->hIcon
= lpwcx
->hIcon
;
1002 Class
->hIconSm
= lpwcx
->hIconSm
;
1003 Class
->hCursor
= lpwcx
->hCursor
;
1005 Class
->hbrBackground
= lpwcx
->hbrBackground
;
1007 /* Make a copy of the string */
1008 if (pszMenuNameBuffer
!= NULL
)
1010 Class
->MenuNameIsString
= TRUE
;
1012 Class
->lpszClientUnicodeMenuName
= pszMenuNameBuffer
;
1013 RtlCopyMemory(Class
->lpszClientUnicodeMenuName
,
1016 Class
->lpszClientUnicodeMenuName
[MenuName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1018 pszMenuNameBuffer
+= (MenuName
->Length
/ sizeof(WCHAR
)) + 1;
1021 Class
->lpszClientUnicodeMenuName
= MenuName
->Buffer
;
1023 /* Save an ANSI copy of the string */
1024 if (pszMenuNameBuffer
!= NULL
)
1026 ANSI_STRING AnsiString
;
1028 Class
->lpszClientAnsiMenuName
= (PSTR
)pszMenuNameBuffer
;
1029 AnsiString
.MaximumLength
= (USHORT
)RtlUnicodeStringToAnsiSize(MenuName
);
1030 AnsiString
.Buffer
= Class
->lpszClientAnsiMenuName
;
1031 Status
= RtlUnicodeStringToAnsiString(&AnsiString
,
1034 if (!NT_SUCCESS(Status
))
1036 ERR("Failed to convert unicode menu name to ansi!\n");
1038 /* Life would've been much prettier if ntoskrnl exported RtlRaiseStatus()... */
1043 Class
->lpszClientAnsiMenuName
= (PSTR
)MenuName
->Buffer
;
1045 /* Save kernel use menu name and ansi class name */
1046 Class
->lpszMenuName
= Class
->lpszClientUnicodeMenuName
; // FIXME!
1047 //Class->lpszAnsiClassName = FIXME
1049 /* Server Side overrides class calling type (A/W)!
1050 User32 whine test_builtinproc: "deftest"
1051 built-in winproc - window A/W type automatically detected */
1052 if (!(Class
->CSF_flags
& CSF_SERVERSIDEPROC
))
1056 /* Due to the wine class "deftest" and most likely no FNID to reference
1057 from, sort through the Server Side list and compare proc addresses
1058 for match. This method will be used in related code.
1060 for ( i
= FNID_FIRST
; i
<= FNID_SWITCH
; i
++)
1061 { // Open ANSI or Unicode, just match, set and break.
1062 if (GETPFNCLIENTW(i
) == Class
->lpfnWndProc
)
1064 WndProc
= GETPFNSERVER(i
);
1067 if (GETPFNCLIENTA(i
) == Class
->lpfnWndProc
)
1069 WndProc
= GETPFNSERVER(i
);
1074 { // If a hit, we are Server Side so set the right flags and proc.
1075 Class
->CSF_flags
|= CSF_SERVERSIDEPROC
;
1076 Class
->CSF_flags
&= ~CSF_ANSIPROC
;
1077 Class
->lpfnWndProc
= WndProc
;
1081 if (!(Class
->CSF_flags
& CSF_ANSIPROC
))
1082 Class
->Unicode
= TRUE
;
1084 if (Class
->style
& CS_GLOBALCLASS
)
1085 Class
->Global
= TRUE
;
1087 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1089 Status
= _SEH2_GetExceptionCode();
1093 if (!NT_SUCCESS(Status
))
1095 ERR("Failed creating the class: 0x%x\n", Status
);
1097 SetLastNtError(Status
);
1099 if (pszMenuName
!= NULL
)
1100 UserHeapFree(pszMenuName
);
1102 DesktopHeapFree(Desktop
,
1106 IntDeregisterClassAtom(Atom
);
1112 ERR("Failed to allocate class on Desktop 0x%p\n", Desktop
);
1114 if (pszMenuName
!= NULL
)
1115 UserHeapFree(pszMenuName
);
1117 IntDeregisterClassAtom(Atom
);
1119 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1122 TRACE("Created class 0x%p with name %wZ and proc 0x%p for atom 0x%x and hInstance 0x%p, global %u\n",
1123 Class
, ClassName
, Class
->lpfnWndProc
, Atom
, Class
->hModule
, Class
->Global
);
1129 IntFindClass(IN RTL_ATOM Atom
,
1130 IN HINSTANCE hInstance
,
1132 OUT PCLS
**Link OPTIONAL
)
1134 PCLS Class
, *PrevLink
= ClassList
;
1137 while (Class
!= NULL
)
1139 if (Class
->atomClassName
== Atom
&&
1140 (hInstance
== NULL
|| Class
->hModule
== hInstance
) &&
1141 !(Class
->CSF_flags
& CSF_WOWDEFERDESTROY
))
1143 ASSERT(Class
->pclsBase
== Class
);
1150 PrevLink
= &Class
->pclsNext
;
1151 Class
= Class
->pclsNext
;
1159 IntGetAtomFromStringOrAtom(
1160 _In_ PUNICODE_STRING ClassName
,
1161 _Out_ RTL_ATOM
*Atom
)
1165 if (ClassName
->Length
!= 0)
1173 /* NOTE: Caller has to protect the call with SEH! */
1175 if (ClassName
->Length
!= 0)
1177 /* FIXME: Don't limit to 64 characters! use SEH when allocating memory! */
1178 if (ClassName
->Length
/ sizeof(WCHAR
) >= sizeof(szBuf
) / sizeof(szBuf
[0]))
1180 EngSetLastError(ERROR_INVALID_PARAMETER
);
1184 /* We need to make a local copy of the class name! The caller could
1185 modify the buffer and we could overflow in RtlLookupAtomInAtomTable.
1186 We're protected by SEH, but the ranges that might be accessed were
1188 RtlCopyMemory(szBuf
,
1191 szBuf
[ClassName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1195 AtomName
= ClassName
->Buffer
;
1197 /* Lookup the atom */
1198 Status
= RtlLookupAtomInAtomTable(gAtomTable
,
1201 if (NT_SUCCESS(Status
))
1207 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
1209 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
1213 SetLastNtError(Status
);
1219 ASSERT(IS_ATOM(ClassName
->Buffer
));
1220 *Atom
= (RTL_ATOM
)((ULONG_PTR
)ClassName
->Buffer
);
1229 _In_ PUNICODE_STRING ClassName
,
1230 IN HINSTANCE hInstance OPTIONAL
,
1231 IN PPROCESSINFO pi OPTIONAL
,
1232 OUT PCLS
*BaseClass OPTIONAL
,
1233 OUT PCLS
**Link OPTIONAL
)
1235 RTL_ATOM Atom
= (RTL_ATOM
)0;
1237 ASSERT(BaseClass
!= NULL
);
1239 if (IntGetAtomFromStringOrAtom(ClassName
,
1241 Atom
!= (RTL_ATOM
)0)
1245 /* Attempt to locate the class object */
1249 /* Step 1: Try to find an exact match of locally registered classes */
1250 Class
= IntFindClass(Atom
,
1252 &pi
->pclsPrivateList
,
1255 { TRACE("Step 1: 0x%p\n",Class
);
1259 /* Step 2: Try to find any globally registered class. The hInstance
1260 is not relevant for global classes */
1261 Class
= IntFindClass(Atom
,
1263 &pi
->pclsPublicList
,
1266 { TRACE("Step 2: 0x%p 0x%p\n",Class
, Class
->hModule
);
1270 /* Step 3: Try to find any local class registered by user32 */
1271 Class
= IntFindClass(Atom
,
1273 &pi
->pclsPrivateList
,
1276 { TRACE("Step 3: 0x%p\n",Class
);
1280 /* Step 4: Try to find any global class registered by user32 */
1281 Class
= IntFindClass(Atom
,
1283 &pi
->pclsPublicList
,
1287 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
1289 }else{TRACE("Step 4: 0x%p\n",Class
);}
1299 IntGetAndReferenceClass(PUNICODE_STRING ClassName
, HINSTANCE hInstance
, BOOL bDesktopThread
)
1301 PCLS
*ClassLink
, Class
= NULL
;
1306 pti
= gptiDesktopThread
;
1308 pti
= PsGetCurrentThreadWin32Thread();
1310 if ( !(pti
->ppi
->W32PF_flags
& W32PF_CLASSESREGISTERED
))
1312 UserRegisterSystemClasses();
1315 /* Check the class. */
1317 TRACE("Finding Class %wZ for hInstance 0x%p\n", ClassName
, hInstance
);
1319 ClassAtom
= IntGetClassAtom(ClassName
,
1325 if (ClassAtom
== (RTL_ATOM
)0)
1327 if (IS_ATOM(ClassName
->Buffer
))
1329 ERR("Class 0x%p not found\n", ClassName
->Buffer
);
1333 ERR("Class \"%wZ\" not found\n", ClassName
);
1336 EngSetLastError(ERROR_CANNOT_FIND_WND_CLASS
);
1340 TRACE("Referencing Class 0x%p with atom 0x%x\n", Class
, ClassAtom
);
1341 Class
= IntReferenceClass(Class
,
1346 ERR("Failed to reference window class!\n");
1354 UserRegisterClass(IN CONST WNDCLASSEXW
* lpwcx
,
1355 IN PUNICODE_STRING ClassName
,
1356 IN PUNICODE_STRING MenuName
,
1364 RTL_ATOM Ret
= (RTL_ATOM
)0;
1366 /* NOTE: Accessing the buffers in ClassName and MenuName may raise exceptions! */
1368 pti
= GetW32ThreadInfo();
1372 // Need only to test for two conditions not four....... Fix more whine tests....
1373 if ( IntGetAtomFromStringOrAtom( ClassName
, &ClassAtom
) &&
1374 ClassAtom
!= (RTL_ATOM
)0 &&
1375 !(dwFlags
& CSF_SERVERSIDEPROC
) ) // Bypass Server Sides
1377 Class
= IntFindClass( ClassAtom
,
1379 &pi
->pclsPrivateList
,
1382 if (Class
!= NULL
&& !Class
->Global
)
1384 // Local class already exists
1385 TRACE("Local Class 0x%x does already exist!\n", ClassAtom
);
1386 EngSetLastError(ERROR_CLASS_ALREADY_EXISTS
);
1390 if (lpwcx
->style
& CS_GLOBALCLASS
)
1392 Class
= IntFindClass( ClassAtom
,
1394 &pi
->pclsPublicList
,
1397 if (Class
!= NULL
&& Class
->Global
)
1399 TRACE("Global Class 0x%x does already exist!\n", ClassAtom
);
1400 EngSetLastError(ERROR_CLASS_ALREADY_EXISTS
);
1406 Class
= IntCreateClass(lpwcx
,
1418 /* Register the class */
1420 List
= &pi
->pclsPublicList
;
1422 List
= &pi
->pclsPrivateList
;
1424 Class
->pclsNext
= *List
;
1425 (void)InterlockedExchangePointer((PVOID
*)List
,
1428 Ret
= Class
->atomClassName
;
1432 ERR("UserRegisterClass: Yes, that is right, you have no Class!\n");
1439 UserUnregisterClass(IN PUNICODE_STRING ClassName
,
1440 IN HINSTANCE hInstance
,
1441 OUT PCLSMENUNAME pClassMenuName
)
1448 pi
= GetW32ProcessInfo();
1450 TRACE("UserUnregisterClass(%wZ, 0x%p)\n", ClassName
, hInstance
);
1452 /* NOTE: Accessing the buffer in ClassName may raise an exception! */
1453 ClassAtom
= IntGetClassAtom(ClassName
,
1458 if (ClassAtom
== (RTL_ATOM
)0)
1460 TRACE("UserUnregisterClass: No Class found.\n");
1464 ASSERT(Class
!= NULL
);
1466 if (Class
->cWndReferenceCount
!= 0 ||
1467 Class
->pclsClone
!= NULL
)
1469 TRACE("UserUnregisterClass: Class has a Window. Ct %u : Clone 0x%p\n", Class
->cWndReferenceCount
, Class
->pclsClone
);
1470 EngSetLastError(ERROR_CLASS_HAS_WINDOWS
);
1474 /* Must be a base class! */
1475 ASSERT(Class
->pclsBase
== Class
);
1477 /* Unlink the class */
1478 *Link
= Class
->pclsNext
;
1480 if (NT_SUCCESS(IntDeregisterClassAtom(Class
->atomClassName
)))
1482 TRACE("Class 0x%p\n", Class
);
1483 TRACE("UserUnregisterClass: Good Exit!\n");
1484 /* Finally free the resources */
1485 IntDestroyClass(Class
);
1488 ERR("UserUnregisterClass: Can not deregister Class Atom.\n");
1493 UserGetClassName(IN PCLS Class
,
1494 IN OUT PUNICODE_STRING ClassName
,
1498 NTSTATUS Status
= STATUS_SUCCESS
;
1499 WCHAR szStaticTemp
[32];
1500 PWSTR szTemp
= NULL
;
1501 ULONG BufLen
= sizeof(szStaticTemp
);
1504 /* Note: Accessing the buffer in ClassName may raise an exception! */
1510 PANSI_STRING AnsiClassName
= (PANSI_STRING
)ClassName
;
1511 UNICODE_STRING UnicodeClassName
;
1513 /* Limit the size of the static buffer on the stack to the
1514 size of the buffer provided by the caller */
1515 if (BufLen
/ sizeof(WCHAR
) > AnsiClassName
->MaximumLength
)
1517 BufLen
= AnsiClassName
->MaximumLength
* sizeof(WCHAR
);
1520 /* Find out how big the buffer needs to be */
1521 Status
= RtlQueryAtomInAtomTable(gAtomTable
,
1522 Class
->atomClassName
,
1527 if (Status
== STATUS_BUFFER_TOO_SMALL
)
1529 if (BufLen
/ sizeof(WCHAR
) > AnsiClassName
->MaximumLength
)
1531 /* The buffer required exceeds the ansi buffer provided,
1532 pretend like we're using the ansi buffer and limit the
1533 size to the buffer size provided */
1534 BufLen
= AnsiClassName
->MaximumLength
* sizeof(WCHAR
);
1537 /* Allocate a temporary buffer that can hold the unicode class name */
1538 szTemp
= ExAllocatePoolWithTag(PagedPool
,
1543 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1547 /* Query the class name */
1548 Status
= RtlQueryAtomInAtomTable(gAtomTable
,
1549 Atom
? Atom
: Class
->atomClassName
,
1556 szTemp
= szStaticTemp
;
1558 if (NT_SUCCESS(Status
))
1560 /* Convert the atom name to ansi */
1562 RtlInitUnicodeString(&UnicodeClassName
,
1565 Status
= RtlUnicodeStringToAnsiString(AnsiClassName
,
1568 if (!NT_SUCCESS(Status
))
1570 SetLastNtError(Status
);
1575 Ret
= BufLen
/ sizeof(WCHAR
);
1579 BufLen
= ClassName
->MaximumLength
;
1581 /* Query the atom name */
1582 Status
= RtlQueryAtomInAtomTable(gAtomTable
,
1583 Atom
? Atom
: Class
->atomClassName
,
1589 if (!NT_SUCCESS(Status
))
1591 SetLastNtError(Status
);
1595 Ret
= BufLen
/ sizeof(WCHAR
);
1598 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1600 SetLastNtError(_SEH2_GetExceptionCode());
1604 if (Ansi
&& szTemp
!= NULL
&& szTemp
!= szStaticTemp
)
1606 ExFreePoolWithTag(szTemp
, USERTAG_CLASS
);
1613 IntSetClassMenuName(IN PCLS Class
,
1614 IN PUNICODE_STRING MenuName
)
1618 /* Change the base class first */
1619 Class
= Class
->pclsBase
;
1621 if (MenuName
->Length
!= 0)
1623 ANSI_STRING AnsiString
;
1626 AnsiString
.MaximumLength
= (USHORT
)RtlUnicodeStringToAnsiSize(MenuName
);
1628 strBufW
= UserHeapAlloc(MenuName
->Length
+ sizeof(UNICODE_NULL
) +
1629 AnsiString
.MaximumLength
);
1630 if (strBufW
!= NULL
)
1636 /* Copy the unicode string */
1637 RtlCopyMemory(strBufW
,
1640 strBufW
[MenuName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1642 /* Create an ANSI copy of the string */
1643 AnsiString
.Buffer
= (PSTR
)(strBufW
+ (MenuName
->Length
/ sizeof(WCHAR
)) + 1);
1644 Status
= RtlUnicodeStringToAnsiString(&AnsiString
,
1647 if (!NT_SUCCESS(Status
))
1649 SetLastNtError(Status
);
1655 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1657 SetLastNtError(_SEH2_GetExceptionCode());
1663 /* Update the base class */
1664 IntFreeClassMenuName(Class
);
1665 Class
->lpszClientUnicodeMenuName
= strBufW
;
1666 Class
->lpszClientAnsiMenuName
= AnsiString
.Buffer
;
1667 Class
->MenuNameIsString
= TRUE
;
1669 /* Update the clones */
1670 Class
= Class
->pclsClone
;
1671 while (Class
!= NULL
)
1673 Class
->lpszClientUnicodeMenuName
= strBufW
;
1674 Class
->lpszClientAnsiMenuName
= AnsiString
.Buffer
;
1675 Class
->MenuNameIsString
= TRUE
;
1677 Class
= Class
->pclsNext
;
1682 ERR("Failed to copy class menu name!\n");
1683 UserHeapFree(strBufW
);
1687 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1691 ASSERT(IS_INTRESOURCE(MenuName
->Buffer
));
1693 /* Update the base class */
1694 IntFreeClassMenuName(Class
);
1695 Class
->lpszClientUnicodeMenuName
= MenuName
->Buffer
;
1696 Class
->lpszClientAnsiMenuName
= (PSTR
)MenuName
->Buffer
;
1697 Class
->MenuNameIsString
= FALSE
;
1699 /* Update the clones */
1700 Class
= Class
->pclsClone
;
1701 while (Class
!= NULL
)
1703 Class
->lpszClientUnicodeMenuName
= MenuName
->Buffer
;
1704 Class
->lpszClientAnsiMenuName
= (PSTR
)MenuName
->Buffer
;
1705 Class
->MenuNameIsString
= FALSE
;
1707 Class
= Class
->pclsNext
;
1716 //// Do this for now in anticipation of new cursor icon code.
1717 BOOLEAN FASTCALL
IntDestroyCurIconObject(PCURICON_OBJECT
, PPROCESSINFO
);
1720 IntClassDestroyIcon(HANDLE hCurIcon
)
1722 PCURICON_OBJECT CurIcon
;
1725 if (!(CurIcon
= UserGetCurIconObject(hCurIcon
)))
1728 ERR("hCurIcon was not found!\n");
1731 Ret
= IntDestroyCurIconObject(CurIcon
, PsGetCurrentProcessWin32Process());
1732 /* Note: IntDestroyCurIconObject will remove our reference for us! */
1735 ERR("hCurIcon was not Destroyed!\n");
1741 UserSetClassLongPtr(IN PCLS Class
,
1743 IN ULONG_PTR NewLong
,
1747 HANDLE hIconSmIntern
= NULL
;
1749 /* NOTE: For GCLP_MENUNAME and GCW_ATOM this function may raise an exception! */
1751 /* Change the information in the base class first, then update the clones */
1752 Class
= Class
->pclsBase
;
1758 TRACE("SetClassLong(%d, %x)\n", Index
, NewLong
);
1760 if (((ULONG
)Index
+ sizeof(ULONG_PTR
)) < (ULONG
)Index
||
1761 ((ULONG
)Index
+ sizeof(ULONG_PTR
)) > (ULONG
)Class
->cbclsExtra
)
1763 EngSetLastError(ERROR_INVALID_PARAMETER
);
1767 Data
= (PULONG_PTR
)((ULONG_PTR
)(Class
+ 1) + Index
);
1769 /* FIXME: Data might be a unaligned pointer! Might be a problem on
1770 certain architectures, maybe using RtlCopyMemory is a
1771 better choice for those architectures! */
1775 /* Update the clones */
1776 Class
= Class
->pclsClone
;
1777 while (Class
!= NULL
)
1779 *(PULONG_PTR
)((ULONG_PTR
)(Class
+ 1) + Index
) = NewLong
;
1780 Class
= Class
->pclsNext
;
1788 case GCL_CBWNDEXTRA
:
1789 Ret
= (ULONG_PTR
)Class
->cbwndExtra
;
1790 Class
->cbwndExtra
= (INT
)NewLong
;
1792 /* Update the clones */
1793 Class
= Class
->pclsClone
;
1794 while (Class
!= NULL
)
1796 Class
->cbwndExtra
= (INT
)NewLong
;
1797 Class
= Class
->pclsNext
;
1802 case GCL_CBCLSEXTRA
:
1803 EngSetLastError(ERROR_INVALID_PARAMETER
);
1806 case GCLP_HBRBACKGROUND
:
1807 Ret
= (ULONG_PTR
)Class
->hbrBackground
;
1808 Class
->hbrBackground
= (HBRUSH
)NewLong
;
1810 /* Update the clones */
1811 Class
= Class
->pclsClone
;
1812 while (Class
!= NULL
)
1814 Class
->hbrBackground
= (HBRUSH
)NewLong
;
1815 Class
= Class
->pclsNext
;
1820 /* FIXME: Get handle from pointer to CURSOR object */
1821 Ret
= (ULONG_PTR
)Class
->hCursor
;
1822 Class
->hCursor
= (HANDLE
)NewLong
;
1824 /* Update the clones */
1825 Class
= Class
->pclsClone
;
1826 while (Class
!= NULL
)
1828 Class
->hCursor
= (HANDLE
)NewLong
;
1829 Class
= Class
->pclsNext
;
1834 // hIconSm, A handle to a small icon that is associated with the window class.
1835 // If this member is NULL, the system searches the icon resource specified by
1836 // the hIcon member for an icon of the appropriate size to use as the small icon.
1839 /* FIXME: Get handle from pointer to ICON object */
1840 Ret
= (ULONG_PTR
)Class
->hIcon
;
1841 if (Class
->hIcon
== (HANDLE
)NewLong
) break;
1842 if (Ret
&& Class
->hIconSmIntern
)
1844 IntClassDestroyIcon(Class
->hIconSmIntern
);
1845 Class
->CSF_flags
&= ~CSF_CACHEDSMICON
;
1846 Class
->hIconSmIntern
= NULL
;
1848 if (NewLong
&& !Class
->hIconSm
)
1850 hIconSmIntern
= Class
->hIconSmIntern
= co_IntCopyImage( (HICON
)NewLong
, IMAGE_ICON
,
1851 UserGetSystemMetrics( SM_CXSMICON
),
1852 UserGetSystemMetrics( SM_CYSMICON
), 0 );
1853 Class
->CSF_flags
|= CSF_CACHEDSMICON
;
1855 Class
->hIcon
= (HANDLE
)NewLong
;
1857 /* Update the clones */
1858 Class
= Class
->pclsClone
;
1859 while (Class
!= NULL
)
1861 Class
->hIcon
= (HANDLE
)NewLong
;
1862 Class
->hIconSmIntern
= hIconSmIntern
;
1863 Class
= Class
->pclsNext
;
1868 /* FIXME: Get handle from pointer to ICON object */
1869 Ret
= (ULONG_PTR
)Class
->hIconSm
;
1870 if (Class
->hIconSm
== (HANDLE
)NewLong
) break;
1871 if (Class
->CSF_flags
& CSF_CACHEDSMICON
)
1873 if (Class
->hIconSmIntern
)
1875 IntClassDestroyIcon(Class
->hIconSmIntern
);
1876 Class
->hIconSmIntern
= NULL
;
1878 Class
->CSF_flags
&= ~CSF_CACHEDSMICON
;
1880 if (Class
->hIcon
&& !Class
->hIconSmIntern
)
1882 hIconSmIntern
= Class
->hIconSmIntern
= co_IntCopyImage( Class
->hIcon
, IMAGE_ICON
,
1883 UserGetSystemMetrics( SM_CXSMICON
),
1884 UserGetSystemMetrics( SM_CYSMICON
), 0 );
1886 if (hIconSmIntern
) Class
->CSF_flags
|= CSF_CACHEDSMICON
;
1887 //// FIXME: Very hacky here but it passes the tests....
1888 //// We should not kill a users handle!!!
1889 if (Class
->hIconSm
) IntClassDestroyIcon(Class
->hIconSm
); // Fixes 1013
1890 Ret
= 0; // Fixes 1009
1892 Class
->hIconSm
= (HANDLE
)NewLong
;
1894 /* Update the clones */
1895 Class
= Class
->pclsClone
;
1896 while (Class
!= NULL
)
1898 Class
->hIconSm
= (HANDLE
)NewLong
;
1899 Class
->hIconSmIntern
= hIconSmIntern
;
1900 Class
= Class
->pclsNext
;
1905 Ret
= (ULONG_PTR
)Class
->hModule
;
1906 Class
->hModule
= (HINSTANCE
)NewLong
;
1908 /* Update the clones */
1909 Class
= Class
->pclsClone
;
1910 while (Class
!= NULL
)
1912 Class
->hModule
= (HINSTANCE
)NewLong
;
1913 Class
= Class
->pclsNext
;
1919 PUNICODE_STRING Value
= (PUNICODE_STRING
)NewLong
;
1921 if (!IntSetClassMenuName(Class
,
1924 ERR("Setting the class menu name failed!\n");
1927 /* FIXME: Really return NULL? Wine does so... */
1932 Ret
= (ULONG_PTR
)Class
->style
;
1933 Class
->style
= (UINT
)NewLong
;
1935 /* FIXME: What if the CS_GLOBALCLASS style is changed? should we
1936 move the class to the appropriate list? For now, we save
1937 the original value in Class->Global, so we can always
1938 locate the appropriate list */
1940 /* Update the clones */
1941 Class
= Class
->pclsClone
;
1942 while (Class
!= NULL
)
1944 Class
->style
= (UINT
)NewLong
;
1945 Class
= Class
->pclsNext
;
1950 Ret
= (ULONG_PTR
)IntSetClassWndProc(Class
,
1957 PUNICODE_STRING Value
= (PUNICODE_STRING
)NewLong
;
1959 Ret
= (ULONG_PTR
)Class
->atomClassName
;
1960 if (!IntSetClassAtom(Class
,
1969 EngSetLastError(ERROR_INVALID_INDEX
);
1977 UserGetClassInfo(IN PCLS Class
,
1978 OUT PWNDCLASSEXW lpwcx
,
1980 HINSTANCE hInstance
)
1982 if (!Class
) return FALSE
;
1984 lpwcx
->style
= Class
->style
;
1986 // If fnId is set, clear the global bit. See wine class test check_style.
1988 lpwcx
->style
&= ~CS_GLOBALCLASS
;
1990 lpwcx
->lpfnWndProc
= IntGetClassWndProc(Class
, Ansi
);
1992 lpwcx
->cbClsExtra
= Class
->cbclsExtra
;
1993 lpwcx
->cbWndExtra
= Class
->cbwndExtra
;
1994 lpwcx
->hIcon
= Class
->hIcon
; /* FIXME: Get handle from pointer */
1995 lpwcx
->hCursor
= Class
->hCursor
; /* FIXME: Get handle from pointer */
1996 lpwcx
->hbrBackground
= Class
->hbrBackground
;
1998 /* Copy non-string to user first. */
2000 ((PWNDCLASSEXA
)lpwcx
)->lpszMenuName
= Class
->lpszClientAnsiMenuName
;
2002 lpwcx
->lpszMenuName
= Class
->lpszClientUnicodeMenuName
;
2004 * FIXME: CLSMENUNAME has the answers! Copy the already made buffers from there!
2005 * Cls: lpszMenuName and lpszAnsiClassName should be used by kernel space.
2006 * lpszClientXxxMenuName should already be mapped to user space.
2008 /* Copy string ptr to user. */
2009 if ( Class
->lpszClientUnicodeMenuName
!= NULL
&&
2010 Class
->MenuNameIsString
)
2012 lpwcx
->lpszMenuName
= UserHeapAddressToUser(Ansi
?
2013 (PVOID
)Class
->lpszClientAnsiMenuName
:
2014 (PVOID
)Class
->lpszClientUnicodeMenuName
);
2017 if (hInstance
== hModClient
)
2018 lpwcx
->hInstance
= NULL
;
2020 lpwcx
->hInstance
= hInstance
;
2022 /* FIXME: Return the string? Okay! This is performed in User32! */
2023 //lpwcx->lpszClassName = (LPCWSTR)((ULONG_PTR)Class->atomClassName);
2025 /* FIXME: Get handle from pointer */
2026 lpwcx
->hIconSm
= Class
->hIconSm
? Class
->hIconSm
: Class
->hIconSmIntern
;
2036 UserRegisterSystemClasses(VOID
)
2039 UNICODE_STRING ClassName
, MenuName
;
2040 PPROCESSINFO ppi
= GetW32ProcessInfo();
2047 if (ppi
->W32PF_flags
& W32PF_CLASSESREGISTERED
)
2050 if ( hModClient
== NULL
)
2053 RtlZeroMemory(&ClassName
, sizeof(ClassName
));
2054 RtlZeroMemory(&MenuName
, sizeof(MenuName
));
2056 for (i
= 0; i
!= ARRAYSIZE(DefaultServerClasses
); i
++)
2058 if (!IS_ATOM(DefaultServerClasses
[i
].ClassName
))
2060 RtlInitUnicodeString(&ClassName
, DefaultServerClasses
[i
].ClassName
);
2064 ClassName
.Buffer
= DefaultServerClasses
[i
].ClassName
;
2065 ClassName
.Length
= 0;
2066 ClassName
.MaximumLength
= 0;
2069 wc
.cbSize
= sizeof(wc
);
2070 wc
.style
= DefaultServerClasses
[i
].Style
;
2072 Flags
|= CSF_SERVERSIDEPROC
;
2074 if (DefaultServerClasses
[i
].ProcW
)
2076 wc
.lpfnWndProc
= DefaultServerClasses
[i
].ProcW
;
2077 wc
.hInstance
= hModuleWin
;
2081 wc
.lpfnWndProc
= GETPFNSERVER(DefaultServerClasses
[i
].fiId
);
2082 wc
.hInstance
= hModClient
;
2086 wc
.cbWndExtra
= DefaultServerClasses
[i
].ExtraBytes
;
2088 wc
.hCursor
= DefaultServerClasses
[i
].hCursor
;
2089 hBrush
= DefaultServerClasses
[i
].hBrush
;
2090 if (hBrush
<= (HBRUSH
)COLOR_MENUBAR
)
2092 hBrush
= IntGetSysColorBrush((INT
)hBrush
);
2094 wc
.hbrBackground
= hBrush
;
2095 wc
.lpszMenuName
= NULL
;
2096 wc
.lpszClassName
= ClassName
.Buffer
;
2099 Class
= IntCreateClass( &wc
,
2102 DefaultServerClasses
[i
].fiId
,
2108 Class
->pclsNext
= ppi
->pclsPublicList
;
2109 (void)InterlockedExchangePointer((PVOID
*)&ppi
->pclsPublicList
,
2112 ppi
->dwRegisteredClasses
|= ICLASS_TO_MASK(DefaultServerClasses
[i
].iCls
);
2116 ERR("!!! Registering system class failed!\n");
2120 if (Ret
) ppi
->W32PF_flags
|= W32PF_CLASSESREGISTERED
;
2124 /* SYSCALLS *****************************************************************/
2128 NtUserRegisterClassExWOW(
2130 PUNICODE_STRING ClassName
,
2131 PUNICODE_STRING ClsNVersion
,
2132 PCLSMENUNAME pClassMenuName
,
2138 * Registers a new class with the window manager
2140 * lpwcx = Win32 extended window class structure
2141 * bUnicodeClass = Whether to send ANSI or unicode strings
2142 * to window procedures
2144 * Atom identifying the new class
2147 WNDCLASSEXW CapturedClassInfo
= {0};
2148 UNICODE_STRING CapturedName
= {0}, CapturedMenuName
= {0};
2149 RTL_ATOM Ret
= (RTL_ATOM
)0;
2150 PPROCESSINFO ppi
= GetW32ProcessInfo();
2152 if (Flags
& ~(CSF_ANSIPROC
))
2154 ERR("NtUserRegisterClassExWOW Bad Flags!\n");
2155 EngSetLastError(ERROR_INVALID_FLAGS
);
2159 UserEnterExclusive();
2161 TRACE("NtUserRegisterClassExWOW ClsN %wZ\n",ClassName
);
2163 if ( !(ppi
->W32PF_flags
& W32PF_CLASSESREGISTERED
))
2165 UserRegisterSystemClasses();
2170 /* Probe the parameters and basic parameter checks */
2171 if (ProbeForReadUint(&lpwcx
->cbSize
) != sizeof(WNDCLASSEXW
))
2173 ERR("NtUserRegisterClassExWOW Wrong cbSize!\n");
2174 goto InvalidParameter
;
2178 sizeof(WNDCLASSEXW
),
2180 RtlCopyMemory(&CapturedClassInfo
,
2182 sizeof(WNDCLASSEXW
));
2184 CapturedName
= ProbeForReadUnicodeString(ClassName
);
2186 ProbeForRead(pClassMenuName
,
2187 sizeof(CLSMENUNAME
),
2190 CapturedMenuName
= ProbeForReadUnicodeString(pClassMenuName
->pusMenuName
);
2192 if ( (CapturedName
.Length
& 1) ||
2193 (CapturedMenuName
.Length
& 1) ||
2194 (CapturedClassInfo
.cbClsExtra
< 0) ||
2195 ((CapturedClassInfo
.cbClsExtra
+ CapturedName
.Length
+
2196 CapturedMenuName
.Length
+ sizeof(CLS
))
2197 < (ULONG
)CapturedClassInfo
.cbClsExtra
) ||
2198 (CapturedClassInfo
.cbWndExtra
< 0) ||
2199 (CapturedClassInfo
.hInstance
== NULL
) )
2201 ERR("NtUserRegisterClassExWOW Invalid Parameter Error!\n");
2202 goto InvalidParameter
;
2205 if (CapturedName
.Length
!= 0)
2207 ProbeForRead(CapturedName
.Buffer
,
2208 CapturedName
.Length
,
2213 if (!IS_ATOM(CapturedName
.Buffer
))
2215 ERR("NtUserRegisterClassExWOW ClassName Error!\n");
2216 goto InvalidParameter
;
2220 if (CapturedMenuName
.Length
!= 0)
2222 ProbeForRead(CapturedMenuName
.Buffer
,
2223 CapturedMenuName
.Length
,
2226 else if (CapturedMenuName
.Buffer
!= NULL
&&
2227 !IS_INTRESOURCE(CapturedMenuName
.Buffer
))
2229 ERR("NtUserRegisterClassExWOW MenuName Error!\n");
2231 EngSetLastError(ERROR_INVALID_PARAMETER
);
2235 if (IsCallProcHandle(lpwcx
->lpfnWndProc
))
2236 { // Never seen this yet, but I'm sure it's a little haxxy trick!
2237 // If this pops up we know what todo!
2238 ERR("NtUserRegisterClassExWOW WndProc is CallProc!!\n");
2241 TRACE("NtUserRegisterClassExWOW MnuN %wZ\n",&CapturedMenuName
);
2243 /* Register the class */
2244 Ret
= UserRegisterClass(&CapturedClassInfo
,
2250 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2252 ERR("NtUserRegisterClassExWOW Exception Error!\n");
2253 SetLastNtError(_SEH2_GetExceptionCode());
2259 ERR("NtUserRegisterClassExWOW Null Return!\n");
2268 NtUserSetClassLong(HWND hWnd
,
2270 ULONG_PTR dwNewLong
,
2277 UserEnterExclusive();
2279 pi
= GetW32ProcessInfo();
2281 Window
= UserGetWindowObject(hWnd
);
2284 if (Window
->head
.pti
->ppi
!= pi
)
2286 EngSetLastError(ERROR_ACCESS_DENIED
);
2292 UNICODE_STRING Value
;
2294 /* Probe the parameters */
2295 if (Offset
== GCW_ATOM
|| Offset
== GCLP_MENUNAME
)
2297 Value
= ProbeForReadUnicodeString((PUNICODE_STRING
)dwNewLong
);
2298 if (Value
.Length
& 1)
2300 goto InvalidParameter
;
2303 if (Value
.Length
!= 0)
2305 ProbeForRead(Value
.Buffer
,
2311 if (Offset
== GCW_ATOM
&& !IS_ATOM(Value
.Buffer
))
2313 goto InvalidParameter
;
2315 else if (Offset
== GCLP_MENUNAME
&& !IS_INTRESOURCE(Value
.Buffer
))
2318 EngSetLastError(ERROR_INVALID_PARAMETER
);
2323 dwNewLong
= (ULONG_PTR
)&Value
;
2326 Ret
= UserSetClassLongPtr(Window
->pcls
,
2331 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2333 SetLastNtError(_SEH2_GetExceptionCode());
2352 * NOTE: Obsoleted in 32-bit windows
2359 NtUserUnregisterClass(
2360 IN PUNICODE_STRING ClassNameOrAtom
,
2361 IN HINSTANCE hInstance
,
2362 OUT PCLSMENUNAME pClassMenuName
)
2364 UNICODE_STRING SafeClassName
;
2368 Status
= ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName
, ClassNameOrAtom
);
2369 if (!NT_SUCCESS(Status
))
2371 ERR("Error capturing the class name\n");
2372 SetLastNtError(Status
);
2376 UserEnterExclusive();
2378 /* Unregister the class */
2379 Ret
= UserUnregisterClass(&SafeClassName
, hInstance
, NULL
); // Null for now~
2383 if (SafeClassName
.Buffer
&& !IS_ATOM(SafeClassName
.Buffer
))
2384 ExFreePoolWithTag(SafeClassName
.Buffer
, TAG_STRING
);
2390 /* NOTE: For system classes hInstance is not NULL here, but User32Instance */
2394 HINSTANCE hInstance
,
2395 PUNICODE_STRING ClassName
,
2396 LPWNDCLASSEXW lpWndClassEx
,
2397 LPWSTR
*ppszMenuName
,
2400 UNICODE_STRING SafeClassName
;
2401 WNDCLASSEXW Safewcexw
;
2403 RTL_ATOM ClassAtom
= 0;
2410 ProbeForWrite( lpWndClassEx
, sizeof(WNDCLASSEXW
), sizeof(ULONG
));
2411 RtlCopyMemory( &Safewcexw
, lpWndClassEx
, sizeof(WNDCLASSEXW
));
2413 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2415 SetLastNtError(_SEH2_GetExceptionCode());
2416 _SEH2_YIELD(return FALSE
);
2420 Status
= ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName
, ClassName
);
2421 if (!NT_SUCCESS(Status
))
2423 ERR("Error capturing the class name\n");
2424 SetLastNtError(Status
);
2428 // If null instance use client.
2429 if (!hInstance
) hInstance
= hModClient
;
2431 TRACE("GetClassInfo(%wZ, %p)\n", SafeClassName
, hInstance
);
2433 /* NOTE: Need exclusive lock because getting the wndproc might require the
2434 creation of a call procedure handle */
2435 UserEnterExclusive();
2437 ppi
= GetW32ProcessInfo();
2438 if (!(ppi
->W32PF_flags
& W32PF_CLASSESREGISTERED
))
2440 UserRegisterSystemClasses();
2443 ClassAtom
= IntGetClassAtom(&SafeClassName
,
2448 if (ClassAtom
!= (RTL_ATOM
)0)
2450 Ret
= UserGetClassInfo(Class
, &Safewcexw
, bAnsi
, hInstance
);
2454 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
2464 /* Emulate Function. */
2465 if (ppszMenuName
) *ppszMenuName
= (LPWSTR
)Safewcexw
.lpszMenuName
;
2467 RtlCopyMemory(lpWndClassEx
, &Safewcexw
, sizeof(WNDCLASSEXW
));
2470 /* We must return the atom of the class here instead of just TRUE. */
2471 /* Undocumented behavior! Return the class atom as a BOOL! */
2472 Ret
= (BOOL
)ClassAtom
;
2474 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2476 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
2482 if (!IS_ATOM(SafeClassName
.Buffer
))
2483 ExFreePoolWithTag(SafeClassName
.Buffer
, TAG_STRING
);
2490 NtUserGetClassName (IN HWND hWnd
,
2492 OUT PUNICODE_STRING ClassName
)
2495 UNICODE_STRING CapturedClassName
;
2501 Window
= UserGetWindowObject(hWnd
);
2504 if (Real
&& Window
->fnid
&& !(Window
->fnid
& FNID_DESTROY
))
2506 if (LookupFnIdToiCls(Window
->fnid
, &iCls
))
2508 Atom
= gpsi
->atomSysClass
[iCls
];
2514 ProbeForWriteUnicodeString(ClassName
);
2515 CapturedClassName
= *ClassName
;
2517 /* Get the class name */
2518 Ret
= UserGetClassName(Window
->pcls
,
2525 /* Update the Length field */
2526 ClassName
->Length
= CapturedClassName
.Length
;
2529 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2531 SetLastNtError(_SEH2_GetExceptionCode());
2541 /* Return Pointer to Class structure. */
2545 HINSTANCE hInstance
,
2546 PUNICODE_STRING ClassName
)
2548 UNICODE_STRING SafeClassName
;
2551 RTL_ATOM ClassAtom
= 0;
2554 Status
= ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName
, ClassName
);
2555 if (!NT_SUCCESS(Status
))
2557 ERR("Error capturing the class name\n");
2558 SetLastNtError(Status
);
2562 UserEnterExclusive();
2564 pi
= GetW32ProcessInfo();
2566 ClassAtom
= IntGetClassAtom(&SafeClassName
,
2573 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
2577 if (SafeClassName
.Buffer
&& !IS_ATOM(SafeClassName
.Buffer
))
2578 ExFreePoolWithTag(SafeClassName
.Buffer
, TAG_STRING
);
2582 // Don't forget to use DesktopPtrToUser( ? ) with return pointer in user space.