[USER32]
[reactos.git] / reactos / win32ss / user / ntuser / class.c
1 /*
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>
7 */
8
9 #include <win32k.h>
10 DBG_DEFAULT_CHANNEL(UserClass);
11
12 BOOL FASTCALL IntClassDestroyIcon(HANDLE hCurIcon);
13 static NTSTATUS IntDeregisterClassAtom(IN RTL_ATOM Atom);
14
15 REGISTER_SYSCLASS DefaultServerClasses[] =
16 {
17 { ((PWSTR)((ULONG_PTR)(WORD)(0x8001))),
18 CS_GLOBALCLASS|CS_DBLCLKS,
19 NULL, // Use User32 procs
20 sizeof(ULONG)*2,
21 (HICON)IDC_ARROW,
22 (HBRUSH)(COLOR_BACKGROUND),
23 FNID_DESKTOP,
24 ICLS_DESKTOP
25 },
26 { ((PWSTR)((ULONG_PTR)(WORD)(0x8003))),
27 CS_VREDRAW|CS_HREDRAW|CS_SAVEBITS,
28 NULL, // Use User32 procs
29 sizeof(LONG),
30 (HICON)IDC_ARROW,
31 NULL,
32 FNID_SWITCH,
33 ICLS_SWITCH
34 },
35 { ((PWSTR)((ULONG_PTR)(WORD)(0x8000))),
36 CS_DBLCLKS|CS_SAVEBITS,
37 NULL, // Use User32 procs
38 sizeof(LONG),
39 (HICON)IDC_ARROW,
40 (HBRUSH)(COLOR_MENU + 1),
41 FNID_MENU,
42 ICLS_MENU
43 },
44 { L"ScrollBar",
45 CS_DBLCLKS|CS_VREDRAW|CS_HREDRAW|CS_PARENTDC,
46 NULL, // Use User32 procs
47 sizeof(SBWND)-sizeof(WND),
48 (HICON)IDC_ARROW,
49 NULL,
50 FNID_SCROLLBAR,
51 ICLS_SCROLLBAR
52 },
53 #if 0
54 { ((PWSTR)((ULONG_PTR)(WORD)(0x8006))), // Tooltips
55 CS_PARENTDC|CS_DBLCLKS,
56 NULL, // Use User32 procs
57 0,
58 (HICON)IDC_ARROW,
59 0,
60 FNID_TOOLTIPS,
61 ICLS_TOOLTIPS
62 },
63 #endif
64 { ((PWSTR)((ULONG_PTR)(WORD)(0x8004))), // IconTitle is here for now...
65 0,
66 NULL, // Use User32 procs
67 0,
68 (HICON)IDC_ARROW,
69 0,
70 FNID_ICONTITLE,
71 ICLS_ICONTITLE
72 },
73 { L"Message",
74 CS_GLOBALCLASS,
75 NULL, // Use User32 procs
76 0,
77 (HICON)IDC_ARROW,
78 NULL,
79 FNID_MESSAGEWND,
80 ICLS_HWNDMESSAGE
81 }
82 };
83
84 static struct
85 {
86 int FnId;
87 int ClsId;
88 } FnidToiCls[] =
89 { /* Function Ids to Class indexes. */
90 { FNID_SCROLLBAR, ICLS_SCROLLBAR},
91 { FNID_ICONTITLE, ICLS_ICONTITLE},
92 { FNID_MENU, ICLS_MENU},
93 { FNID_DESKTOP, ICLS_DESKTOP},
94 { FNID_SWITCH, ICLS_SWITCH},
95 { FNID_MESSAGEWND, ICLS_HWNDMESSAGE},
96 { FNID_BUTTON, ICLS_BUTTON},
97 { FNID_COMBOBOX, ICLS_COMBOBOX},
98 { FNID_COMBOLBOX, ICLS_COMBOLBOX},
99 { FNID_DIALOG, ICLS_DIALOG},
100 { FNID_EDIT, ICLS_EDIT},
101 { FNID_LISTBOX, ICLS_LISTBOX},
102 { FNID_MDICLIENT, ICLS_MDICLIENT},
103 { FNID_STATIC, ICLS_STATIC},
104 { FNID_IME, ICLS_IME},
105 { FNID_GHOST, ICLS_GHOST},
106 { FNID_TOOLTIPS, ICLS_TOOLTIPS}
107 };
108
109 BOOL
110 FASTCALL
111 LookupFnIdToiCls(int FnId, int *iCls )
112 {
113 int i;
114
115 for ( i = 0; i < ARRAYSIZE(FnidToiCls); i++)
116 {
117 if (FnidToiCls[i].FnId == FnId)
118 {
119 if (iCls) *iCls = FnidToiCls[i].ClsId;
120 return TRUE;
121 }
122 }
123 if (iCls) *iCls = 0;
124 return FALSE;
125 }
126
127 _Must_inspect_result_
128 NTSTATUS
129 NTAPI
130 ProbeAndCaptureUnicodeStringOrAtom(
131 _Out_ _When_(return>=0, _At_(pustrOut->Buffer, _Post_ _Notnull_)) PUNICODE_STRING pustrOut,
132 __in_data_source(USER_MODE) _In_ PUNICODE_STRING pustrUnsafe)
133 {
134 NTSTATUS Status = STATUS_SUCCESS;
135
136 /* Default to NULL */
137 pustrOut->Buffer = NULL;
138
139 _SEH2_TRY
140 {
141 ProbeForRead(pustrUnsafe, sizeof(UNICODE_STRING), 1);
142
143 /* Validate the string */
144 if ((pustrUnsafe->Length & 1) || (pustrUnsafe->Buffer == NULL))
145 {
146 /* This is not legal */
147 _SEH2_YIELD(return STATUS_INVALID_PARAMETER);
148 }
149
150 /* Check if this is an atom */
151 if (IS_ATOM(pustrUnsafe->Buffer))
152 {
153 /* Copy the atom, length is 0 */
154 pustrOut->MaximumLength = pustrOut->Length = 0;
155 pustrOut->Buffer = pustrUnsafe->Buffer;
156 }
157 else
158 {
159 /* Get the length, maximum length includes zero termination */
160 pustrOut->Length = pustrUnsafe->Length;
161 pustrOut->MaximumLength = pustrOut->Length + sizeof(WCHAR);
162
163 /* Allocate a buffer */
164 pustrOut->Buffer = ExAllocatePoolWithTag(PagedPool,
165 pustrOut->MaximumLength,
166 TAG_STRING);
167 if (!pustrOut->Buffer)
168 {
169 _SEH2_YIELD(return STATUS_INSUFFICIENT_RESOURCES);
170 }
171
172 /* Copy the string and zero terminate it */
173 ProbeForRead(pustrUnsafe->Buffer, pustrOut->Length, 1);
174 RtlCopyMemory(pustrOut->Buffer, pustrUnsafe->Buffer, pustrOut->Length);
175 pustrOut->Buffer[pustrOut->Length / sizeof(WCHAR)] = L'\0';
176 }
177 }
178 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
179 {
180 /* Check if we already allocated a buffer */
181 if (pustrOut->Buffer)
182 {
183 /* Free the buffer */
184 ExFreePoolWithTag(pustrOut->Buffer, TAG_STRING);
185 Status = _SEH2_GetExceptionCode();
186 }
187 }
188 _SEH2_END;
189
190 return Status;
191 }
192
193 /* WINDOWCLASS ***************************************************************/
194
195 static VOID
196 IntFreeClassMenuName(IN OUT PCLS Class)
197 {
198 /* Free the menu name, if it was changed and allocated */
199 if (Class->lpszClientUnicodeMenuName != NULL && Class->MenuNameIsString)
200 {
201 UserHeapFree(Class->lpszClientUnicodeMenuName);
202 Class->lpszClientUnicodeMenuName = NULL;
203 Class->lpszClientAnsiMenuName = NULL;
204 }
205 }
206
207 static VOID
208 IntDestroyClass(IN OUT PCLS Class)
209 {
210 PDESKTOP pDesk;
211
212 /* There shouldn't be any clones anymore */
213 ASSERT(Class->cWndReferenceCount == 0);
214 ASSERT(Class->pclsClone == NULL);
215
216 if (Class->pclsBase == Class)
217 {
218 PCALLPROCDATA CallProc, NextCallProc;
219
220 /* Destroy allocated callproc handles */
221 CallProc = Class->spcpdFirst;
222 while (CallProc != NULL)
223 {
224 NextCallProc = CallProc->spcpdNext;
225
226 CallProc->spcpdNext = NULL;
227 DestroyCallProc(CallProc);
228
229 CallProc = NextCallProc;
230 }
231
232 // Fixes running the static test then run class test issue.
233 // Some applications do not use UnregisterClass before exiting.
234 // Keep from reusing the same atom with case insensitive
235 // comparisons, remove registration of the atom if not zeroed.
236 if (Class->atomClassName)
237 IntDeregisterClassAtom(Class->atomClassName);
238
239 if (Class->pdce)
240 {
241 DceFreeClassDCE(((PDCE)Class->pdce)->hDC);
242 Class->pdce = NULL;
243 }
244
245 IntFreeClassMenuName(Class);
246 }
247
248 #ifdef NEW_CURSORICON
249 if (Class->spicn)
250 UserDereferenceObject(Class->spicn);
251 if (Class->spicnSm)
252 UserDereferenceObject(Class->spicnSm);
253 if (Class->spcur)
254 UserDereferenceObject(Class->spcur);
255 #else
256 if (Class->hIconSmIntern)
257 IntClassDestroyIcon(Class->hIconSmIntern);
258 #endif
259
260 pDesk = Class->rpdeskParent;
261 Class->rpdeskParent = NULL;
262
263 /* Free the structure */
264 if (pDesk != NULL)
265 {
266 DesktopHeapFree(pDesk, Class);
267 }
268 else
269 {
270 UserHeapFree(Class);
271 }
272 }
273
274
275 /* Clean all process classes. all process windows must cleaned first!! */
276 void FASTCALL DestroyProcessClasses(PPROCESSINFO Process )
277 {
278 PCLS Class;
279 PPROCESSINFO pi = (PPROCESSINFO)Process;
280
281 if (pi != NULL)
282 {
283 /* Free all local classes */
284 Class = pi->pclsPrivateList;
285 while (Class != NULL)
286 {
287 pi->pclsPrivateList = Class->pclsNext;
288
289 ASSERT(Class->pclsBase == Class);
290 IntDestroyClass(Class);
291
292 Class = pi->pclsPrivateList;
293 }
294
295 /* Free all global classes */
296 Class = pi->pclsPublicList;
297 while (Class != NULL)
298 {
299 pi->pclsPublicList = Class->pclsNext;
300
301 ASSERT(Class->pclsBase == Class);
302 IntDestroyClass(Class);
303
304 Class = pi->pclsPublicList;
305 }
306 }
307 }
308
309 static BOOL
310 IntRegisterClassAtom(IN PUNICODE_STRING ClassName,
311 OUT RTL_ATOM *pAtom)
312 {
313 WCHAR szBuf[65];
314 PWSTR AtomName;
315 NTSTATUS Status;
316
317 if (ClassName->Length != 0)
318 {
319 /* FIXME: Don't limit to 64 characters! Use SEH when allocating memory! */
320 if (ClassName->Length / sizeof(WCHAR) >= sizeof(szBuf) / sizeof(szBuf[0]))
321 {
322 EngSetLastError(ERROR_INVALID_PARAMETER);
323 return (RTL_ATOM)0;
324 }
325
326 RtlCopyMemory(szBuf,
327 ClassName->Buffer,
328 ClassName->Length);
329 szBuf[ClassName->Length / sizeof(WCHAR)] = UNICODE_NULL;
330 AtomName = szBuf;
331 }
332 else
333 AtomName = ClassName->Buffer;
334
335 Status = RtlAddAtomToAtomTable(gAtomTable,
336 AtomName,
337 pAtom);
338
339 if (!NT_SUCCESS(Status))
340 {
341 SetLastNtError(Status);
342 return FALSE;
343 }
344
345 return TRUE;
346 }
347
348 static NTSTATUS
349 IntDeregisterClassAtom(IN RTL_ATOM Atom)
350 {
351 return RtlDeleteAtomFromAtomTable(gAtomTable,
352 Atom);
353 }
354
355 VOID
356 UserAddCallProcToClass(IN OUT PCLS Class,
357 IN PCALLPROCDATA CallProc)
358 {
359 PCLS BaseClass;
360
361 ASSERT(CallProc->spcpdNext == NULL);
362
363 BaseClass = Class->pclsBase;
364 ASSERT(CallProc->spcpdNext == NULL);
365 CallProc->spcpdNext = BaseClass->spcpdFirst;
366 BaseClass->spcpdFirst = CallProc;
367
368 /* Update all clones */
369 Class = Class->pclsClone;
370 while (Class != NULL)
371 {
372 Class->spcpdFirst = BaseClass->spcpdFirst;
373 Class = Class->pclsNext;
374 }
375 }
376
377 static BOOL
378 IntSetClassAtom(IN OUT PCLS Class,
379 IN PUNICODE_STRING ClassName)
380 {
381 RTL_ATOM Atom = (RTL_ATOM)0;
382
383 /* Update the base class first */
384 Class = Class->pclsBase;
385
386 if (!IntRegisterClassAtom(ClassName,
387 &Atom))
388 {
389 return FALSE;
390 }
391
392 IntDeregisterClassAtom(Class->atomClassName);
393
394 Class->atomClassName = Atom;
395
396 /* Update the clones */
397 Class = Class->pclsClone;
398 while (Class != NULL)
399 {
400 Class->atomClassName = Atom;
401
402 Class = Class->pclsNext;
403 }
404
405 return TRUE;
406 }
407
408 //
409 // Same as User32:IntGetClsWndProc.
410 //
411 WNDPROC FASTCALL
412 IntGetClassWndProc(PCLS Class, BOOL Ansi)
413 {
414 INT i;
415 WNDPROC gcpd = NULL, Ret = NULL;
416
417 if (Class->CSF_flags & CSF_SERVERSIDEPROC)
418 {
419 for ( i = FNID_FIRST; i <= FNID_SWITCH; i++)
420 {
421 if (GETPFNSERVER(i) == Class->lpfnWndProc)
422 {
423 if (Ansi)
424 Ret = GETPFNCLIENTA(i);
425 else
426 Ret = GETPFNCLIENTW(i);
427 }
428 }
429 return Ret;
430 }
431 Ret = Class->lpfnWndProc;
432
433 if (Class->fnid <= FNID_GHOST && Class->fnid >= FNID_BUTTON)
434 {
435 if (Ansi)
436 {
437 if (GETPFNCLIENTW(Class->fnid) == Class->lpfnWndProc)
438 Ret = GETPFNCLIENTA(Class->fnid);
439 }
440 else
441 {
442 if (GETPFNCLIENTA(Class->fnid) == Class->lpfnWndProc)
443 Ret = GETPFNCLIENTW(Class->fnid);
444 }
445 }
446
447 if ( Ret != Class->lpfnWndProc ||
448 Ansi == !!(Class->CSF_flags & CSF_ANSIPROC) )
449 return Ret;
450
451 gcpd = (WNDPROC)UserGetCPD( Class,
452 (Ansi ? UserGetCPDA2U : UserGetCPDU2A )|UserGetCPDClass,
453 (ULONG_PTR)Ret);
454
455 return (gcpd ? gcpd : Ret);
456 }
457
458
459 static
460 WNDPROC FASTCALL
461 IntSetClassWndProc(IN OUT PCLS Class,
462 IN WNDPROC WndProc,
463 IN BOOL Ansi)
464 {
465 INT i;
466 PCALLPROCDATA pcpd;
467 WNDPROC Ret, chWndProc;
468
469 Ret = IntGetClassWndProc(Class, Ansi);
470
471 // If Server Side, downgrade to Client Side.
472 if (Class->CSF_flags & CSF_SERVERSIDEPROC)
473 {
474 if (Ansi) Class->CSF_flags |= CSF_ANSIPROC;
475 Class->CSF_flags &= ~CSF_SERVERSIDEPROC;
476 Class->Unicode = !Ansi;
477 }
478
479 if (!WndProc) WndProc = Class->lpfnWndProc;
480
481 chWndProc = WndProc;
482
483 // Check if CallProc handle and retrieve previous call proc address and set.
484 if (IsCallProcHandle(WndProc))
485 {
486 pcpd = UserGetObject(gHandleTable, WndProc, TYPE_CALLPROC);
487 if (pcpd) chWndProc = pcpd->pfnClientPrevious;
488 }
489
490 Class->lpfnWndProc = chWndProc;
491
492 // Clear test proc.
493 chWndProc = NULL;
494
495 // Switch from Client Side call to Server Side call if match. Ref: "deftest".
496 for ( i = FNID_FIRST; i <= FNID_SWITCH; i++)
497 {
498 if (GETPFNCLIENTW(i) == Class->lpfnWndProc)
499 {
500 chWndProc = GETPFNSERVER(i);
501 break;
502 }
503 if (GETPFNCLIENTA(i) == Class->lpfnWndProc)
504 {
505 chWndProc = GETPFNSERVER(i);
506 break;
507 }
508 }
509 // If match, set/reset to Server Side and clear ansi.
510 if (chWndProc)
511 {
512 Class->lpfnWndProc = chWndProc;
513 Class->Unicode = TRUE;
514 Class->CSF_flags &= ~CSF_ANSIPROC;
515 Class->CSF_flags |= CSF_SERVERSIDEPROC;
516 }
517 else
518 {
519 Class->Unicode = !Ansi;
520
521 if (Ansi)
522 Class->CSF_flags |= CSF_ANSIPROC;
523 else
524 Class->CSF_flags &= ~CSF_ANSIPROC;
525 }
526
527 /* Update the clones */
528 chWndProc = Class->lpfnWndProc;
529
530 Class = Class->pclsClone;
531 while (Class != NULL)
532 {
533 Class->Unicode = !Ansi;
534 Class->lpfnWndProc = chWndProc;
535
536 Class = Class->pclsNext;
537 }
538
539 return Ret;
540 }
541
542 static PCLS
543 IntGetClassForDesktop(IN OUT PCLS BaseClass,
544 IN OUT PCLS *ClassLink,
545 IN PDESKTOP Desktop)
546 {
547 SIZE_T ClassSize;
548 PCLS Class;
549
550 ASSERT(Desktop != NULL);
551 ASSERT(BaseClass->pclsBase == BaseClass);
552
553 if (BaseClass->rpdeskParent == Desktop)
554 {
555 /* It is most likely that a window is created on the same
556 desktop as the window class. */
557
558 return BaseClass;
559 }
560
561 if (BaseClass->rpdeskParent == NULL)
562 {
563 ASSERT(BaseClass->cWndReferenceCount == 0);
564 ASSERT(BaseClass->pclsClone == NULL);
565
566 /* Classes are also located in the shared heap when the class
567 was created before the thread attached to a desktop. As soon
568 as a window is created for such a class located on the shared
569 heap, the class is cloned into the desktop heap on which the
570 window is created. */
571 Class = NULL;
572 }
573 else
574 {
575 /* The user is asking for a class object on a different desktop,
576 try to find one! */
577 Class = BaseClass->pclsClone;
578 while (Class != NULL)
579 {
580 if (Class->rpdeskParent == Desktop)
581 {
582 ASSERT(Class->pclsBase == BaseClass);
583 ASSERT(Class->pclsClone == NULL);
584 break;
585 }
586
587 Class = Class->pclsNext;
588 }
589 }
590
591 if (Class == NULL)
592 {
593 /* The window is created on a different desktop, we need to
594 clone the class object to the desktop heap of the window! */
595 ClassSize = sizeof(*BaseClass) + (SIZE_T)BaseClass->cbclsExtra;
596
597 Class = DesktopHeapAlloc(Desktop,
598 ClassSize);
599
600 if (Class != NULL)
601 {
602 /* Simply clone the class */
603 RtlCopyMemory( Class, BaseClass, ClassSize);
604
605 #ifdef NEW_CURSORICON
606 /* Reference our objects */
607 if (Class->spcur)
608 UserReferenceObject(Class->spcur);
609 if (Class->spicn)
610 UserReferenceObject(Class->spicn);
611 if (Class->spicnSm)
612 UserReferenceObject(Class->spicnSm);
613 #endif
614
615 TRACE("Clone Class 0x%p hM 0x%p\n %S\n",Class, Class->hModule, Class->lpszClientUnicodeMenuName);
616
617 /* Restore module address if default user class Ref: Bug 4778 */
618 if ( Class->hModule != hModClient &&
619 Class->fnid <= FNID_GHOST &&
620 Class->fnid >= FNID_BUTTON )
621 {
622 Class->hModule = hModClient;
623 TRACE("Clone Class 0x%p Reset hM 0x%p\n",Class, Class->hModule);
624 }
625
626 /* Update some pointers and link the class */
627 Class->rpdeskParent = Desktop;
628 Class->cWndReferenceCount = 0;
629
630 if (BaseClass->rpdeskParent == NULL)
631 {
632 /* We don't really need the base class on the shared
633 heap anymore, delete it so the only class left is
634 the clone we just created, which now serves as the
635 new base class */
636 ASSERT(BaseClass->pclsClone == NULL);
637 ASSERT(Class->pclsClone == NULL);
638 Class->pclsBase = Class;
639 Class->pclsNext = BaseClass->pclsNext;
640
641 /* Replace the base class */
642 (void)InterlockedExchangePointer((PVOID*)ClassLink,
643 Class);
644
645 /* Destroy the obsolete copy on the shared heap */
646 BaseClass->pclsBase = NULL;
647 BaseClass->pclsClone = NULL;
648 IntDestroyClass(BaseClass);
649 }
650 else
651 {
652 /* Link in the clone */
653 Class->pclsClone = NULL;
654 Class->pclsBase = BaseClass;
655 Class->pclsNext = BaseClass->pclsClone;
656 (void)InterlockedExchangePointer((PVOID*)&BaseClass->pclsClone,
657 Class);
658 }
659 }
660 else
661 {
662 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
663 }
664 }
665 return Class;
666 }
667
668 PCLS
669 IntReferenceClass(IN OUT PCLS BaseClass,
670 IN OUT PCLS *ClassLink,
671 IN PDESKTOP Desktop)
672 {
673 PCLS Class;
674 ASSERT(BaseClass->pclsBase == BaseClass);
675
676 if (Desktop != NULL)
677 {
678 Class = IntGetClassForDesktop(BaseClass,
679 ClassLink,
680 Desktop);
681 }
682 else
683 {
684 Class = BaseClass;
685 }
686
687 if (Class != NULL)
688 {
689 Class->cWndReferenceCount++;
690 }
691
692 return Class;
693 }
694
695 static
696 VOID
697 IntMakeCloneBaseClass(IN OUT PCLS Class,
698 IN OUT PCLS *BaseClassLink,
699 IN OUT PCLS *CloneLink)
700 {
701 PCLS Clone;
702
703 ASSERT(Class->pclsBase != Class);
704 ASSERT(Class->pclsBase->pclsClone != NULL);
705 ASSERT(Class->rpdeskParent != NULL);
706 ASSERT(Class->cWndReferenceCount != 0);
707 ASSERT(Class->pclsBase->rpdeskParent != NULL);
708 ASSERT(Class->pclsBase->cWndReferenceCount == 0);
709
710 /* Unlink the clone */
711 *CloneLink = Class->pclsNext;
712 Class->pclsClone = Class->pclsBase->pclsClone;
713
714 /* Update the class information to make it a base class */
715 Class->pclsBase = Class;
716 Class->pclsNext = (*BaseClassLink)->pclsNext;
717
718 /* Update all clones */
719 Clone = Class->pclsClone;
720 while (Clone != NULL)
721 {
722 ASSERT(Clone->pclsClone == NULL);
723 Clone->pclsBase = Class;
724
725 Clone = Clone->pclsNext;
726 }
727
728 /* Link in the new base class */
729 (void)InterlockedExchangePointer((PVOID*)BaseClassLink,
730 Class);
731 }
732
733
734 VOID
735 IntDereferenceClass(IN OUT PCLS Class,
736 IN PDESKTOPINFO Desktop,
737 IN PPROCESSINFO pi)
738 {
739 PCLS *PrevLink, BaseClass, CurrentClass;
740
741 ASSERT(Class->cWndReferenceCount >= 1);
742
743 BaseClass = Class->pclsBase;
744
745 if (--Class->cWndReferenceCount == 0)
746 {
747 if (BaseClass == Class)
748 {
749 ASSERT(Class->pclsBase == Class);
750
751 TRACE("IntDereferenceClass 0x%p\n", Class);
752 /* Check if there are clones of the class on other desktops,
753 link the first clone in if possible. If there are no clones
754 then leave the class on the desktop heap. It will get moved
755 to the shared heap when the thread detaches. */
756 if (BaseClass->pclsClone != NULL)
757 {
758 if (BaseClass->Global)
759 PrevLink = &pi->pclsPublicList;
760 else
761 PrevLink = &pi->pclsPrivateList;
762
763 CurrentClass = *PrevLink;
764 while (CurrentClass != BaseClass)
765 {
766 ASSERT(CurrentClass != NULL);
767
768 PrevLink = &CurrentClass->pclsNext;
769 CurrentClass = CurrentClass->pclsNext;
770 }
771
772 ASSERT(*PrevLink == BaseClass);
773
774 /* Make the first clone become the new base class */
775 IntMakeCloneBaseClass(BaseClass->pclsClone,
776 PrevLink,
777 &BaseClass->pclsClone);
778
779 /* Destroy the class, there's still another clone of the class
780 that now serves as a base class. Make sure we don't destruct
781 resources shared by all classes (Base = NULL)! */
782 BaseClass->pclsBase = NULL;
783 BaseClass->pclsClone = NULL;
784 IntDestroyClass(BaseClass);
785 }
786 }
787 else
788 {
789 TRACE("IntDereferenceClass1 0x%p\n", Class);
790
791 /* Locate the cloned class and unlink it */
792 PrevLink = &BaseClass->pclsClone;
793 CurrentClass = BaseClass->pclsClone;
794 while (CurrentClass != Class)
795 {
796 ASSERT(CurrentClass != NULL);
797
798 PrevLink = &CurrentClass->pclsNext;
799 CurrentClass = CurrentClass->pclsNext;
800 }
801
802 ASSERT(CurrentClass == Class);
803
804 (void)InterlockedExchangePointer((PVOID*)PrevLink,
805 Class->pclsNext);
806
807 ASSERT(Class->pclsBase == BaseClass);
808 ASSERT(Class->pclsClone == NULL);
809
810 /* The class was just a clone, we don't need it anymore */
811 IntDestroyClass(Class);
812 }
813 }
814 }
815
816 static BOOL
817 IntMoveClassToSharedHeap(IN OUT PCLS Class,
818 IN OUT PCLS **ClassLinkPtr)
819 {
820 PCLS NewClass;
821 SIZE_T ClassSize;
822
823 ASSERT(Class->pclsBase == Class);
824 ASSERT(Class->rpdeskParent != NULL);
825 ASSERT(Class->cWndReferenceCount == 0);
826 ASSERT(Class->pclsClone == NULL);
827
828 ClassSize = sizeof(*Class) + (SIZE_T)Class->cbclsExtra;
829
830 /* Allocate the new base class on the shared heap */
831 NewClass = UserHeapAlloc(ClassSize);
832 if (NewClass != NULL)
833 {
834 RtlCopyMemory(NewClass,
835 Class,
836 ClassSize);
837
838 NewClass->rpdeskParent = NULL;
839 NewClass->pclsBase = NewClass;
840
841 /* Replace the class in the list */
842 (void)InterlockedExchangePointer((PVOID*)*ClassLinkPtr,
843 NewClass);
844 *ClassLinkPtr = &NewClass->pclsNext;
845
846 /* Free the obsolete class on the desktop heap */
847 Class->pclsBase = NULL;
848 IntDestroyClass(Class);
849 return TRUE;
850 }
851
852 return FALSE;
853 }
854
855 static VOID
856 IntCheckDesktopClasses(IN PDESKTOP Desktop,
857 IN OUT PCLS *ClassList,
858 IN BOOL FreeOnFailure,
859 OUT BOOL *Ret)
860 {
861 PCLS Class, NextClass, *Link;
862
863 /* NOTE: We only need to check base classes! When classes are no longer needed
864 on a desktop, the clones will be freed automatically as soon as possible.
865 However, we need to move base classes to the shared heap, as soon as
866 the last desktop heap where a class is allocated on is about to be destroyed.
867 If we didn't move the class to the shared heap, the class would become
868 inaccessible! */
869
870 ASSERT(Desktop != NULL);
871
872 Link = ClassList;
873 Class = *Link;
874 while (Class != NULL)
875 {
876 NextClass = Class->pclsNext;
877
878 ASSERT(Class->pclsBase == Class);
879
880 if (Class->rpdeskParent == Desktop &&
881 Class->cWndReferenceCount == 0)
882 {
883 /* There shouldn't be any clones around anymore! */
884 ASSERT(Class->pclsClone == NULL);
885
886 /* FIXME: If process is terminating, don't move the class but rather destroy it! */
887 /* FIXME: We could move the class to another desktop heap if there's still desktops
888 mapped into the process... */
889
890 /* Move the class to the shared heap */
891 if (IntMoveClassToSharedHeap(Class,
892 &Link))
893 {
894 ASSERT(*Link == NextClass);
895 }
896 else
897 {
898 ASSERT(NextClass == Class->pclsNext);
899
900 if (FreeOnFailure)
901 {
902 /* Unlink the base class */
903 (void)InterlockedExchangePointer((PVOID*)Link,
904 Class->pclsNext);
905
906 /* We can free the old base class now */
907 Class->pclsBase = NULL;
908 IntDestroyClass(Class);
909 }
910 else
911 {
912 Link = &Class->pclsNext;
913 *Ret = FALSE;
914 }
915 }
916 }
917 else
918 Link = &Class->pclsNext;
919
920 Class = NextClass;
921 }
922 }
923
924 BOOL
925 IntCheckProcessDesktopClasses(IN PDESKTOP Desktop,
926 IN BOOL FreeOnFailure)
927 {
928 PPROCESSINFO pi;
929 BOOL Ret = TRUE;
930
931 pi = GetW32ProcessInfo();
932
933 /* Check all local classes */
934 IntCheckDesktopClasses(Desktop,
935 &pi->pclsPrivateList,
936 FreeOnFailure,
937 &Ret);
938
939 /* Check all global classes */
940 IntCheckDesktopClasses(Desktop,
941 &pi->pclsPublicList,
942 FreeOnFailure,
943 &Ret);
944 if (!Ret)
945 {
946 ERR("Failed to move process classes from desktop 0x%p to the shared heap!\n", Desktop);
947 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
948 }
949
950 return Ret;
951 }
952
953 PCLS
954 FASTCALL
955 IntCreateClass(IN CONST WNDCLASSEXW* lpwcx,
956 IN PUNICODE_STRING ClassName,
957 IN PUNICODE_STRING MenuName,
958 IN DWORD fnID,
959 IN DWORD dwFlags,
960 IN PDESKTOP Desktop,
961 IN PPROCESSINFO pi)
962 {
963 SIZE_T ClassSize;
964 PCLS Class = NULL;
965 RTL_ATOM Atom;
966 WNDPROC WndProc;
967 PWSTR pszMenuName = NULL;
968 NTSTATUS Status = STATUS_SUCCESS;
969
970 TRACE("lpwcx=%p ClassName=%wZ MenuName=%wZ dwFlags=%08x Desktop=%p pi=%p\n",
971 lpwcx, ClassName, MenuName, dwFlags, Desktop, pi);
972
973 if (!IntRegisterClassAtom(ClassName,
974 &Atom))
975 {
976 ERR("Failed to register class atom!\n");
977 return NULL;
978 }
979
980 ClassSize = sizeof(*Class) + lpwcx->cbClsExtra;
981 if (MenuName->Length != 0)
982 {
983 pszMenuName = UserHeapAlloc(MenuName->Length + sizeof(UNICODE_NULL) +
984 RtlUnicodeStringToAnsiSize(MenuName));
985 if (pszMenuName == NULL)
986 goto NoMem;
987 }
988
989 if (Desktop != NULL)
990 {
991 Class = DesktopHeapAlloc(Desktop,
992 ClassSize);
993 }
994 else
995 {
996 /* FIXME: The class was created before being connected
997 to a desktop. It is possible for the desktop window,
998 but should it be allowed for any other case? */
999 Class = UserHeapAlloc(ClassSize);
1000 }
1001
1002 if (Class != NULL)
1003 {
1004 int iCls = 0;
1005
1006 RtlZeroMemory(Class, ClassSize);
1007
1008 Class->rpdeskParent = Desktop;
1009 Class->pclsBase = Class;
1010 Class->atomClassName = Atom;
1011 Class->fnid = fnID;
1012 Class->CSF_flags = dwFlags;
1013
1014 if (LookupFnIdToiCls(Class->fnid, &iCls))
1015 {
1016 gpsi->atomSysClass[iCls] = Class->atomClassName;
1017 }
1018
1019 _SEH2_TRY
1020 {
1021 PWSTR pszMenuNameBuffer = pszMenuName;
1022
1023 /* Need to protect with SEH since accessing the WNDCLASSEX structure
1024 and string buffers might raise an exception! We don't want to
1025 leak memory... */
1026 // What?! If the user interface was written correctly this would not be an issue!
1027 Class->lpfnWndProc = lpwcx->lpfnWndProc;
1028 Class->style = lpwcx->style;
1029 Class->cbclsExtra = lpwcx->cbClsExtra;
1030 Class->cbwndExtra = lpwcx->cbWndExtra;
1031 Class->hModule = lpwcx->hInstance;
1032 #ifdef NEW_CURSORICON
1033 Class->spicn = lpwcx->hIcon ? UserGetCurIconObject(lpwcx->hIcon) : NULL;
1034 Class->spcur = lpwcx->hCursor ? UserGetCurIconObject(lpwcx->hCursor) : NULL;
1035 Class->spicnSm = lpwcx->hIconSm ? UserGetCurIconObject(lpwcx->hIconSm) : NULL;
1036 #else
1037 Class->hIcon = lpwcx->hIcon;
1038 Class->hIconSm = lpwcx->hIconSm;
1039 Class->hCursor = lpwcx->hCursor;
1040 #endif
1041 ////
1042 Class->hbrBackground = lpwcx->hbrBackground;
1043
1044 /* Make a copy of the string */
1045 if (pszMenuNameBuffer != NULL)
1046 {
1047 Class->MenuNameIsString = TRUE;
1048
1049 Class->lpszClientUnicodeMenuName = pszMenuNameBuffer;
1050 RtlCopyMemory(Class->lpszClientUnicodeMenuName,
1051 MenuName->Buffer,
1052 MenuName->Length);
1053 Class->lpszClientUnicodeMenuName[MenuName->Length / sizeof(WCHAR)] = UNICODE_NULL;
1054
1055 pszMenuNameBuffer += (MenuName->Length / sizeof(WCHAR)) + 1;
1056 }
1057 else
1058 Class->lpszClientUnicodeMenuName = MenuName->Buffer;
1059
1060 /* Save an ANSI copy of the string */
1061 if (pszMenuNameBuffer != NULL)
1062 {
1063 ANSI_STRING AnsiString;
1064
1065 Class->lpszClientAnsiMenuName = (PSTR)pszMenuNameBuffer;
1066 AnsiString.MaximumLength = (USHORT)RtlUnicodeStringToAnsiSize(MenuName);
1067 AnsiString.Buffer = Class->lpszClientAnsiMenuName;
1068 Status = RtlUnicodeStringToAnsiString(&AnsiString,
1069 MenuName,
1070 FALSE);
1071 if (!NT_SUCCESS(Status))
1072 {
1073 ERR("Failed to convert unicode menu name to ansi!\n");
1074
1075 /* Life would've been much prettier if ntoskrnl exported RtlRaiseStatus()... */
1076 _SEH2_LEAVE;
1077 }
1078 }
1079 else
1080 Class->lpszClientAnsiMenuName = (PSTR)MenuName->Buffer;
1081
1082 /* Save kernel use menu name and ansi class name */
1083 Class->lpszMenuName = Class->lpszClientUnicodeMenuName; // FIXME!
1084 //Class->lpszAnsiClassName = FIXME
1085
1086 /* Server Side overrides class calling type (A/W)!
1087 User32 whine test_builtinproc: "deftest"
1088 built-in winproc - window A/W type automatically detected */
1089 if (!(Class->CSF_flags & CSF_SERVERSIDEPROC))
1090 {
1091 int i;
1092 WndProc = NULL;
1093 /* Due to the wine class "deftest" and most likely no FNID to reference
1094 from, sort through the Server Side list and compare proc addresses
1095 for match. This method will be used in related code.
1096 */
1097 for ( i = FNID_FIRST; i <= FNID_SWITCH; i++)
1098 { // Open ANSI or Unicode, just match, set and break.
1099 if (GETPFNCLIENTW(i) == Class->lpfnWndProc)
1100 {
1101 WndProc = GETPFNSERVER(i);
1102 break;
1103 }
1104 if (GETPFNCLIENTA(i) == Class->lpfnWndProc)
1105 {
1106 WndProc = GETPFNSERVER(i);
1107 break;
1108 }
1109 }
1110 if (WndProc)
1111 { // If a hit, we are Server Side so set the right flags and proc.
1112 Class->CSF_flags |= CSF_SERVERSIDEPROC;
1113 Class->CSF_flags &= ~CSF_ANSIPROC;
1114 Class->lpfnWndProc = WndProc;
1115 }
1116 }
1117
1118 if (!(Class->CSF_flags & CSF_ANSIPROC))
1119 Class->Unicode = TRUE;
1120
1121 if (Class->style & CS_GLOBALCLASS)
1122 Class->Global = TRUE;
1123 }
1124 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1125 {
1126 Status = _SEH2_GetExceptionCode();
1127 }
1128 _SEH2_END;
1129
1130 if (!NT_SUCCESS(Status))
1131 {
1132 ERR("Failed creating the class: 0x%x\n", Status);
1133
1134 SetLastNtError(Status);
1135
1136 if (pszMenuName != NULL)
1137 UserHeapFree(pszMenuName);
1138
1139 DesktopHeapFree(Desktop,
1140 Class);
1141 Class = NULL;
1142
1143 IntDeregisterClassAtom(Atom);
1144 }
1145 }
1146 else
1147 {
1148 NoMem:
1149 ERR("Failed to allocate class on Desktop 0x%p\n", Desktop);
1150
1151 if (pszMenuName != NULL)
1152 UserHeapFree(pszMenuName);
1153
1154 IntDeregisterClassAtom(Atom);
1155
1156 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1157 }
1158
1159 TRACE("Created class 0x%p with name %wZ and proc 0x%p for atom 0x%x and hInstance 0x%p, global %u\n",
1160 Class, ClassName, Class->lpfnWndProc, Atom, Class->hModule, Class->Global);
1161
1162 return Class;
1163 }
1164
1165 static PCLS
1166 IntFindClass(IN RTL_ATOM Atom,
1167 IN HINSTANCE hInstance,
1168 IN PCLS *ClassList,
1169 OUT PCLS **Link OPTIONAL)
1170 {
1171 PCLS Class, *PrevLink = ClassList;
1172
1173 Class = *PrevLink;
1174 while (Class != NULL)
1175 {
1176 if (Class->atomClassName == Atom &&
1177 (hInstance == NULL || Class->hModule == hInstance) &&
1178 !(Class->CSF_flags & CSF_WOWDEFERDESTROY))
1179 {
1180 ASSERT(Class->pclsBase == Class);
1181
1182 if (Link != NULL)
1183 *Link = PrevLink;
1184 break;
1185 }
1186
1187 PrevLink = &Class->pclsNext;
1188 Class = Class->pclsNext;
1189 }
1190
1191 return Class;
1192 }
1193
1194 BOOL
1195 NTAPI
1196 IntGetAtomFromStringOrAtom(
1197 _In_ PUNICODE_STRING ClassName,
1198 _Out_ RTL_ATOM *Atom)
1199 {
1200 BOOL Ret = FALSE;
1201
1202 if (ClassName->Length != 0)
1203 {
1204 WCHAR szBuf[65];
1205 PWSTR AtomName;
1206 NTSTATUS Status;
1207
1208 *Atom = 0;
1209
1210 /* NOTE: Caller has to protect the call with SEH! */
1211
1212 if (ClassName->Length != 0)
1213 {
1214 /* FIXME: Don't limit to 64 characters! use SEH when allocating memory! */
1215 if (ClassName->Length / sizeof(WCHAR) >= sizeof(szBuf) / sizeof(szBuf[0]))
1216 {
1217 EngSetLastError(ERROR_INVALID_PARAMETER);
1218 return (RTL_ATOM)0;
1219 }
1220
1221 /* We need to make a local copy of the class name! The caller could
1222 modify the buffer and we could overflow in RtlLookupAtomInAtomTable.
1223 We're protected by SEH, but the ranges that might be accessed were
1224 not probed... */
1225 RtlCopyMemory(szBuf,
1226 ClassName->Buffer,
1227 ClassName->Length);
1228 szBuf[ClassName->Length / sizeof(WCHAR)] = UNICODE_NULL;
1229 AtomName = szBuf;
1230 }
1231 else
1232 AtomName = ClassName->Buffer;
1233
1234 /* Lookup the atom */
1235 Status = RtlLookupAtomInAtomTable(gAtomTable,
1236 AtomName,
1237 Atom);
1238 if (NT_SUCCESS(Status))
1239 {
1240 Ret = TRUE;
1241 }
1242 else
1243 {
1244 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
1245 {
1246 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST);
1247 }
1248 else
1249 {
1250 SetLastNtError(Status);
1251 }
1252 }
1253 }
1254 else
1255 {
1256 ASSERT(IS_ATOM(ClassName->Buffer));
1257 *Atom = (RTL_ATOM)((ULONG_PTR)ClassName->Buffer);
1258 Ret = TRUE;
1259 }
1260
1261 return Ret;
1262 }
1263
1264 RTL_ATOM
1265 IntGetClassAtom(
1266 _In_ PUNICODE_STRING ClassName,
1267 IN HINSTANCE hInstance OPTIONAL,
1268 IN PPROCESSINFO pi OPTIONAL,
1269 OUT PCLS *BaseClass OPTIONAL,
1270 OUT PCLS **Link OPTIONAL)
1271 {
1272 RTL_ATOM Atom = (RTL_ATOM)0;
1273
1274 ASSERT(BaseClass != NULL);
1275
1276 if (IntGetAtomFromStringOrAtom(ClassName,
1277 &Atom) &&
1278 Atom != (RTL_ATOM)0)
1279 {
1280 PCLS Class;
1281
1282 /* Attempt to locate the class object */
1283
1284 ASSERT(pi != NULL);
1285
1286 /* Step 1: Try to find an exact match of locally registered classes */
1287 Class = IntFindClass(Atom,
1288 hInstance,
1289 &pi->pclsPrivateList,
1290 Link);
1291 if (Class != NULL)
1292 { TRACE("Step 1: 0x%p\n",Class );
1293 goto FoundClass;
1294 }
1295
1296 /* Step 2: Try to find any globally registered class. The hInstance
1297 is not relevant for global classes */
1298 Class = IntFindClass(Atom,
1299 NULL,
1300 &pi->pclsPublicList,
1301 Link);
1302 if (Class != NULL)
1303 { TRACE("Step 2: 0x%p 0x%p\n",Class, Class->hModule);
1304 goto FoundClass;
1305 }
1306
1307 /* Step 3: Try to find any local class registered by user32 */
1308 Class = IntFindClass(Atom,
1309 hModClient,
1310 &pi->pclsPrivateList,
1311 Link);
1312 if (Class != NULL)
1313 { TRACE("Step 3: 0x%p\n",Class );
1314 goto FoundClass;
1315 }
1316
1317 /* Step 4: Try to find any global class registered by user32 */
1318 Class = IntFindClass(Atom,
1319 hModClient,
1320 &pi->pclsPublicList,
1321 Link);
1322 if (Class == NULL)
1323 {
1324 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST);
1325 return (RTL_ATOM)0;
1326 }else{TRACE("Step 4: 0x%p\n",Class );}
1327
1328 FoundClass:
1329 *BaseClass = Class;
1330 }
1331
1332 return Atom;
1333 }
1334
1335 PCLS
1336 IntGetAndReferenceClass(PUNICODE_STRING ClassName, HINSTANCE hInstance, BOOL bDesktopThread)
1337 {
1338 PCLS *ClassLink, Class = NULL;
1339 RTL_ATOM ClassAtom;
1340 PTHREADINFO pti;
1341
1342 if (bDesktopThread)
1343 pti = gptiDesktopThread;
1344 else
1345 pti = PsGetCurrentThreadWin32Thread();
1346
1347 if ( !(pti->ppi->W32PF_flags & W32PF_CLASSESREGISTERED ))
1348 {
1349 UserRegisterSystemClasses();
1350 }
1351
1352 /* Check the class. */
1353
1354 TRACE("Finding Class %wZ for hInstance 0x%p\n", ClassName, hInstance);
1355
1356 ClassAtom = IntGetClassAtom(ClassName,
1357 hInstance,
1358 pti->ppi,
1359 &Class,
1360 &ClassLink);
1361
1362 if (ClassAtom == (RTL_ATOM)0)
1363 {
1364 if (IS_ATOM(ClassName->Buffer))
1365 {
1366 ERR("Class 0x%p not found\n", ClassName->Buffer);
1367 }
1368 else
1369 {
1370 ERR("Class \"%wZ\" not found\n", ClassName);
1371 }
1372
1373 EngSetLastError(ERROR_CANNOT_FIND_WND_CLASS);
1374 return NULL;
1375 }
1376
1377 TRACE("Referencing Class 0x%p with atom 0x%x\n", Class, ClassAtom);
1378 Class = IntReferenceClass(Class,
1379 ClassLink,
1380 pti->rpdesk);
1381 if (Class == NULL)
1382 {
1383 ERR("Failed to reference window class!\n");
1384 return NULL;
1385 }
1386
1387 return Class;
1388 }
1389
1390 RTL_ATOM
1391 UserRegisterClass(IN CONST WNDCLASSEXW* lpwcx,
1392 IN PUNICODE_STRING ClassName,
1393 IN PUNICODE_STRING MenuName,
1394 IN DWORD fnID,
1395 IN DWORD dwFlags)
1396 {
1397 PTHREADINFO pti;
1398 PPROCESSINFO pi;
1399 PCLS Class;
1400 RTL_ATOM ClassAtom;
1401 RTL_ATOM Ret = (RTL_ATOM)0;
1402
1403 /* NOTE: Accessing the buffers in ClassName and MenuName may raise exceptions! */
1404
1405 pti = GetW32ThreadInfo();
1406
1407 pi = pti->ppi;
1408
1409 // Need only to test for two conditions not four....... Fix more whine tests....
1410 if ( IntGetAtomFromStringOrAtom( ClassName, &ClassAtom) &&
1411 ClassAtom != (RTL_ATOM)0 &&
1412 !(dwFlags & CSF_SERVERSIDEPROC) ) // Bypass Server Sides
1413 {
1414 Class = IntFindClass( ClassAtom,
1415 lpwcx->hInstance,
1416 &pi->pclsPrivateList,
1417 NULL);
1418
1419 if (Class != NULL && !Class->Global)
1420 {
1421 // Local class already exists
1422 TRACE("Local Class 0x%x does already exist!\n", ClassAtom);
1423 EngSetLastError(ERROR_CLASS_ALREADY_EXISTS);
1424 return (RTL_ATOM)0;
1425 }
1426
1427 if (lpwcx->style & CS_GLOBALCLASS)
1428 {
1429 Class = IntFindClass( ClassAtom,
1430 NULL,
1431 &pi->pclsPublicList,
1432 NULL);
1433
1434 if (Class != NULL && Class->Global)
1435 {
1436 TRACE("Global Class 0x%x does already exist!\n", ClassAtom);
1437 EngSetLastError(ERROR_CLASS_ALREADY_EXISTS);
1438 return (RTL_ATOM)0;
1439 }
1440 }
1441 }
1442
1443 Class = IntCreateClass(lpwcx,
1444 ClassName,
1445 MenuName,
1446 fnID,
1447 dwFlags,
1448 pti->rpdesk,
1449 pi);
1450
1451 if (Class != NULL)
1452 {
1453 PCLS *List;
1454
1455 /* Register the class */
1456 if (Class->Global)
1457 List = &pi->pclsPublicList;
1458 else
1459 List = &pi->pclsPrivateList;
1460
1461 Class->pclsNext = *List;
1462 (void)InterlockedExchangePointer((PVOID*)List,
1463 Class);
1464
1465 Ret = Class->atomClassName;
1466 }
1467 else
1468 {
1469 ERR("UserRegisterClass: Yes, that is right, you have no Class!\n");
1470 }
1471
1472 return Ret;
1473 }
1474
1475 BOOL
1476 UserUnregisterClass(IN PUNICODE_STRING ClassName,
1477 IN HINSTANCE hInstance,
1478 OUT PCLSMENUNAME pClassMenuName)
1479 {
1480 PCLS *Link;
1481 PPROCESSINFO pi;
1482 RTL_ATOM ClassAtom;
1483 PCLS Class;
1484
1485 pi = GetW32ProcessInfo();
1486
1487 TRACE("UserUnregisterClass(%wZ, 0x%p)\n", ClassName, hInstance);
1488
1489 /* NOTE: Accessing the buffer in ClassName may raise an exception! */
1490 ClassAtom = IntGetClassAtom(ClassName,
1491 hInstance,
1492 pi,
1493 &Class,
1494 &Link);
1495 if (ClassAtom == (RTL_ATOM)0)
1496 {
1497 TRACE("UserUnregisterClass: No Class found.\n");
1498 return FALSE;
1499 }
1500
1501 ASSERT(Class != NULL);
1502
1503 if (Class->cWndReferenceCount != 0 ||
1504 Class->pclsClone != NULL)
1505 {
1506 TRACE("UserUnregisterClass: Class has a Window. Ct %u : Clone 0x%p\n", Class->cWndReferenceCount, Class->pclsClone);
1507 EngSetLastError(ERROR_CLASS_HAS_WINDOWS);
1508 return FALSE;
1509 }
1510
1511 /* Must be a base class! */
1512 ASSERT(Class->pclsBase == Class);
1513
1514 /* Unlink the class */
1515 *Link = Class->pclsNext;
1516
1517 if (NT_SUCCESS(IntDeregisterClassAtom(Class->atomClassName)))
1518 {
1519 TRACE("Class 0x%p\n", Class);
1520 TRACE("UserUnregisterClass: Good Exit!\n");
1521 Class->atomClassName = 0; // Don't let it linger...
1522 /* Finally free the resources */
1523 IntDestroyClass(Class);
1524 return TRUE;
1525 }
1526 ERR("UserUnregisterClass: Can not deregister Class Atom.\n");
1527 return FALSE;
1528 }
1529
1530 INT
1531 UserGetClassName(IN PCLS Class,
1532 IN OUT PUNICODE_STRING ClassName,
1533 IN RTL_ATOM Atom,
1534 IN BOOL Ansi)
1535 {
1536 NTSTATUS Status = STATUS_SUCCESS;
1537 WCHAR szStaticTemp[32];
1538 PWSTR szTemp = NULL;
1539 ULONG BufLen = sizeof(szStaticTemp);
1540 INT Ret = 0;
1541
1542 /* Note: Accessing the buffer in ClassName may raise an exception! */
1543
1544 _SEH2_TRY
1545 {
1546 if (Ansi)
1547 {
1548 PANSI_STRING AnsiClassName = (PANSI_STRING)ClassName;
1549 UNICODE_STRING UnicodeClassName;
1550
1551 /* Limit the size of the static buffer on the stack to the
1552 size of the buffer provided by the caller */
1553 if (BufLen / sizeof(WCHAR) > AnsiClassName->MaximumLength)
1554 {
1555 BufLen = AnsiClassName->MaximumLength * sizeof(WCHAR);
1556 }
1557
1558 /* Find out how big the buffer needs to be */
1559 Status = RtlQueryAtomInAtomTable(gAtomTable,
1560 Class->atomClassName,
1561 NULL,
1562 NULL,
1563 szStaticTemp,
1564 &BufLen);
1565 if (Status == STATUS_BUFFER_TOO_SMALL)
1566 {
1567 if (BufLen / sizeof(WCHAR) > AnsiClassName->MaximumLength)
1568 {
1569 /* The buffer required exceeds the ansi buffer provided,
1570 pretend like we're using the ansi buffer and limit the
1571 size to the buffer size provided */
1572 BufLen = AnsiClassName->MaximumLength * sizeof(WCHAR);
1573 }
1574
1575 /* Allocate a temporary buffer that can hold the unicode class name */
1576 szTemp = ExAllocatePoolWithTag(PagedPool,
1577 BufLen,
1578 USERTAG_CLASS);
1579 if (szTemp == NULL)
1580 {
1581 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1582 _SEH2_LEAVE;
1583 }
1584
1585 /* Query the class name */
1586 Status = RtlQueryAtomInAtomTable(gAtomTable,
1587 Atom ? Atom : Class->atomClassName,
1588 NULL,
1589 NULL,
1590 szTemp,
1591 &BufLen);
1592 }
1593 else
1594 szTemp = szStaticTemp;
1595
1596 if (NT_SUCCESS(Status))
1597 {
1598 /* Convert the atom name to ansi */
1599
1600 RtlInitUnicodeString(&UnicodeClassName,
1601 szTemp);
1602
1603 Status = RtlUnicodeStringToAnsiString(AnsiClassName,
1604 &UnicodeClassName,
1605 FALSE);
1606 if (!NT_SUCCESS(Status))
1607 {
1608 SetLastNtError(Status);
1609 _SEH2_LEAVE;
1610 }
1611 }
1612
1613 Ret = BufLen / sizeof(WCHAR);
1614 }
1615 else /* !ANSI */
1616 {
1617 BufLen = ClassName->MaximumLength;
1618
1619 /* Query the atom name */
1620 Status = RtlQueryAtomInAtomTable(gAtomTable,
1621 Atom ? Atom : Class->atomClassName,
1622 NULL,
1623 NULL,
1624 ClassName->Buffer,
1625 &BufLen);
1626
1627 if (!NT_SUCCESS(Status))
1628 {
1629 SetLastNtError(Status);
1630 _SEH2_LEAVE;
1631 }
1632
1633 Ret = BufLen / sizeof(WCHAR);
1634 }
1635 }
1636 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1637 {
1638 SetLastNtError(_SEH2_GetExceptionCode());
1639 }
1640 _SEH2_END;
1641
1642 if (Ansi && szTemp != NULL && szTemp != szStaticTemp)
1643 {
1644 ExFreePoolWithTag(szTemp, USERTAG_CLASS);
1645 }
1646
1647 return Ret;
1648 }
1649
1650 static BOOL
1651 IntSetClassMenuName(IN PCLS Class,
1652 IN PUNICODE_STRING MenuName)
1653 {
1654 BOOL Ret = FALSE;
1655
1656 /* Change the base class first */
1657 Class = Class->pclsBase;
1658
1659 if (MenuName->Length != 0)
1660 {
1661 ANSI_STRING AnsiString;
1662 PWSTR strBufW;
1663
1664 AnsiString.MaximumLength = (USHORT)RtlUnicodeStringToAnsiSize(MenuName);
1665
1666 strBufW = UserHeapAlloc(MenuName->Length + sizeof(UNICODE_NULL) +
1667 AnsiString.MaximumLength);
1668 if (strBufW != NULL)
1669 {
1670 _SEH2_TRY
1671 {
1672 NTSTATUS Status;
1673
1674 /* Copy the unicode string */
1675 RtlCopyMemory(strBufW,
1676 MenuName->Buffer,
1677 MenuName->Length);
1678 strBufW[MenuName->Length / sizeof(WCHAR)] = UNICODE_NULL;
1679
1680 /* Create an ANSI copy of the string */
1681 AnsiString.Buffer = (PSTR)(strBufW + (MenuName->Length / sizeof(WCHAR)) + 1);
1682 Status = RtlUnicodeStringToAnsiString(&AnsiString,
1683 MenuName,
1684 FALSE);
1685 if (!NT_SUCCESS(Status))
1686 {
1687 SetLastNtError(Status);
1688 _SEH2_LEAVE;
1689 }
1690
1691 Ret = TRUE;
1692 }
1693 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1694 {
1695 SetLastNtError(_SEH2_GetExceptionCode());
1696 }
1697 _SEH2_END;
1698
1699 if (Ret)
1700 {
1701 /* Update the base class */
1702 IntFreeClassMenuName(Class);
1703 Class->lpszClientUnicodeMenuName = strBufW;
1704 Class->lpszClientAnsiMenuName = AnsiString.Buffer;
1705 Class->MenuNameIsString = TRUE;
1706
1707 /* Update the clones */
1708 Class = Class->pclsClone;
1709 while (Class != NULL)
1710 {
1711 Class->lpszClientUnicodeMenuName = strBufW;
1712 Class->lpszClientAnsiMenuName = AnsiString.Buffer;
1713 Class->MenuNameIsString = TRUE;
1714
1715 Class = Class->pclsNext;
1716 }
1717 }
1718 else
1719 {
1720 ERR("Failed to copy class menu name!\n");
1721 UserHeapFree(strBufW);
1722 }
1723 }
1724 else
1725 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1726 }
1727 else
1728 {
1729 ASSERT(IS_INTRESOURCE(MenuName->Buffer));
1730
1731 /* Update the base class */
1732 IntFreeClassMenuName(Class);
1733 Class->lpszClientUnicodeMenuName = MenuName->Buffer;
1734 Class->lpszClientAnsiMenuName = (PSTR)MenuName->Buffer;
1735 Class->MenuNameIsString = FALSE;
1736
1737 /* Update the clones */
1738 Class = Class->pclsClone;
1739 while (Class != NULL)
1740 {
1741 Class->lpszClientUnicodeMenuName = MenuName->Buffer;
1742 Class->lpszClientAnsiMenuName = (PSTR)MenuName->Buffer;
1743 Class->MenuNameIsString = FALSE;
1744
1745 Class = Class->pclsNext;
1746 }
1747
1748 Ret = TRUE;
1749 }
1750
1751 return Ret;
1752 }
1753
1754 #ifndef NEW_CURSORICON
1755 BOOL FASTCALL
1756 IntClassDestroyIcon(HANDLE hCurIcon)
1757 {
1758 PCURICON_OBJECT CurIcon;
1759 BOOL Ret;
1760
1761 if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
1762 {
1763
1764 ERR("hCurIcon was not found!\n");
1765 return FALSE;
1766 }
1767 /* Note: IntDestroyCurIconObject will remove our reference for us! */
1768 Ret = IntDestroyCurIconObject(CurIcon, GetW32ProcessInfo());
1769 if (!Ret)
1770 {
1771 ERR("hCurIcon was not Destroyed!\n");
1772 }
1773 return Ret;
1774 }
1775 #endif
1776
1777 ULONG_PTR
1778 UserSetClassLongPtr(IN PCLS Class,
1779 IN INT Index,
1780 IN ULONG_PTR NewLong,
1781 IN BOOL Ansi)
1782 {
1783 ULONG_PTR Ret = 0;
1784 #ifndef NEW_CURSORICON
1785 HANDLE hIconSmIntern = NULL;
1786 #endif
1787
1788 /* NOTE: For GCLP_MENUNAME and GCW_ATOM this function may raise an exception! */
1789
1790 /* Change the information in the base class first, then update the clones */
1791 Class = Class->pclsBase;
1792
1793 if (Index >= 0)
1794 {
1795 PULONG_PTR Data;
1796
1797 TRACE("SetClassLong(%d, %x)\n", Index, NewLong);
1798
1799 if (((ULONG)Index + sizeof(ULONG_PTR)) < (ULONG)Index ||
1800 ((ULONG)Index + sizeof(ULONG_PTR)) > (ULONG)Class->cbclsExtra)
1801 {
1802 EngSetLastError(ERROR_INVALID_PARAMETER);
1803 return 0;
1804 }
1805
1806 Data = (PULONG_PTR)((ULONG_PTR)(Class + 1) + Index);
1807
1808 /* FIXME: Data might be a unaligned pointer! Might be a problem on
1809 certain architectures, maybe using RtlCopyMemory is a
1810 better choice for those architectures! */
1811 Ret = *Data;
1812 *Data = NewLong;
1813
1814 /* Update the clones */
1815 Class = Class->pclsClone;
1816 while (Class != NULL)
1817 {
1818 *(PULONG_PTR)((ULONG_PTR)(Class + 1) + Index) = NewLong;
1819 Class = Class->pclsNext;
1820 }
1821
1822 return Ret;
1823 }
1824
1825 switch (Index)
1826 {
1827 case GCL_CBWNDEXTRA:
1828 Ret = (ULONG_PTR)Class->cbwndExtra;
1829 Class->cbwndExtra = (INT)NewLong;
1830
1831 /* Update the clones */
1832 Class = Class->pclsClone;
1833 while (Class != NULL)
1834 {
1835 Class->cbwndExtra = (INT)NewLong;
1836 Class = Class->pclsNext;
1837 }
1838
1839 break;
1840
1841 case GCL_CBCLSEXTRA:
1842 EngSetLastError(ERROR_INVALID_PARAMETER);
1843 break;
1844
1845 case GCLP_HBRBACKGROUND:
1846 Ret = (ULONG_PTR)Class->hbrBackground;
1847 Class->hbrBackground = (HBRUSH)NewLong;
1848
1849 /* Update the clones */
1850 Class = Class->pclsClone;
1851 while (Class != NULL)
1852 {
1853 Class->hbrBackground = (HBRUSH)NewLong;
1854 Class = Class->pclsNext;
1855 }
1856 break;
1857
1858 #ifdef NEW_CURSORICON
1859 case GCLP_HCURSOR:
1860 {
1861 PCURICON_OBJECT NewCursor = NULL;
1862
1863 if (NewLong)
1864 {
1865 NewCursor = UserGetCurIconObject((HCURSOR)NewLong);
1866 if (!NewCursor)
1867 {
1868 EngSetLastError(ERROR_INVALID_CURSOR_HANDLE);
1869 return 0;
1870 }
1871 }
1872
1873 if (Class->spcur)
1874 {
1875 Ret = (ULONG_PTR)UserHMGetHandle(Class->spcur);
1876 UserDereferenceObject(Class->spcur);
1877 }
1878 else
1879 {
1880 Ret = 0;
1881 }
1882
1883 if (Ret == NewLong)
1884 {
1885 /* It's a nop */
1886 return Ret;
1887 }
1888
1889 Class->spcur = NewCursor;
1890
1891 /* Update the clones */
1892 Class = Class->pclsClone;
1893 while (Class != NULL)
1894 {
1895 if (Class->spcur)
1896 UserDereferenceObject(Class->spcur);
1897 if (NewCursor)
1898 UserReferenceObject(NewCursor);
1899 Class->spcur = NewCursor;
1900 Class = Class->pclsNext;
1901 }
1902
1903 break;
1904 }
1905 #else
1906 case GCLP_HCURSOR:
1907 /* FIXME: Get handle from pointer to CURSOR object */
1908 Ret = (ULONG_PTR)Class->hCursor;
1909 Class->hCursor = (HANDLE)NewLong;
1910
1911 /* Update the clones */
1912 Class = Class->pclsClone;
1913 while (Class != NULL)
1914 {
1915 Class->hCursor = (HANDLE)NewLong;
1916 Class = Class->pclsNext;
1917 }
1918 break;
1919 #endif
1920
1921 // MSDN:
1922 // hIconSm, A handle to a small icon that is associated with the window class.
1923 // If this member is NULL, the system searches the icon resource specified by
1924 // the hIcon member for an icon of the appropriate size to use as the small icon.
1925 //
1926 case GCLP_HICON:
1927 #ifdef NEW_CURSORICON
1928 {
1929 PCURICON_OBJECT NewIcon = NULL;
1930 PCURICON_OBJECT NewSmallIcon = NULL;
1931
1932 if (NewLong)
1933 {
1934 NewIcon = UserGetCurIconObject((HCURSOR)NewLong);
1935 if (!NewIcon)
1936 {
1937 EngSetLastError(ERROR_INVALID_ICON_HANDLE);
1938 return 0;
1939 }
1940 }
1941
1942 if (Class->spicn)
1943 {
1944 Ret = (ULONG_PTR)UserHMGetHandle(Class->spicn);
1945 UserDereferenceObject(Class->spicn);
1946 }
1947 else
1948 {
1949 Ret = 0;
1950 }
1951
1952 if (Ret == NewLong)
1953 {
1954 /* It's a nop */
1955 return Ret;
1956 }
1957
1958 if (Ret && (Class->CSF_flags & CSF_CACHEDSMICON))
1959 {
1960 /* We will change the small icon */
1961 UserDereferenceObject(Class->spicnSm);
1962 Class->spicnSm = NULL;
1963 Class->CSF_flags &= ~CSF_CACHEDSMICON;
1964 }
1965
1966 if (NewLong && !Class->spicnSm)
1967 {
1968 /* Create the new small icon from the new large(?) one */
1969 HICON SmallIconHandle = co_IntCopyImage(
1970 (HICON)NewLong,
1971 IMAGE_ICON,
1972 UserGetSystemMetrics( SM_CXSMICON ),
1973 UserGetSystemMetrics( SM_CYSMICON ),
1974 0);
1975 if (SmallIconHandle)
1976 {
1977 /* So use it */
1978 NewSmallIcon = Class->spicnSm = UserGetCurIconObject(SmallIconHandle);
1979 /* Let the handle go, we have the reference on the object */
1980 NtUserDestroyCursor(SmallIconHandle, FALSE);
1981 Class->CSF_flags |= CSF_CACHEDSMICON;
1982 }
1983 }
1984
1985 Class->spicn = NewIcon;
1986
1987 /* Update the clones */
1988 Class = Class->pclsClone;
1989 while (Class != NULL)
1990 {
1991 if (Class->spicn)
1992 UserDereferenceObject(Class->spicn);
1993 if (NewIcon)
1994 UserReferenceObject(NewIcon);
1995 Class->spicn = NewIcon;
1996 if (NewSmallIcon)
1997 {
1998 if (Class->spicnSm)
1999 UserDereferenceObject(Class->spicnSm);
2000 UserReferenceObject(NewSmallIcon);
2001 Class->spicnSm = NewSmallIcon;
2002 Class->CSF_flags |= CSF_CACHEDSMICON;
2003 }
2004 Class = Class->pclsNext;
2005 }
2006 break;
2007 }
2008 #else
2009 /* FIXME: Get handle from pointer to ICON object */
2010 Ret = (ULONG_PTR)Class->hIcon;
2011 if (Class->hIcon == (HANDLE)NewLong) break;
2012 if (Ret && Class->hIconSmIntern)
2013 {
2014 IntClassDestroyIcon(Class->hIconSmIntern);
2015 Class->CSF_flags &= ~CSF_CACHEDSMICON;
2016 Class->hIconSmIntern = NULL;
2017 }
2018 if (NewLong && !Class->hIconSm)
2019 {
2020 hIconSmIntern = Class->hIconSmIntern = co_IntCopyImage( (HICON)NewLong, IMAGE_ICON,
2021 UserGetSystemMetrics( SM_CXSMICON ),
2022 UserGetSystemMetrics( SM_CYSMICON ), 0 );
2023 Class->CSF_flags |= CSF_CACHEDSMICON;
2024 }
2025 Class->hIcon = (HANDLE)NewLong;
2026
2027 /* Update the clones */
2028 Class = Class->pclsClone;
2029 while (Class != NULL)
2030 {
2031 Class->hIcon = (HANDLE)NewLong;
2032 Class->hIconSmIntern = hIconSmIntern;
2033 Class = Class->pclsNext;
2034 }
2035 break;
2036 #endif
2037
2038 case GCLP_HICONSM:
2039 #ifdef NEW_CURSORICON
2040 {
2041 PCURICON_OBJECT NewSmallIcon = NULL;
2042
2043 if (NewLong)
2044 {
2045 NewSmallIcon = UserGetCurIconObject((HCURSOR)NewLong);
2046 if (!NewSmallIcon)
2047 {
2048 EngSetLastError(ERROR_INVALID_ICON_HANDLE);
2049 return 0;
2050 }
2051 }
2052
2053 if (Class->spicnSm)
2054 {
2055 Ret = (ULONG_PTR)UserHMGetHandle(Class->spicnSm);
2056 UserDereferenceObject(Class->spicnSm);
2057 }
2058 else
2059 {
2060 Ret = 0;
2061 }
2062
2063 Class->CSF_flags &= ~CSF_CACHEDSMICON;
2064 Class->spicnSm = NewSmallIcon;
2065
2066 /* Update the clones */
2067 Class = Class->pclsClone;
2068 while (Class != NULL)
2069 {
2070 if (Class->spicnSm)
2071 UserDereferenceObject(Class->spicnSm);
2072 if (NewSmallIcon)
2073 UserReferenceObject(NewSmallIcon);
2074 Class->CSF_flags &= ~CSF_CACHEDSMICON;
2075 Class->spicnSm = NewSmallIcon;
2076 Class = Class->pclsNext;
2077 }
2078 }
2079 break;
2080 #else
2081 /* FIXME: Get handle from pointer to ICON object */
2082 Ret = (ULONG_PTR)Class->hIconSm;
2083 if (Class->hIconSm == (HANDLE)NewLong) break;
2084 if (Class->CSF_flags & CSF_CACHEDSMICON)
2085 {
2086 if (Class->hIconSmIntern)
2087 {
2088 IntClassDestroyIcon(Class->hIconSmIntern);
2089 Class->hIconSmIntern = NULL;
2090 }
2091 Class->CSF_flags &= ~CSF_CACHEDSMICON;
2092 }
2093 if (Class->hIcon && !Class->hIconSmIntern)
2094 {
2095 hIconSmIntern = Class->hIconSmIntern = co_IntCopyImage( Class->hIcon, IMAGE_ICON,
2096 UserGetSystemMetrics( SM_CXSMICON ),
2097 UserGetSystemMetrics( SM_CYSMICON ), 0 );
2098
2099 if (hIconSmIntern) Class->CSF_flags |= CSF_CACHEDSMICON;
2100 //// FIXME: Very hacky here but it passes the tests....
2101 Ret = 0; // Fixes 1009
2102 }
2103 Class->hIconSm = (HANDLE)NewLong;
2104
2105 /* Update the clones */
2106 Class = Class->pclsClone;
2107 while (Class != NULL)
2108 {
2109 Class->hIconSm = (HANDLE)NewLong;
2110 Class->hIconSmIntern = hIconSmIntern;
2111 Class = Class->pclsNext;
2112 }
2113 break;
2114 #endif
2115
2116 case GCLP_HMODULE:
2117 Ret = (ULONG_PTR)Class->hModule;
2118 Class->hModule = (HINSTANCE)NewLong;
2119
2120 /* Update the clones */
2121 Class = Class->pclsClone;
2122 while (Class != NULL)
2123 {
2124 Class->hModule = (HINSTANCE)NewLong;
2125 Class = Class->pclsNext;
2126 }
2127 break;
2128
2129 case GCLP_MENUNAME:
2130 {
2131 PUNICODE_STRING Value = (PUNICODE_STRING)NewLong;
2132
2133 if (!IntSetClassMenuName(Class,
2134 Value))
2135 {
2136 ERR("Setting the class menu name failed!\n");
2137 }
2138
2139 /* FIXME: Really return NULL? Wine does so... */
2140 break;
2141 }
2142
2143 case GCL_STYLE:
2144 Ret = (ULONG_PTR)Class->style;
2145 Class->style = (UINT)NewLong;
2146
2147 /* FIXME: What if the CS_GLOBALCLASS style is changed? should we
2148 move the class to the appropriate list? For now, we save
2149 the original value in Class->Global, so we can always
2150 locate the appropriate list */
2151
2152 /* Update the clones */
2153 Class = Class->pclsClone;
2154 while (Class != NULL)
2155 {
2156 Class->style = (UINT)NewLong;
2157 Class = Class->pclsNext;
2158 }
2159 break;
2160
2161 case GCLP_WNDPROC:
2162 Ret = (ULONG_PTR)IntSetClassWndProc(Class,
2163 (WNDPROC)NewLong,
2164 Ansi);
2165 break;
2166
2167 case GCW_ATOM:
2168 {
2169 PUNICODE_STRING Value = (PUNICODE_STRING)NewLong;
2170
2171 Ret = (ULONG_PTR)Class->atomClassName;
2172 if (!IntSetClassAtom(Class,
2173 Value))
2174 {
2175 Ret = 0;
2176 }
2177 break;
2178 }
2179
2180 default:
2181 EngSetLastError(ERROR_INVALID_INDEX);
2182 break;
2183 }
2184
2185 return Ret;
2186 }
2187
2188 static BOOL
2189 UserGetClassInfo(IN PCLS Class,
2190 OUT PWNDCLASSEXW lpwcx,
2191 IN BOOL Ansi,
2192 HINSTANCE hInstance)
2193 {
2194 if (!Class) return FALSE;
2195
2196 lpwcx->style = Class->style;
2197
2198 // If fnId is set, clear the global bit. See wine class test check_style.
2199 if (Class->fnid)
2200 lpwcx->style &= ~CS_GLOBALCLASS;
2201
2202 lpwcx->lpfnWndProc = IntGetClassWndProc(Class, Ansi);
2203
2204 lpwcx->cbClsExtra = Class->cbclsExtra;
2205 lpwcx->cbWndExtra = Class->cbwndExtra;
2206 #ifdef NEW_CURSORICON
2207 lpwcx->hIcon = Class->spicn ? UserHMGetHandle(Class->spicn) : NULL;
2208 lpwcx->hCursor = Class->spcur ? UserHMGetHandle(Class->spcur) : NULL;
2209 lpwcx->hIconSm = Class->spicnSm ? UserHMGetHandle(Class->spicnSm) : NULL;
2210 #else
2211 lpwcx->hIcon = Class->hIcon; /* FIXME: Get handle from pointer */
2212 lpwcx->hCursor = Class->hCursor; /* FIXME: Get handle from pointer */
2213 /* FIXME: Get handle from pointer */
2214 lpwcx->hIconSm = Class->hIconSm ? Class->hIconSm : Class->hIconSmIntern;
2215 #endif
2216 lpwcx->hbrBackground = Class->hbrBackground;
2217
2218 /* Copy non-string to user first. */
2219 if (Ansi)
2220 ((PWNDCLASSEXA)lpwcx)->lpszMenuName = Class->lpszClientAnsiMenuName;
2221 else
2222 lpwcx->lpszMenuName = Class->lpszClientUnicodeMenuName;
2223 /*
2224 * FIXME: CLSMENUNAME has the answers! Copy the already made buffers from there!
2225 * Cls: lpszMenuName and lpszAnsiClassName should be used by kernel space.
2226 * lpszClientXxxMenuName should already be mapped to user space.
2227 */
2228 /* Copy string ptr to user. */
2229 if ( Class->lpszClientUnicodeMenuName != NULL &&
2230 Class->MenuNameIsString)
2231 {
2232 lpwcx->lpszMenuName = UserHeapAddressToUser(Ansi ?
2233 (PVOID)Class->lpszClientAnsiMenuName :
2234 (PVOID)Class->lpszClientUnicodeMenuName);
2235 }
2236
2237 if (hInstance == hModClient)
2238 lpwcx->hInstance = NULL;
2239 else
2240 lpwcx->hInstance = hInstance;
2241
2242 /* FIXME: Return the string? Okay! This is performed in User32! */
2243 //lpwcx->lpszClassName = (LPCWSTR)((ULONG_PTR)Class->atomClassName);
2244
2245 return TRUE;
2246 }
2247
2248 //
2249 // ???
2250 //
2251 BOOL
2252 FASTCALL
2253 UserRegisterSystemClasses(VOID)
2254 {
2255 UINT i;
2256 UNICODE_STRING ClassName, MenuName;
2257 PPROCESSINFO ppi = GetW32ProcessInfo();
2258 WNDCLASSEXW wc;
2259 PCLS Class;
2260 BOOL Ret = TRUE;
2261 HBRUSH hBrush;
2262 DWORD Flags = 0;
2263
2264 if (ppi->W32PF_flags & W32PF_CLASSESREGISTERED)
2265 return TRUE;
2266
2267 if ( hModClient == NULL)
2268 return FALSE;
2269
2270 RtlZeroMemory(&ClassName, sizeof(ClassName));
2271 RtlZeroMemory(&MenuName, sizeof(MenuName));
2272
2273 for (i = 0; i != ARRAYSIZE(DefaultServerClasses); i++)
2274 {
2275 if (!IS_ATOM(DefaultServerClasses[i].ClassName))
2276 {
2277 RtlInitUnicodeString(&ClassName, DefaultServerClasses[i].ClassName);
2278 }
2279 else
2280 {
2281 ClassName.Buffer = DefaultServerClasses[i].ClassName;
2282 ClassName.Length = 0;
2283 ClassName.MaximumLength = 0;
2284 }
2285
2286 wc.cbSize = sizeof(wc);
2287 wc.style = DefaultServerClasses[i].Style;
2288
2289 Flags |= CSF_SERVERSIDEPROC;
2290
2291 if (DefaultServerClasses[i].ProcW)
2292 {
2293 wc.lpfnWndProc = DefaultServerClasses[i].ProcW;
2294 wc.hInstance = hModuleWin;
2295 }
2296 else
2297 {
2298 wc.lpfnWndProc = GETPFNSERVER(DefaultServerClasses[i].fiId);
2299 wc.hInstance = hModClient;
2300 }
2301
2302 wc.cbClsExtra = 0;
2303 wc.cbWndExtra = DefaultServerClasses[i].ExtraBytes;
2304 wc.hIcon = NULL;
2305 wc.hCursor = DefaultServerClasses[i].hCursor;
2306 hBrush = DefaultServerClasses[i].hBrush;
2307 if (hBrush <= (HBRUSH)COLOR_MENUBAR)
2308 {
2309 hBrush = IntGetSysColorBrush((INT)hBrush);
2310 }
2311 wc.hbrBackground = hBrush;
2312 wc.lpszMenuName = NULL;
2313 wc.lpszClassName = ClassName.Buffer;
2314 wc.hIconSm = NULL;
2315
2316 Class = IntCreateClass( &wc,
2317 &ClassName,
2318 &MenuName,
2319 DefaultServerClasses[i].fiId,
2320 Flags,
2321 NULL,
2322 ppi);
2323 if (Class != NULL)
2324 {
2325 Class->pclsNext = ppi->pclsPublicList;
2326 (void)InterlockedExchangePointer((PVOID*)&ppi->pclsPublicList,
2327 Class);
2328
2329 ppi->dwRegisteredClasses |= ICLASS_TO_MASK(DefaultServerClasses[i].iCls);
2330 }
2331 else
2332 {
2333 ERR("!!! Registering system class failed!\n");
2334 Ret = FALSE;
2335 }
2336 }
2337 if (Ret) ppi->W32PF_flags |= W32PF_CLASSESREGISTERED;
2338 return Ret;
2339 }
2340
2341 /* SYSCALLS *****************************************************************/
2342
2343 RTL_ATOM
2344 APIENTRY
2345 NtUserRegisterClassExWOW(
2346 WNDCLASSEXW* lpwcx,
2347 PUNICODE_STRING ClassName,
2348 PUNICODE_STRING ClsNVersion,
2349 PCLSMENUNAME pClassMenuName,
2350 DWORD fnID,
2351 DWORD Flags,
2352 LPDWORD pWow)
2353 /*
2354 * FUNCTION:
2355 * Registers a new class with the window manager
2356 * ARGUMENTS:
2357 * lpwcx = Win32 extended window class structure
2358 * bUnicodeClass = Whether to send ANSI or unicode strings
2359 * to window procedures
2360 * RETURNS:
2361 * Atom identifying the new class
2362 */
2363 {
2364 WNDCLASSEXW CapturedClassInfo = {0};
2365 UNICODE_STRING CapturedName = {0}, CapturedMenuName = {0};
2366 RTL_ATOM Ret = (RTL_ATOM)0;
2367 PPROCESSINFO ppi = GetW32ProcessInfo();
2368
2369 if (Flags & ~(CSF_ANSIPROC))
2370 {
2371 ERR("NtUserRegisterClassExWOW Bad Flags!\n");
2372 EngSetLastError(ERROR_INVALID_FLAGS);
2373 return Ret;
2374 }
2375
2376 UserEnterExclusive();
2377
2378 TRACE("NtUserRegisterClassExWOW ClsN %wZ\n",ClassName);
2379
2380 if ( !(ppi->W32PF_flags & W32PF_CLASSESREGISTERED ))
2381 {
2382 UserRegisterSystemClasses();
2383 }
2384
2385 _SEH2_TRY
2386 {
2387 /* Probe the parameters and basic parameter checks */
2388 if (ProbeForReadUint(&lpwcx->cbSize) != sizeof(WNDCLASSEXW))
2389 {
2390 ERR("NtUserRegisterClassExWOW Wrong cbSize!\n");
2391 goto InvalidParameter;
2392 }
2393
2394 ProbeForRead(lpwcx,
2395 sizeof(WNDCLASSEXW),
2396 sizeof(ULONG));
2397 RtlCopyMemory(&CapturedClassInfo,
2398 lpwcx,
2399 sizeof(WNDCLASSEXW));
2400
2401 CapturedName = ProbeForReadUnicodeString(ClassName);
2402
2403 ProbeForRead(pClassMenuName,
2404 sizeof(CLSMENUNAME),
2405 1);
2406
2407 CapturedMenuName = ProbeForReadUnicodeString(pClassMenuName->pusMenuName);
2408
2409 if ( (CapturedName.Length & 1) ||
2410 (CapturedMenuName.Length & 1) ||
2411 (CapturedClassInfo.cbClsExtra < 0) ||
2412 ((CapturedClassInfo.cbClsExtra + CapturedName.Length +
2413 CapturedMenuName.Length + sizeof(CLS))
2414 < (ULONG)CapturedClassInfo.cbClsExtra) ||
2415 (CapturedClassInfo.cbWndExtra < 0) ||
2416 (CapturedClassInfo.hInstance == NULL) )
2417 {
2418 ERR("NtUserRegisterClassExWOW Invalid Parameter Error!\n");
2419 goto InvalidParameter;
2420 }
2421
2422 if (CapturedName.Length != 0)
2423 {
2424 ProbeForRead(CapturedName.Buffer,
2425 CapturedName.Length,
2426 sizeof(WCHAR));
2427 }
2428 else
2429 {
2430 if (!IS_ATOM(CapturedName.Buffer))
2431 {
2432 ERR("NtUserRegisterClassExWOW ClassName Error!\n");
2433 goto InvalidParameter;
2434 }
2435 }
2436
2437 if (CapturedMenuName.Length != 0)
2438 {
2439 ProbeForRead(CapturedMenuName.Buffer,
2440 CapturedMenuName.Length,
2441 sizeof(WCHAR));
2442 }
2443 else if (CapturedMenuName.Buffer != NULL &&
2444 !IS_INTRESOURCE(CapturedMenuName.Buffer))
2445 {
2446 ERR("NtUserRegisterClassExWOW MenuName Error!\n");
2447 InvalidParameter:
2448 EngSetLastError(ERROR_INVALID_PARAMETER);
2449 _SEH2_LEAVE;
2450 }
2451
2452 if (IsCallProcHandle(lpwcx->lpfnWndProc))
2453 { // Never seen this yet, but I'm sure it's a little haxxy trick!
2454 // If this pops up we know what todo!
2455 ERR("NtUserRegisterClassExWOW WndProc is CallProc!!\n");
2456 }
2457
2458 TRACE("NtUserRegisterClassExWOW MnuN %wZ\n",&CapturedMenuName);
2459
2460 /* Register the class */
2461 Ret = UserRegisterClass(&CapturedClassInfo,
2462 &CapturedName,
2463 &CapturedMenuName,
2464 fnID,
2465 Flags);
2466 }
2467 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2468 {
2469 ERR("NtUserRegisterClassExWOW Exception Error!\n");
2470 SetLastNtError(_SEH2_GetExceptionCode());
2471 }
2472 _SEH2_END;
2473 /*
2474 if (!Ret)
2475 {
2476 ERR("NtUserRegisterClassExWOW Null Return!\n");
2477 }
2478 */
2479 UserLeave();
2480
2481 return Ret;
2482 }
2483
2484 ULONG_PTR APIENTRY
2485 NtUserSetClassLong(HWND hWnd,
2486 INT Offset,
2487 ULONG_PTR dwNewLong,
2488 BOOL Ansi)
2489 {
2490 PPROCESSINFO pi;
2491 PWND Window;
2492 ULONG_PTR Ret = 0;
2493
2494 UserEnterExclusive();
2495
2496 pi = GetW32ProcessInfo();
2497
2498 Window = UserGetWindowObject(hWnd);
2499 if (Window != NULL)
2500 {
2501 if (Window->head.pti->ppi != pi)
2502 {
2503 EngSetLastError(ERROR_ACCESS_DENIED);
2504 goto Cleanup;
2505 }
2506
2507 _SEH2_TRY
2508 {
2509 UNICODE_STRING Value;
2510
2511 /* Probe the parameters */
2512 if (Offset == GCW_ATOM || Offset == GCLP_MENUNAME)
2513 {
2514 Value = ProbeForReadUnicodeString((PUNICODE_STRING)dwNewLong);
2515 if (Value.Length & 1)
2516 {
2517 goto InvalidParameter;
2518 }
2519
2520 if (Value.Length != 0)
2521 {
2522 ProbeForRead(Value.Buffer,
2523 Value.Length,
2524 sizeof(WCHAR));
2525 }
2526 else
2527 {
2528 if (Offset == GCW_ATOM && !IS_ATOM(Value.Buffer))
2529 {
2530 goto InvalidParameter;
2531 }
2532 else if (Offset == GCLP_MENUNAME && !IS_INTRESOURCE(Value.Buffer))
2533 {
2534 InvalidParameter:
2535 EngSetLastError(ERROR_INVALID_PARAMETER);
2536 _SEH2_LEAVE;
2537 }
2538 }
2539
2540 dwNewLong = (ULONG_PTR)&Value;
2541 }
2542
2543 Ret = UserSetClassLongPtr(Window->pcls,
2544 Offset,
2545 dwNewLong,
2546 Ansi);
2547 }
2548 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2549 {
2550 SetLastNtError(_SEH2_GetExceptionCode());
2551 }
2552 _SEH2_END;
2553 }
2554
2555 Cleanup:
2556 UserLeave();
2557
2558 return Ret;
2559 }
2560
2561 WORD
2562 APIENTRY
2563 NtUserSetClassWord(
2564 HWND hWnd,
2565 INT nIndex,
2566 WORD wNewWord)
2567 {
2568 /*
2569 * NOTE: Obsoleted in 32-bit windows
2570 */
2571 return(0);
2572 }
2573
2574 BOOL
2575 APIENTRY
2576 NtUserUnregisterClass(
2577 IN PUNICODE_STRING ClassNameOrAtom,
2578 IN HINSTANCE hInstance,
2579 OUT PCLSMENUNAME pClassMenuName)
2580 {
2581 UNICODE_STRING SafeClassName;
2582 NTSTATUS Status;
2583 BOOL Ret;
2584
2585 Status = ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName, ClassNameOrAtom);
2586 if (!NT_SUCCESS(Status))
2587 {
2588 ERR("Error capturing the class name\n");
2589 SetLastNtError(Status);
2590 return FALSE;
2591 }
2592
2593 UserEnterExclusive();
2594
2595 /* Unregister the class */
2596 Ret = UserUnregisterClass(&SafeClassName, hInstance, NULL); // Null for now~
2597
2598 UserLeave();
2599
2600 if (SafeClassName.Buffer && !IS_ATOM(SafeClassName.Buffer))
2601 ExFreePoolWithTag(SafeClassName.Buffer, TAG_STRING);
2602
2603 return Ret;
2604 }
2605
2606
2607 /* NOTE: For system classes hInstance is not NULL here, but User32Instance */
2608 BOOL
2609 APIENTRY
2610 NtUserGetClassInfo(
2611 HINSTANCE hInstance,
2612 PUNICODE_STRING ClassName,
2613 LPWNDCLASSEXW lpWndClassEx,
2614 LPWSTR *ppszMenuName,
2615 BOOL bAnsi)
2616 {
2617 UNICODE_STRING SafeClassName;
2618 WNDCLASSEXW Safewcexw;
2619 PCLS Class;
2620 RTL_ATOM ClassAtom = 0;
2621 PPROCESSINFO ppi;
2622 BOOL Ret = TRUE;
2623 NTSTATUS Status;
2624
2625 _SEH2_TRY
2626 {
2627 ProbeForWrite( lpWndClassEx, sizeof(WNDCLASSEXW), sizeof(ULONG));
2628 RtlCopyMemory( &Safewcexw, lpWndClassEx, sizeof(WNDCLASSEXW));
2629 }
2630 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2631 {
2632 SetLastNtError(_SEH2_GetExceptionCode());
2633 _SEH2_YIELD(return FALSE);
2634 }
2635 _SEH2_END;
2636
2637 Status = ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName, ClassName);
2638 if (!NT_SUCCESS(Status))
2639 {
2640 ERR("Error capturing the class name\n");
2641 SetLastNtError(Status);
2642 return FALSE;
2643 }
2644
2645 // If null instance use client.
2646 if (!hInstance) hInstance = hModClient;
2647
2648 TRACE("GetClassInfo(%wZ, %p)\n", SafeClassName, hInstance);
2649
2650 /* NOTE: Need exclusive lock because getting the wndproc might require the
2651 creation of a call procedure handle */
2652 UserEnterExclusive();
2653
2654 ppi = GetW32ProcessInfo();
2655 if (!(ppi->W32PF_flags & W32PF_CLASSESREGISTERED))
2656 {
2657 UserRegisterSystemClasses();
2658 }
2659
2660 ClassAtom = IntGetClassAtom(&SafeClassName,
2661 hInstance,
2662 ppi,
2663 &Class,
2664 NULL);
2665 if (ClassAtom != (RTL_ATOM)0)
2666 {
2667 Ret = UserGetClassInfo(Class, &Safewcexw, bAnsi, hInstance);
2668 }
2669 else
2670 {
2671 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST);
2672 Ret = FALSE;
2673 }
2674
2675 UserLeave();
2676
2677 if (Ret)
2678 {
2679 _SEH2_TRY
2680 {
2681 /* Emulate Function. */
2682 if (ppszMenuName) *ppszMenuName = (LPWSTR)Safewcexw.lpszMenuName;
2683
2684 RtlCopyMemory(lpWndClassEx, &Safewcexw, sizeof(WNDCLASSEXW));
2685
2686 // From Wine:
2687 /* We must return the atom of the class here instead of just TRUE. */
2688 /* Undocumented behavior! Return the class atom as a BOOL! */
2689 Ret = (BOOL)ClassAtom;
2690 }
2691 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2692 {
2693 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST);
2694 Ret = FALSE;
2695 }
2696 _SEH2_END;
2697 }
2698
2699 if (!IS_ATOM(SafeClassName.Buffer))
2700 ExFreePoolWithTag(SafeClassName.Buffer, TAG_STRING);
2701
2702 return Ret;
2703 }
2704
2705
2706 INT APIENTRY
2707 NtUserGetClassName (IN HWND hWnd,
2708 IN BOOL Real,
2709 OUT PUNICODE_STRING ClassName)
2710 {
2711 PWND Window;
2712 UNICODE_STRING CapturedClassName;
2713 INT iCls, Ret = 0;
2714 RTL_ATOM Atom = 0;
2715
2716 UserEnterShared();
2717
2718 Window = UserGetWindowObject(hWnd);
2719 if (Window != NULL)
2720 {
2721 if (Real && Window->fnid && !(Window->fnid & FNID_DESTROY))
2722 {
2723 if (LookupFnIdToiCls(Window->fnid, &iCls))
2724 {
2725 Atom = gpsi->atomSysClass[iCls];
2726 }
2727 }
2728
2729 _SEH2_TRY
2730 {
2731 ProbeForWriteUnicodeString(ClassName);
2732 CapturedClassName = *ClassName;
2733
2734 /* Get the class name */
2735 Ret = UserGetClassName(Window->pcls,
2736 &CapturedClassName,
2737 Atom,
2738 FALSE);
2739
2740 if (Ret != 0)
2741 {
2742 /* Update the Length field */
2743 ClassName->Length = CapturedClassName.Length;
2744 }
2745 }
2746 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2747 {
2748 SetLastNtError(_SEH2_GetExceptionCode());
2749 }
2750 _SEH2_END;
2751 }
2752
2753 UserLeave();
2754
2755 return Ret;
2756 }
2757
2758 /* Return Pointer to Class structure. */
2759 PCLS
2760 APIENTRY
2761 NtUserGetWOWClass(
2762 HINSTANCE hInstance,
2763 PUNICODE_STRING ClassName)
2764 {
2765 UNICODE_STRING SafeClassName;
2766 PPROCESSINFO pi;
2767 PCLS Class = NULL;
2768 RTL_ATOM ClassAtom = 0;
2769 NTSTATUS Status;
2770
2771 Status = ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName, ClassName);
2772 if (!NT_SUCCESS(Status))
2773 {
2774 ERR("Error capturing the class name\n");
2775 SetLastNtError(Status);
2776 return FALSE;
2777 }
2778
2779 UserEnterExclusive();
2780
2781 pi = GetW32ProcessInfo();
2782
2783 ClassAtom = IntGetClassAtom(&SafeClassName,
2784 hInstance,
2785 pi,
2786 &Class,
2787 NULL);
2788 if (!ClassAtom)
2789 {
2790 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST);
2791 }
2792
2793
2794 if (SafeClassName.Buffer && !IS_ATOM(SafeClassName.Buffer))
2795 ExFreePoolWithTag(SafeClassName.Buffer, TAG_STRING);
2796
2797 UserLeave();
2798 //
2799 // Don't forget to use DesktopPtrToUser( ? ) with return pointer in user space.
2800 //
2801 return Class;
2802 }
2803
2804 /* EOF */