[Win32k]
[reactos.git] / reactos / win32ss / user / ntuser / class.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
4 * PURPOSE: Window classes
5 * FILE: subsystems/win32/win32k/ntuser/class.c
6 * PROGRAMER: Thomas Weidenmueller <w3seek@reactos.com>
7 */
8
9 #include <win32k.h>
10 DBG_DEFAULT_CHANNEL(UserClass);
11
12 BOOL FASTCALL IntClassDestroyIcon(HANDLE hCurIcon);
13 static NTSTATUS IntDeregisterClassAtom(IN RTL_ATOM Atom);
14
15 REGISTER_SYSCLASS DefaultServerClasses[] =
16 {
17 { ((PWSTR)((ULONG_PTR)(WORD)(0x8001))),
18 CS_GLOBALCLASS|CS_DBLCLKS,
19 NULL, // Use User32 procs
20 sizeof(ULONG)*2,
21 (HICON)IDC_ARROW,
22 (HBRUSH)(COLOR_BACKGROUND),
23 FNID_DESKTOP,
24 ICLS_DESKTOP
25 },
26 { ((PWSTR)((ULONG_PTR)(WORD)(0x8003))),
27 CS_VREDRAW|CS_HREDRAW|CS_SAVEBITS,
28 NULL, // Use User32 procs
29 sizeof(LONG),
30 (HICON)IDC_ARROW,
31 NULL,
32 FNID_SWITCH,
33 ICLS_SWITCH
34 },
35 { ((PWSTR)((ULONG_PTR)(WORD)(0x8000))),
36 CS_DBLCLKS|CS_SAVEBITS,
37 NULL, // Use User32 procs
38 sizeof(LONG),
39 (HICON)IDC_ARROW,
40 (HBRUSH)(COLOR_MENU + 1),
41 FNID_MENU,
42 ICLS_MENU
43 },
44 { L"ScrollBar",
45 CS_DBLCLKS|CS_VREDRAW|CS_HREDRAW|CS_PARENTDC,
46 NULL, // Use User32 procs
47 sizeof(SBWND)-sizeof(WND),
48 (HICON)IDC_ARROW,
49 NULL,
50 FNID_SCROLLBAR,
51 ICLS_SCROLLBAR
52 },
53 #if 0
54 { ((PWSTR)((ULONG_PTR)(WORD)(0x8006))), // Tooltips
55 CS_PARENTDC|CS_DBLCLKS,
56 NULL, // Use User32 procs
57 0,
58 (HICON)IDC_ARROW,
59 0,
60 FNID_TOOLTIPS,
61 ICLS_TOOLTIPS
62 },
63 #endif
64 { ((PWSTR)((ULONG_PTR)(WORD)(0x8004))), // IconTitle is here for now...
65 0,
66 NULL, // Use User32 procs
67 0,
68 (HICON)IDC_ARROW,
69 0,
70 FNID_ICONTITLE,
71 ICLS_ICONTITLE
72 },
73 { L"Message",
74 CS_GLOBALCLASS,
75 NULL, // Use User32 procs
76 0,
77 (HICON)IDC_ARROW,
78 NULL,
79 FNID_MESSAGEWND,
80 ICLS_HWNDMESSAGE
81 }
82 };
83
84 static struct
85 {
86 int FnId;
87 int ClsId;
88 } FnidToiCls[] =
89 { /* Function Ids to Class indexes. */
90 { FNID_SCROLLBAR, ICLS_SCROLLBAR},
91 { FNID_ICONTITLE, ICLS_ICONTITLE},
92 { FNID_MENU, ICLS_MENU},
93 { FNID_DESKTOP, ICLS_DESKTOP},
94 { FNID_SWITCH, ICLS_SWITCH},
95 { FNID_MESSAGEWND, ICLS_HWNDMESSAGE},
96 { FNID_BUTTON, ICLS_BUTTON},
97 { FNID_COMBOBOX, ICLS_COMBOBOX},
98 { FNID_COMBOLBOX, ICLS_COMBOLBOX},
99 { FNID_DIALOG, ICLS_DIALOG},
100 { FNID_EDIT, ICLS_EDIT},
101 { FNID_LISTBOX, ICLS_LISTBOX},
102 { FNID_MDICLIENT, ICLS_MDICLIENT},
103 { FNID_STATIC, ICLS_STATIC},
104 { FNID_IME, ICLS_IME},
105 { FNID_GHOST, ICLS_GHOST},
106 { FNID_TOOLTIPS, ICLS_TOOLTIPS}
107 };
108
109 BOOL
110 FASTCALL
111 LookupFnIdToiCls(int FnId, int *iCls )
112 {
113 int i;
114
115 for ( i = 0; i < ARRAYSIZE(FnidToiCls); i++)
116 {
117 if (FnidToiCls[i].FnId == FnId)
118 {
119 if (iCls) *iCls = FnidToiCls[i].ClsId;
120 return TRUE;
121 }
122 }
123 if (iCls) *iCls = 0;
124 return FALSE;
125 }
126
127 _Must_inspect_result_
128 NTSTATUS
129 NTAPI
130 ProbeAndCaptureUnicodeStringOrAtom(
131 _Out_ _When_(return>=0, _At_(pustrOut->Buffer, _Post_ _Notnull_)) PUNICODE_STRING pustrOut,
132 __in_data_source(USER_MODE) _In_ PUNICODE_STRING pustrUnsafe)
133 {
134 NTSTATUS Status = STATUS_SUCCESS;
135
136 /* Default to NULL */
137 pustrOut->Buffer = NULL;
138
139 _SEH2_TRY
140 {
141 ProbeForRead(pustrUnsafe, sizeof(UNICODE_STRING), 1);
142
143 /* Validate the string */
144 if ((pustrUnsafe->Length & 1) || (pustrUnsafe->Buffer == NULL))
145 {
146 /* This is not legal */
147 _SEH2_YIELD(return STATUS_INVALID_PARAMETER);
148 }
149
150 /* Check if this is an atom */
151 if (IS_ATOM(pustrUnsafe->Buffer))
152 {
153 /* Copy the atom, length is 0 */
154 pustrOut->MaximumLength = pustrOut->Length = 0;
155 pustrOut->Buffer = pustrUnsafe->Buffer;
156 }
157 else
158 {
159 /* Get the length, maximum length includes zero termination */
160 pustrOut->Length = pustrUnsafe->Length;
161 pustrOut->MaximumLength = pustrOut->Length + sizeof(WCHAR);
162
163 /* Allocate a buffer */
164 pustrOut->Buffer = ExAllocatePoolWithTag(PagedPool,
165 pustrOut->MaximumLength,
166 TAG_STRING);
167 if (!pustrOut->Buffer)
168 {
169 _SEH2_YIELD(return STATUS_INSUFFICIENT_RESOURCES);
170 }
171
172 /* Copy the string and zero terminate it */
173 ProbeForRead(pustrUnsafe->Buffer, pustrOut->Length, 1);
174 RtlCopyMemory(pustrOut->Buffer, pustrUnsafe->Buffer, pustrOut->Length);
175 pustrOut->Buffer[pustrOut->Length / sizeof(WCHAR)] = L'\0';
176 }
177 }
178 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
179 {
180 /* Check if we already allocated a buffer */
181 if (pustrOut->Buffer)
182 {
183 /* Free the buffer */
184 ExFreePoolWithTag(pustrOut->Buffer, TAG_STRING);
185 Status = _SEH2_GetExceptionCode();
186 }
187 }
188 _SEH2_END;
189
190 return Status;
191 }
192
193 /* WINDOWCLASS ***************************************************************/
194
195 static VOID
196 IntFreeClassMenuName(IN OUT PCLS Class)
197 {
198 /* Free the menu name, if it was changed and allocated */
199 if (Class->lpszClientUnicodeMenuName != NULL && Class->MenuNameIsString)
200 {
201 UserHeapFree(Class->lpszClientUnicodeMenuName);
202 Class->lpszClientUnicodeMenuName = NULL;
203 Class->lpszClientAnsiMenuName = NULL;
204 }
205 }
206
207 static VOID
208 IntDestroyClass(IN OUT PCLS Class)
209 {
210 PDESKTOP pDesk;
211
212 /* There shouldn't be any clones anymore */
213 ASSERT(Class->cWndReferenceCount == 0);
214 ASSERT(Class->pclsClone == NULL);
215
216 if (Class->pclsBase == Class)
217 {
218 PCALLPROCDATA CallProc, NextCallProc;
219
220 /* Destroy allocated callproc handles */
221 CallProc = Class->spcpdFirst;
222 while (CallProc != NULL)
223 {
224 NextCallProc = CallProc->spcpdNext;
225
226 CallProc->spcpdNext = NULL;
227 DestroyCallProc(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 BOOLEAN FASTCALL IntDestroyCurIconObject(PCURICON_OBJECT, PPROCESSINFO);
1732
1733 BOOL FASTCALL
1734 IntClassDestroyIcon(HANDLE hCurIcon)
1735 {
1736 PCURICON_OBJECT CurIcon;
1737 BOOL Ret;
1738
1739 if (!(CurIcon = UserGetCurIconObject(hCurIcon)))
1740 {
1741
1742 ERR("hCurIcon was not found!\n");
1743 return FALSE;
1744 }
1745 Ret = IntDestroyCurIconObject(CurIcon, PsGetCurrentProcessWin32Process());
1746 /* Note: IntDestroyCurIconObject will remove our reference for us! */
1747 if (!Ret)
1748 {
1749 ERR("hCurIcon was not Destroyed!\n");
1750 }
1751 return Ret;
1752 }
1753
1754 ULONG_PTR
1755 UserSetClassLongPtr(IN PCLS Class,
1756 IN INT Index,
1757 IN ULONG_PTR NewLong,
1758 IN BOOL Ansi)
1759 {
1760 ULONG_PTR Ret = 0;
1761 HANDLE hIconSmIntern = NULL;
1762
1763 /* NOTE: For GCLP_MENUNAME and GCW_ATOM this function may raise an exception! */
1764
1765 /* Change the information in the base class first, then update the clones */
1766 Class = Class->pclsBase;
1767
1768 if (Index >= 0)
1769 {
1770 PULONG_PTR Data;
1771
1772 TRACE("SetClassLong(%d, %x)\n", Index, NewLong);
1773
1774 if (((ULONG)Index + sizeof(ULONG_PTR)) < (ULONG)Index ||
1775 ((ULONG)Index + sizeof(ULONG_PTR)) > (ULONG)Class->cbclsExtra)
1776 {
1777 EngSetLastError(ERROR_INVALID_PARAMETER);
1778 return 0;
1779 }
1780
1781 Data = (PULONG_PTR)((ULONG_PTR)(Class + 1) + Index);
1782
1783 /* FIXME: Data might be a unaligned pointer! Might be a problem on
1784 certain architectures, maybe using RtlCopyMemory is a
1785 better choice for those architectures! */
1786 Ret = *Data;
1787 *Data = NewLong;
1788
1789 /* Update the clones */
1790 Class = Class->pclsClone;
1791 while (Class != NULL)
1792 {
1793 *(PULONG_PTR)((ULONG_PTR)(Class + 1) + Index) = NewLong;
1794 Class = Class->pclsNext;
1795 }
1796
1797 return Ret;
1798 }
1799
1800 switch (Index)
1801 {
1802 case GCL_CBWNDEXTRA:
1803 Ret = (ULONG_PTR)Class->cbwndExtra;
1804 Class->cbwndExtra = (INT)NewLong;
1805
1806 /* Update the clones */
1807 Class = Class->pclsClone;
1808 while (Class != NULL)
1809 {
1810 Class->cbwndExtra = (INT)NewLong;
1811 Class = Class->pclsNext;
1812 }
1813
1814 break;
1815
1816 case GCL_CBCLSEXTRA:
1817 EngSetLastError(ERROR_INVALID_PARAMETER);
1818 break;
1819
1820 case GCLP_HBRBACKGROUND:
1821 Ret = (ULONG_PTR)Class->hbrBackground;
1822 Class->hbrBackground = (HBRUSH)NewLong;
1823
1824 /* Update the clones */
1825 Class = Class->pclsClone;
1826 while (Class != NULL)
1827 {
1828 Class->hbrBackground = (HBRUSH)NewLong;
1829 Class = Class->pclsNext;
1830 }
1831 break;
1832
1833 case GCLP_HCURSOR:
1834 /* FIXME: Get handle from pointer to CURSOR object */
1835 Ret = (ULONG_PTR)Class->hCursor;
1836 Class->hCursor = (HANDLE)NewLong;
1837
1838 /* Update the clones */
1839 Class = Class->pclsClone;
1840 while (Class != NULL)
1841 {
1842 Class->hCursor = (HANDLE)NewLong;
1843 Class = Class->pclsNext;
1844 }
1845 break;
1846
1847 // MSDN:
1848 // hIconSm, A handle to a small icon that is associated with the window class.
1849 // If this member is NULL, the system searches the icon resource specified by
1850 // the hIcon member for an icon of the appropriate size to use as the small icon.
1851 //
1852 case GCLP_HICON:
1853 /* FIXME: Get handle from pointer to ICON object */
1854 Ret = (ULONG_PTR)Class->hIcon;
1855 if (Class->hIcon == (HANDLE)NewLong) break;
1856 if (Ret && Class->hIconSmIntern)
1857 {
1858 IntClassDestroyIcon(Class->hIconSmIntern);
1859 Class->CSF_flags &= ~CSF_CACHEDSMICON;
1860 Class->hIconSmIntern = NULL;
1861 }
1862 if (NewLong && !Class->hIconSm)
1863 {
1864 hIconSmIntern = Class->hIconSmIntern = co_IntCopyImage( (HICON)NewLong, IMAGE_ICON,
1865 UserGetSystemMetrics( SM_CXSMICON ),
1866 UserGetSystemMetrics( SM_CYSMICON ), 0 );
1867 Class->CSF_flags |= CSF_CACHEDSMICON;
1868 }
1869 Class->hIcon = (HANDLE)NewLong;
1870
1871 /* Update the clones */
1872 Class = Class->pclsClone;
1873 while (Class != NULL)
1874 {
1875 Class->hIcon = (HANDLE)NewLong;
1876 Class->hIconSmIntern = hIconSmIntern;
1877 Class = Class->pclsNext;
1878 }
1879 break;
1880
1881 case GCLP_HICONSM:
1882 /* FIXME: Get handle from pointer to ICON object */
1883 Ret = (ULONG_PTR)Class->hIconSm;
1884 if (Class->hIconSm == (HANDLE)NewLong) break;
1885 if (Class->CSF_flags & CSF_CACHEDSMICON)
1886 {
1887 if (Class->hIconSmIntern)
1888 {
1889 IntClassDestroyIcon(Class->hIconSmIntern);
1890 Class->hIconSmIntern = NULL;
1891 }
1892 Class->CSF_flags &= ~CSF_CACHEDSMICON;
1893 }
1894 if (Class->hIcon && !Class->hIconSmIntern)
1895 {
1896 hIconSmIntern = Class->hIconSmIntern = co_IntCopyImage( Class->hIcon, IMAGE_ICON,
1897 UserGetSystemMetrics( SM_CXSMICON ),
1898 UserGetSystemMetrics( SM_CYSMICON ), 0 );
1899
1900 if (hIconSmIntern) Class->CSF_flags |= CSF_CACHEDSMICON;
1901 //// FIXME: Very hacky here but it passes the tests....
1902 //// We should not kill a users handle!!!
1903 if (Class->hIconSm) IntClassDestroyIcon(Class->hIconSm); // Fixes 1013
1904 Ret = 0; // Fixes 1009
1905 }
1906 Class->hIconSm = (HANDLE)NewLong;
1907
1908 /* Update the clones */
1909 Class = Class->pclsClone;
1910 while (Class != NULL)
1911 {
1912 Class->hIconSm = (HANDLE)NewLong;
1913 Class->hIconSmIntern = hIconSmIntern;
1914 Class = Class->pclsNext;
1915 }
1916 break;
1917
1918 case GCLP_HMODULE:
1919 Ret = (ULONG_PTR)Class->hModule;
1920 Class->hModule = (HINSTANCE)NewLong;
1921
1922 /* Update the clones */
1923 Class = Class->pclsClone;
1924 while (Class != NULL)
1925 {
1926 Class->hModule = (HINSTANCE)NewLong;
1927 Class = Class->pclsNext;
1928 }
1929 break;
1930
1931 case GCLP_MENUNAME:
1932 {
1933 PUNICODE_STRING Value = (PUNICODE_STRING)NewLong;
1934
1935 if (!IntSetClassMenuName(Class,
1936 Value))
1937 {
1938 ERR("Setting the class menu name failed!\n");
1939 }
1940
1941 /* FIXME: Really return NULL? Wine does so... */
1942 break;
1943 }
1944
1945 case GCL_STYLE:
1946 Ret = (ULONG_PTR)Class->style;
1947 Class->style = (UINT)NewLong;
1948
1949 /* FIXME: What if the CS_GLOBALCLASS style is changed? should we
1950 move the class to the appropriate list? For now, we save
1951 the original value in Class->Global, so we can always
1952 locate the appropriate list */
1953
1954 /* Update the clones */
1955 Class = Class->pclsClone;
1956 while (Class != NULL)
1957 {
1958 Class->style = (UINT)NewLong;
1959 Class = Class->pclsNext;
1960 }
1961 break;
1962
1963 case GCLP_WNDPROC:
1964 Ret = (ULONG_PTR)IntSetClassWndProc(Class,
1965 (WNDPROC)NewLong,
1966 Ansi);
1967 break;
1968
1969 case GCW_ATOM:
1970 {
1971 PUNICODE_STRING Value = (PUNICODE_STRING)NewLong;
1972
1973 Ret = (ULONG_PTR)Class->atomClassName;
1974 if (!IntSetClassAtom(Class,
1975 Value))
1976 {
1977 Ret = 0;
1978 }
1979 break;
1980 }
1981
1982 default:
1983 EngSetLastError(ERROR_INVALID_INDEX);
1984 break;
1985 }
1986
1987 return Ret;
1988 }
1989
1990 static BOOL
1991 UserGetClassInfo(IN PCLS Class,
1992 OUT PWNDCLASSEXW lpwcx,
1993 IN BOOL Ansi,
1994 HINSTANCE hInstance)
1995 {
1996 if (!Class) return FALSE;
1997
1998 lpwcx->style = Class->style;
1999
2000 // If fnId is set, clear the global bit. See wine class test check_style.
2001 if (Class->fnid)
2002 lpwcx->style &= ~CS_GLOBALCLASS;
2003
2004 lpwcx->lpfnWndProc = IntGetClassWndProc(Class, Ansi);
2005
2006 lpwcx->cbClsExtra = Class->cbclsExtra;
2007 lpwcx->cbWndExtra = Class->cbwndExtra;
2008 lpwcx->hIcon = Class->hIcon; /* FIXME: Get handle from pointer */
2009 lpwcx->hCursor = Class->hCursor; /* FIXME: Get handle from pointer */
2010 lpwcx->hbrBackground = Class->hbrBackground;
2011
2012 /* Copy non-string to user first. */
2013 if (Ansi)
2014 ((PWNDCLASSEXA)lpwcx)->lpszMenuName = Class->lpszClientAnsiMenuName;
2015 else
2016 lpwcx->lpszMenuName = Class->lpszClientUnicodeMenuName;
2017 /*
2018 * FIXME: CLSMENUNAME has the answers! Copy the already made buffers from there!
2019 * Cls: lpszMenuName and lpszAnsiClassName should be used by kernel space.
2020 * lpszClientXxxMenuName should already be mapped to user space.
2021 */
2022 /* Copy string ptr to user. */
2023 if ( Class->lpszClientUnicodeMenuName != NULL &&
2024 Class->MenuNameIsString)
2025 {
2026 lpwcx->lpszMenuName = UserHeapAddressToUser(Ansi ?
2027 (PVOID)Class->lpszClientAnsiMenuName :
2028 (PVOID)Class->lpszClientUnicodeMenuName);
2029 }
2030
2031 if (hInstance == hModClient)
2032 lpwcx->hInstance = NULL;
2033 else
2034 lpwcx->hInstance = hInstance;
2035
2036 /* FIXME: Return the string? Okay! This is performed in User32! */
2037 //lpwcx->lpszClassName = (LPCWSTR)((ULONG_PTR)Class->atomClassName);
2038
2039 /* FIXME: Get handle from pointer */
2040 lpwcx->hIconSm = Class->hIconSm ? Class->hIconSm : Class->hIconSmIntern;
2041
2042 return TRUE;
2043 }
2044
2045 //
2046 // ???
2047 //
2048 BOOL
2049 FASTCALL
2050 UserRegisterSystemClasses(VOID)
2051 {
2052 UINT i;
2053 UNICODE_STRING ClassName, MenuName;
2054 PPROCESSINFO ppi = GetW32ProcessInfo();
2055 WNDCLASSEXW wc;
2056 PCLS Class;
2057 BOOL Ret = TRUE;
2058 HBRUSH hBrush;
2059 DWORD Flags = 0;
2060
2061 if (ppi->W32PF_flags & W32PF_CLASSESREGISTERED)
2062 return TRUE;
2063
2064 if ( hModClient == NULL)
2065 return FALSE;
2066
2067 RtlZeroMemory(&ClassName, sizeof(ClassName));
2068 RtlZeroMemory(&MenuName, sizeof(MenuName));
2069
2070 for (i = 0; i != ARRAYSIZE(DefaultServerClasses); i++)
2071 {
2072 if (!IS_ATOM(DefaultServerClasses[i].ClassName))
2073 {
2074 RtlInitUnicodeString(&ClassName, DefaultServerClasses[i].ClassName);
2075 }
2076 else
2077 {
2078 ClassName.Buffer = DefaultServerClasses[i].ClassName;
2079 ClassName.Length = 0;
2080 ClassName.MaximumLength = 0;
2081 }
2082
2083 wc.cbSize = sizeof(wc);
2084 wc.style = DefaultServerClasses[i].Style;
2085
2086 Flags |= CSF_SERVERSIDEPROC;
2087
2088 if (DefaultServerClasses[i].ProcW)
2089 {
2090 wc.lpfnWndProc = DefaultServerClasses[i].ProcW;
2091 wc.hInstance = hModuleWin;
2092 }
2093 else
2094 {
2095 wc.lpfnWndProc = GETPFNSERVER(DefaultServerClasses[i].fiId);
2096 wc.hInstance = hModClient;
2097 }
2098
2099 wc.cbClsExtra = 0;
2100 wc.cbWndExtra = DefaultServerClasses[i].ExtraBytes;
2101 wc.hIcon = NULL;
2102 wc.hCursor = DefaultServerClasses[i].hCursor;
2103 hBrush = DefaultServerClasses[i].hBrush;
2104 if (hBrush <= (HBRUSH)COLOR_MENUBAR)
2105 {
2106 hBrush = IntGetSysColorBrush((INT)hBrush);
2107 }
2108 wc.hbrBackground = hBrush;
2109 wc.lpszMenuName = NULL;
2110 wc.lpszClassName = ClassName.Buffer;
2111 wc.hIconSm = NULL;
2112
2113 Class = IntCreateClass( &wc,
2114 &ClassName,
2115 &MenuName,
2116 DefaultServerClasses[i].fiId,
2117 Flags,
2118 NULL,
2119 ppi);
2120 if (Class != NULL)
2121 {
2122 Class->pclsNext = ppi->pclsPublicList;
2123 (void)InterlockedExchangePointer((PVOID*)&ppi->pclsPublicList,
2124 Class);
2125
2126 ppi->dwRegisteredClasses |= ICLASS_TO_MASK(DefaultServerClasses[i].iCls);
2127 }
2128 else
2129 {
2130 ERR("!!! Registering system class failed!\n");
2131 Ret = FALSE;
2132 }
2133 }
2134 if (Ret) ppi->W32PF_flags |= W32PF_CLASSESREGISTERED;
2135 return Ret;
2136 }
2137
2138 /* SYSCALLS *****************************************************************/
2139
2140 RTL_ATOM
2141 APIENTRY
2142 NtUserRegisterClassExWOW(
2143 WNDCLASSEXW* lpwcx,
2144 PUNICODE_STRING ClassName,
2145 PUNICODE_STRING ClsNVersion,
2146 PCLSMENUNAME pClassMenuName,
2147 DWORD fnID,
2148 DWORD Flags,
2149 LPDWORD pWow)
2150 /*
2151 * FUNCTION:
2152 * Registers a new class with the window manager
2153 * ARGUMENTS:
2154 * lpwcx = Win32 extended window class structure
2155 * bUnicodeClass = Whether to send ANSI or unicode strings
2156 * to window procedures
2157 * RETURNS:
2158 * Atom identifying the new class
2159 */
2160 {
2161 WNDCLASSEXW CapturedClassInfo = {0};
2162 UNICODE_STRING CapturedName = {0}, CapturedMenuName = {0};
2163 RTL_ATOM Ret = (RTL_ATOM)0;
2164 PPROCESSINFO ppi = GetW32ProcessInfo();
2165
2166 if (Flags & ~(CSF_ANSIPROC))
2167 {
2168 ERR("NtUserRegisterClassExWOW Bad Flags!\n");
2169 EngSetLastError(ERROR_INVALID_FLAGS);
2170 return Ret;
2171 }
2172
2173 UserEnterExclusive();
2174
2175 TRACE("NtUserRegisterClassExWOW ClsN %wZ\n",ClassName);
2176
2177 if ( !(ppi->W32PF_flags & W32PF_CLASSESREGISTERED ))
2178 {
2179 UserRegisterSystemClasses();
2180 }
2181
2182 _SEH2_TRY
2183 {
2184 /* Probe the parameters and basic parameter checks */
2185 if (ProbeForReadUint(&lpwcx->cbSize) != sizeof(WNDCLASSEXW))
2186 {
2187 ERR("NtUserRegisterClassExWOW Wrong cbSize!\n");
2188 goto InvalidParameter;
2189 }
2190
2191 ProbeForRead(lpwcx,
2192 sizeof(WNDCLASSEXW),
2193 sizeof(ULONG));
2194 RtlCopyMemory(&CapturedClassInfo,
2195 lpwcx,
2196 sizeof(WNDCLASSEXW));
2197
2198 CapturedName = ProbeForReadUnicodeString(ClassName);
2199
2200 ProbeForRead(pClassMenuName,
2201 sizeof(CLSMENUNAME),
2202 1);
2203
2204 CapturedMenuName = ProbeForReadUnicodeString(pClassMenuName->pusMenuName);
2205
2206 if ( (CapturedName.Length & 1) ||
2207 (CapturedMenuName.Length & 1) ||
2208 (CapturedClassInfo.cbClsExtra < 0) ||
2209 ((CapturedClassInfo.cbClsExtra + CapturedName.Length +
2210 CapturedMenuName.Length + sizeof(CLS))
2211 < (ULONG)CapturedClassInfo.cbClsExtra) ||
2212 (CapturedClassInfo.cbWndExtra < 0) ||
2213 (CapturedClassInfo.hInstance == NULL) )
2214 {
2215 ERR("NtUserRegisterClassExWOW Invalid Parameter Error!\n");
2216 goto InvalidParameter;
2217 }
2218
2219 if (CapturedName.Length != 0)
2220 {
2221 ProbeForRead(CapturedName.Buffer,
2222 CapturedName.Length,
2223 sizeof(WCHAR));
2224 }
2225 else
2226 {
2227 if (!IS_ATOM(CapturedName.Buffer))
2228 {
2229 ERR("NtUserRegisterClassExWOW ClassName Error!\n");
2230 goto InvalidParameter;
2231 }
2232 }
2233
2234 if (CapturedMenuName.Length != 0)
2235 {
2236 ProbeForRead(CapturedMenuName.Buffer,
2237 CapturedMenuName.Length,
2238 sizeof(WCHAR));
2239 }
2240 else if (CapturedMenuName.Buffer != NULL &&
2241 !IS_INTRESOURCE(CapturedMenuName.Buffer))
2242 {
2243 ERR("NtUserRegisterClassExWOW MenuName Error!\n");
2244 InvalidParameter:
2245 EngSetLastError(ERROR_INVALID_PARAMETER);
2246 _SEH2_LEAVE;
2247 }
2248
2249 if (IsCallProcHandle(lpwcx->lpfnWndProc))
2250 { // Never seen this yet, but I'm sure it's a little haxxy trick!
2251 // If this pops up we know what todo!
2252 ERR("NtUserRegisterClassExWOW WndProc is CallProc!!\n");
2253 }
2254
2255 TRACE("NtUserRegisterClassExWOW MnuN %wZ\n",&CapturedMenuName);
2256
2257 /* Register the class */
2258 Ret = UserRegisterClass(&CapturedClassInfo,
2259 &CapturedName,
2260 &CapturedMenuName,
2261 fnID,
2262 Flags);
2263 }
2264 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2265 {
2266 ERR("NtUserRegisterClassExWOW Exception Error!\n");
2267 SetLastNtError(_SEH2_GetExceptionCode());
2268 }
2269 _SEH2_END;
2270 /*
2271 if (!Ret)
2272 {
2273 ERR("NtUserRegisterClassExWOW Null Return!\n");
2274 }
2275 */
2276 UserLeave();
2277
2278 return Ret;
2279 }
2280
2281 ULONG_PTR APIENTRY
2282 NtUserSetClassLong(HWND hWnd,
2283 INT Offset,
2284 ULONG_PTR dwNewLong,
2285 BOOL Ansi)
2286 {
2287 PPROCESSINFO pi;
2288 PWND Window;
2289 ULONG_PTR Ret = 0;
2290
2291 UserEnterExclusive();
2292
2293 pi = GetW32ProcessInfo();
2294
2295 Window = UserGetWindowObject(hWnd);
2296 if (Window != NULL)
2297 {
2298 if (Window->head.pti->ppi != pi)
2299 {
2300 EngSetLastError(ERROR_ACCESS_DENIED);
2301 goto Cleanup;
2302 }
2303
2304 _SEH2_TRY
2305 {
2306 UNICODE_STRING Value;
2307
2308 /* Probe the parameters */
2309 if (Offset == GCW_ATOM || Offset == GCLP_MENUNAME)
2310 {
2311 Value = ProbeForReadUnicodeString((PUNICODE_STRING)dwNewLong);
2312 if (Value.Length & 1)
2313 {
2314 goto InvalidParameter;
2315 }
2316
2317 if (Value.Length != 0)
2318 {
2319 ProbeForRead(Value.Buffer,
2320 Value.Length,
2321 sizeof(WCHAR));
2322 }
2323 else
2324 {
2325 if (Offset == GCW_ATOM && !IS_ATOM(Value.Buffer))
2326 {
2327 goto InvalidParameter;
2328 }
2329 else if (Offset == GCLP_MENUNAME && !IS_INTRESOURCE(Value.Buffer))
2330 {
2331 InvalidParameter:
2332 EngSetLastError(ERROR_INVALID_PARAMETER);
2333 _SEH2_LEAVE;
2334 }
2335 }
2336
2337 dwNewLong = (ULONG_PTR)&Value;
2338 }
2339
2340 Ret = UserSetClassLongPtr(Window->pcls,
2341 Offset,
2342 dwNewLong,
2343 Ansi);
2344 }
2345 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2346 {
2347 SetLastNtError(_SEH2_GetExceptionCode());
2348 }
2349 _SEH2_END;
2350 }
2351
2352 Cleanup:
2353 UserLeave();
2354
2355 return Ret;
2356 }
2357
2358 WORD
2359 APIENTRY
2360 NtUserSetClassWord(
2361 HWND hWnd,
2362 INT nIndex,
2363 WORD wNewWord)
2364 {
2365 /*
2366 * NOTE: Obsoleted in 32-bit windows
2367 */
2368 return(0);
2369 }
2370
2371 BOOL
2372 APIENTRY
2373 NtUserUnregisterClass(
2374 IN PUNICODE_STRING ClassNameOrAtom,
2375 IN HINSTANCE hInstance,
2376 OUT PCLSMENUNAME pClassMenuName)
2377 {
2378 UNICODE_STRING SafeClassName;
2379 NTSTATUS Status;
2380 BOOL Ret;
2381
2382 Status = ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName, ClassNameOrAtom);
2383 if (!NT_SUCCESS(Status))
2384 {
2385 ERR("Error capturing the class name\n");
2386 SetLastNtError(Status);
2387 return FALSE;
2388 }
2389
2390 UserEnterExclusive();
2391
2392 /* Unregister the class */
2393 Ret = UserUnregisterClass(&SafeClassName, hInstance, NULL); // Null for now~
2394
2395 UserLeave();
2396
2397 if (SafeClassName.Buffer && !IS_ATOM(SafeClassName.Buffer))
2398 ExFreePoolWithTag(SafeClassName.Buffer, TAG_STRING);
2399
2400 return Ret;
2401 }
2402
2403
2404 /* NOTE: For system classes hInstance is not NULL here, but User32Instance */
2405 BOOL
2406 APIENTRY
2407 NtUserGetClassInfo(
2408 HINSTANCE hInstance,
2409 PUNICODE_STRING ClassName,
2410 LPWNDCLASSEXW lpWndClassEx,
2411 LPWSTR *ppszMenuName,
2412 BOOL bAnsi)
2413 {
2414 UNICODE_STRING SafeClassName;
2415 WNDCLASSEXW Safewcexw;
2416 PCLS Class;
2417 RTL_ATOM ClassAtom = 0;
2418 PPROCESSINFO ppi;
2419 BOOL Ret = TRUE;
2420 NTSTATUS Status;
2421
2422 _SEH2_TRY
2423 {
2424 ProbeForWrite( lpWndClassEx, sizeof(WNDCLASSEXW), sizeof(ULONG));
2425 RtlCopyMemory( &Safewcexw, lpWndClassEx, sizeof(WNDCLASSEXW));
2426 }
2427 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2428 {
2429 SetLastNtError(_SEH2_GetExceptionCode());
2430 _SEH2_YIELD(return FALSE);
2431 }
2432 _SEH2_END;
2433
2434 Status = ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName, ClassName);
2435 if (!NT_SUCCESS(Status))
2436 {
2437 ERR("Error capturing the class name\n");
2438 SetLastNtError(Status);
2439 return FALSE;
2440 }
2441
2442 // If null instance use client.
2443 if (!hInstance) hInstance = hModClient;
2444
2445 TRACE("GetClassInfo(%wZ, %p)\n", SafeClassName, hInstance);
2446
2447 /* NOTE: Need exclusive lock because getting the wndproc might require the
2448 creation of a call procedure handle */
2449 UserEnterExclusive();
2450
2451 ppi = GetW32ProcessInfo();
2452 if (!(ppi->W32PF_flags & W32PF_CLASSESREGISTERED))
2453 {
2454 UserRegisterSystemClasses();
2455 }
2456
2457 ClassAtom = IntGetClassAtom(&SafeClassName,
2458 hInstance,
2459 ppi,
2460 &Class,
2461 NULL);
2462 if (ClassAtom != (RTL_ATOM)0)
2463 {
2464 Ret = UserGetClassInfo(Class, &Safewcexw, bAnsi, hInstance);
2465 }
2466 else
2467 {
2468 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST);
2469 Ret = FALSE;
2470 }
2471
2472 UserLeave();
2473
2474 if (Ret)
2475 {
2476 _SEH2_TRY
2477 {
2478 /* Emulate Function. */
2479 if (ppszMenuName) *ppszMenuName = (LPWSTR)Safewcexw.lpszMenuName;
2480
2481 RtlCopyMemory(lpWndClassEx, &Safewcexw, sizeof(WNDCLASSEXW));
2482
2483 // From Wine:
2484 /* We must return the atom of the class here instead of just TRUE. */
2485 /* Undocumented behavior! Return the class atom as a BOOL! */
2486 Ret = (BOOL)ClassAtom;
2487 }
2488 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2489 {
2490 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST);
2491 Ret = FALSE;
2492 }
2493 _SEH2_END;
2494 }
2495
2496 if (!IS_ATOM(SafeClassName.Buffer))
2497 ExFreePoolWithTag(SafeClassName.Buffer, TAG_STRING);
2498
2499 return Ret;
2500 }
2501
2502
2503 INT APIENTRY
2504 NtUserGetClassName (IN HWND hWnd,
2505 IN BOOL Real,
2506 OUT PUNICODE_STRING ClassName)
2507 {
2508 PWND Window;
2509 UNICODE_STRING CapturedClassName;
2510 INT iCls, Ret = 0;
2511 RTL_ATOM Atom = 0;
2512
2513 UserEnterShared();
2514
2515 Window = UserGetWindowObject(hWnd);
2516 if (Window != NULL)
2517 {
2518 if (Real && Window->fnid && !(Window->fnid & FNID_DESTROY))
2519 {
2520 if (LookupFnIdToiCls(Window->fnid, &iCls))
2521 {
2522 Atom = gpsi->atomSysClass[iCls];
2523 }
2524 }
2525
2526 _SEH2_TRY
2527 {
2528 ProbeForWriteUnicodeString(ClassName);
2529 CapturedClassName = *ClassName;
2530
2531 /* Get the class name */
2532 Ret = UserGetClassName(Window->pcls,
2533 &CapturedClassName,
2534 Atom,
2535 FALSE);
2536
2537 if (Ret != 0)
2538 {
2539 /* Update the Length field */
2540 ClassName->Length = CapturedClassName.Length;
2541 }
2542 }
2543 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2544 {
2545 SetLastNtError(_SEH2_GetExceptionCode());
2546 }
2547 _SEH2_END;
2548 }
2549
2550 UserLeave();
2551
2552 return Ret;
2553 }
2554
2555 /* Return Pointer to Class structure. */
2556 PCLS
2557 APIENTRY
2558 NtUserGetWOWClass(
2559 HINSTANCE hInstance,
2560 PUNICODE_STRING ClassName)
2561 {
2562 UNICODE_STRING SafeClassName;
2563 PPROCESSINFO pi;
2564 PCLS Class = NULL;
2565 RTL_ATOM ClassAtom = 0;
2566 NTSTATUS Status;
2567
2568 Status = ProbeAndCaptureUnicodeStringOrAtom(&SafeClassName, ClassName);
2569 if (!NT_SUCCESS(Status))
2570 {
2571 ERR("Error capturing the class name\n");
2572 SetLastNtError(Status);
2573 return FALSE;
2574 }
2575
2576 UserEnterExclusive();
2577
2578 pi = GetW32ProcessInfo();
2579
2580 ClassAtom = IntGetClassAtom(&SafeClassName,
2581 hInstance,
2582 pi,
2583 &Class,
2584 NULL);
2585 if (!ClassAtom)
2586 {
2587 EngSetLastError(ERROR_CLASS_DOES_NOT_EXIST);
2588 }
2589
2590
2591 if (SafeClassName.Buffer && !IS_ATOM(SafeClassName.Buffer))
2592 ExFreePoolWithTag(SafeClassName.Buffer, TAG_STRING);
2593
2594 UserLeave();
2595 //
2596 // Don't forget to use DesktopPtrToUser( ? ) with return pointer in user space.
2597 //
2598 return Class;
2599 }
2600
2601 /* EOF */