[WIN32K]
[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 #ifdef NEW_CURSORICON
842 if (NewClass->spcur)
843 UserReferenceObject(NewClass->spcur);
844 if (NewClass->spicn)
845 UserReferenceObject(NewClass->spicn);
846 if (NewClass->spicnSm)
847 UserReferenceObject(NewClass->spicnSm);
848 #endif
849
850
851 /* Replace the class in the list */
852 (void)InterlockedExchangePointer((PVOID*)*ClassLinkPtr,
853 NewClass);
854 *ClassLinkPtr = &NewClass->pclsNext;
855
856 /* Free the obsolete class on the desktop heap */
857 Class->pclsBase = NULL;
858 IntDestroyClass(Class);
859 return TRUE;
860 }
861
862 return FALSE;
863 }
864
865 static VOID
866 IntCheckDesktopClasses(IN PDESKTOP Desktop,
867 IN OUT PCLS *ClassList,
868 IN BOOL FreeOnFailure,
869 OUT BOOL *Ret)
870 {
871 PCLS Class, NextClass, *Link;
872
873 /* NOTE: We only need to check base classes! When classes are no longer needed
874 on a desktop, the clones will be freed automatically as soon as possible.
875 However, we need to move base classes to the shared heap, as soon as
876 the last desktop heap where a class is allocated on is about to be destroyed.
877 If we didn't move the class to the shared heap, the class would become
878 inaccessible! */
879
880 ASSERT(Desktop != NULL);
881
882 Link = ClassList;
883 Class = *Link;
884 while (Class != NULL)
885 {
886 NextClass = Class->pclsNext;
887
888 ASSERT(Class->pclsBase == Class);
889
890 if (Class->rpdeskParent == Desktop &&
891 Class->cWndReferenceCount == 0)
892 {
893 /* There shouldn't be any clones around anymore! */
894 ASSERT(Class->pclsClone == NULL);
895
896 /* FIXME: If process is terminating, don't move the class but rather destroy it! */
897 /* FIXME: We could move the class to another desktop heap if there's still desktops
898 mapped into the process... */
899
900 /* Move the class to the shared heap */
901 if (IntMoveClassToSharedHeap(Class,
902 &Link))
903 {
904 ASSERT(*Link == NextClass);
905 }
906 else
907 {
908 ASSERT(NextClass == Class->pclsNext);
909
910 if (FreeOnFailure)
911 {
912 /* Unlink the base class */
913 (void)InterlockedExchangePointer((PVOID*)Link,
914 Class->pclsNext);
915
916 /* We can free the old base class now */
917 Class->pclsBase = NULL;
918 IntDestroyClass(Class);
919 }
920 else
921 {
922 Link = &Class->pclsNext;
923 *Ret = FALSE;
924 }
925 }
926 }
927 else
928 Link = &Class->pclsNext;
929
930 Class = NextClass;
931 }
932 }
933
934 BOOL
935 IntCheckProcessDesktopClasses(IN PDESKTOP Desktop,
936 IN BOOL FreeOnFailure)
937 {
938 PPROCESSINFO pi;
939 BOOL Ret = TRUE;
940
941 pi = GetW32ProcessInfo();
942
943 /* Check all local classes */
944 IntCheckDesktopClasses(Desktop,
945 &pi->pclsPrivateList,
946 FreeOnFailure,
947 &Ret);
948
949 /* Check all global classes */
950 IntCheckDesktopClasses(Desktop,
951 &pi->pclsPublicList,
952 FreeOnFailure,
953 &Ret);
954 if (!Ret)
955 {
956 ERR("Failed to move process classes from desktop 0x%p to the shared heap!\n", Desktop);
957 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
958 }
959
960 return Ret;
961 }
962
963 PCLS
964 FASTCALL
965 IntCreateClass(IN CONST WNDCLASSEXW* lpwcx,
966 IN PUNICODE_STRING ClassName,
967 IN PUNICODE_STRING MenuName,
968 IN DWORD fnID,
969 IN DWORD dwFlags,
970 IN PDESKTOP Desktop,
971 IN PPROCESSINFO pi)
972 {
973 SIZE_T ClassSize;
974 PCLS Class = NULL;
975 RTL_ATOM Atom;
976 WNDPROC WndProc;
977 PWSTR pszMenuName = NULL;
978 NTSTATUS Status = STATUS_SUCCESS;
979
980 TRACE("lpwcx=%p ClassName=%wZ MenuName=%wZ dwFlags=%08x Desktop=%p pi=%p\n",
981 lpwcx, ClassName, MenuName, dwFlags, Desktop, pi);
982
983 if (!IntRegisterClassAtom(ClassName,
984 &Atom))
985 {
986 ERR("Failed to register class atom!\n");
987 return NULL;
988 }
989
990 ClassSize = sizeof(*Class) + lpwcx->cbClsExtra;
991 if (MenuName->Length != 0)
992 {
993 pszMenuName = UserHeapAlloc(MenuName->Length + sizeof(UNICODE_NULL) +
994 RtlUnicodeStringToAnsiSize(MenuName));
995 if (pszMenuName == NULL)
996 goto NoMem;
997 }
998
999 if (Desktop != NULL)
1000 {
1001 Class = DesktopHeapAlloc(Desktop,
1002 ClassSize);
1003 }
1004 else
1005 {
1006 /* FIXME: The class was created before being connected
1007 to a desktop. It is possible for the desktop window,
1008 but should it be allowed for any other case? */
1009 Class = UserHeapAlloc(ClassSize);
1010 }
1011
1012 if (Class != NULL)
1013 {
1014 int iCls = 0;
1015
1016 RtlZeroMemory(Class, ClassSize);
1017
1018 Class->rpdeskParent = Desktop;
1019 Class->pclsBase = Class;
1020 Class->atomClassName = Atom;
1021 Class->fnid = fnID;
1022 Class->CSF_flags = dwFlags;
1023
1024 if (LookupFnIdToiCls(Class->fnid, &iCls))
1025 {
1026 gpsi->atomSysClass[iCls] = Class->atomClassName;
1027 }
1028
1029 _SEH2_TRY
1030 {
1031 PWSTR pszMenuNameBuffer = pszMenuName;
1032
1033 /* Need to protect with SEH since accessing the WNDCLASSEX structure
1034 and string buffers might raise an exception! We don't want to
1035 leak memory... */
1036 // What?! If the user interface was written correctly this would not be an issue!
1037 Class->lpfnWndProc = lpwcx->lpfnWndProc;
1038 Class->style = lpwcx->style;
1039 Class->cbclsExtra = lpwcx->cbClsExtra;
1040 Class->cbwndExtra = lpwcx->cbWndExtra;
1041 Class->hModule = lpwcx->hInstance;
1042 #ifdef NEW_CURSORICON
1043 Class->spicn = lpwcx->hIcon ? UserGetCurIconObject(lpwcx->hIcon) : NULL;
1044 Class->spcur = lpwcx->hCursor ? UserGetCurIconObject(lpwcx->hCursor) : NULL;
1045 Class->spicnSm = lpwcx->hIconSm ? UserGetCurIconObject(lpwcx->hIconSm) : NULL;
1046 #else
1047 Class->hIcon = lpwcx->hIcon;
1048 Class->hIconSm = lpwcx->hIconSm;
1049 Class->hCursor = lpwcx->hCursor;
1050 #endif
1051 ////
1052 Class->hbrBackground = lpwcx->hbrBackground;
1053
1054 /* Make a copy of the string */
1055 if (pszMenuNameBuffer != NULL)
1056 {
1057 Class->MenuNameIsString = TRUE;
1058
1059 Class->lpszClientUnicodeMenuName = pszMenuNameBuffer;
1060 RtlCopyMemory(Class->lpszClientUnicodeMenuName,
1061 MenuName->Buffer,
1062 MenuName->Length);
1063 Class->lpszClientUnicodeMenuName[MenuName->Length / sizeof(WCHAR)] = UNICODE_NULL;
1064
1065 pszMenuNameBuffer += (MenuName->Length / sizeof(WCHAR)) + 1;
1066 }
1067 else
1068 Class->lpszClientUnicodeMenuName = MenuName->Buffer;
1069
1070 /* Save an ANSI copy of the string */
1071 if (pszMenuNameBuffer != NULL)
1072 {
1073 ANSI_STRING AnsiString;
1074
1075 Class->lpszClientAnsiMenuName = (PSTR)pszMenuNameBuffer;
1076 AnsiString.MaximumLength = (USHORT)RtlUnicodeStringToAnsiSize(MenuName);
1077 AnsiString.Buffer = Class->lpszClientAnsiMenuName;
1078 Status = RtlUnicodeStringToAnsiString(&AnsiString,
1079 MenuName,
1080 FALSE);
1081 if (!NT_SUCCESS(Status))
1082 {
1083 ERR("Failed to convert unicode menu name to ansi!\n");
1084
1085 /* Life would've been much prettier if ntoskrnl exported RtlRaiseStatus()... */
1086 _SEH2_LEAVE;
1087 }
1088 }
1089 else
1090 Class->lpszClientAnsiMenuName = (PSTR)MenuName->Buffer;
1091
1092 /* Save kernel use menu name and ansi class name */
1093 Class->lpszMenuName = Class->lpszClientUnicodeMenuName; // FIXME!
1094 //Class->lpszAnsiClassName = FIXME
1095
1096 /* Server Side overrides class calling type (A/W)!
1097 User32 whine test_builtinproc: "deftest"
1098 built-in winproc - window A/W type automatically detected */
1099 if (!(Class->CSF_flags & CSF_SERVERSIDEPROC))
1100 {
1101 int i;
1102 WndProc = NULL;
1103 /* Due to the wine class "deftest" and most likely no FNID to reference
1104 from, sort through the Server Side list and compare proc addresses
1105 for match. This method will be used in related code.
1106 */
1107 for ( i = FNID_FIRST; i <= FNID_SWITCH; i++)
1108 { // Open ANSI or Unicode, just match, set and break.
1109 if (GETPFNCLIENTW(i) == Class->lpfnWndProc)
1110 {
1111 WndProc = GETPFNSERVER(i);
1112 break;
1113 }
1114 if (GETPFNCLIENTA(i) == Class->lpfnWndProc)
1115 {
1116 WndProc = GETPFNSERVER(i);
1117 break;
1118 }
1119 }
1120 if (WndProc)
1121 { // If a hit, we are Server Side so set the right flags and proc.
1122 Class->CSF_flags |= CSF_SERVERSIDEPROC;
1123 Class->CSF_flags &= ~CSF_ANSIPROC;
1124 Class->lpfnWndProc = WndProc;
1125 }
1126 }
1127
1128 if (!(Class->CSF_flags & CSF_ANSIPROC))
1129 Class->Unicode = TRUE;
1130
1131 if (Class->style & CS_GLOBALCLASS)
1132 Class->Global = TRUE;
1133 }
1134 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1135 {
1136 Status = _SEH2_GetExceptionCode();
1137 }
1138 _SEH2_END;
1139
1140 if (!NT_SUCCESS(Status))
1141 {
1142 ERR("Failed creating the class: 0x%x\n", Status);
1143
1144 SetLastNtError(Status);
1145
1146 if (pszMenuName != NULL)
1147 UserHeapFree(pszMenuName);
1148
1149 DesktopHeapFree(Desktop,
1150 Class);
1151 Class = NULL;
1152
1153 IntDeregisterClassAtom(Atom);
1154 }
1155 }
1156 else
1157 {
1158 NoMem:
1159 ERR("Failed to allocate class on Desktop 0x%p\n", Desktop);
1160
1161 if (pszMenuName != NULL)
1162 UserHeapFree(pszMenuName);
1163
1164 IntDeregisterClassAtom(Atom);
1165
1166 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1167 }
1168
1169 TRACE("Created class 0x%p with name %wZ and proc 0x%p for atom 0x%x and hInstance 0x%p, global %u\n",
1170 Class, ClassName, Class->lpfnWndProc, Atom, Class->hModule, Class->Global);
1171
1172 return Class;
1173 }
1174
1175 static PCLS
1176 IntFindClass(IN RTL_ATOM Atom,
1177 IN HINSTANCE hInstance,
1178 IN PCLS *ClassList,
1179 OUT PCLS **Link OPTIONAL)
1180 {
1181 PCLS Class, *PrevLink = ClassList;
1182
1183 Class = *PrevLink;
1184 while (Class != NULL)
1185 {
1186 if (Class->atomClassName == Atom &&
1187 (hInstance == NULL || Class->hModule == hInstance) &&
1188 !(Class->CSF_flags & CSF_WOWDEFERDESTROY))
1189 {
1190 ASSERT(Class->pclsBase == Class);
1191
1192 if (Link != NULL)
1193 *Link = PrevLink;
1194 break;
1195 }
1196
1197 PrevLink = &Class->pclsNext;
1198 Class = Class->pclsNext;
1199 }
1200
1201 return Class;
1202 }
1203
1204 BOOL
1205 NTAPI
1206 IntGetAtomFromStringOrAtom(
1207 _In_ PUNICODE_STRING ClassName,
1208 _Out_ RTL_ATOM *Atom)
1209 {
1210 BOOL Ret = FALSE;
1211
1212 if (ClassName->Length != 0)
1213 {
1214 WCHAR szBuf[65];
1215 PWSTR AtomName;
1216 NTSTATUS Status;
1217
1218 *Atom = 0;
1219
1220 /* NOTE: Caller has to protect the call with SEH! */
1221
1222 if (ClassName->Length != 0)
1223 {
1224 /* FIXME: Don't limit to 64 characters! use SEH when allocating memory! */
1225 if (ClassName->Length / sizeof(WCHAR) >= sizeof(szBuf) / sizeof(szBuf[0]))
1226 {
1227 EngSetLastError(ERROR_INVALID_PARAMETER);
1228 return (RTL_ATOM)0;
1229 }
1230
1231 /* We need to make a local copy of the class name! The caller could
1232 modify the buffer and we could overflow in RtlLookupAtomInAtomTable.
1233 We're protected by SEH, but the ranges that might be accessed were
1234 not probed... */
1235 RtlCopyMemory(szBuf,
1236 ClassName->Buffer,
1237 ClassName->Length);
1238 szBuf[ClassName->Length / sizeof(WCHAR)] = UNICODE_NULL;
1239 AtomName = szBuf;
1240 }
1241 else
1242 AtomName = ClassName->Buffer;
1243
1244 /* Lookup the atom */
1245 Status = RtlLookupAtomInAtomTable(gAtomTable,
1246 AtomName,
1247 Atom);
1248 if (NT_SUCCESS(Status))
1249 {
1250 Ret = TRUE;
1251 }
1252 else
1253 {
1254 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
1255 {
1256 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST);
1257 }
1258 else
1259 {
1260 SetLastNtError(Status);
1261 }
1262 }
1263 }
1264 else
1265 {
1266 ASSERT(IS_ATOM(ClassName->Buffer));
1267 *Atom = (RTL_ATOM)((ULONG_PTR)ClassName->Buffer);
1268 Ret = TRUE;
1269 }
1270
1271 return Ret;
1272 }
1273
1274 RTL_ATOM
1275 IntGetClassAtom(
1276 _In_ PUNICODE_STRING ClassName,
1277 IN HINSTANCE hInstance OPTIONAL,
1278 IN PPROCESSINFO pi OPTIONAL,
1279 OUT PCLS *BaseClass OPTIONAL,
1280 OUT PCLS **Link OPTIONAL)
1281 {
1282 RTL_ATOM Atom = (RTL_ATOM)0;
1283
1284 ASSERT(BaseClass != NULL);
1285
1286 if (IntGetAtomFromStringOrAtom(ClassName,
1287 &Atom) &&
1288 Atom != (RTL_ATOM)0)
1289 {
1290 PCLS Class;
1291
1292 /* Attempt to locate the class object */
1293
1294 ASSERT(pi != NULL);
1295
1296 /* Step 1: Try to find an exact match of locally registered classes */
1297 Class = IntFindClass(Atom,
1298 hInstance,
1299 &pi->pclsPrivateList,
1300 Link);
1301 if (Class != NULL)
1302 { TRACE("Step 1: 0x%p\n",Class );
1303 goto FoundClass;
1304 }
1305
1306 /* Step 2: Try to find any globally registered class. The hInstance
1307 is not relevant for global classes */
1308 Class = IntFindClass(Atom,
1309 NULL,
1310 &pi->pclsPublicList,
1311 Link);
1312 if (Class != NULL)
1313 { TRACE("Step 2: 0x%p 0x%p\n",Class, Class->hModule);
1314 goto FoundClass;
1315 }
1316
1317 /* Step 3: Try to find any local class registered by user32 */
1318 Class = IntFindClass(Atom,
1319 hModClient,
1320 &pi->pclsPrivateList,
1321 Link);
1322 if (Class != NULL)
1323 { TRACE("Step 3: 0x%p\n",Class );
1324 goto FoundClass;
1325 }
1326
1327 /* Step 4: Try to find any global class registered by user32 */
1328 Class = IntFindClass(Atom,
1329 hModClient,
1330 &pi->pclsPublicList,
1331 Link);
1332 if (Class == NULL)
1333 {
1334 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST);
1335 return (RTL_ATOM)0;
1336 }else{TRACE("Step 4: 0x%p\n",Class );}
1337
1338 FoundClass:
1339 *BaseClass = Class;
1340 }
1341
1342 return Atom;
1343 }
1344
1345 PCLS
1346 IntGetAndReferenceClass(PUNICODE_STRING ClassName, HINSTANCE hInstance, BOOL bDesktopThread)
1347 {
1348 PCLS *ClassLink, Class = NULL;
1349 RTL_ATOM ClassAtom;
1350 PTHREADINFO pti;
1351
1352 if (bDesktopThread)
1353 pti = gptiDesktopThread;
1354 else
1355 pti = PsGetCurrentThreadWin32Thread();
1356
1357 if ( !(pti->ppi->W32PF_flags & W32PF_CLASSESREGISTERED ))
1358 {
1359 UserRegisterSystemClasses();
1360 }
1361
1362 /* Check the class. */
1363
1364 TRACE("Finding Class %wZ for hInstance 0x%p\n", ClassName, hInstance);
1365
1366 ClassAtom = IntGetClassAtom(ClassName,
1367 hInstance,
1368 pti->ppi,
1369 &Class,
1370 &ClassLink);
1371
1372 if (ClassAtom == (RTL_ATOM)0)
1373 {
1374 if (IS_ATOM(ClassName->Buffer))
1375 {
1376 ERR("Class 0x%p not found\n", ClassName->Buffer);
1377 }
1378 else
1379 {
1380 ERR("Class \"%wZ\" not found\n", ClassName);
1381 }
1382
1383 EngSetLastError(ERROR_CANNOT_FIND_WND_CLASS);
1384 return NULL;
1385 }
1386
1387 TRACE("Referencing Class 0x%p with atom 0x%x\n", Class, ClassAtom);
1388 Class = IntReferenceClass(Class,
1389 ClassLink,
1390 pti->rpdesk);
1391 if (Class == NULL)
1392 {
1393 ERR("Failed to reference window class!\n");
1394 return NULL;
1395 }
1396
1397 return Class;
1398 }
1399
1400 RTL_ATOM
1401 UserRegisterClass(IN CONST WNDCLASSEXW* lpwcx,
1402 IN PUNICODE_STRING ClassName,
1403 IN PUNICODE_STRING MenuName,
1404 IN DWORD fnID,
1405 IN DWORD dwFlags)
1406 {
1407 PTHREADINFO pti;
1408 PPROCESSINFO pi;
1409 PCLS Class;
1410 RTL_ATOM ClassAtom;
1411 RTL_ATOM Ret = (RTL_ATOM)0;
1412
1413 /* NOTE: Accessing the buffers in ClassName and MenuName may raise exceptions! */
1414
1415 pti = GetW32ThreadInfo();
1416
1417 pi = pti->ppi;
1418
1419 // Need only to test for two conditions not four....... Fix more whine tests....
1420 if ( IntGetAtomFromStringOrAtom( ClassName, &ClassAtom) &&
1421 ClassAtom != (RTL_ATOM)0 &&
1422 !(dwFlags & CSF_SERVERSIDEPROC) ) // Bypass Server Sides
1423 {
1424 Class = IntFindClass( ClassAtom,
1425 lpwcx->hInstance,
1426 &pi->pclsPrivateList,
1427 NULL);
1428
1429 if (Class != NULL && !Class->Global)
1430 {
1431 // Local class already exists
1432 TRACE("Local Class 0x%x does already exist!\n", ClassAtom);
1433 EngSetLastError(ERROR_CLASS_ALREADY_EXISTS);
1434 return (RTL_ATOM)0;
1435 }
1436
1437 if (lpwcx->style & CS_GLOBALCLASS)
1438 {
1439 Class = IntFindClass( ClassAtom,
1440 NULL,
1441 &pi->pclsPublicList,
1442 NULL);
1443
1444 if (Class != NULL && Class->Global)
1445 {
1446 TRACE("Global Class 0x%x does already exist!\n", ClassAtom);
1447 EngSetLastError(ERROR_CLASS_ALREADY_EXISTS);
1448 return (RTL_ATOM)0;
1449 }
1450 }
1451 }
1452
1453 Class = IntCreateClass(lpwcx,
1454 ClassName,
1455 MenuName,
1456 fnID,
1457 dwFlags,
1458 pti->rpdesk,
1459 pi);
1460
1461 if (Class != NULL)
1462 {
1463 PCLS *List;
1464
1465 /* Register the class */
1466 if (Class->Global)
1467 List = &pi->pclsPublicList;
1468 else
1469 List = &pi->pclsPrivateList;
1470
1471 Class->pclsNext = *List;
1472 (void)InterlockedExchangePointer((PVOID*)List,
1473 Class);
1474
1475 Ret = Class->atomClassName;
1476 }
1477 else
1478 {
1479 ERR("UserRegisterClass: Yes, that is right, you have no Class!\n");
1480 }
1481
1482 return Ret;
1483 }
1484
1485 BOOL
1486 UserUnregisterClass(IN PUNICODE_STRING ClassName,
1487 IN HINSTANCE hInstance,
1488 OUT PCLSMENUNAME pClassMenuName)
1489 {
1490 PCLS *Link;
1491 PPROCESSINFO pi;
1492 RTL_ATOM ClassAtom;
1493 PCLS Class;
1494
1495 pi = GetW32ProcessInfo();
1496
1497 TRACE("UserUnregisterClass(%wZ, 0x%p)\n", ClassName, hInstance);
1498
1499 /* NOTE: Accessing the buffer in ClassName may raise an exception! */
1500 ClassAtom = IntGetClassAtom(ClassName,
1501 hInstance,
1502 pi,
1503 &Class,
1504 &Link);
1505 if (ClassAtom == (RTL_ATOM)0)
1506 {
1507 TRACE("UserUnregisterClass: No Class found.\n");
1508 return FALSE;
1509 }
1510
1511 ASSERT(Class != NULL);
1512
1513 if (Class->cWndReferenceCount != 0 ||
1514 Class->pclsClone != NULL)
1515 {
1516 TRACE("UserUnregisterClass: Class has a Window. Ct %u : Clone 0x%p\n", Class->cWndReferenceCount, Class->pclsClone);
1517 EngSetLastError(ERROR_CLASS_HAS_WINDOWS);
1518 return FALSE;
1519 }
1520
1521 /* Must be a base class! */
1522 ASSERT(Class->pclsBase == Class);
1523
1524 /* Unlink the class */
1525 *Link = Class->pclsNext;
1526
1527 if (NT_SUCCESS(IntDeregisterClassAtom(Class->atomClassName)))
1528 {
1529 TRACE("Class 0x%p\n", Class);
1530 TRACE("UserUnregisterClass: Good Exit!\n");
1531 Class->atomClassName = 0; // Don't let it linger...
1532 /* Finally free the resources */
1533 IntDestroyClass(Class);
1534 return TRUE;
1535 }
1536 ERR("UserUnregisterClass: Can not deregister Class Atom.\n");
1537 return FALSE;
1538 }
1539
1540 INT
1541 UserGetClassName(IN PCLS Class,
1542 IN OUT PUNICODE_STRING ClassName,
1543 IN RTL_ATOM Atom,
1544 IN BOOL Ansi)
1545 {
1546 NTSTATUS Status = STATUS_SUCCESS;
1547 WCHAR szStaticTemp[32];
1548 PWSTR szTemp = NULL;
1549 ULONG BufLen = sizeof(szStaticTemp);
1550 INT Ret = 0;
1551
1552 /* Note: Accessing the buffer in ClassName may raise an exception! */
1553
1554 _SEH2_TRY
1555 {
1556 if (Ansi)
1557 {
1558 PANSI_STRING AnsiClassName = (PANSI_STRING)ClassName;
1559 UNICODE_STRING UnicodeClassName;
1560
1561 /* Limit the size of the static buffer on the stack to the
1562 size of the buffer provided by the caller */
1563 if (BufLen / sizeof(WCHAR) > AnsiClassName->MaximumLength)
1564 {
1565 BufLen = AnsiClassName->MaximumLength * sizeof(WCHAR);
1566 }
1567
1568 /* Find out how big the buffer needs to be */
1569 Status = RtlQueryAtomInAtomTable(gAtomTable,
1570 Class->atomClassName,
1571 NULL,
1572 NULL,
1573 szStaticTemp,
1574 &BufLen);
1575 if (Status == STATUS_BUFFER_TOO_SMALL)
1576 {
1577 if (BufLen / sizeof(WCHAR) > AnsiClassName->MaximumLength)
1578 {
1579 /* The buffer required exceeds the ansi buffer provided,
1580 pretend like we're using the ansi buffer and limit the
1581 size to the buffer size provided */
1582 BufLen = AnsiClassName->MaximumLength * sizeof(WCHAR);
1583 }
1584
1585 /* Allocate a temporary buffer that can hold the unicode class name */
1586 szTemp = ExAllocatePoolWithTag(PagedPool,
1587 BufLen,
1588 USERTAG_CLASS);
1589 if (szTemp == NULL)
1590 {
1591 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1592 _SEH2_LEAVE;
1593 }
1594
1595 /* Query the class name */
1596 Status = RtlQueryAtomInAtomTable(gAtomTable,
1597 Atom ? Atom : Class->atomClassName,
1598 NULL,
1599 NULL,
1600 szTemp,
1601 &BufLen);
1602 }
1603 else
1604 szTemp = szStaticTemp;
1605
1606 if (NT_SUCCESS(Status))
1607 {
1608 /* Convert the atom name to ansi */
1609
1610 RtlInitUnicodeString(&UnicodeClassName,
1611 szTemp);
1612
1613 Status = RtlUnicodeStringToAnsiString(AnsiClassName,
1614 &UnicodeClassName,
1615 FALSE);
1616 if (!NT_SUCCESS(Status))
1617 {
1618 SetLastNtError(Status);
1619 _SEH2_LEAVE;
1620 }
1621 }
1622
1623 Ret = BufLen / sizeof(WCHAR);
1624 }
1625 else /* !ANSI */
1626 {
1627 BufLen = ClassName->MaximumLength;
1628
1629 /* Query the atom name */
1630 Status = RtlQueryAtomInAtomTable(gAtomTable,
1631 Atom ? Atom : Class->atomClassName,
1632 NULL,
1633 NULL,
1634 ClassName->Buffer,
1635 &BufLen);
1636
1637 if (!NT_SUCCESS(Status))
1638 {
1639 SetLastNtError(Status);
1640 _SEH2_LEAVE;
1641 }
1642
1643 Ret = BufLen / sizeof(WCHAR);
1644 }
1645 }
1646 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1647 {
1648 SetLastNtError(_SEH2_GetExceptionCode());
1649 }
1650 _SEH2_END;
1651
1652 if (Ansi && szTemp != NULL && szTemp != szStaticTemp)
1653 {
1654 ExFreePoolWithTag(szTemp, USERTAG_CLASS);
1655 }
1656
1657 return Ret;
1658 }
1659
1660 static BOOL
1661 IntSetClassMenuName(IN PCLS Class,
1662 IN PUNICODE_STRING MenuName)
1663 {
1664 BOOL Ret = FALSE;
1665
1666 /* Change the base class first */
1667 Class = Class->pclsBase;
1668
1669 if (MenuName->Length != 0)
1670 {
1671 ANSI_STRING AnsiString;
1672 PWSTR strBufW;
1673
1674 AnsiString.MaximumLength = (USHORT)RtlUnicodeStringToAnsiSize(MenuName);
1675
1676 strBufW = UserHeapAlloc(MenuName->Length + sizeof(UNICODE_NULL) +
1677 AnsiString.MaximumLength);
1678 if (strBufW != NULL)
1679 {
1680 _SEH2_TRY
1681 {
1682 NTSTATUS Status;
1683
1684 /* Copy the unicode string */
1685 RtlCopyMemory(strBufW,
1686 MenuName->Buffer,
1687 MenuName->Length);
1688 strBufW[MenuName->Length / sizeof(WCHAR)] = UNICODE_NULL;
1689
1690 /* Create an ANSI copy of the string */
1691 AnsiString.Buffer = (PSTR)(strBufW + (MenuName->Length / sizeof(WCHAR)) + 1);
1692 Status = RtlUnicodeStringToAnsiString(&AnsiString,
1693 MenuName,
1694 FALSE);
1695 if (!NT_SUCCESS(Status))
1696 {
1697 SetLastNtError(Status);
1698 _SEH2_LEAVE;
1699 }
1700
1701 Ret = TRUE;
1702 }
1703 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1704 {
1705 SetLastNtError(_SEH2_GetExceptionCode());
1706 }
1707 _SEH2_END;
1708
1709 if (Ret)
1710 {
1711 /* Update the base class */
1712 IntFreeClassMenuName(Class);
1713 Class->lpszClientUnicodeMenuName = strBufW;
1714 Class->lpszClientAnsiMenuName = AnsiString.Buffer;
1715 Class->MenuNameIsString = TRUE;
1716
1717 /* Update the clones */
1718 Class = Class->pclsClone;
1719 while (Class != NULL)
1720 {
1721 Class->lpszClientUnicodeMenuName = strBufW;
1722 Class->lpszClientAnsiMenuName = AnsiString.Buffer;
1723 Class->MenuNameIsString = TRUE;
1724
1725 Class = Class->pclsNext;
1726 }
1727 }
1728 else
1729 {
1730 ERR("Failed to copy class menu name!\n");
1731 UserHeapFree(strBufW);
1732 }
1733 }
1734 else
1735 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1736 }
1737 else
1738 {
1739 ASSERT(IS_INTRESOURCE(MenuName->Buffer));
1740
1741 /* Update the base class */
1742 IntFreeClassMenuName(Class);
1743 Class->lpszClientUnicodeMenuName = MenuName->Buffer;
1744 Class->lpszClientAnsiMenuName = (PSTR)MenuName->Buffer;
1745 Class->MenuNameIsString = FALSE;
1746
1747 /* Update the clones */
1748 Class = Class->pclsClone;
1749 while (Class != NULL)
1750 {
1751 Class->lpszClientUnicodeMenuName = MenuName->Buffer;
1752 Class->lpszClientAnsiMenuName = (PSTR)MenuName->Buffer;
1753 Class->MenuNameIsString = FALSE;
1754
1755 Class = Class->pclsNext;
1756 }
1757
1758 Ret = TRUE;
1759 }
1760
1761 return Ret;
1762 }
1763
1764 #ifndef NEW_CURSORICON
1765 BOOL FASTCALL
1766 IntClassDestroyIcon(HANDLE hCurIcon)
1767 {
1768 PCURICON_OBJECT CurIcon;
1769 BOOL Ret;
1770
1771 if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
1772 {
1773
1774 ERR("hCurIcon was not found!\n");
1775 return FALSE;
1776 }
1777 /* Note: IntDestroyCurIconObject will remove our reference for us! */
1778 Ret = IntDestroyCurIconObject(CurIcon, GetW32ProcessInfo());
1779 if (!Ret)
1780 {
1781 ERR("hCurIcon was not Destroyed!\n");
1782 }
1783 return Ret;
1784 }
1785 #endif
1786
1787 ULONG_PTR
1788 UserSetClassLongPtr(IN PCLS Class,
1789 IN INT Index,
1790 IN ULONG_PTR NewLong,
1791 IN BOOL Ansi)
1792 {
1793 ULONG_PTR Ret = 0;
1794 #ifndef NEW_CURSORICON
1795 HANDLE hIconSmIntern = NULL;
1796 #endif
1797
1798 /* NOTE: For GCLP_MENUNAME and GCW_ATOM this function may raise an exception! */
1799
1800 /* Change the information in the base class first, then update the clones */
1801 Class = Class->pclsBase;
1802
1803 if (Index >= 0)
1804 {
1805 PULONG_PTR Data;
1806
1807 TRACE("SetClassLong(%d, %x)\n", Index, NewLong);
1808
1809 if (((ULONG)Index + sizeof(ULONG_PTR)) < (ULONG)Index ||
1810 ((ULONG)Index + sizeof(ULONG_PTR)) > (ULONG)Class->cbclsExtra)
1811 {
1812 EngSetLastError(ERROR_INVALID_PARAMETER);
1813 return 0;
1814 }
1815
1816 Data = (PULONG_PTR)((ULONG_PTR)(Class + 1) + Index);
1817
1818 /* FIXME: Data might be a unaligned pointer! Might be a problem on
1819 certain architectures, maybe using RtlCopyMemory is a
1820 better choice for those architectures! */
1821 Ret = *Data;
1822 *Data = NewLong;
1823
1824 /* Update the clones */
1825 Class = Class->pclsClone;
1826 while (Class != NULL)
1827 {
1828 *(PULONG_PTR)((ULONG_PTR)(Class + 1) + Index) = NewLong;
1829 Class = Class->pclsNext;
1830 }
1831
1832 return Ret;
1833 }
1834
1835 switch (Index)
1836 {
1837 case GCL_CBWNDEXTRA:
1838 Ret = (ULONG_PTR)Class->cbwndExtra;
1839 Class->cbwndExtra = (INT)NewLong;
1840
1841 /* Update the clones */
1842 Class = Class->pclsClone;
1843 while (Class != NULL)
1844 {
1845 Class->cbwndExtra = (INT)NewLong;
1846 Class = Class->pclsNext;
1847 }
1848
1849 break;
1850
1851 case GCL_CBCLSEXTRA:
1852 EngSetLastError(ERROR_INVALID_PARAMETER);
1853 break;
1854
1855 case GCLP_HBRBACKGROUND:
1856 Ret = (ULONG_PTR)Class->hbrBackground;
1857 Class->hbrBackground = (HBRUSH)NewLong;
1858
1859 /* Update the clones */
1860 Class = Class->pclsClone;
1861 while (Class != NULL)
1862 {
1863 Class->hbrBackground = (HBRUSH)NewLong;
1864 Class = Class->pclsNext;
1865 }
1866 break;
1867
1868 #ifdef NEW_CURSORICON
1869 case GCLP_HCURSOR:
1870 {
1871 PCURICON_OBJECT NewCursor = NULL;
1872
1873 if (NewLong)
1874 {
1875 NewCursor = UserGetCurIconObject((HCURSOR)NewLong);
1876 if (!NewCursor)
1877 {
1878 EngSetLastError(ERROR_INVALID_CURSOR_HANDLE);
1879 return 0;
1880 }
1881 }
1882
1883 if (Class->spcur)
1884 {
1885 Ret = (ULONG_PTR)UserHMGetHandle(Class->spcur);
1886 UserDereferenceObject(Class->spcur);
1887 }
1888 else
1889 {
1890 Ret = 0;
1891 }
1892
1893 if (Ret == NewLong)
1894 {
1895 /* It's a nop */
1896 return Ret;
1897 }
1898
1899 Class->spcur = NewCursor;
1900
1901 /* Update the clones */
1902 Class = Class->pclsClone;
1903 while (Class != NULL)
1904 {
1905 if (Class->spcur)
1906 UserDereferenceObject(Class->spcur);
1907 if (NewCursor)
1908 UserReferenceObject(NewCursor);
1909 Class->spcur = NewCursor;
1910 Class = Class->pclsNext;
1911 }
1912
1913 break;
1914 }
1915 #else
1916 case GCLP_HCURSOR:
1917 /* FIXME: Get handle from pointer to CURSOR object */
1918 Ret = (ULONG_PTR)Class->hCursor;
1919 Class->hCursor = (HANDLE)NewLong;
1920
1921 /* Update the clones */
1922 Class = Class->pclsClone;
1923 while (Class != NULL)
1924 {
1925 Class->hCursor = (HANDLE)NewLong;
1926 Class = Class->pclsNext;
1927 }
1928 break;
1929 #endif
1930
1931 // MSDN:
1932 // hIconSm, A handle to a small icon that is associated with the window class.
1933 // If this member is NULL, the system searches the icon resource specified by
1934 // the hIcon member for an icon of the appropriate size to use as the small icon.
1935 //
1936 case GCLP_HICON:
1937 #ifdef NEW_CURSORICON
1938 {
1939 PCURICON_OBJECT NewIcon = NULL;
1940 PCURICON_OBJECT NewSmallIcon = NULL;
1941
1942 if (NewLong)
1943 {
1944 NewIcon = UserGetCurIconObject((HCURSOR)NewLong);
1945 if (!NewIcon)
1946 {
1947 EngSetLastError(ERROR_INVALID_ICON_HANDLE);
1948 return 0;
1949 }
1950 }
1951
1952 if (Class->spicn)
1953 {
1954 Ret = (ULONG_PTR)UserHMGetHandle(Class->spicn);
1955 UserDereferenceObject(Class->spicn);
1956 }
1957 else
1958 {
1959 Ret = 0;
1960 }
1961
1962 if (Ret == NewLong)
1963 {
1964 /* It's a nop */
1965 return Ret;
1966 }
1967
1968 if (Ret && (Class->CSF_flags & CSF_CACHEDSMICON))
1969 {
1970 /* We will change the small icon */
1971 UserDereferenceObject(Class->spicnSm);
1972 Class->spicnSm = NULL;
1973 Class->CSF_flags &= ~CSF_CACHEDSMICON;
1974 }
1975
1976 if (NewLong && !Class->spicnSm)
1977 {
1978 /* Create the new small icon from the new large(?) one */
1979 HICON SmallIconHandle = co_IntCopyImage(
1980 (HICON)NewLong,
1981 IMAGE_ICON,
1982 UserGetSystemMetrics( SM_CXSMICON ),
1983 UserGetSystemMetrics( SM_CYSMICON ),
1984 0);
1985 if (SmallIconHandle)
1986 {
1987 /* So use it */
1988 NewSmallIcon = Class->spicnSm = UserGetCurIconObject(SmallIconHandle);
1989 /* Let the handle go, we have the reference on the object */
1990 NtUserDestroyCursor(SmallIconHandle, FALSE);
1991 Class->CSF_flags |= CSF_CACHEDSMICON;
1992 }
1993 }
1994
1995 Class->spicn = NewIcon;
1996
1997 /* Update the clones */
1998 Class = Class->pclsClone;
1999 while (Class != NULL)
2000 {
2001 if (Class->spicn)
2002 UserDereferenceObject(Class->spicn);
2003 if (NewIcon)
2004 UserReferenceObject(NewIcon);
2005 Class->spicn = NewIcon;
2006 if (NewSmallIcon)
2007 {
2008 if (Class->spicnSm)
2009 UserDereferenceObject(Class->spicnSm);
2010 UserReferenceObject(NewSmallIcon);
2011 Class->spicnSm = NewSmallIcon;
2012 Class->CSF_flags |= CSF_CACHEDSMICON;
2013 }
2014 Class = Class->pclsNext;
2015 }
2016 break;
2017 }
2018 #else
2019 /* FIXME: Get handle from pointer to ICON object */
2020 Ret = (ULONG_PTR)Class->hIcon;
2021 if (Class->hIcon == (HANDLE)NewLong) break;
2022 if (Ret && Class->hIconSmIntern)
2023 {
2024 IntClassDestroyIcon(Class->hIconSmIntern);
2025 Class->CSF_flags &= ~CSF_CACHEDSMICON;
2026 Class->hIconSmIntern = NULL;
2027 }
2028 if (NewLong && !Class->hIconSm)
2029 {
2030 hIconSmIntern = Class->hIconSmIntern = co_IntCopyImage( (HICON)NewLong, IMAGE_ICON,
2031 UserGetSystemMetrics( SM_CXSMICON ),
2032 UserGetSystemMetrics( SM_CYSMICON ), 0 );
2033 Class->CSF_flags |= CSF_CACHEDSMICON;
2034 }
2035 Class->hIcon = (HANDLE)NewLong;
2036
2037 /* Update the clones */
2038 Class = Class->pclsClone;
2039 while (Class != NULL)
2040 {
2041 Class->hIcon = (HANDLE)NewLong;
2042 Class->hIconSmIntern = hIconSmIntern;
2043 Class = Class->pclsNext;
2044 }
2045 break;
2046 #endif
2047
2048 case GCLP_HICONSM:
2049 #ifdef NEW_CURSORICON
2050 {
2051 PCURICON_OBJECT NewSmallIcon = NULL;
2052
2053 if (NewLong)
2054 {
2055 NewSmallIcon = UserGetCurIconObject((HCURSOR)NewLong);
2056 if (!NewSmallIcon)
2057 {
2058 EngSetLastError(ERROR_INVALID_ICON_HANDLE);
2059 return 0;
2060 }
2061 }
2062
2063 if (Class->spicnSm)
2064 {
2065 Ret = (ULONG_PTR)UserHMGetHandle(Class->spicnSm);
2066 UserDereferenceObject(Class->spicnSm);
2067 }
2068 else
2069 {
2070 Ret = 0;
2071 }
2072
2073 Class->CSF_flags &= ~CSF_CACHEDSMICON;
2074 Class->spicnSm = NewSmallIcon;
2075
2076 /* Update the clones */
2077 Class = Class->pclsClone;
2078 while (Class != NULL)
2079 {
2080 if (Class->spicnSm)
2081 UserDereferenceObject(Class->spicnSm);
2082 if (NewSmallIcon)
2083 UserReferenceObject(NewSmallIcon);
2084 Class->CSF_flags &= ~CSF_CACHEDSMICON;
2085 Class->spicnSm = NewSmallIcon;
2086 Class = Class->pclsNext;
2087 }
2088 }
2089 break;
2090 #else
2091 /* FIXME: Get handle from pointer to ICON object */
2092 Ret = (ULONG_PTR)Class->hIconSm;
2093 if (Class->hIconSm == (HANDLE)NewLong) break;
2094 if (Class->CSF_flags & CSF_CACHEDSMICON)
2095 {
2096 if (Class->hIconSmIntern)
2097 {
2098 IntClassDestroyIcon(Class->hIconSmIntern);
2099 Class->hIconSmIntern = NULL;
2100 }
2101 Class->CSF_flags &= ~CSF_CACHEDSMICON;
2102 }
2103 if (Class->hIcon && !Class->hIconSmIntern)
2104 {
2105 hIconSmIntern = Class->hIconSmIntern = co_IntCopyImage( Class->hIcon, IMAGE_ICON,
2106 UserGetSystemMetrics( SM_CXSMICON ),
2107 UserGetSystemMetrics( SM_CYSMICON ), 0 );
2108
2109 if (hIconSmIntern) Class->CSF_flags |= CSF_CACHEDSMICON;
2110 //// FIXME: Very hacky here but it passes the tests....
2111 Ret = 0; // Fixes 1009
2112 }
2113 Class->hIconSm = (HANDLE)NewLong;
2114
2115 /* Update the clones */
2116 Class = Class->pclsClone;
2117 while (Class != NULL)
2118 {
2119 Class->hIconSm = (HANDLE)NewLong;
2120 Class->hIconSmIntern = hIconSmIntern;
2121 Class = Class->pclsNext;
2122 }
2123 break;
2124 #endif
2125
2126 case GCLP_HMODULE:
2127 Ret = (ULONG_PTR)Class->hModule;
2128 Class->hModule = (HINSTANCE)NewLong;
2129
2130 /* Update the clones */
2131 Class = Class->pclsClone;
2132 while (Class != NULL)
2133 {
2134 Class->hModule = (HINSTANCE)NewLong;
2135 Class = Class->pclsNext;
2136 }
2137 break;
2138
2139 case GCLP_MENUNAME:
2140 {
2141 PUNICODE_STRING Value = (PUNICODE_STRING)NewLong;
2142
2143 if (!IntSetClassMenuName(Class,
2144 Value))
2145 {
2146 ERR("Setting the class menu name failed!\n");
2147 }
2148
2149 /* FIXME: Really return NULL? Wine does so... */
2150 break;
2151 }
2152
2153 case GCL_STYLE:
2154 Ret = (ULONG_PTR)Class->style;
2155 Class->style = (UINT)NewLong;
2156
2157 /* FIXME: What if the CS_GLOBALCLASS style is changed? should we
2158 move the class to the appropriate list? For now, we save
2159 the original value in Class->Global, so we can always
2160 locate the appropriate list */
2161
2162 /* Update the clones */
2163 Class = Class->pclsClone;
2164 while (Class != NULL)
2165 {
2166 Class->style = (UINT)NewLong;
2167 Class = Class->pclsNext;
2168 }
2169 break;
2170
2171 case GCLP_WNDPROC:
2172 Ret = (ULONG_PTR)IntSetClassWndProc(Class,
2173 (WNDPROC)NewLong,
2174 Ansi);
2175 break;
2176
2177 case GCW_ATOM:
2178 {
2179 PUNICODE_STRING Value = (PUNICODE_STRING)NewLong;
2180
2181 Ret = (ULONG_PTR)Class->atomClassName;
2182 if (!IntSetClassAtom(Class,
2183 Value))
2184 {
2185 Ret = 0;
2186 }
2187 break;
2188 }
2189
2190 default:
2191 EngSetLastError(ERROR_INVALID_INDEX);
2192 break;
2193 }
2194
2195 return Ret;
2196 }
2197
2198 static BOOL
2199 UserGetClassInfo(IN PCLS Class,
2200 OUT PWNDCLASSEXW lpwcx,
2201 IN BOOL Ansi,
2202 HINSTANCE hInstance)
2203 {
2204 if (!Class) return FALSE;
2205
2206 lpwcx->style = Class->style;
2207
2208 // If fnId is set, clear the global bit. See wine class test check_style.
2209 if (Class->fnid)
2210 lpwcx->style &= ~CS_GLOBALCLASS;
2211
2212 lpwcx->lpfnWndProc = IntGetClassWndProc(Class, Ansi);
2213
2214 lpwcx->cbClsExtra = Class->cbclsExtra;
2215 lpwcx->cbWndExtra = Class->cbwndExtra;
2216 #ifdef NEW_CURSORICON
2217 lpwcx->hIcon = Class->spicn ? UserHMGetHandle(Class->spicn) : NULL;
2218 lpwcx->hCursor = Class->spcur ? UserHMGetHandle(Class->spcur) : NULL;
2219 lpwcx->hIconSm = Class->spicnSm ? UserHMGetHandle(Class->spicnSm) : NULL;
2220 #else
2221 lpwcx->hIcon = Class->hIcon; /* FIXME: Get handle from pointer */
2222 lpwcx->hCursor = Class->hCursor; /* FIXME: Get handle from pointer */
2223 /* FIXME: Get handle from pointer */
2224 lpwcx->hIconSm = Class->hIconSm ? Class->hIconSm : Class->hIconSmIntern;
2225 #endif
2226 lpwcx->hbrBackground = Class->hbrBackground;
2227
2228 /* Copy non-string to user first. */
2229 if (Ansi)
2230 ((PWNDCLASSEXA)lpwcx)->lpszMenuName = Class->lpszClientAnsiMenuName;
2231 else
2232 lpwcx->lpszMenuName = Class->lpszClientUnicodeMenuName;
2233 /*
2234 * FIXME: CLSMENUNAME has the answers! Copy the already made buffers from there!
2235 * Cls: lpszMenuName and lpszAnsiClassName should be used by kernel space.
2236 * lpszClientXxxMenuName should already be mapped to user space.
2237 */
2238 /* Copy string ptr to user. */
2239 if ( Class->lpszClientUnicodeMenuName != NULL &&
2240 Class->MenuNameIsString)
2241 {
2242 lpwcx->lpszMenuName = UserHeapAddressToUser(Ansi ?
2243 (PVOID)Class->lpszClientAnsiMenuName :
2244 (PVOID)Class->lpszClientUnicodeMenuName);
2245 }
2246
2247 if (hInstance == hModClient)
2248 lpwcx->hInstance = NULL;
2249 else
2250 lpwcx->hInstance = hInstance;
2251
2252 /* FIXME: Return the string? Okay! This is performed in User32! */
2253 //lpwcx->lpszClassName = (LPCWSTR)((ULONG_PTR)Class->atomClassName);
2254
2255 return TRUE;
2256 }
2257
2258 //
2259 // ???
2260 //
2261 BOOL
2262 FASTCALL
2263 UserRegisterSystemClasses(VOID)
2264 {
2265 UINT i;
2266 UNICODE_STRING ClassName, MenuName;
2267 PPROCESSINFO ppi = GetW32ProcessInfo();
2268 WNDCLASSEXW wc;
2269 PCLS Class;
2270 BOOL Ret = TRUE;
2271 HBRUSH hBrush;
2272 DWORD Flags = 0;
2273
2274 if (ppi->W32PF_flags & W32PF_CLASSESREGISTERED)
2275 return TRUE;
2276
2277 if ( hModClient == NULL)
2278 return FALSE;
2279
2280 RtlZeroMemory(&ClassName, sizeof(ClassName));
2281 RtlZeroMemory(&MenuName, sizeof(MenuName));
2282
2283 for (i = 0; i != ARRAYSIZE(DefaultServerClasses); i++)
2284 {
2285 if (!IS_ATOM(DefaultServerClasses[i].ClassName))
2286 {
2287 RtlInitUnicodeString(&ClassName, DefaultServerClasses[i].ClassName);
2288 }
2289 else
2290 {
2291 ClassName.Buffer = DefaultServerClasses[i].ClassName;
2292 ClassName.Length = 0;
2293 ClassName.MaximumLength = 0;
2294 }
2295
2296 wc.cbSize = sizeof(wc);
2297 wc.style = DefaultServerClasses[i].Style;
2298
2299 Flags |= CSF_SERVERSIDEPROC;
2300
2301 if (DefaultServerClasses[i].ProcW)
2302 {
2303 wc.lpfnWndProc = DefaultServerClasses[i].ProcW;
2304 wc.hInstance = hModuleWin;
2305 }
2306 else
2307 {
2308 wc.lpfnWndProc = GETPFNSERVER(DefaultServerClasses[i].fiId);
2309 wc.hInstance = hModClient;
2310 }
2311
2312 wc.cbClsExtra = 0;
2313 wc.cbWndExtra = DefaultServerClasses[i].ExtraBytes;
2314 wc.hIcon = NULL;
2315 wc.hCursor = DefaultServerClasses[i].hCursor;
2316 hBrush = DefaultServerClasses[i].hBrush;
2317 if (hBrush <= (HBRUSH)COLOR_MENUBAR)
2318 {
2319 hBrush = IntGetSysColorBrush((INT)hBrush);
2320 }
2321 wc.hbrBackground = hBrush;
2322 wc.lpszMenuName = NULL;
2323 wc.lpszClassName = ClassName.Buffer;
2324 wc.hIconSm = NULL;
2325
2326 Class = IntCreateClass( &wc,
2327 &ClassName,
2328 &MenuName,
2329 DefaultServerClasses[i].fiId,
2330 Flags,
2331 NULL,
2332 ppi);
2333 if (Class != NULL)
2334 {
2335 Class->pclsNext = ppi->pclsPublicList;
2336 (void)InterlockedExchangePointer((PVOID*)&ppi->pclsPublicList,
2337 Class);
2338
2339 ppi->dwRegisteredClasses |= ICLASS_TO_MASK(DefaultServerClasses[i].iCls);
2340 }
2341 else
2342 {
2343 ERR("!!! Registering system class failed!\n");
2344 Ret = FALSE;
2345 }
2346 }
2347 if (Ret) ppi->W32PF_flags |= W32PF_CLASSESREGISTERED;
2348 return Ret;
2349 }
2350
2351 /* SYSCALLS *****************************************************************/
2352
2353 RTL_ATOM
2354 APIENTRY
2355 NtUserRegisterClassExWOW(
2356 WNDCLASSEXW* lpwcx,
2357 PUNICODE_STRING ClassName,
2358 PUNICODE_STRING ClsNVersion,
2359 PCLSMENUNAME pClassMenuName,
2360 DWORD fnID,
2361 DWORD Flags,
2362 LPDWORD pWow)
2363 /*
2364 * FUNCTION:
2365 * Registers a new class with the window manager
2366 * ARGUMENTS:
2367 * lpwcx = Win32 extended window class structure
2368 * bUnicodeClass = Whether to send ANSI or unicode strings
2369 * to window procedures
2370 * RETURNS:
2371 * Atom identifying the new class
2372 */
2373 {
2374 WNDCLASSEXW CapturedClassInfo = {0};
2375 UNICODE_STRING CapturedName = {0}, CapturedMenuName = {0};
2376 RTL_ATOM Ret = (RTL_ATOM)0;
2377 PPROCESSINFO ppi = GetW32ProcessInfo();
2378
2379 if (Flags & ~(CSF_ANSIPROC))
2380 {
2381 ERR("NtUserRegisterClassExWOW Bad Flags!\n");
2382 EngSetLastError(ERROR_INVALID_FLAGS);
2383 return Ret;
2384 }
2385
2386 UserEnterExclusive();
2387
2388 TRACE("NtUserRegisterClassExWOW ClsN %wZ\n",ClassName);
2389
2390 if ( !(ppi->W32PF_flags & W32PF_CLASSESREGISTERED ))
2391 {
2392 UserRegisterSystemClasses();
2393 }
2394
2395 _SEH2_TRY
2396 {
2397 /* Probe the parameters and basic parameter checks */
2398 if (ProbeForReadUint(&lpwcx->cbSize) != sizeof(WNDCLASSEXW))
2399 {
2400 ERR("NtUserRegisterClassExWOW Wrong cbSize!\n");
2401 goto InvalidParameter;
2402 }
2403
2404 ProbeForRead(lpwcx,
2405 sizeof(WNDCLASSEXW),
2406 sizeof(ULONG));
2407 RtlCopyMemory(&CapturedClassInfo,
2408 lpwcx,
2409 sizeof(WNDCLASSEXW));
2410
2411 CapturedName = ProbeForReadUnicodeString(ClassName);
2412
2413 ProbeForRead(pClassMenuName,
2414 sizeof(CLSMENUNAME),
2415 1);
2416
2417 CapturedMenuName = ProbeForReadUnicodeString(pClassMenuName->pusMenuName);
2418
2419 if ( (CapturedName.Length & 1) ||
2420 (CapturedMenuName.Length & 1) ||
2421 (CapturedClassInfo.cbClsExtra < 0) ||
2422 ((CapturedClassInfo.cbClsExtra + CapturedName.Length +
2423 CapturedMenuName.Length + sizeof(CLS))
2424 < (ULONG)CapturedClassInfo.cbClsExtra) ||
2425 (CapturedClassInfo.cbWndExtra < 0) ||
2426 (CapturedClassInfo.hInstance == NULL) )
2427 {
2428 ERR("NtUserRegisterClassExWOW Invalid Parameter Error!\n");
2429 goto InvalidParameter;
2430 }
2431
2432 if (CapturedName.Length != 0)
2433 {
2434 ProbeForRead(CapturedName.Buffer,
2435 CapturedName.Length,
2436 sizeof(WCHAR));
2437 }
2438 else
2439 {
2440 if (!IS_ATOM(CapturedName.Buffer))
2441 {
2442 ERR("NtUserRegisterClassExWOW ClassName Error!\n");
2443 goto InvalidParameter;
2444 }
2445 }
2446
2447 if (CapturedMenuName.Length != 0)
2448 {
2449 ProbeForRead(CapturedMenuName.Buffer,
2450 CapturedMenuName.Length,
2451 sizeof(WCHAR));
2452 }
2453 else if (CapturedMenuName.Buffer != NULL &&
2454 !IS_INTRESOURCE(CapturedMenuName.Buffer))
2455 {
2456 ERR("NtUserRegisterClassExWOW MenuName Error!\n");
2457 InvalidParameter:
2458 EngSetLastError(ERROR_INVALID_PARAMETER);
2459 _SEH2_LEAVE;
2460 }
2461
2462 if (IsCallProcHandle(lpwcx->lpfnWndProc))
2463 { // Never seen this yet, but I'm sure it's a little haxxy trick!
2464 // If this pops up we know what todo!
2465 ERR("NtUserRegisterClassExWOW WndProc is CallProc!!\n");
2466 }
2467
2468 TRACE("NtUserRegisterClassExWOW MnuN %wZ\n",&CapturedMenuName);
2469
2470 /* Register the class */
2471 Ret = UserRegisterClass(&CapturedClassInfo,
2472 &CapturedName,
2473 &CapturedMenuName,
2474 fnID,
2475 Flags);
2476 }
2477 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2478 {
2479 ERR("NtUserRegisterClassExWOW Exception Error!\n");
2480 SetLastNtError(_SEH2_GetExceptionCode());
2481 }
2482 _SEH2_END;
2483 /*
2484 if (!Ret)
2485 {
2486 ERR("NtUserRegisterClassExWOW Null Return!\n");
2487 }
2488 */
2489 UserLeave();
2490
2491 return Ret;
2492 }
2493
2494 ULONG_PTR APIENTRY
2495 NtUserSetClassLong(HWND hWnd,
2496 INT Offset,
2497 ULONG_PTR dwNewLong,
2498 BOOL Ansi)
2499 {
2500 PPROCESSINFO pi;
2501 PWND Window;
2502 ULONG_PTR Ret = 0;
2503
2504 UserEnterExclusive();
2505
2506 pi = GetW32ProcessInfo();
2507
2508 Window = UserGetWindowObject(hWnd);
2509 if (Window != NULL)
2510 {
2511 if (Window->head.pti->ppi != pi)
2512 {
2513 EngSetLastError(ERROR_ACCESS_DENIED);
2514 goto Cleanup;
2515 }
2516
2517 _SEH2_TRY
2518 {
2519 UNICODE_STRING Value;
2520
2521 /* Probe the parameters */
2522 if (Offset == GCW_ATOM || Offset == GCLP_MENUNAME)
2523 {
2524 Value = ProbeForReadUnicodeString((PUNICODE_STRING)dwNewLong);
2525 if (Value.Length & 1)
2526 {
2527 goto InvalidParameter;
2528 }
2529
2530 if (Value.Length != 0)
2531 {
2532 ProbeForRead(Value.Buffer,
2533 Value.Length,
2534 sizeof(WCHAR));
2535 }
2536 else
2537 {
2538 if (Offset == GCW_ATOM && !IS_ATOM(Value.Buffer))
2539 {
2540 goto InvalidParameter;
2541 }
2542 else if (Offset == GCLP_MENUNAME && !IS_INTRESOURCE(Value.Buffer))
2543 {
2544 InvalidParameter:
2545 EngSetLastError(ERROR_INVALID_PARAMETER);
2546 _SEH2_LEAVE;
2547 }
2548 }
2549
2550 dwNewLong = (ULONG_PTR)&Value;
2551 }
2552
2553 Ret = UserSetClassLongPtr(Window->pcls,
2554 Offset,
2555 dwNewLong,
2556 Ansi);
2557 }
2558 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2559 {
2560 SetLastNtError(_SEH2_GetExceptionCode());
2561 }
2562 _SEH2_END;
2563 }
2564
2565 Cleanup:
2566 UserLeave();
2567
2568 return Ret;
2569 }
2570
2571 WORD
2572 APIENTRY
2573 NtUserSetClassWord(
2574 HWND hWnd,
2575 INT nIndex,
2576 WORD wNewWord)
2577 {
2578 /*
2579 * NOTE: Obsoleted in 32-bit windows
2580 */
2581 return(0);
2582 }
2583
2584 BOOL
2585 APIENTRY
2586 NtUserUnregisterClass(
2587 IN PUNICODE_STRING ClassNameOrAtom,
2588 IN HINSTANCE hInstance,
2589 OUT PCLSMENUNAME pClassMenuName)
2590 {
2591 UNICODE_STRING SafeClassName;
2592 NTSTATUS Status;
2593 BOOL Ret;
2594
2595 Status = ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName, ClassNameOrAtom);
2596 if (!NT_SUCCESS(Status))
2597 {
2598 ERR("Error capturing the class name\n");
2599 SetLastNtError(Status);
2600 return FALSE;
2601 }
2602
2603 UserEnterExclusive();
2604
2605 /* Unregister the class */
2606 Ret = UserUnregisterClass(&SafeClassName, hInstance, NULL); // Null for now~
2607
2608 UserLeave();
2609
2610 if (SafeClassName.Buffer && !IS_ATOM(SafeClassName.Buffer))
2611 ExFreePoolWithTag(SafeClassName.Buffer, TAG_STRING);
2612
2613 return Ret;
2614 }
2615
2616
2617 /* NOTE: For system classes hInstance is not NULL here, but User32Instance */
2618 BOOL
2619 APIENTRY
2620 NtUserGetClassInfo(
2621 HINSTANCE hInstance,
2622 PUNICODE_STRING ClassName,
2623 LPWNDCLASSEXW lpWndClassEx,
2624 LPWSTR *ppszMenuName,
2625 BOOL bAnsi)
2626 {
2627 UNICODE_STRING SafeClassName;
2628 WNDCLASSEXW Safewcexw;
2629 PCLS Class;
2630 RTL_ATOM ClassAtom = 0;
2631 PPROCESSINFO ppi;
2632 BOOL Ret = TRUE;
2633 NTSTATUS Status;
2634
2635 _SEH2_TRY
2636 {
2637 ProbeForWrite( lpWndClassEx, sizeof(WNDCLASSEXW), sizeof(ULONG));
2638 RtlCopyMemory( &Safewcexw, lpWndClassEx, sizeof(WNDCLASSEXW));
2639 }
2640 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2641 {
2642 SetLastNtError(_SEH2_GetExceptionCode());
2643 _SEH2_YIELD(return FALSE);
2644 }
2645 _SEH2_END;
2646
2647 Status = ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName, ClassName);
2648 if (!NT_SUCCESS(Status))
2649 {
2650 ERR("Error capturing the class name\n");
2651 SetLastNtError(Status);
2652 return FALSE;
2653 }
2654
2655 // If null instance use client.
2656 if (!hInstance) hInstance = hModClient;
2657
2658 TRACE("GetClassInfo(%wZ, %p)\n", SafeClassName, hInstance);
2659
2660 /* NOTE: Need exclusive lock because getting the wndproc might require the
2661 creation of a call procedure handle */
2662 UserEnterExclusive();
2663
2664 ppi = GetW32ProcessInfo();
2665 if (!(ppi->W32PF_flags & W32PF_CLASSESREGISTERED))
2666 {
2667 UserRegisterSystemClasses();
2668 }
2669
2670 ClassAtom = IntGetClassAtom(&SafeClassName,
2671 hInstance,
2672 ppi,
2673 &Class,
2674 NULL);
2675 if (ClassAtom != (RTL_ATOM)0)
2676 {
2677 Ret = UserGetClassInfo(Class, &Safewcexw, bAnsi, hInstance);
2678 }
2679 else
2680 {
2681 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST);
2682 Ret = FALSE;
2683 }
2684
2685 UserLeave();
2686
2687 if (Ret)
2688 {
2689 _SEH2_TRY
2690 {
2691 /* Emulate Function. */
2692 if (ppszMenuName) *ppszMenuName = (LPWSTR)Safewcexw.lpszMenuName;
2693
2694 RtlCopyMemory(lpWndClassEx, &Safewcexw, sizeof(WNDCLASSEXW));
2695
2696 // From Wine:
2697 /* We must return the atom of the class here instead of just TRUE. */
2698 /* Undocumented behavior! Return the class atom as a BOOL! */
2699 Ret = (BOOL)ClassAtom;
2700 }
2701 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2702 {
2703 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST);
2704 Ret = FALSE;
2705 }
2706 _SEH2_END;
2707 }
2708
2709 if (!IS_ATOM(SafeClassName.Buffer))
2710 ExFreePoolWithTag(SafeClassName.Buffer, TAG_STRING);
2711
2712 return Ret;
2713 }
2714
2715
2716 INT APIENTRY
2717 NtUserGetClassName (IN HWND hWnd,
2718 IN BOOL Real,
2719 OUT PUNICODE_STRING ClassName)
2720 {
2721 PWND Window;
2722 UNICODE_STRING CapturedClassName;
2723 INT iCls, Ret = 0;
2724 RTL_ATOM Atom = 0;
2725
2726 UserEnterShared();
2727
2728 Window = UserGetWindowObject(hWnd);
2729 if (Window != NULL)
2730 {
2731 if (Real && Window->fnid && !(Window->fnid & FNID_DESTROY))
2732 {
2733 if (LookupFnIdToiCls(Window->fnid, &iCls))
2734 {
2735 Atom = gpsi->atomSysClass[iCls];
2736 }
2737 }
2738
2739 _SEH2_TRY
2740 {
2741 ProbeForWriteUnicodeString(ClassName);
2742 CapturedClassName = *ClassName;
2743
2744 /* Get the class name */
2745 Ret = UserGetClassName(Window->pcls,
2746 &CapturedClassName,
2747 Atom,
2748 FALSE);
2749
2750 if (Ret != 0)
2751 {
2752 /* Update the Length field */
2753 ClassName->Length = CapturedClassName.Length;
2754 }
2755 }
2756 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2757 {
2758 SetLastNtError(_SEH2_GetExceptionCode());
2759 }
2760 _SEH2_END;
2761 }
2762
2763 UserLeave();
2764
2765 return Ret;
2766 }
2767
2768 /* Return Pointer to Class structure. */
2769 PCLS
2770 APIENTRY
2771 NtUserGetWOWClass(
2772 HINSTANCE hInstance,
2773 PUNICODE_STRING ClassName)
2774 {
2775 UNICODE_STRING SafeClassName;
2776 PPROCESSINFO pi;
2777 PCLS Class = NULL;
2778 RTL_ATOM ClassAtom = 0;
2779 NTSTATUS Status;
2780
2781 Status = ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName, ClassName);
2782 if (!NT_SUCCESS(Status))
2783 {
2784 ERR("Error capturing the class name\n");
2785 SetLastNtError(Status);
2786 return FALSE;
2787 }
2788
2789 UserEnterExclusive();
2790
2791 pi = GetW32ProcessInfo();
2792
2793 ClassAtom = IntGetClassAtom(&SafeClassName,
2794 hInstance,
2795 pi,
2796 &Class,
2797 NULL);
2798 if (!ClassAtom)
2799 {
2800 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST);
2801 }
2802
2803
2804 if (SafeClassName.Buffer && !IS_ATOM(SafeClassName.Buffer))
2805 ExFreePoolWithTag(SafeClassName.Buffer, TAG_STRING);
2806
2807 UserLeave();
2808 //
2809 // Don't forget to use DesktopPtrToUser( ? ) with return pointer in user space.
2810 //
2811 return Class;
2812 }
2813
2814 /* EOF */