* Sync up to trunk head (r64921).
[reactos.git] / 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->spcur)
252 UserDereferenceObject(Class->spcur);
253 if (Class->spicnSm)
254 UserDereferenceObject(Class->spicnSm);
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 = NULL;
1980 if((NewIcon->CURSORF_flags & (CURSORF_LRSHARED | CURSORF_FROMRESOURCE))
1981 == (CURSORF_LRSHARED | CURSORF_FROMRESOURCE))
1982 {
1983 SmallIconHandle = co_IntCopyImage(
1984 (HICON)NewLong,
1985 IMAGE_ICON,
1986 UserGetSystemMetrics( SM_CXSMICON ),
1987 UserGetSystemMetrics( SM_CYSMICON ),
1988 LR_COPYFROMRESOURCE | LR_SHARED);
1989 }
1990 if (!SmallIconHandle)
1991 {
1992 /* Retry without copying from resource */
1993 SmallIconHandle = co_IntCopyImage(
1994 (HICON)NewLong,
1995 IMAGE_ICON,
1996 UserGetSystemMetrics( SM_CXSMICON ),
1997 UserGetSystemMetrics( SM_CYSMICON ),
1998 LR_SHARED);
1999 }
2000 if (SmallIconHandle)
2001 {
2002 /* So use it */
2003 NewSmallIcon = Class->spicnSm = UserGetCurIconObject(SmallIconHandle);
2004 Class->CSF_flags |= CSF_CACHEDSMICON;
2005 }
2006 }
2007
2008 Class->spicn = NewIcon;
2009
2010 /* Update the clones */
2011 Class = Class->pclsClone;
2012 while (Class != NULL)
2013 {
2014 if (Class->spicn)
2015 UserDereferenceObject(Class->spicn);
2016 if (NewIcon)
2017 UserReferenceObject(NewIcon);
2018 Class->spicn = NewIcon;
2019 if (NewSmallIcon)
2020 {
2021 if (Class->spicnSm)
2022 UserDereferenceObject(Class->spicnSm);
2023 UserReferenceObject(NewSmallIcon);
2024 Class->spicnSm = NewSmallIcon;
2025 Class->CSF_flags |= CSF_CACHEDSMICON;
2026 }
2027 Class = Class->pclsNext;
2028 }
2029 break;
2030 }
2031 #else
2032 /* FIXME: Get handle from pointer to ICON object */
2033 Ret = (ULONG_PTR)Class->hIcon;
2034 if (Class->hIcon == (HANDLE)NewLong) break;
2035 if (Ret && Class->hIconSmIntern)
2036 {
2037 IntClassDestroyIcon(Class->hIconSmIntern);
2038 Class->CSF_flags &= ~CSF_CACHEDSMICON;
2039 Class->hIconSmIntern = NULL;
2040 }
2041 if (NewLong && !Class->hIconSm)
2042 {
2043 hIconSmIntern = Class->hIconSmIntern = co_IntCopyImage( (HICON)NewLong, IMAGE_ICON,
2044 UserGetSystemMetrics( SM_CXSMICON ),
2045 UserGetSystemMetrics( SM_CYSMICON ), 0 );
2046 Class->CSF_flags |= CSF_CACHEDSMICON;
2047 }
2048 Class->hIcon = (HANDLE)NewLong;
2049
2050 /* Update the clones */
2051 Class = Class->pclsClone;
2052 while (Class != NULL)
2053 {
2054 Class->hIcon = (HANDLE)NewLong;
2055 Class->hIconSmIntern = hIconSmIntern;
2056 Class = Class->pclsNext;
2057 }
2058 break;
2059 #endif
2060
2061 case GCLP_HICONSM:
2062 #ifdef NEW_CURSORICON
2063 {
2064 PCURICON_OBJECT NewSmallIcon = NULL;
2065
2066 if (NewLong)
2067 {
2068 NewSmallIcon = UserGetCurIconObject((HCURSOR)NewLong);
2069 if (!NewSmallIcon)
2070 {
2071 EngSetLastError(ERROR_INVALID_ICON_HANDLE);
2072 return 0;
2073 }
2074 }
2075
2076 if (Class->spicnSm)
2077 {
2078 Ret = (ULONG_PTR)UserHMGetHandle(Class->spicnSm);
2079 UserDereferenceObject(Class->spicnSm);
2080 }
2081 else
2082 {
2083 Ret = 0;
2084 }
2085
2086 Class->CSF_flags &= ~CSF_CACHEDSMICON;
2087 Class->spicnSm = NewSmallIcon;
2088
2089 /* Update the clones */
2090 Class = Class->pclsClone;
2091 while (Class != NULL)
2092 {
2093 if (Class->spicnSm)
2094 UserDereferenceObject(Class->spicnSm);
2095 if (NewSmallIcon)
2096 UserReferenceObject(NewSmallIcon);
2097 Class->CSF_flags &= ~CSF_CACHEDSMICON;
2098 Class->spicnSm = NewSmallIcon;
2099 Class = Class->pclsNext;
2100 }
2101 }
2102 break;
2103 #else
2104 /* FIXME: Get handle from pointer to ICON object */
2105 Ret = (ULONG_PTR)Class->hIconSm;
2106 if (Class->hIconSm == (HANDLE)NewLong) break;
2107 if (Class->CSF_flags & CSF_CACHEDSMICON)
2108 {
2109 if (Class->hIconSmIntern)
2110 {
2111 IntClassDestroyIcon(Class->hIconSmIntern);
2112 Class->hIconSmIntern = NULL;
2113 }
2114 Class->CSF_flags &= ~CSF_CACHEDSMICON;
2115 }
2116 if (Class->hIcon && !Class->hIconSmIntern)
2117 {
2118 hIconSmIntern = Class->hIconSmIntern = co_IntCopyImage( Class->hIcon, IMAGE_ICON,
2119 UserGetSystemMetrics( SM_CXSMICON ),
2120 UserGetSystemMetrics( SM_CYSMICON ), 0 );
2121
2122 if (hIconSmIntern) Class->CSF_flags |= CSF_CACHEDSMICON;
2123 //// FIXME: Very hacky here but it passes the tests....
2124 Ret = 0; // Fixes 1009
2125 }
2126 Class->hIconSm = (HANDLE)NewLong;
2127
2128 /* Update the clones */
2129 Class = Class->pclsClone;
2130 while (Class != NULL)
2131 {
2132 Class->hIconSm = (HANDLE)NewLong;
2133 Class->hIconSmIntern = hIconSmIntern;
2134 Class = Class->pclsNext;
2135 }
2136 break;
2137 #endif
2138
2139 case GCLP_HMODULE:
2140 Ret = (ULONG_PTR)Class->hModule;
2141 Class->hModule = (HINSTANCE)NewLong;
2142
2143 /* Update the clones */
2144 Class = Class->pclsClone;
2145 while (Class != NULL)
2146 {
2147 Class->hModule = (HINSTANCE)NewLong;
2148 Class = Class->pclsNext;
2149 }
2150 break;
2151
2152 case GCLP_MENUNAME:
2153 {
2154 PUNICODE_STRING Value = (PUNICODE_STRING)NewLong;
2155
2156 if (!IntSetClassMenuName(Class,
2157 Value))
2158 {
2159 ERR("Setting the class menu name failed!\n");
2160 }
2161
2162 /* FIXME: Really return NULL? Wine does so... */
2163 break;
2164 }
2165
2166 case GCL_STYLE:
2167 Ret = (ULONG_PTR)Class->style;
2168 Class->style = (UINT)NewLong;
2169
2170 /* FIXME: What if the CS_GLOBALCLASS style is changed? should we
2171 move the class to the appropriate list? For now, we save
2172 the original value in Class->Global, so we can always
2173 locate the appropriate list */
2174
2175 /* Update the clones */
2176 Class = Class->pclsClone;
2177 while (Class != NULL)
2178 {
2179 Class->style = (UINT)NewLong;
2180 Class = Class->pclsNext;
2181 }
2182 break;
2183
2184 case GCLP_WNDPROC:
2185 Ret = (ULONG_PTR)IntSetClassWndProc(Class,
2186 (WNDPROC)NewLong,
2187 Ansi);
2188 break;
2189
2190 case GCW_ATOM:
2191 {
2192 PUNICODE_STRING Value = (PUNICODE_STRING)NewLong;
2193
2194 Ret = (ULONG_PTR)Class->atomClassName;
2195 if (!IntSetClassAtom(Class,
2196 Value))
2197 {
2198 Ret = 0;
2199 }
2200 break;
2201 }
2202
2203 default:
2204 EngSetLastError(ERROR_INVALID_INDEX);
2205 break;
2206 }
2207
2208 return Ret;
2209 }
2210
2211 static BOOL
2212 UserGetClassInfo(IN PCLS Class,
2213 OUT PWNDCLASSEXW lpwcx,
2214 IN BOOL Ansi,
2215 HINSTANCE hInstance)
2216 {
2217 if (!Class) return FALSE;
2218
2219 lpwcx->style = Class->style;
2220
2221 // If fnId is set, clear the global bit. See wine class test check_style.
2222 if (Class->fnid)
2223 lpwcx->style &= ~CS_GLOBALCLASS;
2224
2225 lpwcx->lpfnWndProc = IntGetClassWndProc(Class, Ansi);
2226
2227 lpwcx->cbClsExtra = Class->cbclsExtra;
2228 lpwcx->cbWndExtra = Class->cbwndExtra;
2229 #ifdef NEW_CURSORICON
2230 lpwcx->hIcon = Class->spicn ? UserHMGetHandle(Class->spicn) : NULL;
2231 lpwcx->hCursor = Class->spcur ? UserHMGetHandle(Class->spcur) : NULL;
2232 lpwcx->hIconSm = Class->spicnSm ? UserHMGetHandle(Class->spicnSm) : NULL;
2233 #else
2234 lpwcx->hIcon = Class->hIcon; /* FIXME: Get handle from pointer */
2235 lpwcx->hCursor = Class->hCursor; /* FIXME: Get handle from pointer */
2236 /* FIXME: Get handle from pointer */
2237 lpwcx->hIconSm = Class->hIconSm ? Class->hIconSm : Class->hIconSmIntern;
2238 #endif
2239 lpwcx->hbrBackground = Class->hbrBackground;
2240
2241 /* Copy non-string to user first. */
2242 if (Ansi)
2243 ((PWNDCLASSEXA)lpwcx)->lpszMenuName = Class->lpszClientAnsiMenuName;
2244 else
2245 lpwcx->lpszMenuName = Class->lpszClientUnicodeMenuName;
2246 /*
2247 * FIXME: CLSMENUNAME has the answers! Copy the already made buffers from there!
2248 * Cls: lpszMenuName and lpszAnsiClassName should be used by kernel space.
2249 * lpszClientXxxMenuName should already be mapped to user space.
2250 */
2251 /* Copy string ptr to user. */
2252 if ( Class->lpszClientUnicodeMenuName != NULL &&
2253 Class->MenuNameIsString)
2254 {
2255 lpwcx->lpszMenuName = UserHeapAddressToUser(Ansi ?
2256 (PVOID)Class->lpszClientAnsiMenuName :
2257 (PVOID)Class->lpszClientUnicodeMenuName);
2258 }
2259
2260 if (hInstance == hModClient)
2261 lpwcx->hInstance = NULL;
2262 else
2263 lpwcx->hInstance = hInstance;
2264
2265 /* FIXME: Return the string? Okay! This is performed in User32! */
2266 //lpwcx->lpszClassName = (LPCWSTR)((ULONG_PTR)Class->atomClassName);
2267
2268 return TRUE;
2269 }
2270
2271 //
2272 // ???
2273 //
2274 BOOL
2275 FASTCALL
2276 UserRegisterSystemClasses(VOID)
2277 {
2278 UINT i;
2279 UNICODE_STRING ClassName, MenuName;
2280 PPROCESSINFO ppi = GetW32ProcessInfo();
2281 WNDCLASSEXW wc;
2282 PCLS Class;
2283 BOOL Ret = TRUE;
2284 HBRUSH hBrush;
2285 DWORD Flags = 0;
2286
2287 if (ppi->W32PF_flags & W32PF_CLASSESREGISTERED)
2288 return TRUE;
2289
2290 if ( hModClient == NULL)
2291 return FALSE;
2292
2293 RtlZeroMemory(&ClassName, sizeof(ClassName));
2294 RtlZeroMemory(&MenuName, sizeof(MenuName));
2295
2296 for (i = 0; i != ARRAYSIZE(DefaultServerClasses); i++)
2297 {
2298 if (!IS_ATOM(DefaultServerClasses[i].ClassName))
2299 {
2300 RtlInitUnicodeString(&ClassName, DefaultServerClasses[i].ClassName);
2301 }
2302 else
2303 {
2304 ClassName.Buffer = DefaultServerClasses[i].ClassName;
2305 ClassName.Length = 0;
2306 ClassName.MaximumLength = 0;
2307 }
2308
2309 wc.cbSize = sizeof(wc);
2310 wc.style = DefaultServerClasses[i].Style;
2311
2312 Flags |= CSF_SERVERSIDEPROC;
2313
2314 if (DefaultServerClasses[i].ProcW)
2315 {
2316 wc.lpfnWndProc = DefaultServerClasses[i].ProcW;
2317 wc.hInstance = hModuleWin;
2318 }
2319 else
2320 {
2321 wc.lpfnWndProc = GETPFNSERVER(DefaultServerClasses[i].fiId);
2322 wc.hInstance = hModClient;
2323 }
2324
2325 wc.cbClsExtra = 0;
2326 wc.cbWndExtra = DefaultServerClasses[i].ExtraBytes;
2327 wc.hIcon = NULL;
2328 wc.hCursor = DefaultServerClasses[i].hCursor;
2329 hBrush = DefaultServerClasses[i].hBrush;
2330 if (hBrush <= (HBRUSH)COLOR_MENUBAR)
2331 {
2332 hBrush = IntGetSysColorBrush((INT)hBrush);
2333 }
2334 wc.hbrBackground = hBrush;
2335 wc.lpszMenuName = NULL;
2336 wc.lpszClassName = ClassName.Buffer;
2337 wc.hIconSm = NULL;
2338
2339 Class = IntCreateClass( &wc,
2340 &ClassName,
2341 &MenuName,
2342 DefaultServerClasses[i].fiId,
2343 Flags,
2344 NULL,
2345 ppi);
2346 if (Class != NULL)
2347 {
2348 Class->pclsNext = ppi->pclsPublicList;
2349 (void)InterlockedExchangePointer((PVOID*)&ppi->pclsPublicList,
2350 Class);
2351
2352 ppi->dwRegisteredClasses |= ICLASS_TO_MASK(DefaultServerClasses[i].iCls);
2353 }
2354 else
2355 {
2356 ERR("!!! Registering system class failed!\n");
2357 Ret = FALSE;
2358 }
2359 }
2360 if (Ret) ppi->W32PF_flags |= W32PF_CLASSESREGISTERED;
2361 return Ret;
2362 }
2363
2364 /* SYSCALLS *****************************************************************/
2365
2366 RTL_ATOM
2367 APIENTRY
2368 NtUserRegisterClassExWOW(
2369 WNDCLASSEXW* lpwcx,
2370 PUNICODE_STRING ClassName,
2371 PUNICODE_STRING ClsNVersion,
2372 PCLSMENUNAME pClassMenuName,
2373 DWORD fnID,
2374 DWORD Flags,
2375 LPDWORD pWow)
2376 /*
2377 * FUNCTION:
2378 * Registers a new class with the window manager
2379 * ARGUMENTS:
2380 * lpwcx = Win32 extended window class structure
2381 * bUnicodeClass = Whether to send ANSI or unicode strings
2382 * to window procedures
2383 * RETURNS:
2384 * Atom identifying the new class
2385 */
2386 {
2387 WNDCLASSEXW CapturedClassInfo = {0};
2388 UNICODE_STRING CapturedName = {0}, CapturedMenuName = {0};
2389 RTL_ATOM Ret = (RTL_ATOM)0;
2390 PPROCESSINFO ppi = GetW32ProcessInfo();
2391
2392 if (Flags & ~(CSF_ANSIPROC))
2393 {
2394 ERR("NtUserRegisterClassExWOW Bad Flags!\n");
2395 EngSetLastError(ERROR_INVALID_FLAGS);
2396 return Ret;
2397 }
2398
2399 UserEnterExclusive();
2400
2401 TRACE("NtUserRegisterClassExWOW ClsN %wZ\n",ClassName);
2402
2403 if ( !(ppi->W32PF_flags & W32PF_CLASSESREGISTERED ))
2404 {
2405 UserRegisterSystemClasses();
2406 }
2407
2408 _SEH2_TRY
2409 {
2410 /* Probe the parameters and basic parameter checks */
2411 if (ProbeForReadUint(&lpwcx->cbSize) != sizeof(WNDCLASSEXW))
2412 {
2413 ERR("NtUserRegisterClassExWOW Wrong cbSize!\n");
2414 goto InvalidParameter;
2415 }
2416
2417 ProbeForRead(lpwcx,
2418 sizeof(WNDCLASSEXW),
2419 sizeof(ULONG));
2420 RtlCopyMemory(&CapturedClassInfo,
2421 lpwcx,
2422 sizeof(WNDCLASSEXW));
2423
2424 CapturedName = ProbeForReadUnicodeString(ClassName);
2425
2426 ProbeForRead(pClassMenuName,
2427 sizeof(CLSMENUNAME),
2428 1);
2429
2430 CapturedMenuName = ProbeForReadUnicodeString(pClassMenuName->pusMenuName);
2431
2432 if ( (CapturedName.Length & 1) ||
2433 (CapturedMenuName.Length & 1) ||
2434 (CapturedClassInfo.cbClsExtra < 0) ||
2435 ((CapturedClassInfo.cbClsExtra + CapturedName.Length +
2436 CapturedMenuName.Length + sizeof(CLS))
2437 < (ULONG)CapturedClassInfo.cbClsExtra) ||
2438 (CapturedClassInfo.cbWndExtra < 0) ||
2439 (CapturedClassInfo.hInstance == NULL) )
2440 {
2441 ERR("NtUserRegisterClassExWOW Invalid Parameter Error!\n");
2442 goto InvalidParameter;
2443 }
2444
2445 if (CapturedName.Length != 0)
2446 {
2447 ProbeForRead(CapturedName.Buffer,
2448 CapturedName.Length,
2449 sizeof(WCHAR));
2450 }
2451 else
2452 {
2453 if (!IS_ATOM(CapturedName.Buffer))
2454 {
2455 ERR("NtUserRegisterClassExWOW ClassName Error!\n");
2456 goto InvalidParameter;
2457 }
2458 }
2459
2460 if (CapturedMenuName.Length != 0)
2461 {
2462 ProbeForRead(CapturedMenuName.Buffer,
2463 CapturedMenuName.Length,
2464 sizeof(WCHAR));
2465 }
2466 else if (CapturedMenuName.Buffer != NULL &&
2467 !IS_INTRESOURCE(CapturedMenuName.Buffer))
2468 {
2469 ERR("NtUserRegisterClassExWOW MenuName Error!\n");
2470 InvalidParameter:
2471 EngSetLastError(ERROR_INVALID_PARAMETER);
2472 _SEH2_LEAVE;
2473 }
2474
2475 if (IsCallProcHandle(lpwcx->lpfnWndProc))
2476 { // Never seen this yet, but I'm sure it's a little haxxy trick!
2477 // If this pops up we know what todo!
2478 ERR("NtUserRegisterClassExWOW WndProc is CallProc!!\n");
2479 }
2480
2481 TRACE("NtUserRegisterClassExWOW MnuN %wZ\n",&CapturedMenuName);
2482
2483 /* Register the class */
2484 Ret = UserRegisterClass(&CapturedClassInfo,
2485 &CapturedName,
2486 &CapturedMenuName,
2487 fnID,
2488 Flags);
2489 }
2490 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2491 {
2492 ERR("NtUserRegisterClassExWOW Exception Error!\n");
2493 SetLastNtError(_SEH2_GetExceptionCode());
2494 }
2495 _SEH2_END;
2496 /*
2497 if (!Ret)
2498 {
2499 ERR("NtUserRegisterClassExWOW Null Return!\n");
2500 }
2501 */
2502 UserLeave();
2503
2504 return Ret;
2505 }
2506
2507 ULONG_PTR APIENTRY
2508 NtUserSetClassLong(HWND hWnd,
2509 INT Offset,
2510 ULONG_PTR dwNewLong,
2511 BOOL Ansi)
2512 {
2513 PPROCESSINFO pi;
2514 PWND Window;
2515 ULONG_PTR Ret = 0;
2516
2517 UserEnterExclusive();
2518
2519 pi = GetW32ProcessInfo();
2520
2521 Window = UserGetWindowObject(hWnd);
2522 if (Window != NULL)
2523 {
2524 if (Window->head.pti->ppi != pi)
2525 {
2526 EngSetLastError(ERROR_ACCESS_DENIED);
2527 goto Cleanup;
2528 }
2529
2530 _SEH2_TRY
2531 {
2532 UNICODE_STRING Value;
2533
2534 /* Probe the parameters */
2535 if (Offset == GCW_ATOM || Offset == GCLP_MENUNAME)
2536 {
2537 Value = ProbeForReadUnicodeString((PUNICODE_STRING)dwNewLong);
2538 if (Value.Length & 1)
2539 {
2540 goto InvalidParameter;
2541 }
2542
2543 if (Value.Length != 0)
2544 {
2545 ProbeForRead(Value.Buffer,
2546 Value.Length,
2547 sizeof(WCHAR));
2548 }
2549 else
2550 {
2551 if (Offset == GCW_ATOM && !IS_ATOM(Value.Buffer))
2552 {
2553 goto InvalidParameter;
2554 }
2555 else if (Offset == GCLP_MENUNAME && !IS_INTRESOURCE(Value.Buffer))
2556 {
2557 InvalidParameter:
2558 EngSetLastError(ERROR_INVALID_PARAMETER);
2559 _SEH2_LEAVE;
2560 }
2561 }
2562
2563 dwNewLong = (ULONG_PTR)&Value;
2564 }
2565
2566 Ret = UserSetClassLongPtr(Window->pcls,
2567 Offset,
2568 dwNewLong,
2569 Ansi);
2570 }
2571 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2572 {
2573 SetLastNtError(_SEH2_GetExceptionCode());
2574 }
2575 _SEH2_END;
2576 }
2577
2578 Cleanup:
2579 UserLeave();
2580
2581 return Ret;
2582 }
2583
2584 WORD
2585 APIENTRY
2586 NtUserSetClassWord(
2587 HWND hWnd,
2588 INT nIndex,
2589 WORD wNewWord)
2590 {
2591 /*
2592 * NOTE: Obsoleted in 32-bit windows
2593 */
2594 return(0);
2595 }
2596
2597 BOOL
2598 APIENTRY
2599 NtUserUnregisterClass(
2600 IN PUNICODE_STRING ClassNameOrAtom,
2601 IN HINSTANCE hInstance,
2602 OUT PCLSMENUNAME pClassMenuName)
2603 {
2604 UNICODE_STRING SafeClassName;
2605 NTSTATUS Status;
2606 BOOL Ret;
2607
2608 Status = ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName, ClassNameOrAtom);
2609 if (!NT_SUCCESS(Status))
2610 {
2611 ERR("Error capturing the class name\n");
2612 SetLastNtError(Status);
2613 return FALSE;
2614 }
2615
2616 UserEnterExclusive();
2617
2618 /* Unregister the class */
2619 Ret = UserUnregisterClass(&SafeClassName, hInstance, NULL); // Null for now~
2620
2621 UserLeave();
2622
2623 if (SafeClassName.Buffer && !IS_ATOM(SafeClassName.Buffer))
2624 ExFreePoolWithTag(SafeClassName.Buffer, TAG_STRING);
2625
2626 return Ret;
2627 }
2628
2629
2630 /* NOTE: For system classes hInstance is not NULL here, but User32Instance */
2631 BOOL
2632 APIENTRY
2633 NtUserGetClassInfo(
2634 HINSTANCE hInstance,
2635 PUNICODE_STRING ClassName,
2636 LPWNDCLASSEXW lpWndClassEx,
2637 LPWSTR *ppszMenuName,
2638 BOOL bAnsi)
2639 {
2640 UNICODE_STRING SafeClassName;
2641 WNDCLASSEXW Safewcexw;
2642 PCLS Class;
2643 RTL_ATOM ClassAtom = 0;
2644 PPROCESSINFO ppi;
2645 BOOL Ret = TRUE;
2646 NTSTATUS Status;
2647
2648 _SEH2_TRY
2649 {
2650 ProbeForWrite( lpWndClassEx, sizeof(WNDCLASSEXW), sizeof(ULONG));
2651 RtlCopyMemory( &Safewcexw, lpWndClassEx, sizeof(WNDCLASSEXW));
2652 }
2653 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2654 {
2655 SetLastNtError(_SEH2_GetExceptionCode());
2656 _SEH2_YIELD(return FALSE);
2657 }
2658 _SEH2_END;
2659
2660 Status = ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName, ClassName);
2661 if (!NT_SUCCESS(Status))
2662 {
2663 ERR("Error capturing the class name\n");
2664 SetLastNtError(Status);
2665 return FALSE;
2666 }
2667
2668 // If null instance use client.
2669 if (!hInstance) hInstance = hModClient;
2670
2671 TRACE("GetClassInfo(%wZ, %p)\n", SafeClassName, hInstance);
2672
2673 /* NOTE: Need exclusive lock because getting the wndproc might require the
2674 creation of a call procedure handle */
2675 UserEnterExclusive();
2676
2677 ppi = GetW32ProcessInfo();
2678 if (!(ppi->W32PF_flags & W32PF_CLASSESREGISTERED))
2679 {
2680 UserRegisterSystemClasses();
2681 }
2682
2683 ClassAtom = IntGetClassAtom(&SafeClassName,
2684 hInstance,
2685 ppi,
2686 &Class,
2687 NULL);
2688 if (ClassAtom != (RTL_ATOM)0)
2689 {
2690 Ret = UserGetClassInfo(Class, &Safewcexw, bAnsi, hInstance);
2691 }
2692 else
2693 {
2694 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST);
2695 Ret = FALSE;
2696 }
2697
2698 UserLeave();
2699
2700 if (Ret)
2701 {
2702 _SEH2_TRY
2703 {
2704 /* Emulate Function. */
2705 if (ppszMenuName) *ppszMenuName = (LPWSTR)Safewcexw.lpszMenuName;
2706
2707 RtlCopyMemory(lpWndClassEx, &Safewcexw, sizeof(WNDCLASSEXW));
2708
2709 // From Wine:
2710 /* We must return the atom of the class here instead of just TRUE. */
2711 /* Undocumented behavior! Return the class atom as a BOOL! */
2712 Ret = (BOOL)ClassAtom;
2713 }
2714 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2715 {
2716 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST);
2717 Ret = FALSE;
2718 }
2719 _SEH2_END;
2720 }
2721
2722 if (!IS_ATOM(SafeClassName.Buffer))
2723 ExFreePoolWithTag(SafeClassName.Buffer, TAG_STRING);
2724
2725 return Ret;
2726 }
2727
2728
2729 INT APIENTRY
2730 NtUserGetClassName (IN HWND hWnd,
2731 IN BOOL Real,
2732 OUT PUNICODE_STRING ClassName)
2733 {
2734 PWND Window;
2735 UNICODE_STRING CapturedClassName;
2736 INT iCls, Ret = 0;
2737 RTL_ATOM Atom = 0;
2738
2739 UserEnterShared();
2740
2741 Window = UserGetWindowObject(hWnd);
2742 if (Window != NULL)
2743 {
2744 if (Real && Window->fnid && !(Window->fnid & FNID_DESTROY))
2745 {
2746 if (LookupFnIdToiCls(Window->fnid, &iCls))
2747 {
2748 Atom = gpsi->atomSysClass[iCls];
2749 }
2750 }
2751
2752 _SEH2_TRY
2753 {
2754 ProbeForWriteUnicodeString(ClassName);
2755 CapturedClassName = *ClassName;
2756
2757 /* Get the class name */
2758 Ret = UserGetClassName(Window->pcls,
2759 &CapturedClassName,
2760 Atom,
2761 FALSE);
2762
2763 if (Ret != 0)
2764 {
2765 /* Update the Length field */
2766 ClassName->Length = CapturedClassName.Length;
2767 }
2768 }
2769 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2770 {
2771 SetLastNtError(_SEH2_GetExceptionCode());
2772 }
2773 _SEH2_END;
2774 }
2775
2776 UserLeave();
2777
2778 return Ret;
2779 }
2780
2781 /* Return Pointer to Class structure. */
2782 PCLS
2783 APIENTRY
2784 NtUserGetWOWClass(
2785 HINSTANCE hInstance,
2786 PUNICODE_STRING ClassName)
2787 {
2788 UNICODE_STRING SafeClassName;
2789 PPROCESSINFO pi;
2790 PCLS Class = NULL;
2791 RTL_ATOM ClassAtom = 0;
2792 NTSTATUS Status;
2793
2794 Status = ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName, ClassName);
2795 if (!NT_SUCCESS(Status))
2796 {
2797 ERR("Error capturing the class name\n");
2798 SetLastNtError(Status);
2799 return FALSE;
2800 }
2801
2802 UserEnterExclusive();
2803
2804 pi = GetW32ProcessInfo();
2805
2806 ClassAtom = IntGetClassAtom(&SafeClassName,
2807 hInstance,
2808 pi,
2809 &Class,
2810 NULL);
2811 if (!ClassAtom)
2812 {
2813 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST);
2814 }
2815
2816
2817 if (SafeClassName.Buffer && !IS_ATOM(SafeClassName.Buffer))
2818 ExFreePoolWithTag(SafeClassName.Buffer, TAG_STRING);
2819
2820 UserLeave();
2821 //
2822 // Don't forget to use DesktopPtrToUser( ? ) with return pointer in user space.
2823 //
2824 return Class;
2825 }
2826
2827 /* EOF */