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