2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
4 * PURPOSE: Window classes
5 * FILE: subsystems/win32/win32k/ntuser/class.c
6 * PROGRAMER: Thomas Weidenmueller <w3seek@reactos.com>
10 DBG_DEFAULT_CHANNEL(UserClass
);
12 REGISTER_SYSCLASS DefaultServerClasses
[] =
14 { ((PWSTR
)((ULONG_PTR
)(WORD
)(0x8001))),
15 CS_GLOBALCLASS
|CS_DBLCLKS
,
16 NULL
, // Use User32 procs
19 (HBRUSH
)(COLOR_BACKGROUND
),
23 { ((PWSTR
)((ULONG_PTR
)(WORD
)(0x8003))),
24 CS_VREDRAW
|CS_HREDRAW
|CS_SAVEBITS
,
25 NULL
, // Use User32 procs
32 { ((PWSTR
)((ULONG_PTR
)(WORD
)(0x8000))),
33 CS_DBLCLKS
|CS_SAVEBITS
,
34 NULL
, // Use User32 procs
37 (HBRUSH
)(COLOR_MENU
+ 1),
42 CS_DBLCLKS
|CS_VREDRAW
|CS_HREDRAW
|CS_PARENTDC
,
43 NULL
, // Use User32 procs
44 sizeof(SBWND
)-sizeof(WND
),
51 { ((PWSTR
)((ULONG_PTR
)(WORD
)(0x8006))), // Tooltips
52 CS_PARENTDC
|CS_DBLCLKS
,
53 NULL
, // Use User32 procs
61 { ((PWSTR
)((ULONG_PTR
)(WORD
)(0x8004))), // IconTitle is here for now...
63 NULL
, // Use User32 procs
72 NULL
, // Use User32 procs
86 { /* Function Ids to Class indexes. */
87 { FNID_SCROLLBAR
, ICLS_SCROLLBAR
},
88 { FNID_ICONTITLE
, ICLS_ICONTITLE
},
89 { FNID_MENU
, ICLS_MENU
},
90 { FNID_DESKTOP
, ICLS_DESKTOP
},
91 { FNID_SWITCH
, ICLS_SWITCH
},
92 { FNID_MESSAGEWND
, ICLS_HWNDMESSAGE
},
93 { FNID_BUTTON
, ICLS_BUTTON
},
94 { FNID_COMBOBOX
, ICLS_COMBOBOX
},
95 { FNID_COMBOLBOX
, ICLS_COMBOLBOX
},
96 { FNID_DIALOG
, ICLS_DIALOG
},
97 { FNID_EDIT
, ICLS_EDIT
},
98 { FNID_LISTBOX
, ICLS_LISTBOX
},
99 { FNID_MDICLIENT
, ICLS_MDICLIENT
},
100 { FNID_STATIC
, ICLS_STATIC
},
101 { FNID_IME
, ICLS_IME
},
102 { FNID_GHOST
, ICLS_GHOST
},
103 { FNID_TOOLTIPS
, ICLS_TOOLTIPS
}
108 LookupFnIdToiCls(int FnId
, int *iCls
)
112 for ( i
= 0; i
< ARRAYSIZE(FnidToiCls
); i
++)
114 if (FnidToiCls
[i
].FnId
== FnId
)
116 if (iCls
) *iCls
= FnidToiCls
[i
].ClsId
;
124 _Must_inspect_result_
127 ProbeAndCaptureUnicodeStringOrAtom(
128 _Out_
_When_(return>=0, _At_(pustrOut
->Buffer
, _Post_ _Notnull_
)) PUNICODE_STRING pustrOut
,
129 __in_data_source(USER_MODE
) _In_ PUNICODE_STRING pustrUnsafe
)
131 NTSTATUS Status
= STATUS_SUCCESS
;
133 /* Default to NULL */
134 pustrOut
->Buffer
= NULL
;
138 ProbeForRead(pustrUnsafe
, sizeof(UNICODE_STRING
), 1);
140 /* Validate the string */
141 if ((pustrUnsafe
->Length
& 1) || (pustrUnsafe
->Buffer
== NULL
))
143 /* This is not legal */
144 _SEH2_YIELD(return STATUS_INVALID_PARAMETER
);
147 /* Check if this is an atom */
148 if (IS_ATOM(pustrUnsafe
->Buffer
))
150 /* Copy the atom, length is 0 */
151 pustrOut
->MaximumLength
= pustrOut
->Length
= 0;
152 pustrOut
->Buffer
= pustrUnsafe
->Buffer
;
156 /* Get the length, maximum length includes zero termination */
157 pustrOut
->Length
= pustrUnsafe
->Length
;
158 pustrOut
->MaximumLength
= pustrOut
->Length
+ sizeof(WCHAR
);
160 /* Allocate a buffer */
161 pustrOut
->Buffer
= ExAllocatePoolWithTag(PagedPool
,
162 pustrOut
->MaximumLength
,
164 if (!pustrOut
->Buffer
)
166 _SEH2_YIELD(return STATUS_INSUFFICIENT_RESOURCES
);
169 /* Copy the string and zero terminate it */
170 ProbeForRead(pustrUnsafe
->Buffer
, pustrOut
->Length
, 1);
171 RtlCopyMemory(pustrOut
->Buffer
, pustrUnsafe
->Buffer
, pustrOut
->Length
);
172 pustrOut
->Buffer
[pustrOut
->Length
/ sizeof(WCHAR
)] = L
'\0';
175 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
177 /* Check if we already allocated a buffer */
178 if (pustrOut
->Buffer
)
180 /* Free the buffer */
181 ExFreePoolWithTag(pustrOut
->Buffer
, TAG_STRING
);
182 Status
= _SEH2_GetExceptionCode();
190 /* WINDOWCLASS ***************************************************************/
193 IntFreeClassMenuName(IN OUT PCLS Class
)
195 /* Free the menu name, if it was changed and allocated */
196 if (Class
->lpszClientUnicodeMenuName
!= NULL
&& Class
->MenuNameIsString
)
198 UserHeapFree(Class
->lpszClientUnicodeMenuName
);
199 Class
->lpszClientUnicodeMenuName
= NULL
;
200 Class
->lpszClientAnsiMenuName
= NULL
;
205 IntDestroyClass(IN OUT PCLS Class
)
209 /* There shouldn't be any clones anymore */
210 ASSERT(Class
->cWndReferenceCount
== 0);
211 ASSERT(Class
->pclsClone
== NULL
);
213 if (Class
->pclsBase
== Class
)
215 PCALLPROCDATA CallProc
, NextCallProc
;
217 /* Destroy allocated callproc handles */
218 CallProc
= Class
->spcpdFirst
;
219 while (CallProc
!= NULL
)
221 NextCallProc
= CallProc
->spcpdNext
;
223 CallProc
->spcpdNext
= NULL
;
224 DestroyCallProc(NULL
,
227 CallProc
= NextCallProc
;
232 DceFreeClassDCE(((PDCE
)Class
->pdce
)->hDC
);
236 IntFreeClassMenuName(Class
);
239 pDesk
= Class
->rpdeskParent
;
240 Class
->rpdeskParent
= NULL
;
242 /* Free the structure */
245 DesktopHeapFree(pDesk
, Class
);
254 /* Clean all process classes. all process windows must cleaned first!! */
255 void FASTCALL
DestroyProcessClasses(PPROCESSINFO Process
)
258 PPROCESSINFO pi
= (PPROCESSINFO
)Process
;
262 /* Free all local classes */
263 Class
= pi
->pclsPrivateList
;
264 while (Class
!= NULL
)
266 pi
->pclsPrivateList
= Class
->pclsNext
;
268 ASSERT(Class
->pclsBase
== Class
);
269 IntDestroyClass(Class
);
271 Class
= pi
->pclsPrivateList
;
274 /* Free all global classes */
275 Class
= pi
->pclsPublicList
;
276 while (Class
!= NULL
)
278 pi
->pclsPublicList
= Class
->pclsNext
;
280 ASSERT(Class
->pclsBase
== Class
);
281 IntDestroyClass(Class
);
283 Class
= pi
->pclsPublicList
;
289 IntRegisterClassAtom(IN PUNICODE_STRING ClassName
,
296 if (ClassName
->Length
!= 0)
298 /* FIXME: Don't limit to 64 characters! Use SEH when allocating memory! */
299 if (ClassName
->Length
/ sizeof(WCHAR
) >= sizeof(szBuf
) / sizeof(szBuf
[0]))
301 EngSetLastError(ERROR_INVALID_PARAMETER
);
308 szBuf
[ClassName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
312 AtomName
= ClassName
->Buffer
;
314 Status
= RtlAddAtomToAtomTable(gAtomTable
,
318 if (!NT_SUCCESS(Status
))
320 SetLastNtError(Status
);
328 IntDeregisterClassAtom(IN RTL_ATOM Atom
)
330 return RtlDeleteAtomFromAtomTable(gAtomTable
,
335 UserAddCallProcToClass(IN OUT PCLS Class
,
336 IN PCALLPROCDATA CallProc
)
340 ASSERT(CallProc
->spcpdNext
== NULL
);
342 BaseClass
= Class
->pclsBase
;
343 ASSERT(CallProc
->spcpdNext
== NULL
);
344 CallProc
->spcpdNext
= BaseClass
->spcpdFirst
;
345 BaseClass
->spcpdFirst
= CallProc
;
347 /* Update all clones */
348 Class
= Class
->pclsClone
;
349 while (Class
!= NULL
)
351 Class
->spcpdFirst
= BaseClass
->spcpdFirst
;
352 Class
= Class
->pclsNext
;
357 IntSetClassAtom(IN OUT PCLS Class
,
358 IN PUNICODE_STRING ClassName
)
360 RTL_ATOM Atom
= (RTL_ATOM
)0;
362 /* Update the base class first */
363 Class
= Class
->pclsBase
;
365 if (!IntRegisterClassAtom(ClassName
,
371 IntDeregisterClassAtom(Class
->atomClassName
);
373 Class
->atomClassName
= Atom
;
375 /* Update the clones */
376 Class
= Class
->pclsClone
;
377 while (Class
!= NULL
)
379 Class
->atomClassName
= Atom
;
381 Class
= Class
->pclsNext
;
388 // Same as User32:IntGetClsWndProc.
391 IntGetClassWndProc(PCLS Class
, BOOL Ansi
)
394 WNDPROC gcpd
= NULL
, Ret
= NULL
;
396 if (Class
->CSF_flags
& CSF_SERVERSIDEPROC
)
398 for ( i
= FNID_FIRST
; i
<= FNID_SWITCH
; i
++)
400 if (GETPFNSERVER(i
) == Class
->lpfnWndProc
)
403 Ret
= GETPFNCLIENTA(i
);
405 Ret
= GETPFNCLIENTW(i
);
410 Ret
= Class
->lpfnWndProc
;
412 if (Class
->fnid
<= FNID_GHOST
&& Class
->fnid
>= FNID_BUTTON
)
416 if (GETPFNCLIENTW(Class
->fnid
) == Class
->lpfnWndProc
)
417 Ret
= GETPFNCLIENTA(Class
->fnid
);
421 if (GETPFNCLIENTA(Class
->fnid
) == Class
->lpfnWndProc
)
422 Ret
= GETPFNCLIENTW(Class
->fnid
);
426 if ( Ret
!= Class
->lpfnWndProc
||
427 Ansi
== !!(Class
->CSF_flags
& CSF_ANSIPROC
) )
430 gcpd
= (WNDPROC
)UserGetCPD( Class
,
431 (Ansi
? UserGetCPDA2U
: UserGetCPDU2A
)|UserGetCPDClass
,
434 return (gcpd
? gcpd
: Ret
);
440 IntSetClassWndProc(IN OUT PCLS Class
,
446 WNDPROC Ret
, chWndProc
;
448 Ret
= IntGetClassWndProc(Class
, Ansi
);
450 // If Server Side, downgrade to Client Side.
451 if (Class
->CSF_flags
& CSF_SERVERSIDEPROC
)
453 if (Ansi
) Class
->CSF_flags
|= CSF_ANSIPROC
;
454 Class
->CSF_flags
&= ~CSF_SERVERSIDEPROC
;
455 Class
->Unicode
= !Ansi
;
458 if (!WndProc
) WndProc
= Class
->lpfnWndProc
;
462 // Check if CallProc handle and retrieve previous call proc address and set.
463 if (IsCallProcHandle(WndProc
))
465 pcpd
= UserGetObject(gHandleTable
, WndProc
, TYPE_CALLPROC
);
466 if (pcpd
) chWndProc
= pcpd
->pfnClientPrevious
;
469 Class
->lpfnWndProc
= chWndProc
;
474 // Switch from Client Side call to Server Side call if match. Ref: "deftest".
475 for ( i
= FNID_FIRST
; i
<= FNID_SWITCH
; i
++)
477 if (GETPFNCLIENTW(i
) == Class
->lpfnWndProc
)
479 chWndProc
= GETPFNSERVER(i
);
482 if (GETPFNCLIENTA(i
) == Class
->lpfnWndProc
)
484 chWndProc
= GETPFNSERVER(i
);
488 // If match, set/reset to Server Side and clear ansi.
491 Class
->lpfnWndProc
= chWndProc
;
492 Class
->Unicode
= TRUE
;
493 Class
->CSF_flags
&= ~CSF_ANSIPROC
;
494 Class
->CSF_flags
|= CSF_SERVERSIDEPROC
;
498 Class
->Unicode
= !Ansi
;
501 Class
->CSF_flags
|= CSF_ANSIPROC
;
503 Class
->CSF_flags
&= ~CSF_ANSIPROC
;
506 /* Update the clones */
507 chWndProc
= Class
->lpfnWndProc
;
509 Class
= Class
->pclsClone
;
510 while (Class
!= NULL
)
512 Class
->Unicode
= !Ansi
;
513 Class
->lpfnWndProc
= chWndProc
;
515 Class
= Class
->pclsNext
;
522 IntGetClassForDesktop(IN OUT PCLS BaseClass
,
523 IN OUT PCLS
*ClassLink
,
529 ASSERT(Desktop
!= NULL
);
530 ASSERT(BaseClass
->pclsBase
== BaseClass
);
532 if (BaseClass
->rpdeskParent
== Desktop
)
534 /* It is most likely that a window is created on the same
535 desktop as the window class. */
540 if (BaseClass
->rpdeskParent
== NULL
)
542 ASSERT(BaseClass
->cWndReferenceCount
== 0);
543 ASSERT(BaseClass
->pclsClone
== NULL
);
545 /* Classes are also located in the shared heap when the class
546 was created before the thread attached to a desktop. As soon
547 as a window is created for such a class located on the shared
548 heap, the class is cloned into the desktop heap on which the
549 window is created. */
554 /* The user is asking for a class object on a different desktop,
556 Class
= BaseClass
->pclsClone
;
557 while (Class
!= NULL
)
559 if (Class
->rpdeskParent
== Desktop
)
561 ASSERT(Class
->pclsBase
== BaseClass
);
562 ASSERT(Class
->pclsClone
== NULL
);
566 Class
= Class
->pclsNext
;
572 /* The window is created on a different desktop, we need to
573 clone the class object to the desktop heap of the window! */
574 ClassSize
= sizeof(*BaseClass
) + (SIZE_T
)BaseClass
->cbclsExtra
;
576 Class
= DesktopHeapAlloc(Desktop
,
580 /* Simply clone the class */
581 RtlCopyMemory( Class
, BaseClass
, ClassSize
);
583 TRACE("Clone Class 0x%p hM 0x%p\n %S\n",Class
, Class
->hModule
, Class
->lpszClientUnicodeMenuName
);
585 /* Restore module address if default user class Ref: Bug 4778 */
586 if ( Class
->hModule
!= hModClient
&&
587 Class
->fnid
<= FNID_GHOST
&&
588 Class
->fnid
>= FNID_BUTTON
)
590 Class
->hModule
= hModClient
;
591 TRACE("Clone Class 0x%p Reset hM 0x%p\n",Class
, Class
->hModule
);
594 /* Update some pointers and link the class */
595 Class
->rpdeskParent
= Desktop
;
596 Class
->cWndReferenceCount
= 0;
598 if (BaseClass
->rpdeskParent
== NULL
)
600 /* We don't really need the base class on the shared
601 heap anymore, delete it so the only class left is
602 the clone we just created, which now serves as the
604 ASSERT(BaseClass
->pclsClone
== NULL
);
605 ASSERT(Class
->pclsClone
== NULL
);
606 Class
->pclsBase
= Class
;
607 Class
->pclsNext
= BaseClass
->pclsNext
;
609 /* Replace the base class */
610 (void)InterlockedExchangePointer((PVOID
*)ClassLink
,
613 /* Destroy the obsolete copy on the shared heap */
614 BaseClass
->pclsBase
= NULL
;
615 BaseClass
->pclsClone
= NULL
;
616 IntDestroyClass(BaseClass
);
620 /* Link in the clone */
621 Class
->pclsClone
= NULL
;
622 Class
->pclsBase
= BaseClass
;
623 Class
->pclsNext
= BaseClass
->pclsClone
;
624 (void)InterlockedExchangePointer((PVOID
*)&BaseClass
->pclsClone
,
630 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
637 IntReferenceClass(IN OUT PCLS BaseClass
,
638 IN OUT PCLS
*ClassLink
,
642 ASSERT(BaseClass
->pclsBase
== BaseClass
);
646 Class
= IntGetClassForDesktop(BaseClass
,
657 Class
->cWndReferenceCount
++;
665 IntMakeCloneBaseClass(IN OUT PCLS Class
,
666 IN OUT PCLS
*BaseClassLink
,
667 IN OUT PCLS
*CloneLink
)
671 ASSERT(Class
->pclsBase
!= Class
);
672 ASSERT(Class
->pclsBase
->pclsClone
!= NULL
);
673 ASSERT(Class
->rpdeskParent
!= NULL
);
674 ASSERT(Class
->cWndReferenceCount
!= 0);
675 ASSERT(Class
->pclsBase
->rpdeskParent
!= NULL
);
676 ASSERT(Class
->pclsBase
->cWndReferenceCount
== 0);
678 /* Unlink the clone */
679 *CloneLink
= Class
->pclsNext
;
680 Class
->pclsClone
= Class
->pclsBase
->pclsClone
;
682 /* Update the class information to make it a base class */
683 Class
->pclsBase
= Class
;
684 Class
->pclsNext
= (*BaseClassLink
)->pclsNext
;
686 /* Update all clones */
687 Clone
= Class
->pclsClone
;
688 while (Clone
!= NULL
)
690 ASSERT(Clone
->pclsClone
== NULL
);
691 Clone
->pclsBase
= Class
;
693 Clone
= Clone
->pclsNext
;
696 /* Link in the new base class */
697 (void)InterlockedExchangePointer((PVOID
*)BaseClassLink
,
703 IntDereferenceClass(IN OUT PCLS Class
,
704 IN PDESKTOPINFO Desktop
,
707 PCLS
*PrevLink
, BaseClass
, CurrentClass
;
709 ASSERT(Class
->cWndReferenceCount
>= 1);
711 BaseClass
= Class
->pclsBase
;
713 if (--Class
->cWndReferenceCount
== 0)
715 if (BaseClass
== Class
)
717 ASSERT(Class
->pclsBase
== Class
);
719 TRACE("IntDereferenceClass 0x%p\n", Class
);
720 /* Check if there are clones of the class on other desktops,
721 link the first clone in if possible. If there are no clones
722 then leave the class on the desktop heap. It will get moved
723 to the shared heap when the thread detaches. */
724 if (BaseClass
->pclsClone
!= NULL
)
726 if (BaseClass
->Global
)
727 PrevLink
= &pi
->pclsPublicList
;
729 PrevLink
= &pi
->pclsPrivateList
;
731 CurrentClass
= *PrevLink
;
732 while (CurrentClass
!= BaseClass
)
734 ASSERT(CurrentClass
!= NULL
);
736 PrevLink
= &CurrentClass
->pclsNext
;
737 CurrentClass
= CurrentClass
->pclsNext
;
740 ASSERT(*PrevLink
== BaseClass
);
742 /* Make the first clone become the new base class */
743 IntMakeCloneBaseClass(BaseClass
->pclsClone
,
745 &BaseClass
->pclsClone
);
747 /* Destroy the class, there's still another clone of the class
748 that now serves as a base class. Make sure we don't destruct
749 resources shared by all classes (Base = NULL)! */
750 BaseClass
->pclsBase
= NULL
;
751 BaseClass
->pclsClone
= NULL
;
752 IntDestroyClass(BaseClass
);
757 TRACE("IntDereferenceClass1 0x%p\n", Class
);
759 /* Locate the cloned class and unlink it */
760 PrevLink
= &BaseClass
->pclsClone
;
761 CurrentClass
= BaseClass
->pclsClone
;
762 while (CurrentClass
!= Class
)
764 ASSERT(CurrentClass
!= NULL
);
766 PrevLink
= &CurrentClass
->pclsNext
;
767 CurrentClass
= CurrentClass
->pclsNext
;
770 ASSERT(CurrentClass
== Class
);
772 (void)InterlockedExchangePointer((PVOID
*)PrevLink
,
775 ASSERT(Class
->pclsBase
== BaseClass
);
776 ASSERT(Class
->pclsClone
== NULL
);
778 /* The class was just a clone, we don't need it anymore */
779 IntDestroyClass(Class
);
785 IntMoveClassToSharedHeap(IN OUT PCLS Class
,
786 IN OUT PCLS
**ClassLinkPtr
)
791 ASSERT(Class
->pclsBase
== Class
);
792 ASSERT(Class
->rpdeskParent
!= NULL
);
793 ASSERT(Class
->cWndReferenceCount
== 0);
794 ASSERT(Class
->pclsClone
== NULL
);
796 ClassSize
= sizeof(*Class
) + (SIZE_T
)Class
->cbclsExtra
;
798 /* Allocate the new base class on the shared heap */
799 NewClass
= UserHeapAlloc(ClassSize
);
800 if (NewClass
!= NULL
)
802 RtlCopyMemory(NewClass
,
806 NewClass
->rpdeskParent
= NULL
;
807 NewClass
->pclsBase
= NewClass
;
809 /* Replace the class in the list */
810 (void)InterlockedExchangePointer((PVOID
*)*ClassLinkPtr
,
812 *ClassLinkPtr
= &NewClass
->pclsNext
;
814 /* Free the obsolete class on the desktop heap */
815 Class
->pclsBase
= NULL
;
816 IntDestroyClass(Class
);
824 IntCheckDesktopClasses(IN PDESKTOP Desktop
,
825 IN OUT PCLS
*ClassList
,
826 IN BOOL FreeOnFailure
,
829 PCLS Class
, NextClass
, *Link
;
831 /* NOTE: We only need to check base classes! When classes are no longer needed
832 on a desktop, the clones will be freed automatically as soon as possible.
833 However, we need to move base classes to the shared heap, as soon as
834 the last desktop heap where a class is allocated on is about to be destroyed.
835 If we didn't move the class to the shared heap, the class would become
838 ASSERT(Desktop
!= NULL
);
842 while (Class
!= NULL
)
844 NextClass
= Class
->pclsNext
;
846 ASSERT(Class
->pclsBase
== Class
);
848 if (Class
->rpdeskParent
== Desktop
&&
849 Class
->cWndReferenceCount
== 0)
851 /* There shouldn't be any clones around anymore! */
852 ASSERT(Class
->pclsClone
== NULL
);
854 /* FIXME: If process is terminating, don't move the class but rather destroy it! */
855 /* FIXME: We could move the class to another desktop heap if there's still desktops
856 mapped into the process... */
858 /* Move the class to the shared heap */
859 if (IntMoveClassToSharedHeap(Class
,
862 ASSERT(*Link
== NextClass
);
866 ASSERT(NextClass
== Class
->pclsNext
);
870 /* Unlink the base class */
871 (void)InterlockedExchangePointer((PVOID
*)Link
,
874 /* We can free the old base class now */
875 Class
->pclsBase
= NULL
;
876 IntDestroyClass(Class
);
880 Link
= &Class
->pclsNext
;
886 Link
= &Class
->pclsNext
;
893 IntCheckProcessDesktopClasses(IN PDESKTOP Desktop
,
894 IN BOOL FreeOnFailure
)
899 pi
= GetW32ProcessInfo();
901 /* Check all local classes */
902 IntCheckDesktopClasses(Desktop
,
903 &pi
->pclsPrivateList
,
907 /* Check all global classes */
908 IntCheckDesktopClasses(Desktop
,
914 ERR("Failed to move process classes from desktop 0x%p to the shared heap!\n", Desktop
);
915 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
923 IntCreateClass(IN CONST WNDCLASSEXW
* lpwcx
,
924 IN PUNICODE_STRING ClassName
,
925 IN PUNICODE_STRING MenuName
,
935 PWSTR pszMenuName
= NULL
;
936 NTSTATUS Status
= STATUS_SUCCESS
;
938 TRACE("lpwcx=%p ClassName=%wZ MenuName=%wZ dwFlags=%08x Desktop=%p pi=%p\n",
939 lpwcx
, ClassName
, MenuName
, dwFlags
, Desktop
, pi
);
941 if (!IntRegisterClassAtom(ClassName
,
944 ERR("Failed to register class atom!\n");
948 ClassSize
= sizeof(*Class
) + lpwcx
->cbClsExtra
;
949 if (MenuName
->Length
!= 0)
951 pszMenuName
= UserHeapAlloc(MenuName
->Length
+ sizeof(UNICODE_NULL
) +
952 RtlUnicodeStringToAnsiSize(MenuName
));
953 if (pszMenuName
== NULL
)
959 Class
= DesktopHeapAlloc(Desktop
,
964 /* FIXME: The class was created before being connected
965 to a desktop. It is possible for the desktop window,
966 but should it be allowed for any other case? */
967 Class
= UserHeapAlloc(ClassSize
);
974 RtlZeroMemory(Class
, ClassSize
);
976 Class
->rpdeskParent
= Desktop
;
977 Class
->pclsBase
= Class
;
978 Class
->atomClassName
= Atom
;
980 Class
->CSF_flags
= dwFlags
;
982 if (LookupFnIdToiCls(Class
->fnid
, &iCls
))
984 gpsi
->atomSysClass
[iCls
] = Class
->atomClassName
;
989 PWSTR pszMenuNameBuffer
= pszMenuName
;
991 /* Need to protect with SEH since accessing the WNDCLASSEX structure
992 and string buffers might raise an exception! We don't want to
994 // What?! If the user interface was written correctly this would not be an issue!
995 Class
->lpfnWndProc
= lpwcx
->lpfnWndProc
;
996 Class
->style
= lpwcx
->style
;
997 Class
->cbclsExtra
= lpwcx
->cbClsExtra
;
998 Class
->cbwndExtra
= lpwcx
->cbWndExtra
;
999 Class
->hModule
= lpwcx
->hInstance
;
1000 Class
->hIcon
= lpwcx
->hIcon
;
1001 Class
->hIconSm
= lpwcx
->hIconSm
;
1002 //// Sure W2k3 does not do this..... wine test inconclusive.
1003 if (lpwcx
->hIcon
&& !lpwcx
->hIconSm
)
1005 Class
->hIconSmIntern
= co_IntCopyImage( lpwcx
->hIcon
, IMAGE_ICON
,
1006 UserGetSystemMetrics( SM_CXSMICON
),
1007 UserGetSystemMetrics( SM_CYSMICON
), 0 );
1008 ERR("IntCreateClass hIconSmIntern %p\n",Class
->hIconSmIntern
);
1009 Class
->CSF_flags
|= CSF_CACHEDSMICON
;
1012 Class
->hCursor
= lpwcx
->hCursor
;
1013 Class
->hbrBackground
= lpwcx
->hbrBackground
;
1015 /* Make a copy of the string */
1016 if (pszMenuNameBuffer
!= NULL
)
1018 Class
->MenuNameIsString
= TRUE
;
1020 Class
->lpszClientUnicodeMenuName
= pszMenuNameBuffer
;
1021 RtlCopyMemory(Class
->lpszClientUnicodeMenuName
,
1024 Class
->lpszClientUnicodeMenuName
[MenuName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1026 pszMenuNameBuffer
+= (MenuName
->Length
/ sizeof(WCHAR
)) + 1;
1029 Class
->lpszClientUnicodeMenuName
= MenuName
->Buffer
;
1031 /* Save an ANSI copy of the string */
1032 if (pszMenuNameBuffer
!= NULL
)
1034 ANSI_STRING AnsiString
;
1036 Class
->lpszClientAnsiMenuName
= (PSTR
)pszMenuNameBuffer
;
1037 AnsiString
.MaximumLength
= (USHORT
)RtlUnicodeStringToAnsiSize(MenuName
);
1038 AnsiString
.Buffer
= Class
->lpszClientAnsiMenuName
;
1039 Status
= RtlUnicodeStringToAnsiString(&AnsiString
,
1042 if (!NT_SUCCESS(Status
))
1044 ERR("Failed to convert unicode menu name to ansi!\n");
1046 /* Life would've been much prettier if ntoskrnl exported RtlRaiseStatus()... */
1051 Class
->lpszClientAnsiMenuName
= (PSTR
)MenuName
->Buffer
;
1053 /* Save kernel use menu name and ansi class name */
1054 Class
->lpszMenuName
= Class
->lpszClientUnicodeMenuName
; // FIXME!
1055 //Class->lpszAnsiClassName = FIXME
1057 /* Server Side overrides class calling type (A/W)!
1058 User32 whine test_builtinproc: "deftest"
1059 built-in winproc - window A/W type automatically detected */
1060 if (!(Class
->CSF_flags
& CSF_SERVERSIDEPROC
))
1064 /* Due to the wine class "deftest" and most likely no FNID to reference
1065 from, sort through the Server Side list and compare proc addresses
1066 for match. This method will be used in related code.
1068 for ( i
= FNID_FIRST
; i
<= FNID_SWITCH
; i
++)
1069 { // Open ANSI or Unicode, just match, set and break.
1070 if (GETPFNCLIENTW(i
) == Class
->lpfnWndProc
)
1072 WndProc
= GETPFNSERVER(i
);
1075 if (GETPFNCLIENTA(i
) == Class
->lpfnWndProc
)
1077 WndProc
= GETPFNSERVER(i
);
1082 { // If a hit, we are Server Side so set the right flags and proc.
1083 Class
->CSF_flags
|= CSF_SERVERSIDEPROC
;
1084 Class
->CSF_flags
&= ~CSF_ANSIPROC
;
1085 Class
->lpfnWndProc
= WndProc
;
1089 if (!(Class
->CSF_flags
& CSF_ANSIPROC
))
1090 Class
->Unicode
= TRUE
;
1092 if (Class
->style
& CS_GLOBALCLASS
)
1093 Class
->Global
= TRUE
;
1095 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1097 Status
= _SEH2_GetExceptionCode();
1101 if (!NT_SUCCESS(Status
))
1103 ERR("Failed creating the class: 0x%x\n", Status
);
1105 SetLastNtError(Status
);
1107 if (pszMenuName
!= NULL
)
1108 UserHeapFree(pszMenuName
);
1110 DesktopHeapFree(Desktop
,
1114 IntDeregisterClassAtom(Atom
);
1120 ERR("Failed to allocate class on Desktop 0x%p\n", Desktop
);
1122 if (pszMenuName
!= NULL
)
1123 UserHeapFree(pszMenuName
);
1125 IntDeregisterClassAtom(Atom
);
1127 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1130 TRACE("Created class 0x%p with name %wZ and proc 0x%p for atom 0x%x and hInstance 0x%p, global %u\n",
1131 Class
, ClassName
, Class
->lpfnWndProc
, Atom
, Class
->hModule
, Class
->Global
);
1137 IntFindClass(IN RTL_ATOM Atom
,
1138 IN HINSTANCE hInstance
,
1140 OUT PCLS
**Link OPTIONAL
)
1142 PCLS Class
, *PrevLink
= ClassList
;
1145 while (Class
!= NULL
)
1147 if (Class
->atomClassName
== Atom
&&
1148 (hInstance
== NULL
|| Class
->hModule
== hInstance
) &&
1149 !(Class
->CSF_flags
& CSF_WOWDEFERDESTROY
))
1151 ASSERT(Class
->pclsBase
== Class
);
1158 PrevLink
= &Class
->pclsNext
;
1159 Class
= Class
->pclsNext
;
1167 IntGetAtomFromStringOrAtom(
1168 _In_ PUNICODE_STRING ClassName
,
1169 _Out_ RTL_ATOM
*Atom
)
1173 if (ClassName
->Length
!= 0)
1181 /* NOTE: Caller has to protect the call with SEH! */
1183 if (ClassName
->Length
!= 0)
1185 /* FIXME: Don't limit to 64 characters! use SEH when allocating memory! */
1186 if (ClassName
->Length
/ sizeof(WCHAR
) >= sizeof(szBuf
) / sizeof(szBuf
[0]))
1188 EngSetLastError(ERROR_INVALID_PARAMETER
);
1192 /* We need to make a local copy of the class name! The caller could
1193 modify the buffer and we could overflow in RtlLookupAtomInAtomTable.
1194 We're protected by SEH, but the ranges that might be accessed were
1196 RtlCopyMemory(szBuf
,
1199 szBuf
[ClassName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1203 AtomName
= ClassName
->Buffer
;
1205 /* Lookup the atom */
1206 Status
= RtlLookupAtomInAtomTable(gAtomTable
,
1209 if (NT_SUCCESS(Status
))
1215 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
1217 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
1221 SetLastNtError(Status
);
1227 ASSERT(IS_ATOM(ClassName
->Buffer
));
1228 *Atom
= (RTL_ATOM
)((ULONG_PTR
)ClassName
->Buffer
);
1237 _In_ PUNICODE_STRING ClassName
,
1238 IN HINSTANCE hInstance OPTIONAL
,
1239 IN PPROCESSINFO pi OPTIONAL
,
1240 OUT PCLS
*BaseClass OPTIONAL
,
1241 OUT PCLS
**Link OPTIONAL
)
1243 RTL_ATOM Atom
= (RTL_ATOM
)0;
1245 ASSERT(BaseClass
!= NULL
);
1247 if (IntGetAtomFromStringOrAtom(ClassName
,
1249 Atom
!= (RTL_ATOM
)0)
1253 /* Attempt to locate the class object */
1257 /* Step 1: Try to find an exact match of locally registered classes */
1258 Class
= IntFindClass(Atom
,
1260 &pi
->pclsPrivateList
,
1263 { TRACE("Step 1: 0x%p\n",Class
);
1267 /* Step 2: Try to find any globally registered class. The hInstance
1268 is not relevant for global classes */
1269 Class
= IntFindClass(Atom
,
1271 &pi
->pclsPublicList
,
1274 { TRACE("Step 2: 0x%p 0x%p\n",Class
, Class
->hModule
);
1278 /* Step 3: Try to find any local class registered by user32 */
1279 Class
= IntFindClass(Atom
,
1281 &pi
->pclsPrivateList
,
1284 { TRACE("Step 3: 0x%p\n",Class
);
1288 /* Step 4: Try to find any global class registered by user32 */
1289 Class
= IntFindClass(Atom
,
1291 &pi
->pclsPublicList
,
1295 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
1297 }else{TRACE("Step 4: 0x%p\n",Class
);}
1307 IntGetAndReferenceClass(PUNICODE_STRING ClassName
, HINSTANCE hInstance
, BOOL bDesktopThread
)
1309 PCLS
*ClassLink
, Class
= NULL
;
1314 pti
= gptiDesktopThread
;
1316 pti
= PsGetCurrentThreadWin32Thread();
1318 if ( !(pti
->ppi
->W32PF_flags
& W32PF_CLASSESREGISTERED
))
1320 UserRegisterSystemClasses();
1323 /* Check the class. */
1325 TRACE("Finding Class %wZ for hInstance 0x%p\n", ClassName
, hInstance
);
1327 ClassAtom
= IntGetClassAtom(ClassName
,
1333 if (ClassAtom
== (RTL_ATOM
)0)
1335 if (IS_ATOM(ClassName
->Buffer
))
1337 ERR("Class 0x%p not found\n", ClassName
->Buffer
);
1341 ERR("Class \"%wZ\" not found\n", ClassName
);
1344 EngSetLastError(ERROR_CANNOT_FIND_WND_CLASS
);
1348 TRACE("Referencing Class 0x%p with atom 0x%x\n", Class
, ClassAtom
);
1349 Class
= IntReferenceClass(Class
,
1354 ERR("Failed to reference window class!\n");
1362 UserRegisterClass(IN CONST WNDCLASSEXW
* lpwcx
,
1363 IN PUNICODE_STRING ClassName
,
1364 IN PUNICODE_STRING MenuName
,
1372 RTL_ATOM Ret
= (RTL_ATOM
)0;
1374 /* NOTE: Accessing the buffers in ClassName and MenuName may raise exceptions! */
1376 pti
= GetW32ThreadInfo();
1380 // Need only to test for two conditions not four....... Fix more whine tests....
1381 if ( IntGetAtomFromStringOrAtom( ClassName
, &ClassAtom
) &&
1382 ClassAtom
!= (RTL_ATOM
)0 &&
1383 !(dwFlags
& CSF_SERVERSIDEPROC
) ) // Bypass Server Sides
1385 Class
= IntFindClass( ClassAtom
,
1387 &pi
->pclsPrivateList
,
1390 if (Class
!= NULL
&& !Class
->Global
)
1392 // Local class already exists
1393 TRACE("Local Class 0x%x does already exist!\n", ClassAtom
);
1394 EngSetLastError(ERROR_CLASS_ALREADY_EXISTS
);
1398 if (lpwcx
->style
& CS_GLOBALCLASS
)
1400 Class
= IntFindClass( ClassAtom
,
1402 &pi
->pclsPublicList
,
1405 if (Class
!= NULL
&& Class
->Global
)
1407 TRACE("Global Class 0x%x does already exist!\n", ClassAtom
);
1408 EngSetLastError(ERROR_CLASS_ALREADY_EXISTS
);
1414 Class
= IntCreateClass(lpwcx
,
1426 /* Register the class */
1428 List
= &pi
->pclsPublicList
;
1430 List
= &pi
->pclsPrivateList
;
1432 Class
->pclsNext
= *List
;
1433 (void)InterlockedExchangePointer((PVOID
*)List
,
1436 Ret
= Class
->atomClassName
;
1440 ERR("UserRegisterClass: Yes, that is right, you have no Class!\n");
1447 UserUnregisterClass(IN PUNICODE_STRING ClassName
,
1448 IN HINSTANCE hInstance
,
1449 OUT PCLSMENUNAME pClassMenuName
)
1456 pi
= GetW32ProcessInfo();
1458 TRACE("UserUnregisterClass(%wZ, 0x%p)\n", ClassName
, hInstance
);
1460 /* NOTE: Accessing the buffer in ClassName may raise an exception! */
1461 ClassAtom
= IntGetClassAtom(ClassName
,
1466 if (ClassAtom
== (RTL_ATOM
)0)
1468 TRACE("UserUnregisterClass: No Class found.\n");
1472 ASSERT(Class
!= NULL
);
1474 if (Class
->cWndReferenceCount
!= 0 ||
1475 Class
->pclsClone
!= NULL
)
1477 TRACE("UserUnregisterClass: Class has a Window. Ct %u : Clone 0x%p\n", Class
->cWndReferenceCount
, Class
->pclsClone
);
1478 EngSetLastError(ERROR_CLASS_HAS_WINDOWS
);
1482 /* Must be a base class! */
1483 ASSERT(Class
->pclsBase
== Class
);
1485 /* Unlink the class */
1486 *Link
= Class
->pclsNext
;
1488 if (NT_SUCCESS(IntDeregisterClassAtom(Class
->atomClassName
)))
1490 TRACE("Class 0x%p\n", Class
);
1491 TRACE("UserUnregisterClass: Good Exit!\n");
1492 /* Finally free the resources */
1493 IntDestroyClass(Class
);
1496 ERR("UserUnregisterClass: Can not deregister Class Atom.\n");
1501 UserGetClassName(IN PCLS Class
,
1502 IN OUT PUNICODE_STRING ClassName
,
1506 NTSTATUS Status
= STATUS_SUCCESS
;
1507 WCHAR szStaticTemp
[32];
1508 PWSTR szTemp
= NULL
;
1509 ULONG BufLen
= sizeof(szStaticTemp
);
1512 /* Note: Accessing the buffer in ClassName may raise an exception! */
1518 PANSI_STRING AnsiClassName
= (PANSI_STRING
)ClassName
;
1519 UNICODE_STRING UnicodeClassName
;
1521 /* Limit the size of the static buffer on the stack to the
1522 size of the buffer provided by the caller */
1523 if (BufLen
/ sizeof(WCHAR
) > AnsiClassName
->MaximumLength
)
1525 BufLen
= AnsiClassName
->MaximumLength
* sizeof(WCHAR
);
1528 /* Find out how big the buffer needs to be */
1529 Status
= RtlQueryAtomInAtomTable(gAtomTable
,
1530 Class
->atomClassName
,
1535 if (Status
== STATUS_BUFFER_TOO_SMALL
)
1537 if (BufLen
/ sizeof(WCHAR
) > AnsiClassName
->MaximumLength
)
1539 /* The buffer required exceeds the ansi buffer provided,
1540 pretend like we're using the ansi buffer and limit the
1541 size to the buffer size provided */
1542 BufLen
= AnsiClassName
->MaximumLength
* sizeof(WCHAR
);
1545 /* Allocate a temporary buffer that can hold the unicode class name */
1546 szTemp
= ExAllocatePoolWithTag(PagedPool
,
1551 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1555 /* Query the class name */
1556 Status
= RtlQueryAtomInAtomTable(gAtomTable
,
1557 Atom
? Atom
: Class
->atomClassName
,
1564 szTemp
= szStaticTemp
;
1566 if (NT_SUCCESS(Status
))
1568 /* Convert the atom name to ansi */
1570 RtlInitUnicodeString(&UnicodeClassName
,
1573 Status
= RtlUnicodeStringToAnsiString(AnsiClassName
,
1576 if (!NT_SUCCESS(Status
))
1578 SetLastNtError(Status
);
1583 Ret
= BufLen
/ sizeof(WCHAR
);
1587 BufLen
= ClassName
->MaximumLength
;
1589 /* Query the atom name */
1590 Status
= RtlQueryAtomInAtomTable(gAtomTable
,
1591 Atom
? Atom
: Class
->atomClassName
,
1597 if (!NT_SUCCESS(Status
))
1599 SetLastNtError(Status
);
1603 Ret
= BufLen
/ sizeof(WCHAR
);
1606 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1608 SetLastNtError(_SEH2_GetExceptionCode());
1612 if (Ansi
&& szTemp
!= NULL
&& szTemp
!= szStaticTemp
)
1614 ExFreePoolWithTag(szTemp
, USERTAG_CLASS
);
1621 IntSetClassMenuName(IN PCLS Class
,
1622 IN PUNICODE_STRING MenuName
)
1626 /* Change the base class first */
1627 Class
= Class
->pclsBase
;
1629 if (MenuName
->Length
!= 0)
1631 ANSI_STRING AnsiString
;
1634 AnsiString
.MaximumLength
= (USHORT
)RtlUnicodeStringToAnsiSize(MenuName
);
1636 strBufW
= UserHeapAlloc(MenuName
->Length
+ sizeof(UNICODE_NULL
) +
1637 AnsiString
.MaximumLength
);
1638 if (strBufW
!= NULL
)
1644 /* Copy the unicode string */
1645 RtlCopyMemory(strBufW
,
1648 strBufW
[MenuName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
1650 /* Create an ANSI copy of the string */
1651 AnsiString
.Buffer
= (PSTR
)(strBufW
+ (MenuName
->Length
/ sizeof(WCHAR
)) + 1);
1652 Status
= RtlUnicodeStringToAnsiString(&AnsiString
,
1655 if (!NT_SUCCESS(Status
))
1657 SetLastNtError(Status
);
1663 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1665 SetLastNtError(_SEH2_GetExceptionCode());
1671 /* Update the base class */
1672 IntFreeClassMenuName(Class
);
1673 Class
->lpszClientUnicodeMenuName
= strBufW
;
1674 Class
->lpszClientAnsiMenuName
= AnsiString
.Buffer
;
1675 Class
->MenuNameIsString
= TRUE
;
1677 /* Update the clones */
1678 Class
= Class
->pclsClone
;
1679 while (Class
!= NULL
)
1681 Class
->lpszClientUnicodeMenuName
= strBufW
;
1682 Class
->lpszClientAnsiMenuName
= AnsiString
.Buffer
;
1683 Class
->MenuNameIsString
= TRUE
;
1685 Class
= Class
->pclsNext
;
1690 ERR("Failed to copy class menu name!\n");
1691 UserHeapFree(strBufW
);
1695 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1699 ASSERT(IS_INTRESOURCE(MenuName
->Buffer
));
1701 /* Update the base class */
1702 IntFreeClassMenuName(Class
);
1703 Class
->lpszClientUnicodeMenuName
= MenuName
->Buffer
;
1704 Class
->lpszClientAnsiMenuName
= (PSTR
)MenuName
->Buffer
;
1705 Class
->MenuNameIsString
= FALSE
;
1707 /* Update the clones */
1708 Class
= Class
->pclsClone
;
1709 while (Class
!= NULL
)
1711 Class
->lpszClientUnicodeMenuName
= MenuName
->Buffer
;
1712 Class
->lpszClientAnsiMenuName
= (PSTR
)MenuName
->Buffer
;
1713 Class
->MenuNameIsString
= FALSE
;
1715 Class
= Class
->pclsNext
;
1724 //// Do this for now in anticipation of new cursor icon code.
1725 BOOLEAN FASTCALL
IntDestroyCurIconObject(PCURICON_OBJECT
, PPROCESSINFO
);
1728 IntClassDestroyIcon(HANDLE hCurIcon
)
1730 PCURICON_OBJECT CurIcon
;
1733 if (!(CurIcon
= UserGetCurIconObject(hCurIcon
)))
1736 ERR("hCurIcon was not found!\n");
1739 Ret
= IntDestroyCurIconObject(CurIcon
, PsGetCurrentProcessWin32Process());
1740 /* Note: IntDestroyCurIconObject will remove our reference for us! */
1743 ERR("hCurIcon was not Destroyed!\n");
1749 UserSetClassLongPtr(IN PCLS Class
,
1751 IN ULONG_PTR NewLong
,
1755 HANDLE hIconSmIntern
= NULL
;
1757 /* NOTE: For GCLP_MENUNAME and GCW_ATOM this function may raise an exception! */
1759 /* Change the information in the base class first, then update the clones */
1760 Class
= Class
->pclsBase
;
1766 TRACE("SetClassLong(%d, %x)\n", Index
, NewLong
);
1768 if (((ULONG
)Index
+ sizeof(ULONG_PTR
)) < (ULONG
)Index
||
1769 ((ULONG
)Index
+ sizeof(ULONG_PTR
)) > (ULONG
)Class
->cbclsExtra
)
1771 EngSetLastError(ERROR_INVALID_PARAMETER
);
1775 Data
= (PULONG_PTR
)((ULONG_PTR
)(Class
+ 1) + Index
);
1777 /* FIXME: Data might be a unaligned pointer! Might be a problem on
1778 certain architectures, maybe using RtlCopyMemory is a
1779 better choice for those architectures! */
1783 /* Update the clones */
1784 Class
= Class
->pclsClone
;
1785 while (Class
!= NULL
)
1787 *(PULONG_PTR
)((ULONG_PTR
)(Class
+ 1) + Index
) = NewLong
;
1788 Class
= Class
->pclsNext
;
1796 case GCL_CBWNDEXTRA
:
1797 Ret
= (ULONG_PTR
)Class
->cbwndExtra
;
1798 Class
->cbwndExtra
= (INT
)NewLong
;
1800 /* Update the clones */
1801 Class
= Class
->pclsClone
;
1802 while (Class
!= NULL
)
1804 Class
->cbwndExtra
= (INT
)NewLong
;
1805 Class
= Class
->pclsNext
;
1810 case GCL_CBCLSEXTRA
:
1811 EngSetLastError(ERROR_INVALID_PARAMETER
);
1814 case GCLP_HBRBACKGROUND
:
1815 Ret
= (ULONG_PTR
)Class
->hbrBackground
;
1816 Class
->hbrBackground
= (HBRUSH
)NewLong
;
1818 /* Update the clones */
1819 Class
= Class
->pclsClone
;
1820 while (Class
!= NULL
)
1822 Class
->hbrBackground
= (HBRUSH
)NewLong
;
1823 Class
= Class
->pclsNext
;
1828 /* FIXME: Get handle from pointer to CURSOR object */
1829 Ret
= (ULONG_PTR
)Class
->hCursor
;
1830 Class
->hCursor
= (HANDLE
)NewLong
;
1832 /* Update the clones */
1833 Class
= Class
->pclsClone
;
1834 while (Class
!= NULL
)
1836 Class
->hCursor
= (HANDLE
)NewLong
;
1837 Class
= Class
->pclsNext
;
1842 /* FIXME: Get handle from pointer to ICON object */
1843 Ret
= (ULONG_PTR
)Class
->hIcon
;
1844 if (Class
->hIcon
== (HANDLE
)NewLong
) break;
1845 if (Ret
&& Class
->hIconSmIntern
)
1847 IntClassDestroyIcon(Class
->hIconSmIntern
);
1848 Class
->CSF_flags
&= ~CSF_CACHEDSMICON
;
1849 Class
->hIconSmIntern
= NULL
;
1851 if (NewLong
&& !Class
->hIconSm
)
1853 hIconSmIntern
= Class
->hIconSmIntern
= co_IntCopyImage( (HICON
)NewLong
, IMAGE_ICON
,
1854 UserGetSystemMetrics( SM_CXSMICON
),
1855 UserGetSystemMetrics( SM_CYSMICON
), 0 );
1856 Class
->CSF_flags
|= CSF_CACHEDSMICON
;
1858 Class
->hIcon
= (HANDLE
)NewLong
;
1860 /* Update the clones */
1861 Class
= Class
->pclsClone
;
1862 while (Class
!= NULL
)
1864 Class
->hIcon
= (HANDLE
)NewLong
;
1865 Class
->hIconSmIntern
= hIconSmIntern
;
1866 Class
= Class
->pclsNext
;
1871 /* FIXME: Get handle from pointer to ICON object */
1872 Ret
= (ULONG_PTR
)Class
->hIconSm
;
1873 if (Class
->hIconSm
== (HANDLE
)NewLong
) break;
1874 if (Class
->CSF_flags
& CSF_CACHEDSMICON
)
1876 if (Class
->hIconSmIntern
)
1878 IntClassDestroyIcon(Class
->hIconSmIntern
);
1879 Class
->hIconSmIntern
= NULL
;
1881 Class
->CSF_flags
&= ~CSF_CACHEDSMICON
;
1883 if (Class
->hIcon
&& !Class
->hIconSmIntern
)
1885 hIconSmIntern
= Class
->hIconSmIntern
= co_IntCopyImage( Class
->hIcon
, IMAGE_ICON
,
1886 UserGetSystemMetrics( SM_CXSMICON
),
1887 UserGetSystemMetrics( SM_CYSMICON
), 0 );
1889 if (hIconSmIntern
) Class
->CSF_flags
|= CSF_CACHEDSMICON
;
1890 //// FIXME: Very hacky here but it passes the tests....
1891 //// We should not kill a users handle!!!
1892 if (Class
->hIconSm
) IntClassDestroyIcon(Class
->hIconSm
); // Fixes 1013
1893 Ret
= 0; // Fixes 1009
1895 Class
->hIconSm
= (HANDLE
)NewLong
;
1897 /* Update the clones */
1898 Class
= Class
->pclsClone
;
1899 while (Class
!= NULL
)
1901 Class
->hIconSm
= (HANDLE
)NewLong
;
1902 Class
->hIconSmIntern
= hIconSmIntern
;
1903 Class
= Class
->pclsNext
;
1908 Ret
= (ULONG_PTR
)Class
->hModule
;
1909 Class
->hModule
= (HINSTANCE
)NewLong
;
1911 /* Update the clones */
1912 Class
= Class
->pclsClone
;
1913 while (Class
!= NULL
)
1915 Class
->hModule
= (HINSTANCE
)NewLong
;
1916 Class
= Class
->pclsNext
;
1922 PUNICODE_STRING Value
= (PUNICODE_STRING
)NewLong
;
1924 if (!IntSetClassMenuName(Class
,
1927 ERR("Setting the class menu name failed!\n");
1930 /* FIXME: Really return NULL? Wine does so... */
1935 Ret
= (ULONG_PTR
)Class
->style
;
1936 Class
->style
= (UINT
)NewLong
;
1938 /* FIXME: What if the CS_GLOBALCLASS style is changed? should we
1939 move the class to the appropriate list? For now, we save
1940 the original value in Class->Global, so we can always
1941 locate the appropriate list */
1943 /* Update the clones */
1944 Class
= Class
->pclsClone
;
1945 while (Class
!= NULL
)
1947 Class
->style
= (UINT
)NewLong
;
1948 Class
= Class
->pclsNext
;
1953 Ret
= (ULONG_PTR
)IntSetClassWndProc(Class
,
1960 PUNICODE_STRING Value
= (PUNICODE_STRING
)NewLong
;
1962 Ret
= (ULONG_PTR
)Class
->atomClassName
;
1963 if (!IntSetClassAtom(Class
,
1972 EngSetLastError(ERROR_INVALID_INDEX
);
1980 UserGetClassInfo(IN PCLS Class
,
1981 OUT PWNDCLASSEXW lpwcx
,
1983 HINSTANCE hInstance
)
1985 if (!Class
) return FALSE
;
1987 lpwcx
->style
= Class
->style
;
1989 // If fnId is set, clear the global bit. See wine class test check_style.
1991 lpwcx
->style
&= ~CS_GLOBALCLASS
;
1993 lpwcx
->lpfnWndProc
= IntGetClassWndProc(Class
, Ansi
);
1995 lpwcx
->cbClsExtra
= Class
->cbclsExtra
;
1996 lpwcx
->cbWndExtra
= Class
->cbwndExtra
;
1997 lpwcx
->hIcon
= Class
->hIcon
; /* FIXME: Get handle from pointer */
1998 lpwcx
->hCursor
= Class
->hCursor
; /* FIXME: Get handle from pointer */
1999 lpwcx
->hbrBackground
= Class
->hbrBackground
;
2001 /* Copy non-string to user first. */
2003 ((PWNDCLASSEXA
)lpwcx
)->lpszMenuName
= Class
->lpszClientAnsiMenuName
;
2005 lpwcx
->lpszMenuName
= Class
->lpszClientUnicodeMenuName
;
2007 * FIXME: CLSMENUNAME has the answers! Copy the already made buffers from there!
2008 * Cls: lpszMenuName and lpszAnsiClassName should be used by kernel space.
2009 * lpszClientXxxMenuName should already be mapped to user space.
2011 /* Copy string ptr to user. */
2012 if ( Class
->lpszClientUnicodeMenuName
!= NULL
&&
2013 Class
->MenuNameIsString
)
2015 lpwcx
->lpszMenuName
= UserHeapAddressToUser(Ansi
?
2016 (PVOID
)Class
->lpszClientAnsiMenuName
:
2017 (PVOID
)Class
->lpszClientUnicodeMenuName
);
2020 if (hInstance
== hModClient
)
2021 lpwcx
->hInstance
= NULL
;
2023 lpwcx
->hInstance
= hInstance
;
2025 /* FIXME: Return the string? Okay! This is performed in User32! */
2026 //lpwcx->lpszClassName = (LPCWSTR)((ULONG_PTR)Class->atomClassName);
2028 /* FIXME: Get handle from pointer */
2029 lpwcx
->hIconSm
= Class
->hIconSm
? Class
->hIconSm
: Class
->hIconSmIntern
;
2039 UserRegisterSystemClasses(VOID
)
2042 UNICODE_STRING ClassName
, MenuName
;
2043 PPROCESSINFO ppi
= GetW32ProcessInfo();
2050 if (ppi
->W32PF_flags
& W32PF_CLASSESREGISTERED
)
2053 if ( hModClient
== NULL
)
2056 RtlZeroMemory(&ClassName
, sizeof(ClassName
));
2057 RtlZeroMemory(&MenuName
, sizeof(MenuName
));
2059 for (i
= 0; i
!= ARRAYSIZE(DefaultServerClasses
); i
++)
2061 if (!IS_ATOM(DefaultServerClasses
[i
].ClassName
))
2063 RtlInitUnicodeString(&ClassName
, DefaultServerClasses
[i
].ClassName
);
2067 ClassName
.Buffer
= DefaultServerClasses
[i
].ClassName
;
2068 ClassName
.Length
= 0;
2069 ClassName
.MaximumLength
= 0;
2072 wc
.cbSize
= sizeof(wc
);
2073 wc
.style
= DefaultServerClasses
[i
].Style
;
2075 Flags
|= CSF_SERVERSIDEPROC
;
2077 if (DefaultServerClasses
[i
].ProcW
)
2079 wc
.lpfnWndProc
= DefaultServerClasses
[i
].ProcW
;
2080 wc
.hInstance
= hModuleWin
;
2084 wc
.lpfnWndProc
= GETPFNSERVER(DefaultServerClasses
[i
].fiId
);
2085 wc
.hInstance
= hModClient
;
2089 wc
.cbWndExtra
= DefaultServerClasses
[i
].ExtraBytes
;
2091 wc
.hCursor
= DefaultServerClasses
[i
].hCursor
;
2092 hBrush
= DefaultServerClasses
[i
].hBrush
;
2093 if (hBrush
<= (HBRUSH
)COLOR_MENUBAR
)
2095 hBrush
= IntGetSysColorBrush((INT
)hBrush
);
2097 wc
.hbrBackground
= hBrush
;
2098 wc
.lpszMenuName
= NULL
;
2099 wc
.lpszClassName
= ClassName
.Buffer
;
2102 Class
= IntCreateClass( &wc
,
2105 DefaultServerClasses
[i
].fiId
,
2111 Class
->pclsNext
= ppi
->pclsPublicList
;
2112 (void)InterlockedExchangePointer((PVOID
*)&ppi
->pclsPublicList
,
2115 ppi
->dwRegisteredClasses
|= ICLASS_TO_MASK(DefaultServerClasses
[i
].iCls
);
2119 ERR("!!! Registering system class failed!\n");
2123 if (Ret
) ppi
->W32PF_flags
|= W32PF_CLASSESREGISTERED
;
2127 /* SYSCALLS *****************************************************************/
2131 NtUserRegisterClassExWOW(
2133 PUNICODE_STRING ClassName
,
2134 PUNICODE_STRING ClsNVersion
,
2135 PCLSMENUNAME pClassMenuName
,
2141 * Registers a new class with the window manager
2143 * lpwcx = Win32 extended window class structure
2144 * bUnicodeClass = Whether to send ANSI or unicode strings
2145 * to window procedures
2147 * Atom identifying the new class
2150 WNDCLASSEXW CapturedClassInfo
= {0};
2151 UNICODE_STRING CapturedName
= {0}, CapturedMenuName
= {0};
2152 RTL_ATOM Ret
= (RTL_ATOM
)0;
2153 PPROCESSINFO ppi
= GetW32ProcessInfo();
2155 if (Flags
& ~(CSF_ANSIPROC
))
2157 ERR("NtUserRegisterClassExWOW Bad Flags!\n");
2158 EngSetLastError(ERROR_INVALID_FLAGS
);
2162 UserEnterExclusive();
2164 TRACE("NtUserRegisterClassExWOW ClsN %wZ\n",ClassName
);
2166 if ( !(ppi
->W32PF_flags
& W32PF_CLASSESREGISTERED
))
2168 UserRegisterSystemClasses();
2173 /* Probe the parameters and basic parameter checks */
2174 if (ProbeForReadUint(&lpwcx
->cbSize
) != sizeof(WNDCLASSEXW
))
2176 ERR("NtUserRegisterClassExWOW Wrong cbSize!\n");
2177 goto InvalidParameter
;
2181 sizeof(WNDCLASSEXW
),
2183 RtlCopyMemory(&CapturedClassInfo
,
2185 sizeof(WNDCLASSEXW
));
2187 CapturedName
= ProbeForReadUnicodeString(ClassName
);
2189 ProbeForRead(pClassMenuName
,
2190 sizeof(CLSMENUNAME
),
2193 CapturedMenuName
= ProbeForReadUnicodeString(pClassMenuName
->pusMenuName
);
2195 if ( (CapturedName
.Length
& 1) ||
2196 (CapturedMenuName
.Length
& 1) ||
2197 (CapturedClassInfo
.cbClsExtra
< 0) ||
2198 ((CapturedClassInfo
.cbClsExtra
+ CapturedName
.Length
+
2199 CapturedMenuName
.Length
+ sizeof(CLS
))
2200 < (ULONG
)CapturedClassInfo
.cbClsExtra
) ||
2201 (CapturedClassInfo
.cbWndExtra
< 0) ||
2202 (CapturedClassInfo
.hInstance
== NULL
) )
2204 ERR("NtUserRegisterClassExWOW Invalid Parameter Error!\n");
2205 goto InvalidParameter
;
2208 if (CapturedName
.Length
!= 0)
2210 ProbeForRead(CapturedName
.Buffer
,
2211 CapturedName
.Length
,
2216 if (!IS_ATOM(CapturedName
.Buffer
))
2218 ERR("NtUserRegisterClassExWOW ClassName Error!\n");
2219 goto InvalidParameter
;
2223 if (CapturedMenuName
.Length
!= 0)
2225 ProbeForRead(CapturedMenuName
.Buffer
,
2226 CapturedMenuName
.Length
,
2229 else if (CapturedMenuName
.Buffer
!= NULL
&&
2230 !IS_INTRESOURCE(CapturedMenuName
.Buffer
))
2232 ERR("NtUserRegisterClassExWOW MenuName Error!\n");
2234 EngSetLastError(ERROR_INVALID_PARAMETER
);
2238 if (IsCallProcHandle(lpwcx
->lpfnWndProc
))
2239 { // Never seen this yet, but I'm sure it's a little haxxy trick!
2240 // If this pops up we know what todo!
2241 ERR("NtUserRegisterClassExWOW WndProc is CallProc!!\n");
2244 TRACE("NtUserRegisterClassExWOW MnuN %wZ\n",&CapturedMenuName
);
2246 /* Register the class */
2247 Ret
= UserRegisterClass(&CapturedClassInfo
,
2253 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2255 ERR("NtUserRegisterClassExWOW Exception Error!\n");
2256 SetLastNtError(_SEH2_GetExceptionCode());
2262 ERR("NtUserRegisterClassExWOW Null Return!\n");
2271 NtUserSetClassLong(HWND hWnd
,
2273 ULONG_PTR dwNewLong
,
2280 UserEnterExclusive();
2282 pi
= GetW32ProcessInfo();
2284 Window
= UserGetWindowObject(hWnd
);
2287 if (Window
->head
.pti
->ppi
!= pi
)
2289 EngSetLastError(ERROR_ACCESS_DENIED
);
2295 UNICODE_STRING Value
;
2297 /* Probe the parameters */
2298 if (Offset
== GCW_ATOM
|| Offset
== GCLP_MENUNAME
)
2300 Value
= ProbeForReadUnicodeString((PUNICODE_STRING
)dwNewLong
);
2301 if (Value
.Length
& 1)
2303 goto InvalidParameter
;
2306 if (Value
.Length
!= 0)
2308 ProbeForRead(Value
.Buffer
,
2314 if (Offset
== GCW_ATOM
&& !IS_ATOM(Value
.Buffer
))
2316 goto InvalidParameter
;
2318 else if (Offset
== GCLP_MENUNAME
&& !IS_INTRESOURCE(Value
.Buffer
))
2321 EngSetLastError(ERROR_INVALID_PARAMETER
);
2326 dwNewLong
= (ULONG_PTR
)&Value
;
2329 Ret
= UserSetClassLongPtr(Window
->pcls
,
2334 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2336 SetLastNtError(_SEH2_GetExceptionCode());
2355 * NOTE: Obsoleted in 32-bit windows
2362 NtUserUnregisterClass(
2363 IN PUNICODE_STRING ClassNameOrAtom
,
2364 IN HINSTANCE hInstance
,
2365 OUT PCLSMENUNAME pClassMenuName
)
2367 UNICODE_STRING SafeClassName
;
2371 Status
= ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName
, ClassNameOrAtom
);
2372 if (!NT_SUCCESS(Status
))
2374 ERR("Error capturing the class name\n");
2375 SetLastNtError(Status
);
2379 UserEnterExclusive();
2381 /* Unregister the class */
2382 Ret
= UserUnregisterClass(&SafeClassName
, hInstance
, NULL
); // Null for now~
2386 if (SafeClassName
.Buffer
&& !IS_ATOM(SafeClassName
.Buffer
))
2387 ExFreePoolWithTag(SafeClassName
.Buffer
, TAG_STRING
);
2393 /* NOTE: For system classes hInstance is not NULL here, but User32Instance */
2397 HINSTANCE hInstance
,
2398 PUNICODE_STRING ClassName
,
2399 LPWNDCLASSEXW lpWndClassEx
,
2400 LPWSTR
*ppszMenuName
,
2403 UNICODE_STRING SafeClassName
;
2404 WNDCLASSEXW Safewcexw
;
2406 RTL_ATOM ClassAtom
= 0;
2413 ProbeForWrite( lpWndClassEx
, sizeof(WNDCLASSEXW
), sizeof(ULONG
));
2414 RtlCopyMemory( &Safewcexw
, lpWndClassEx
, sizeof(WNDCLASSEXW
));
2416 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2418 SetLastNtError(_SEH2_GetExceptionCode());
2419 _SEH2_YIELD(return FALSE
);
2423 Status
= ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName
, ClassName
);
2424 if (!NT_SUCCESS(Status
))
2426 ERR("Error capturing the class name\n");
2427 SetLastNtError(Status
);
2431 // If null instance use client.
2432 if (!hInstance
) hInstance
= hModClient
;
2434 TRACE("GetClassInfo(%wZ, %p)\n", SafeClassName
, hInstance
);
2436 /* NOTE: Need exclusive lock because getting the wndproc might require the
2437 creation of a call procedure handle */
2438 UserEnterExclusive();
2440 ppi
= GetW32ProcessInfo();
2441 if (!(ppi
->W32PF_flags
& W32PF_CLASSESREGISTERED
))
2443 UserRegisterSystemClasses();
2446 ClassAtom
= IntGetClassAtom(&SafeClassName
,
2451 if (ClassAtom
!= (RTL_ATOM
)0)
2453 Ret
= UserGetClassInfo(Class
, &Safewcexw
, bAnsi
, hInstance
);
2457 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
2467 /* Emulate Function. */
2468 if (ppszMenuName
) *ppszMenuName
= (LPWSTR
)Safewcexw
.lpszMenuName
;
2470 RtlCopyMemory(lpWndClassEx
, &Safewcexw
, sizeof(WNDCLASSEXW
));
2473 /* We must return the atom of the class here instead of just TRUE. */
2474 /* Undocumented behavior! Return the class atom as a BOOL! */
2475 Ret
= (BOOL
)ClassAtom
;
2477 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2479 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
2485 if (!IS_ATOM(SafeClassName
.Buffer
))
2486 ExFreePoolWithTag(SafeClassName
.Buffer
, TAG_STRING
);
2493 NtUserGetClassName (IN HWND hWnd
,
2495 OUT PUNICODE_STRING ClassName
)
2498 UNICODE_STRING CapturedClassName
;
2504 Window
= UserGetWindowObject(hWnd
);
2507 if (Real
&& Window
->fnid
&& !(Window
->fnid
& FNID_DESTROY
))
2509 if (LookupFnIdToiCls(Window
->fnid
, &iCls
))
2511 Atom
= gpsi
->atomSysClass
[iCls
];
2517 ProbeForWriteUnicodeString(ClassName
);
2518 CapturedClassName
= *ClassName
;
2520 /* Get the class name */
2521 Ret
= UserGetClassName(Window
->pcls
,
2528 /* Update the Length field */
2529 ClassName
->Length
= CapturedClassName
.Length
;
2532 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2534 SetLastNtError(_SEH2_GetExceptionCode());
2544 /* Return Pointer to Class structure. */
2548 HINSTANCE hInstance
,
2549 PUNICODE_STRING ClassName
)
2551 UNICODE_STRING SafeClassName
;
2554 RTL_ATOM ClassAtom
= 0;
2557 Status
= ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName
, ClassName
);
2558 if (!NT_SUCCESS(Status
))
2560 ERR("Error capturing the class name\n");
2561 SetLastNtError(Status
);
2565 UserEnterExclusive();
2567 pi
= GetW32ProcessInfo();
2569 ClassAtom
= IntGetClassAtom(&SafeClassName
,
2576 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST
);
2580 if (SafeClassName
.Buffer
&& !IS_ATOM(SafeClassName
.Buffer
))
2581 ExFreePoolWithTag(SafeClassName
.Buffer
, TAG_STRING
);
2585 // Don't forget to use DesktopPtrToUser( ? ) with return pointer in user space.