/* * UserUnloadKbl * * Unloads specified Keyboard Layout if possible */ BOOL UserUnloadKbl(PKL pKl) { /* According to msdn, UnloadKeyboardLayout can fail if the keyboard layout identifier was preloaded. */ if (pKl == gspklBaseLayout) { if (pKl->pklNext == pKl->pklPrev) { /* There is only one layout */ return FALSE; } /* Set next layout as default */ gspklBaseLayout = pKl->pklNext; } if (pKl->head.cLockObj > 1) { /* Layout is used by other threads */ pKl->dwKL_Flags |= KLF_UNLOAD; return FALSE; } /* Unload the layout */ pKl->pklPrev->pklNext = pKl->pklNext; pKl->pklNext->pklPrev = pKl->pklPrev; UnloadKbdFile(pKl->spkf); UserDeleteObject(pKl->head.h, TYPE_KBDLAYOUT); return TRUE; }
static BOOL FASTCALL RemoveTimer(PTIMER pTmr) { BOOL Ret = FALSE; if (pTmr) { /* Set the flag, it will be removed when ready */ RemoveEntryList(&pTmr->ptmrList); if ((pTmr->pWnd == NULL) && (!(pTmr->flags & TMRF_SYSTEM))) // System timers are reusable. { UINT_PTR IDEvent; IDEvent = NUM_WINDOW_LESS_TIMERS - pTmr->nID; IntLockWindowlessTimerBitmap(); RtlClearBit(&WindowLessTimersBitMap, IDEvent); IntUnlockWindowlessTimerBitmap(); } UserDereferenceObject(pTmr); Ret = UserDeleteObject( UserHMGetHandle(pTmr), otTimer); } if (!Ret) ERR("Warning: Unable to delete timer\n"); return Ret; }
/* * UserLoadKbdLayout * * Loads keyboard layout and creates KL object */ static PKL UserLoadKbdLayout(PUNICODE_STRING pwszKLID, HKL hKL) { LCID lCid; CHARSETINFO cs; PKL pKl; /* Create keyboard layout object */ pKl = UserCreateObject(gHandleTable, NULL, NULL, NULL, TYPE_KBDLAYOUT, sizeof(KL)); if (!pKl) { ERR("Failed to create object!\n"); return NULL; } pKl->hkl = hKL; pKl->spkf = UserLoadKbdFile(pwszKLID); /* Dereference keyboard layout */ UserDereferenceObject(pKl); /* If we failed, remove KL object */ if (!pKl->spkf) { ERR("UserLoadKbdFile(%wZ) failed!\n", pwszKLID); UserDeleteObject(pKl->head.h, TYPE_KBDLAYOUT); return NULL; } // Up to Language Identifiers.. RtlUnicodeStringToInteger(pwszKLID, (ULONG)16, (PULONG)&lCid); TRACE("Language Identifiers %wZ LCID 0x%x\n", pwszKLID, lCid); if (co_IntGetCharsetInfo(lCid, &cs)) { pKl->iBaseCharset = cs.ciCharset; pKl->dwFontSigs = cs.fs.fsCsb[0]; pKl->CodePage = (USHORT)cs.ciACP; TRACE("Charset %u Font Sig %lu CodePage %u\n", pKl->iBaseCharset, pKl->dwFontSigs, pKl->CodePage); } else { pKl->iBaseCharset = ANSI_CHARSET; pKl->dwFontSigs = FS_LATIN1; pKl->CodePage = CP_ACP; } // Set initial system character set and font signature. if (gSystemFS == 0) { gSystemCPCharSet = pKl->iBaseCharset; gSystemFS = pKl->dwFontSigs; } return pKl; }
/* IntAttachMonitor * * Creates a new MONITOR and appends it to the list of monitors. * * Arguments * * pGdiDevice Pointer to the PDEVOBJ onto which the monitor was attached * DisplayNumber Display Number (starting with 0) * * Return value * Returns a NTSTATUS */ NTSTATUS IntAttachMonitor(IN PDEVOBJ *pGdiDevice, IN ULONG DisplayNumber) { PMONITOR Monitor; WCHAR Buffer[CCHDEVICENAME]; TRACE("Attaching monitor...\n"); /* create new monitor object */ Monitor = IntCreateMonitorObject(); if (Monitor == NULL) { TRACE("Couldnt create monitor object\n"); return STATUS_INSUFFICIENT_RESOURCES; } _snwprintf(Buffer, CCHDEVICENAME, L"\\\\.\\DISPLAY%d", DisplayNumber + 1); if (!RtlCreateUnicodeString(&Monitor->DeviceName, Buffer)) { TRACE("Couldn't duplicate monitor name!\n"); UserDereferenceObject(Monitor); UserDeleteObject(UserHMGetHandle(Monitor), otMonitor); return STATUS_INSUFFICIENT_RESOURCES; } Monitor->GdiDevice = pGdiDevice; Monitor->cWndStack = 0; if (gMonitorList == NULL) { TRACE("Primary monitor is beeing attached\n"); Monitor->IsPrimary = TRUE; gMonitorList = Monitor; } else { PMONITOR p; TRACE("Additional monitor is beeing attached\n"); for (p = gMonitorList; p->Next != NULL; p = p->Next) { p->Next = Monitor; } Monitor->Prev = p; } IntUpdateMonitorSize(pGdiDevice); return STATUS_SUCCESS; }
/* IntDestroyMonitorObject * * Destroys a MONITOR * You have to be the owner of the monitors lock to safely destroy it. * * Arguments * * pMonitor * Pointer to the MONITOR which shall be deleted */ static void IntDestroyMonitorObject(IN PMONITOR pMonitor) { /* Remove monitor region */ if (pMonitor->hrgnMonitor) { GreSetObjectOwner(pMonitor->hrgnMonitor, GDI_OBJ_HMGR_POWNED); GreDeleteObject(pMonitor->hrgnMonitor); } /* Destroy monitor object */ UserDereferenceObject(pMonitor); UserDeleteObject(UserHMGetHandle(pMonitor), TYPE_MONITOR); }
static VOID FASTCALL IntFreeElementData(PCLIP pElement) { if (!IS_DATA_DELAYED(pElement) && !IS_DATA_SYNTHESIZED(pElement)) { if (pElement->fGlobalHandle) UserDeleteObject(pElement->hData, TYPE_CLIPDATA); else if (pElement->fmt == CF_BITMAP || pElement->fmt == CF_PALETTE || pElement->fmt == CF_DSPBITMAP) { GreSetObjectOwner(pElement->hData, GDI_OBJ_HMGR_POWNED); GreDeleteObject(pElement->hData); } } }
static BOOL FASTCALL IntRemoveEvent(PEVENTHOOK pEH) { if (pEH) { TRACE("IntRemoveEvent pEH 0x%x\n",pEH); KeEnterCriticalRegion(); RemoveEntryList(&pEH->Chain); GlobalEvents->Counts--; if (!GlobalEvents->Counts) gpsi->dwInstalledEventHooks = 0; UserDeleteObject(UserHMGetHandle(pEH), otEvent); KeLeaveCriticalRegion(); return TRUE; } return FALSE; }
HANDLE APIENTRY NtUserConvertMemHandle( PVOID pData, DWORD cbData) { HANDLE hMem = NULL; PCLIPBOARDDATA pMemObj; UserEnterExclusive(); /* Create Clipboard data object */ pMemObj = UserCreateObject(gHandleTable, NULL, NULL, &hMem, TYPE_CLIPDATA, sizeof(CLIPBOARDDATA) + cbData); if (!pMemObj) goto cleanup; pMemObj->cbData = cbData; /* Copy data */ _SEH2_TRY { ProbeForRead(pData, cbData, 1); memcpy(pMemObj->Data, pData, cbData); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { pMemObj = NULL; } _SEH2_END; /* Release the extra reference (UserCreateObject added 2 references) */ UserDereferenceObject(pMemObj); /* If we failed to copy data, remove handle */ if (!pMemObj) { UserDeleteObject(hMem, TYPE_CLIPDATA); hMem = NULL; } cleanup: UserLeave(); return hMem; }
/* * UnloadKbdFile * * Destroys specified Keyboard File object */ static VOID UnloadKbdFile(_In_ PKBDFILE pkf) { PKBDFILE *ppkfLink = &gpkfList; NT_ASSERT(pkf != NULL); /* Find previous object */ while (*ppkfLink) { if (*ppkfLink == pkf) break; ppkfLink = &(*ppkfLink)->pkfNext; } if (*ppkfLink == pkf) *ppkfLink = pkf->pkfNext; EngUnloadImage(pkf->hBase); UserDeleteObject(pkf->head.h, TYPE_KBDFILE); }
BOOLEAN APIENTRY NtUserDestroyAcceleratorTable( HACCEL hAccel) { PACCELERATOR_TABLE Accel; DECLARE_RETURN(BOOLEAN); /* FIXME: If the handle table is from a call to LoadAcceleratorTable, decrement it's usage count (and return TRUE). FIXME: Destroy only tables created using CreateAcceleratorTable. */ TRACE("NtUserDestroyAcceleratorTable(Table %p)\n", hAccel); UserEnterExclusive(); if (!(Accel = UserGetAccelObject(hAccel))) { RETURN( FALSE); } if (Accel->Table != NULL) { ExFreePoolWithTag(Accel->Table, USERTAG_ACCEL); Accel->Table = NULL; } UserDeleteObject(hAccel, TYPE_ACCELTABLE); RETURN( TRUE); CLEANUP: TRACE("Leave NtUserDestroyAcceleratorTable(Table %p) = %u\n", hAccel, _ret_); UserLeave(); END_CLEANUP; }
HACCEL APIENTRY NtUserCreateAcceleratorTable( LPACCEL Entries, ULONG EntriesCount) { PACCELERATOR_TABLE Accel; HACCEL hAccel; ULONG Index; NTSTATUS Status = STATUS_SUCCESS; DECLARE_RETURN(HACCEL); TRACE("Enter NtUserCreateAcceleratorTable(Entries %p, EntriesCount %u)\n", Entries, EntriesCount); UserEnterExclusive(); if (!Entries || EntriesCount <= 0) { SetLastNtError(STATUS_INVALID_PARAMETER); RETURN( (HACCEL) NULL ); } Accel = UserCreateObject(gHandleTable, NULL, NULL, (PHANDLE)&hAccel, TYPE_ACCELTABLE, sizeof(ACCELERATOR_TABLE)); if (Accel == NULL) { SetLastNtError(STATUS_NO_MEMORY); RETURN( (HACCEL) NULL ); } Accel->Count = EntriesCount; Accel->Table = ExAllocatePoolWithTag(PagedPool, EntriesCount * sizeof(ACCEL), USERTAG_ACCEL); if (Accel->Table == NULL) { UserDereferenceObject(Accel); UserDeleteObject(hAccel, TYPE_ACCELTABLE); SetLastNtError(STATUS_NO_MEMORY); RETURN( (HACCEL) NULL); } _SEH2_TRY { ProbeForRead(Entries, EntriesCount * sizeof(ACCEL), 4); for (Index = 0; Index < EntriesCount; Index++) { Accel->Table[Index].fVirt = Entries[Index].fVirt & FVIRT_MASK; if(Accel->Table[Index].fVirt & FVIRTKEY) { Accel->Table[Index].key = Entries[Index].key; } else { RtlMultiByteToUnicodeN(&Accel->Table[Index].key, sizeof(WCHAR), NULL, (PCSTR)&Entries[Index].key, sizeof(CHAR)); } Accel->Table[Index].cmd = Entries[Index].cmd; } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; if (!NT_SUCCESS(Status)) { ExFreePoolWithTag(Accel->Table, USERTAG_ACCEL); UserDereferenceObject(Accel); UserDeleteObject(hAccel, TYPE_ACCELTABLE); SetLastNtError(Status); RETURN( (HACCEL) NULL); } /* FIXME: Save HandleTable in a list somewhere so we can clean it up again */ RETURN(hAccel); CLEANUP: TRACE("Leave NtUserCreateAcceleratorTable(Entries %p, EntriesCount %u) = %p\n", Entries, EntriesCount, _ret_); UserLeave(); END_CLEANUP; }
/* * @implemented */ DWORD_PTR APIENTRY NtUserCallOneParam( DWORD_PTR Param, DWORD Routine) { DECLARE_RETURN(DWORD_PTR); TRACE("Enter NtUserCallOneParam\n"); UserEnterExclusive(); switch(Routine) { case ONEPARAM_ROUTINE_POSTQUITMESSAGE: { PTHREADINFO pti; pti = PsGetCurrentThreadWin32Thread(); MsqPostQuitMessage(pti->MessageQueue, Param); RETURN(TRUE); } case ONEPARAM_ROUTINE_BEGINDEFERWNDPOS: { PSMWP psmwp; HDWP hDwp = NULL; INT count = (INT)Param; if (count < 0) { EngSetLastError(ERROR_INVALID_PARAMETER); RETURN(0); } /* Windows allows zero count, in which case it allocates context for 8 moves */ if (count == 0) count = 8; psmwp = (PSMWP) UserCreateObject( gHandleTable, NULL, (PHANDLE)&hDwp, otSMWP, sizeof(SMWP)); if (!psmwp) RETURN(0); psmwp->acvr = ExAllocatePoolWithTag(PagedPool, count * sizeof(CVR), USERTAG_SWP); if (!psmwp->acvr) { UserDeleteObject(hDwp, otSMWP); RETURN(0); } RtlZeroMemory(psmwp->acvr, count * sizeof(CVR)); psmwp->bHandle = TRUE; psmwp->ccvr = 0; // actualCount psmwp->ccvrAlloc = count; // suggestedCount RETURN((DWORD_PTR)hDwp); } case ONEPARAM_ROUTINE_SHOWCURSOR: RETURN( (DWORD_PTR)UserShowCursor((BOOL)Param) ); case ONEPARAM_ROUTINE_GETDESKTOPMAPPING: { PTHREADINFO ti; ti = GetW32ThreadInfo(); if (ti != NULL) { /* Try convert the pointer to a user mode pointer if the desktop is mapped into the process */ RETURN((DWORD_PTR)DesktopHeapAddressToUser((PVOID)Param)); } else { RETURN(0); } } case ONEPARAM_ROUTINE_WINDOWFROMDC: RETURN( (DWORD_PTR)IntWindowFromDC((HDC)Param)); case ONEPARAM_ROUTINE_SWAPMOUSEBUTTON: { DWORD_PTR Result; Result = gspv.bMouseBtnSwap; gspv.bMouseBtnSwap = Param ? TRUE : FALSE; gpsi->aiSysMet[SM_SWAPBUTTON] = gspv.bMouseBtnSwap; RETURN(Result); } case ONEPARAM_ROUTINE_SWITCHCARETSHOWING: RETURN( (DWORD_PTR)IntSwitchCaretShowing((PVOID)Param)); case ONEPARAM_ROUTINE_SETCARETBLINKTIME: RETURN( (DWORD_PTR)IntSetCaretBlinkTime((UINT)Param)); case ONEPARAM_ROUTINE_SETMESSAGEEXTRAINFO: RETURN( (DWORD_PTR)MsqSetMessageExtraInfo((LPARAM)Param)); case ONEPARAM_ROUTINE_CREATEEMPTYCUROBJECT: { PCURICON_OBJECT CurIcon; DWORD_PTR Result ; if (!(CurIcon = IntCreateCurIconHandle())) { EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); RETURN(0); } Result = (DWORD_PTR)CurIcon->Self; UserDereferenceObject(CurIcon); RETURN(Result); } case ONEPARAM_ROUTINE_GETCURSORPOSITION: { BOOL ret = TRUE; _SEH2_TRY { ProbeForWrite((POINT*)Param,sizeof(POINT),1); RtlCopyMemory((POINT*)Param,&gpsi->ptCursor,sizeof(POINT)); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { SetLastNtError(_SEH2_GetExceptionCode()); ret = FALSE; } _SEH2_END; RETURN (ret); } case ONEPARAM_ROUTINE_ENABLEPROCWNDGHSTING: { BOOL Enable; PPROCESSINFO Process = PsGetCurrentProcessWin32Process(); if(Process != NULL) { Enable = (BOOL)(Param != 0); if(Enable) { Process->W32PF_flags &= ~W32PF_NOWINDOWGHOSTING; } else { Process->W32PF_flags |= W32PF_NOWINDOWGHOSTING; } RETURN( TRUE); } RETURN( FALSE); } case ONEPARAM_ROUTINE_GETINPUTEVENT: RETURN( (DWORD_PTR)IntMsqSetWakeMask(Param)); case ONEPARAM_ROUTINE_GETKEYBOARDTYPE: RETURN( UserGetKeyboardType(Param)); case ONEPARAM_ROUTINE_GETKEYBOARDLAYOUT: RETURN( (DWORD_PTR)UserGetKeyboardLayout(Param)); case ONEPARAM_ROUTINE_RELEASEDC: RETURN (UserReleaseDC(NULL, (HDC) Param, FALSE)); case ONEPARAM_ROUTINE_REALIZEPALETTE: RETURN (UserRealizePalette((HDC) Param)); case ONEPARAM_ROUTINE_GETQUEUESTATUS: { RETURN (IntGetQueueStatus((DWORD)Param)); } case ONEPARAM_ROUTINE_ENUMCLIPBOARDFORMATS: /* FIXME: Should use UserEnterShared */ RETURN(IntEnumClipboardFormats(Param)); case ONEPARAM_ROUTINE_CSRSS_GUICHECK: IntUserManualGuiCheck(Param); RETURN(TRUE); case ONEPARAM_ROUTINE_GETCURSORPOS: { BOOL Ret = TRUE; PPOINTL pptl; PTHREADINFO pti = PsGetCurrentThreadWin32Thread(); if (pti->hdesk != InputDesktopHandle) RETURN(FALSE); _SEH2_TRY { pptl = (PPOINTL)Param; *pptl = gpsi->ptCursor; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Ret = FALSE; } _SEH2_END; RETURN(Ret); } case ONEPARAM_ROUTINE_SETPROCDEFLAYOUT: { PPROCESSINFO ppi; if (Param & LAYOUT_ORIENTATIONMASK) { ppi = PsGetCurrentProcessWin32Process(); ppi->dwLayout = Param; RETURN(TRUE); } EngSetLastError(ERROR_INVALID_PARAMETER); RETURN(FALSE); } case ONEPARAM_ROUTINE_GETPROCDEFLAYOUT: { BOOL Ret = TRUE; PPROCESSINFO ppi; PDWORD pdwLayout; if ( PsGetCurrentProcess() == CsrProcess) { EngSetLastError(ERROR_INVALID_ACCESS); RETURN(FALSE); } ppi = PsGetCurrentProcessWin32Process(); _SEH2_TRY { pdwLayout = (PDWORD)Param; *pdwLayout = ppi->dwLayout; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { SetLastNtError(_SEH2_GetExceptionCode()); Ret = FALSE; } _SEH2_END; RETURN(Ret); } case ONEPARAM_ROUTINE_REPLYMESSAGE: RETURN (co_MsqReplyMessage((LRESULT) Param)); case ONEPARAM_ROUTINE_MESSAGEBEEP: RETURN ( UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_MESSAGE_BEEP, Param) ); /* TODO: Implement sound sentry */ } ERR("Calling invalid routine number 0x%x in NtUserCallOneParam(), Param=0x%x\n", Routine, Param); EngSetLastError(ERROR_INVALID_PARAMETER); RETURN( 0); CLEANUP: TRACE("Leave NtUserCallOneParam, ret=%i\n",_ret_); UserLeave(); END_CLEANUP; }
/* * UserLoadKbdFile * * Loads keyboard layout DLL and creates KBDFILE object */ static PKBDFILE UserLoadKbdFile(PUNICODE_STRING pwszKLID) { PKBDFILE pkf, pRet = NULL; NTSTATUS Status; ULONG cbSize; HKEY hKey = NULL; WCHAR wszLayoutPath[MAX_PATH] = L"\\SystemRoot\\System32\\"; WCHAR wszLayoutRegKey[256] = L"\\REGISTRY\\Machine\\SYSTEM\\CurrentControlSet\\" L"Control\\Keyboard Layouts\\"; /* Create keyboard layout file object */ pkf = UserCreateObject(gHandleTable, NULL, NULL, NULL, TYPE_KBDFILE, sizeof(KBDFILE)); if (!pkf) { ERR("Failed to create object!\n"); return NULL; } /* Set keyboard layout name */ swprintf(pkf->awchKF, L"%wZ", pwszKLID); /* Open layout registry key */ RtlStringCbCatW(wszLayoutRegKey, sizeof(wszLayoutRegKey), pkf->awchKF); Status = RegOpenKey(wszLayoutRegKey, &hKey); if (!NT_SUCCESS(Status)) { ERR("Failed to open keyboard layouts registry key %ws (%lx)\n", wszLayoutRegKey, Status); goto cleanup; } /* Read filename of layout DLL */ cbSize = sizeof(wszLayoutPath) - wcslen(wszLayoutPath)*sizeof(WCHAR); Status = RegQueryValue(hKey, L"Layout File", REG_SZ, wszLayoutPath + wcslen(wszLayoutPath), &cbSize); if (!NT_SUCCESS(Status)) { ERR("Can't get layout filename for %wZ (%lx)\n", pwszKLID, Status); goto cleanup; } /* Load keyboard file now */ if (!UserLoadKbdDll(wszLayoutPath, &pkf->hBase, &pkf->pKbdTbl)) { ERR("Failed to load %ws dll!\n", wszLayoutPath); goto cleanup; } /* Update next field */ pkf->pkfNext = gpkfList; gpkfList = pkf; /* Return keyboard file */ pRet = pkf; cleanup: if (hKey) ZwClose(hKey); if (pkf) UserDereferenceObject(pkf); // we dont need ptr anymore if (!pRet) { /* We have failed - destroy created object */ if (pkf) UserDeleteObject(pkf->head.h, TYPE_KBDFILE); } return pRet; }
BOOLEAN FASTCALL IntDestroyCurIconObject(PCURICON_OBJECT CurIcon, BOOLEAN bForce) { if(CurIcon->CURSORF_flags & CURSORF_CURRENT) { /* Mark the object as destroyed, and fail, as per tests */ TRACE("Cursor is current, marking as destroyed.\n"); UserDeleteObject(CurIcon->head.h, TYPE_CURSOR); return FALSE; } if(CurIcon->head.ppi != PsGetCurrentProcessWin32Process()) { /* This object doesn't belong to the current process */ WARN("Trying to delete foreign cursor!\n"); UserDereferenceObject(CurIcon); EngSetLastError(ERROR_DESTROY_OBJECT_OF_OTHER_THREAD); return FALSE; } /* Do not destroy it if it is shared. (And we're not forced to) */ if((CurIcon->CURSORF_flags & CURSORF_LRSHARED) && !bForce) { /* Tests show this is a valid call */ WARN("Trying to destroy shared cursor!\n"); UserDereferenceObject(CurIcon); return TRUE; } if(!(CurIcon->CURSORF_flags & CURSORF_ACON)) { HBITMAP bmpMask = CurIcon->hbmMask; HBITMAP bmpColor = CurIcon->hbmColor; HBITMAP bmpAlpha = CurIcon->hbmAlpha; /* Delete bitmaps */ if (bmpMask) { GreSetObjectOwner(bmpMask, GDI_OBJ_HMGR_POWNED); GreDeleteObject(bmpMask); CurIcon->hbmMask = NULL; } if (bmpColor) { GreSetObjectOwner(bmpColor, GDI_OBJ_HMGR_POWNED); GreDeleteObject(bmpColor); CurIcon->hbmColor = NULL; } if (bmpAlpha) { GreSetObjectOwner(bmpAlpha, GDI_OBJ_HMGR_POWNED); GreDeleteObject(bmpAlpha); CurIcon->hbmAlpha = NULL; } } else { PACON AniCurIcon = (PACON)CurIcon; UINT i; for(i = 0; i < AniCurIcon->cpcur; i++) IntDestroyCurIconObject(AniCurIcon->aspcur[i], TRUE); ExFreePoolWithTag(AniCurIcon->aspcur, USERTAG_CURSOR); } if (CurIcon->CURSORF_flags & CURSORF_LRSHARED) { if (!IS_INTRESOURCE(CurIcon->strName.Buffer)) ExFreePoolWithTag(CurIcon->strName.Buffer, TAG_STRING); if (CurIcon->atomModName) RtlDeleteAtomFromAtomTable(gAtomTable, CurIcon->atomModName); CurIcon->strName.Buffer = NULL; CurIcon->atomModName = 0; } /* We were given a pointer, no need to keep the reference any longer! */ UserDereferenceObject(CurIcon); return UserDeleteObject(CurIcon->head.h, TYPE_CURSOR); }