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