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