//*************************************************************************** // // (c) 1998-1999 by Microsoft Corp. // // PerfAcc.CPP // // Windows NT Performance Data Access helper functions // // bobw 8-Jub-98 Created for use with NT Perf counters // //*************************************************************************** // #include "wpheader.h" #include #include "oahelp.inl" #include // NOTE: Consider reading this from the registry LONG lExtCounterTestLevel = EXT_TEST_ALL; // // // precompiled security descriptor // System and NetworkService has full access // // since this is RELATIVE, it will work on both IA32 and IA64 // DWORD g_PrecSD[] = { 0x80040001 , 0x00000044 , 0x00000050 , 0x00000000 , 0x00000014 , 0x00300002 , 0x00000002 , 0x00140000 , 0x001f0001 , 0x00000101 , 0x05000000 , 0x00000012 , 0x00140000 , 0x001f0001 , 0x00000101 , 0x05000000 , 0x00000014 , 0x00000101 , 0x05000000 , 0x00000014 , 0x00000101 , 0x05000000 , 0x00000014 }; DWORD g_SizeSD = 0; DWORD g_RuntimeSD[(sizeof(ACL)+sizeof(ACCESS_ALLOWED_ACE)+sizeof(SECURITY_DESCRIPTOR_RELATIVE)+4*(sizeof(SID)+SID_MAX_SUB_AUTHORITIES*sizeof(DWORD)))/sizeof(DWORD)]; typedef BOOLEAN ( * fnRtlValidRelativeSecurityDescriptor)( IN PSECURITY_DESCRIPTOR SecurityDescriptorInput, IN ULONG SecurityDescriptorLength, IN SECURITY_INFORMATION RequiredInformation ); fnRtlValidRelativeSecurityDescriptor RtlValidRelativeSecurityDescriptor; // // Build a SD with owner == This // group == This // DACL // ACE[0] MUTEX_ALL_ACCESS Owner // ACE[1] MUTEX_ALL_ACCESS System /////////////////////////////////////////////////////////////////// BOOL CreateSD( ) { if (!RtlValidRelativeSecurityDescriptor) { HMODULE hModule = GetModuleHandleW(L"ntdll.dll"); if (hModule) { RtlValidRelativeSecurityDescriptor = (fnRtlValidRelativeSecurityDescriptor)GetProcAddress(hModule,"RtlValidRelativeSecurityDescriptor"); if (!RtlValidRelativeSecurityDescriptor) { return FALSE; } } } HANDLE hToken; BOOL bRet; bRet = OpenProcessToken(GetCurrentProcess(),TOKEN_QUERY,&hToken); if (bRet) { TOKEN_USER * pToken_User; DWORD dwSize = sizeof(TOKEN_USER)+sizeof(SID)+(SID_MAX_SUB_AUTHORITIES*sizeof(DWORD)); pToken_User = (TOKEN_USER *)_alloca(dwSize); bRet = GetTokenInformation(hToken,TokenUser,pToken_User,dwSize,&dwSize); if (bRet) { SID SystemSid = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, SECURITY_LOCAL_SYSTEM_RID }; PSID pSIDUser = pToken_User->User.Sid; dwSize = GetLengthSid(pSIDUser); DWORD dwSids = 2; // Owner and System DWORD ACLLength = (ULONG) sizeof(ACL) + (dwSids * ((ULONG) sizeof(ACCESS_ALLOWED_ACE) - sizeof(ULONG))) + dwSize + sizeof(SystemSid); DWORD dwSizeSD = sizeof(SECURITY_DESCRIPTOR_RELATIVE) + dwSize + dwSize + ACLLength; SECURITY_DESCRIPTOR_RELATIVE * pLocalSD = (SECURITY_DESCRIPTOR_RELATIVE *)_alloca(dwSizeSD); memset(pLocalSD,0,sizeof(SECURITY_DESCRIPTOR_RELATIVE)); pLocalSD->Revision = SECURITY_DESCRIPTOR_REVISION; pLocalSD->Control = SE_DACL_PRESENT|SE_SELF_RELATIVE; //SetSecurityDescriptorOwner(pLocalSD,pSIDUser,FALSE); memcpy((BYTE*)pLocalSD+sizeof(SECURITY_DESCRIPTOR_RELATIVE),pSIDUser,dwSize); pLocalSD->Owner = (DWORD)sizeof(SECURITY_DESCRIPTOR_RELATIVE); //SetSecurityDescriptorGroup(pLocalSD,pSIDUser,FALSE); memcpy((BYTE*)pLocalSD+sizeof(SECURITY_DESCRIPTOR_RELATIVE)+dwSize,pSIDUser,dwSize); pLocalSD->Group = (DWORD)(sizeof(SECURITY_DESCRIPTOR_RELATIVE)+dwSize); PACL pDacl = (PACL)_alloca(ACLLength); bRet = InitializeAcl( pDacl, ACLLength, ACL_REVISION); if (bRet) { bRet = AddAccessAllowedAceEx (pDacl,ACL_REVISION,0,MUTEX_ALL_ACCESS,&SystemSid); if (bRet) { bRet = AddAccessAllowedAceEx (pDacl,ACL_REVISION,0,MUTEX_ALL_ACCESS,pSIDUser); if (bRet) { //bRet = SetSecurityDescriptorDacl(pLocalSD,TRUE,pDacl,FALSE); memcpy((BYTE*)pLocalSD+sizeof(SECURITY_DESCRIPTOR_RELATIVE)+dwSize+dwSize,pDacl,ACLLength); pLocalSD->Dacl = (DWORD)(sizeof(SECURITY_DESCRIPTOR_RELATIVE)+dwSize+dwSize); if (RtlValidRelativeSecurityDescriptor(pLocalSD, dwSizeSD, OWNER_SECURITY_INFORMATION| GROUP_SECURITY_INFORMATION| DACL_SECURITY_INFORMATION)) { g_SizeSD = dwSizeSD; memcpy(g_RuntimeSD,pLocalSD,dwSizeSD); } else { bRet = FALSE; } } } } } CloseHandle(hToken); } return bRet; }; //*************************************************************************** // // HANDLE CreateMutexAsProcess(LPCWSTR pwszName) // // This function will create a mutex using the process' security context // //*************************************************************************** // HANDLE CreateMutexAsProcess(LPCWSTR pwszName) { BOOL bImpersonating = FALSE; HANDLE hThreadToken = NULL; // Determine if we are impersonating bImpersonating = OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &hThreadToken); if(bImpersonating) { // Determine if we are impersonating bImpersonating = RevertToSelf(); } // Create the mutex as using the process token. HANDLE hRet = OpenMutexW(MUTEX_ALL_ACCESS,FALSE,pwszName); if (NULL == hRet) { SECURITY_ATTRIBUTES sa; if (0 == g_SizeSD) { if (CreateSD()) { sa.nLength = g_SizeSD; sa.lpSecurityDescriptor = (LPVOID)g_RuntimeSD; sa.bInheritHandle = FALSE; } else { sa.nLength = sizeof(g_PrecSD); sa.lpSecurityDescriptor = (LPVOID)g_PrecSD; sa.bInheritHandle = FALSE; } } else { sa.nLength = g_SizeSD; sa.lpSecurityDescriptor = (LPVOID)g_RuntimeSD; sa.bInheritHandle = FALSE; } hRet = CreateMutexW(&sa, FALSE, pwszName); } // If code was oringinally impersonating, resume impersonation if(bImpersonating) SetThreadToken(NULL, hThreadToken); if(hThreadToken) CloseHandle(hThreadToken); return hRet; } //*************************************************************************** // // CPerfDataLibrary ::CPerfDataLibrary // // This object is used to abstract the perf data library // //*************************************************************************** // CPerfDataLibrary::CPerfDataLibrary (void) { pLibInfo = NULL; memset ((LPVOID)szQueryString, 0, sizeof(szQueryString)); dwRefCount = 0; // number of classes referencing this object } CPerfDataLibrary::~CPerfDataLibrary (void) { // all libraries should be closed before this is // destructed assert (dwRefCount == 0); assert (pLibInfo == NULL); } //*************************************************************************** // // CPerfObjectAccess::CPerfObjectAccess // // This object is used to abstract a data object within a perf library // //*************************************************************************** // CPerfObjectAccess::CPerfObjectAccess () { m_hObjectHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS, 0x10000, 0); if (m_hObjectHeap == NULL) { // then just use the process heap m_hObjectHeap = GetProcessHeap(); } for (DWORD n=0; n < PL_TIMER_NUM_OBJECTS; n++) { hTimerHandles[n] = NULL; } hTimerDataMutex = NULL; hPerflibTimingThread = NULL; pTimerItemListHead = NULL; m_aLibraries.Empty(); lEventLogLevel = LOG_UNDEFINED; hEventLog = NULL; } CPerfObjectAccess::~CPerfObjectAccess () { int nNumLibraries; int nIdx; CPerfDataLibrary *pThisLibrary; if (pTimerItemListHead != NULL) { DestroyPerflibFunctionTimer(); pTimerItemListHead = NULL; } // the DestroyPerflibFunctionTimer should take care of these // but just in case.... if (hTimerDataMutex != NULL) { CloseHandle (hTimerDataMutex); hTimerDataMutex = NULL; } if (hPerflibTimingThread != NULL) { CloseHandle (hPerflibTimingThread); hPerflibTimingThread = NULL; } for (DWORD n=0; n < PL_TIMER_NUM_OBJECTS; n++) { if (hTimerHandles[n] != NULL) { CloseHandle (hTimerHandles[n]); hTimerHandles[n] = NULL; } } // close any lingering libraries nNumLibraries = m_aLibraries.Size(); for (nIdx = 0; nIdx < nNumLibraries; nIdx++) { pThisLibrary = (CPerfDataLibrary *)m_aLibraries[nIdx]; CloseLibrary (pThisLibrary); FREEMEM(m_hObjectHeap, 0, pThisLibrary->pLibInfo); pThisLibrary->pLibInfo = NULL; delete pThisLibrary; } m_aLibraries.Empty(); if ((m_hObjectHeap != NULL) && (m_hObjectHeap != GetProcessHeap())) { HeapDestroy (m_hObjectHeap); } } // // Timer functions // DWORD __stdcall PerflibTimerFunction ( LPVOID pArg ) /*++ PerflibTimerFunction Timing thread used to write an event log message if the timer expires. This thread runs until the Exit event is set or the wait for the Exit event times out. While the start event is set, then the timer checks the current events to be timed and reports on any that have expired. It then sleeps for the duration of the timing interval after which it checks the status of the start & exit events to begin the next cycle. The timing events are added and deleted from the list only by the StartPerflibFunctionTimer and KillPerflibFunctionTimer functions. Arguments pArg -- parent class object --*/ { CPerfObjectAccess *pObj = (CPerfObjectAccess *)pArg; LONG lStatus = ERROR_SUCCESS; BOOL bKeepTiming = TRUE; LPOPEN_PROC_WAIT_INFO pLocalInfo; LPWSTR szMessageArray[2]; DWORD dwData; if (lStatus == ERROR_SUCCESS) { while (bKeepTiming) { // wait for either the start or exit event flags to be set lStatus = WaitForMultipleObjects ( PL_TIMER_NUM_OBJECTS, &pObj->hTimerHandles[0], FALSE, //wait for either one to be set PERFLIB_TIMING_THREAD_TIMEOUT); if (lStatus != WAIT_TIMEOUT) { if ((lStatus - WAIT_OBJECT_0) == PL_TIMER_EXIT_EVENT ) { // then that's all bKeepTiming = FALSE; break; } else if ((lStatus - WAIT_OBJECT_0) == PL_TIMER_START_EVENT) { // then the timer is running so wait the interval period // wait on exit event here to prevent hanging lStatus = WaitForSingleObject ( pObj->hTimerHandles[PL_TIMER_EXIT_EVENT], PERFLIB_TIMING_THREAD_TIMEOUT); if (lStatus == WAIT_TIMEOUT) { // then the wait time expired without being told // to terminate the thread so // now evaluate the list of timed events // lock the data mutex lStatus = WaitForSingleObject ( pObj->hTimerDataMutex, (PERFLIB_TIMER_INTERVAL * 2)); for (pLocalInfo = pObj->pTimerItemListHead; pLocalInfo != NULL; pLocalInfo = pLocalInfo->pNext) { if (pLocalInfo->dwWaitTime != 0) { if (pLocalInfo->dwWaitTime == 1) { // then this is the last interval so log error szMessageArray[0] = pLocalInfo->szServiceName; szMessageArray[1] = pLocalInfo->szLibraryName; dwData = pLocalInfo->dwWaitTime * 1000; ReportEventW (hEventLog, EVENTLOG_WARNING_TYPE, // error type 0, // category (not used) (DWORD)WBEMPERF_OPEN_PROC_TIMEOUT, // event, NULL, // SID (not used), 2, // number of strings sizeof(DWORD), // sizeof raw data (LPCWSTR *)szMessageArray, // message text array (LPBYTE)&dwData); // raw data } pLocalInfo->dwWaitTime--; } } ReleaseMutex (pObj->hTimerDataMutex); } else { // we've been told to exit so lStatus = ERROR_SUCCESS; bKeepTiming = FALSE; break; } } else { // some unexpected error was returned assert (FALSE); } } else { // the wait timed out so it's time to go lStatus = ERROR_SUCCESS; bKeepTiming = FALSE; break; } } } return lStatus; } HANDLE CPerfObjectAccess::StartPerflibFunctionTimer ( IN LPOPEN_PROC_WAIT_INFO pInfo ) /*++ Starts a timing event by adding it to the list of timing events. If the timer thread is not running, then the is started as well. If this is the first event in the list then the Start Event is set indicating that the timing thread can begin processing timing event(s). --*/ { LONG Status = ERROR_SUCCESS; LPOPEN_PROC_WAIT_INFO pLocalInfo; DWORD dwLibNameLen; DWORD dwBufferLength = sizeof (OPEN_PROC_WAIT_INFO); HANDLE hReturn = NULL; if (pInfo == NULL) { // no required argument Status = ERROR_INVALID_PARAMETER; } else { // check on or create sync objects // allocate timing events for the timing thread if (hTimerHandles[PL_TIMER_START_EVENT] == NULL) { // create the event as NOT signaled since we're not ready to start hTimerHandles[PL_TIMER_START_EVENT] = CreateEvent (NULL, TRUE, FALSE, NULL); if (hTimerHandles[PL_TIMER_START_EVENT] == NULL) { Status = GetLastError(); } } if (hTimerHandles[PL_TIMER_EXIT_EVENT] == NULL) { hTimerHandles[PL_TIMER_EXIT_EVENT] = CreateEvent (NULL, TRUE, FALSE, NULL); if (hTimerHandles[PL_TIMER_EXIT_EVENT] == NULL) { Status = GetLastError(); } } // create data sync mutex if it hasn't already been created if (hTimerDataMutex == NULL) { hTimerDataMutex = CreateMutex (NULL, FALSE, NULL); if (hTimerDataMutex == NULL) { Status = GetLastError(); } } } if (Status == ERROR_SUCCESS) { // continue creating timer entry if (hPerflibTimingThread != NULL) { // see if the handle is valid (i.e the thread is alive) Status = WaitForSingleObject (hPerflibTimingThread, 0); if (Status == WAIT_OBJECT_0) { // the thread has terminated so close the handle CloseHandle (hPerflibTimingThread); hPerflibTimingThread = NULL; Status = ERROR_SUCCESS; } else if (Status == WAIT_TIMEOUT) { // the thread is still running so continue Status = ERROR_SUCCESS; } else { // some other, probably serious, error // so pass it on through } } else { // the thread has never been created yet so continue } if (hPerflibTimingThread == NULL) { DWORD hThreadID; // create the timing thread assert (pTimerItemListHead == NULL); // there should be no entries, yet // everything is ready for the timer thread hPerflibTimingThread = CreateThread ( NULL, 0, (LPTHREAD_START_ROUTINE)PerflibTimerFunction, (LPVOID)this, 0, &hThreadID); assert (hPerflibTimingThread != NULL); if (hPerflibTimingThread == NULL) { Status = GetLastError(); } } if (Status == ERROR_SUCCESS) { // compute the length of the required buffer; dwLibNameLen = (lstrlenW (pInfo->szLibraryName) + 1) * sizeof(WCHAR); dwBufferLength += dwLibNameLen; dwBufferLength += (lstrlenW (pInfo->szServiceName) + 1) * sizeof(WCHAR); dwBufferLength = DWORD_MULTIPLE (dwBufferLength); pLocalInfo = (LPOPEN_PROC_WAIT_INFO)ALLOCMEM ( m_hObjectHeap, HEAP_ZERO_MEMORY, dwBufferLength); if (pLocalInfo) { // copy the arg buffer to the local list pLocalInfo->szLibraryName = (LPWSTR)&pLocalInfo[1]; lstrcpyW (pLocalInfo->szLibraryName, pInfo->szLibraryName); pLocalInfo->szServiceName = (LPWSTR) ((LPBYTE)pLocalInfo->szLibraryName + dwLibNameLen); lstrcpyW (pLocalInfo->szServiceName, pInfo->szServiceName); // convert wait time in milliseconds to the number of "loops" pLocalInfo->dwWaitTime = pInfo->dwWaitTime / PERFLIB_TIMER_INTERVAL; // wait for access to the data if (hTimerDataMutex != NULL) { Status = WaitForSingleObject ( hTimerDataMutex, (PERFLIB_TIMER_INTERVAL * 2)); } else { Status = ERROR_NOT_READY; } if (Status == WAIT_OBJECT_0) { // we have access to the data so add this item to the front of the list pLocalInfo->pNext = pTimerItemListHead; pTimerItemListHead = pLocalInfo; ReleaseMutex (hTimerDataMutex); if (pLocalInfo->pNext == NULL) { // then the list was empty before this call so start the timer // going SetEvent (hTimerHandles[PL_TIMER_START_EVENT]); } hReturn = (HANDLE)pLocalInfo; } else { SetLastError (Status); } } else { Status = ERROR_NOT_ENOUGH_MEMORY; SetLastError (Status); } } else { // unable to create thread SetLastError (Status); } } else { // unable to start timer SetLastError (Status); } return hReturn; } DWORD CPerfObjectAccess::KillPerflibFunctionTimer ( IN HANDLE hPerflibTimer ) /*++ Terminates a timing event by removing it from the list. When the last item is removed from the list the Start event is reset so the timing thread will wait for either the next start event, exit event or it's timeout to expire. --*/ { DWORD Status; LPOPEN_PROC_WAIT_INFO pArg = (LPOPEN_PROC_WAIT_INFO)hPerflibTimer; LPOPEN_PROC_WAIT_INFO pLocalInfo; BOOL bFound = FALSE; DWORD dwReturn = ERROR_SUCCESS; if (hTimerDataMutex == NULL) { dwReturn = ERROR_NOT_READY; } else if (pArg == NULL) { dwReturn = ERROR_INVALID_HANDLE; } else { // so far so good // wait for access to the data Status = WaitForSingleObject ( hTimerDataMutex, (PERFLIB_TIMER_INTERVAL * 2)); if (Status == WAIT_OBJECT_0) { // we have access to the list so walk down the list and remove the // specified item // see if it's the first one in the list if (pArg == pTimerItemListHead) { // then remove it pTimerItemListHead = pArg->pNext; bFound = TRUE; } else { for (pLocalInfo = pTimerItemListHead; pLocalInfo != NULL; pLocalInfo = pLocalInfo->pNext) { if (pLocalInfo->pNext == pArg) { pLocalInfo->pNext = pArg->pNext; bFound = TRUE; break; } } } assert (bFound); if (bFound) { // it's out of the list so release the lock ReleaseMutex (hTimerDataMutex); if (pTimerItemListHead == NULL) { // then the list is empty now so stop timing // going ResetEvent (hTimerHandles[PL_TIMER_START_EVENT]); } // free memory FREEMEM (m_hObjectHeap, 0, pArg); dwReturn = ERROR_SUCCESS; } else { dwReturn = ERROR_NOT_FOUND; } } else { dwReturn = ERROR_TIMEOUT; } } return dwReturn; } DWORD CPerfObjectAccess::DestroyPerflibFunctionTimer ( ) /*++ Terminates the timing thread and cancels any current timer events. --*/ { LONG Status; LPOPEN_PROC_WAIT_INFO pThisItem; LPOPEN_PROC_WAIT_INFO pNextItem; // wait for data mutex Status = WaitForSingleObject ( hTimerDataMutex, (PERFLIB_TIMER_INTERVAL * 2)); assert (Status != WAIT_TIMEOUT); // free all entries in the list for (pNextItem = pTimerItemListHead; pNextItem != NULL;) { pThisItem = pNextItem; pNextItem = pThisItem->pNext; FREEMEM (m_hObjectHeap, 0, pThisItem); } // set exit event SetEvent (hTimerHandles[PL_TIMER_EXIT_EVENT]); // wait for thread to terminate Status = WaitForSingleObject ( hPerflibTimingThread, (PERFLIB_TIMER_INTERVAL * 5)); assert (Status != WAIT_TIMEOUT); if (hPerflibTimingThread != NULL) { CloseHandle (hPerflibTimingThread); hPerflibTimingThread = NULL; } if (hTimerDataMutex != NULL) { // cloes handles and leave ReleaseMutex (hTimerDataMutex); CloseHandle (hTimerDataMutex); hTimerDataMutex = NULL; } if (hTimerHandles[PL_TIMER_START_EVENT] != NULL) { CloseHandle (hTimerHandles[PL_TIMER_START_EVENT]); hTimerHandles[PL_TIMER_START_EVENT] = NULL; } if (hTimerHandles[PL_TIMER_EXIT_EVENT] != NULL) { CloseHandle (hTimerHandles[PL_TIMER_EXIT_EVENT]); hTimerHandles[PL_TIMER_EXIT_EVENT] = NULL; } return ERROR_SUCCESS; } //*************************************************************************** // // CPerfObjectAccess::CloseLibrary (CPerfDataLibrary *pLib) // // removes a reference to the library that contains this object and closes // the library when the last reference is removed // //*************************************************************************** // DWORD CPerfObjectAccess::CloseLibrary (CPerfDataLibrary *pLib) { pExtObject pInfo; LONG lStatus; assert (pLib != NULL); assert (pLib->pLibInfo != NULL); pInfo = pLib->pLibInfo; assert (pLib->dwRefCount > 0); if (pLib->dwRefCount > 0) { pLib->dwRefCount--; if (pLib->dwRefCount == 0) { // if there's a close proc to call, then // call close procedure to close anything that may have // been allocated by the library if (pInfo->hMutex != NULL){ lStatus = WaitForSingleObject ( pInfo->hMutex, pInfo->dwOpenTimeout); // BUG!!A-DCREWS: If CloseProc is uninitialized, then the condition will // result in the lockoutcount being incremented if ((lStatus != WAIT_TIMEOUT) && (pInfo->CloseProc != NULL)) { lStatus = (*pInfo->CloseProc) (); ReleaseMutex(pInfo->hMutex); } else { pInfo->dwLockoutCount++; } } else { lStatus = ERROR_LOCK_FAILED; } // then close everything if (pInfo->hMutex != NULL) { CloseHandle (pInfo->hMutex); pInfo->hMutex = NULL; } if (pInfo->hLibrary != NULL) { FreeLibrary (pInfo->hLibrary); pInfo->hLibrary = NULL; } if (pInfo->hPerfKey != NULL) { RegCloseKey (pInfo->hPerfKey); pInfo->hPerfKey = NULL; } } } return pLib->dwRefCount; // returns remaining references } //*************************************************************************** // // CPerfObjectAccess::OpenExtObjectLibrary (pExtObject pObj) // // OpenExtObjectLibrary // // Opens the specified library and looks up the functions used by // the performance library. If the library is successfully // loaded and opened then the open procedure is called to initialize // the object. // // This function expects locked and exclusive access to the object while // it is opening. This must be provided by the calling function. // // Arguments: // // pObj -- pointer to the object information structure of the // perf object to close // //*************************************************************************** // DWORD CPerfObjectAccess::OpenExtObjectLibrary (pExtObject pObj) { DWORD Status = ERROR_SUCCESS; DWORD dwOpenEvent = 0; DWORD dwType; DWORD dwSize; DWORD dwValue; // variables used for event logging DWORD dwDataIndex; WORD wStringIndex; DWORD dwRawDataDwords[8]; LPWSTR szMessageArray[8]; HANDLE hPerflibFuncTimer = NULL; OPEN_PROC_WAIT_INFO opwInfo; UINT nErrorMode; // check to see if the library has already been opened if (pObj->hLibrary == NULL) { // library isn't loaded yet, so // check to see if this function is enabled dwType = 0; dwSize = sizeof (dwValue); dwValue = 0; Status = RegQueryValueExW ( pObj->hPerfKey, cszDisablePerformanceCounters, NULL, &dwType, (LPBYTE)&dwValue, &dwSize); if ((Status == ERROR_SUCCESS) && (dwType == REG_DWORD) && (dwValue == 1)) { // then DON'T Load this library Status = ERROR_SERVICE_DISABLED; } else { Status = ERROR_SUCCESS; // go ahead and load it nErrorMode = SetErrorMode (SEM_FAILCRITICALERRORS); // then load library & look up functions pObj->hLibrary = LoadLibraryExW (pObj->szLibraryName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); if (pObj->hLibrary != NULL) { // lookup function names pObj->OpenProc = (OPENPROC)GetProcAddress( pObj->hLibrary, pObj->szOpenProcName); if (pObj->OpenProc == NULL) { if (lEventLogLevel >= LOG_USER) { Status = GetLastError(); // load data for eventlog message dwDataIndex = wStringIndex = 0; dwRawDataDwords[dwDataIndex++] = (DWORD)Status; szMessageArray[wStringIndex++] = ConvertProcName(pObj->szOpenProcName); szMessageArray[wStringIndex++] = pObj->szLibraryName; szMessageArray[wStringIndex++] = pObj->szServiceName; ReportEventW (hEventLog, EVENTLOG_ERROR_TYPE, // error type 0, // category (not used) (DWORD)WBEMPERF_OPEN_PROC_NOT_FOUND, // event, NULL, // SID (not used), wStringIndex, // number of strings dwDataIndex*sizeof(DWORD), // sizeof raw data (LPCWSTR *)szMessageArray, // message text array (LPVOID)&dwRawDataDwords[0]); // raw data } } if (Status == ERROR_SUCCESS) { if (pObj->dwFlags & PERF_EO_QUERY_FUNC) { pObj->QueryProc = (QUERYPROC)GetProcAddress ( pObj->hLibrary, pObj->szCollectProcName); pObj->CollectProc = (COLLECTPROC)pObj->QueryProc; } else { pObj->CollectProc = (COLLECTPROC)GetProcAddress ( pObj->hLibrary, pObj->szCollectProcName); pObj->QueryProc = (QUERYPROC)pObj->CollectProc; } if (pObj->CollectProc == NULL) { if (lEventLogLevel >= LOG_USER) { Status = GetLastError(); // load data for eventlog message dwDataIndex = wStringIndex = 0; dwRawDataDwords[dwDataIndex++] = (DWORD)Status; szMessageArray[wStringIndex++] = ConvertProcName(pObj->szCollectProcName); szMessageArray[wStringIndex++] = pObj->szLibraryName; szMessageArray[wStringIndex++] = pObj->szServiceName; ReportEventW (hEventLog, EVENTLOG_ERROR_TYPE, // error type 0, // category (not used) (DWORD)WBEMPERF_COLLECT_PROC_NOT_FOUND, // event, NULL, // SID (not used), wStringIndex, // number of strings dwDataIndex*sizeof(DWORD), // sizeof raw data (LPCWSTR *)szMessageArray, // message text array (LPVOID)&dwRawDataDwords[0]); // raw data } } } if (Status == ERROR_SUCCESS) { pObj->CloseProc = (CLOSEPROC)GetProcAddress ( pObj->hLibrary, pObj->szCloseProcName); if (pObj->CloseProc == NULL) { if (lEventLogLevel >= LOG_USER) { Status = GetLastError(); // load data for eventlog message dwDataIndex = wStringIndex = 0; dwRawDataDwords[dwDataIndex++] = (DWORD)Status; szMessageArray[wStringIndex++] = ConvertProcName(pObj->szCloseProcName); szMessageArray[wStringIndex++] = pObj->szLibraryName; szMessageArray[wStringIndex++] = pObj->szServiceName; ReportEventW (hEventLog, EVENTLOG_ERROR_TYPE, // error type 0, // category (not used) (DWORD)WBEMPERF_CLOSE_PROC_NOT_FOUND, // event, NULL, // SID (not used), wStringIndex, // number of strings dwDataIndex*sizeof(DWORD), // sizeof raw data (LPCWSTR *)szMessageArray, // message text array (LPVOID)&dwRawDataDwords[0]); // raw data } } } if (Status == ERROR_SUCCESS) { __try { // start timer opwInfo.pNext = NULL; opwInfo.szLibraryName = pObj->szLibraryName; opwInfo.szServiceName = pObj->szServiceName; opwInfo.dwWaitTime = pObj->dwOpenTimeout; #if 0 // disabled for testing hPerflibFuncTimer = StartPerflibFunctionTimer(&opwInfo); // if no timer, continue anyway, even though things may // hang, it's better than not loading the DLL since they // usually load OK // if (hPerflibFuncTimer == NULL) { // unable to get a timer entry DebugPrint (("\nPERFLIB: Unable to acquire timer for Open Proc")); } #endif //test section // call open procedure to initialize DLL if (pObj->hMutex != NULL) { Status = WaitForSingleObject ( pObj->hMutex, pObj->dwOpenTimeout); // BUG!!A-DCREWS: If OpenProc is uninitialized, then the condition will // result in the lockoutcount being incremented if ((Status != WAIT_TIMEOUT) && (pObj->OpenProc != NULL)) { Status = (*pObj->OpenProc)(pObj->szLinkageString); ReleaseMutex(pObj->hMutex); } else { pObj->dwLockoutCount++; } } else { Status = ERROR_LOCK_FAILED; } // check the result. if (Status != ERROR_SUCCESS) { dwOpenEvent = WBEMPERF_OPEN_PROC_FAILURE; } else { InterlockedIncrement((LONG *)&pObj->dwOpenCount); } if (hPerflibFuncTimer != NULL) { // kill timer Status = KillPerflibFunctionTimer (hPerflibFuncTimer); } else dwOpenEvent = WBEMPERF_OPEN_PROC_FAILURE; } __except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); dwOpenEvent = WBEMPERF_OPEN_PROC_EXCEPTION; } if (Status != ERROR_SUCCESS) { // load data for eventlog message dwDataIndex = wStringIndex = 0; dwRawDataDwords[dwDataIndex++] = (DWORD)Status; szMessageArray[wStringIndex++] = pObj->szServiceName; szMessageArray[wStringIndex++] = pObj->szLibraryName; ReportEventW (hEventLog, (WORD)EVENTLOG_ERROR_TYPE, // error type 0, // category (not used) dwOpenEvent, // event, NULL, // SID (not used), wStringIndex, // number of strings dwDataIndex*sizeof(DWORD), // sizeof raw data (LPCWSTR *)szMessageArray, // message text array (LPVOID)&dwRawDataDwords[0]); // raw data } } if (Status != ERROR_SUCCESS) { // clear fields pObj->OpenProc = NULL; pObj->CollectProc = NULL; pObj->QueryProc = NULL; pObj->CloseProc = NULL; if (pObj->hLibrary != NULL) { FreeLibrary (pObj->hLibrary); pObj->hLibrary = NULL; } } else { pObj->llLastUsedTime = GetTimeAsLongLong(); } } else { Status = GetLastError(); } SetErrorMode (nErrorMode); } } else { // else already open so bump the ref count pObj->llLastUsedTime = GetTimeAsLongLong(); } return Status; } //*************************************************************************** // // CPerfObjectAccess::AddLibrary ( // IWbemClassObject *pClass, // IWbemQualifierSet *pClassQualifiers, // LPCWSTR szRegistryKey, // DWORD dwPerfIndex) // // Adds the library referenced by the class object to the list of // libraries to call // //*************************************************************************** // DWORD CPerfObjectAccess::AddLibrary ( IWbemClassObject *pClass, IWbemQualifierSet *pClassQualifiers, LPCWSTR szRegistryKey, DWORD dwPerfIndex) { CPerfDataLibrary *pLibEntry = NULL; LONG Status = ERROR_SUCCESS; HKEY hServicesKey = NULL; HKEY hPerfKey = NULL; LPWSTR szServiceName = NULL; HKEY hKeyLinkage; BOOL bUseQueryFn = FALSE; pExtObject pReturnObject = NULL; DWORD dwType = 0; DWORD dwSize = 0; DWORD dwFlags = 0; DWORD dwKeep; DWORD dwObjectArray[MAX_PERF_OBJECTS_IN_QUERY_FUNCTION]; DWORD dwObjIndex = 0; DWORD dwMemBlockSize = sizeof(ExtObject); DWORD dwLinkageStringLen = 0; CHAR szOpenProcName[MAX_PATH]; CHAR szCollectProcName[MAX_PATH]; CHAR szCloseProcName[MAX_PATH]; WCHAR szLibraryString[MAX_PATH]; WCHAR szLibraryExpPath[MAX_PATH]; WCHAR mszObjectList[MAX_PATH]; WCHAR szLinkageKeyPath[MAX_PATH]; WCHAR szLinkageString[MAX_PATH]; DWORD dwOpenTimeout = 0; DWORD dwCollectTimeout = 0; LPWSTR szThisObject; LPWSTR szThisChar; LPSTR pNextStringA; LPWSTR pNextStringW; WCHAR szServicePath[MAX_PATH]; WCHAR szMutexName[MAX_PATH]; WCHAR szPID[32]; assert(pClass != NULL); assert(pClassQualifiers != NULL); UNREFERENCED_PARAMETER(pClassQualifiers); UNREFERENCED_PARAMETER(pClass); pLibEntry = new CPerfDataLibrary; if ((pLibEntry != NULL) && (szRegistryKey != NULL)) { lstrcpyW (szServicePath, cszHklmServicesKey); Status = RegOpenKeyExW (HKEY_LOCAL_MACHINE, szServicePath, 0, KEY_READ, &hServicesKey); if (Status == ERROR_SUCCESS) { lstrcpyW (szServicePath, szRegistryKey); lstrcatW (szServicePath, cszPerformance); Status = RegOpenKeyExW (hServicesKey, szServicePath, 0, KEY_READ, &hPerfKey); if (Status == ERROR_SUCCESS) { szServiceName = (LPWSTR)szRegistryKey; // read the performance DLL name dwType = 0; dwSize = sizeof(szLibraryString); memset (szLibraryString, 0, sizeof(szLibraryString)); memset (szLibraryString, 0, sizeof(szLibraryExpPath)); Status = RegQueryValueExW (hPerfKey, cszDLLValue, NULL, &dwType, (LPBYTE)szLibraryString, &dwSize); } } if (Status == ERROR_SUCCESS) { if (dwType == REG_EXPAND_SZ) { // expand any environment vars dwSize = ExpandEnvironmentStringsW( szLibraryString, szLibraryExpPath, MAX_PATH); if ((dwSize > MAX_PATH) || (dwSize == 0)) { Status = ERROR_INVALID_DLL; } else { dwSize += 1; dwSize *= sizeof(WCHAR); dwMemBlockSize += DWORD_MULTIPLE(dwSize); } } else if (dwType == REG_SZ) { // look for dll and save full file Path dwSize = SearchPathW ( NULL, // use standard system search path szLibraryString, NULL, MAX_PATH, szLibraryExpPath, NULL); if ((dwSize > MAX_PATH) || (dwSize == 0)) { Status = ERROR_INVALID_DLL; } else { dwSize += 1; dwSize *= sizeof(WCHAR); dwMemBlockSize += DWORD_MULTIPLE(dwSize); } } else { Status = ERROR_INVALID_DLL; } if (Status == ERROR_SUCCESS) { // we have the DLL name so get the procedure names dwType = 0; dwSize = sizeof(szOpenProcName); memset (szOpenProcName, 0, sizeof(szOpenProcName)); Status = RegQueryValueExA (hPerfKey, caszOpenValue, NULL, &dwType, (LPBYTE)szOpenProcName, &dwSize); } if (Status == ERROR_SUCCESS) { // add in size of previous string // the size value includes the Term. NULL dwMemBlockSize += DWORD_MULTIPLE(dwSize); // we have the procedure name so get the timeout value dwType = 0; dwSize = sizeof(dwOpenTimeout); Status = RegQueryValueExW (hPerfKey, cszOpenTimeout, NULL, &dwType, (LPBYTE)&dwOpenTimeout, &dwSize); // if error, then apply default if ((Status != ERROR_SUCCESS) || (dwType != REG_DWORD)) { dwOpenTimeout = dwExtCtrOpenProcWaitMs; Status = ERROR_SUCCESS; } } if (Status == ERROR_SUCCESS) { // get next string dwType = 0; dwSize = sizeof(szCloseProcName); memset (szCloseProcName, 0, sizeof(szCloseProcName)); Status = RegQueryValueExA (hPerfKey, caszCloseValue, NULL, &dwType, (LPBYTE)szCloseProcName, &dwSize); } if (Status == ERROR_SUCCESS) { // add in size of previous string // the size value includes the Term. NULL dwMemBlockSize += DWORD_MULTIPLE(dwSize); // try to look up the query function which is the // preferred interface if it's not found, then // try the collect function name. If that's not found, // then bail dwType = 0; dwSize = sizeof(szCollectProcName); memset (szCollectProcName, 0, sizeof(szCollectProcName)); Status = RegQueryValueExA (hPerfKey, caszQueryValue, NULL, &dwType, (LPBYTE)szCollectProcName, &dwSize); if (Status == ERROR_SUCCESS) { // add in size of the Query Function Name // the size value includes the Term. NULL dwMemBlockSize += DWORD_MULTIPLE(dwSize); // get next string bUseQueryFn = TRUE; // the query function can support a static object list // so look it up } else { // the QueryFunction wasn't found so look up the // Collect Function name instead dwType = 0; dwSize = sizeof(szCollectProcName); memset (szCollectProcName, 0, sizeof(szCollectProcName)); Status = RegQueryValueExA (hPerfKey, caszCollectValue, NULL, &dwType, (LPBYTE)szCollectProcName, &dwSize); if (Status == ERROR_SUCCESS) { // add in size of Collect Function Name // the size value includes the Term. NULL dwMemBlockSize += DWORD_MULTIPLE(dwSize); } } if (Status == ERROR_SUCCESS) { // we have the procedure name so get the timeout value dwType = 0; dwSize = sizeof(dwCollectTimeout); Status = RegQueryValueExW (hPerfKey, cszCollectTimeout, NULL, &dwType, (LPBYTE)&dwCollectTimeout, &dwSize); // if error, then apply default if ((Status != ERROR_SUCCESS) || (dwType != REG_DWORD)) { dwCollectTimeout = dwExtCtrOpenProcWaitMs; Status = ERROR_SUCCESS; } } // get the list of supported objects if provided by the registry dwType = 0; dwSize = sizeof(mszObjectList); memset (mszObjectList, 0, sizeof(mszObjectList)); Status = RegQueryValueExW (hPerfKey, cszObjListValue, NULL, &dwType, (LPBYTE)mszObjectList, &dwSize); if (Status == ERROR_SUCCESS) { if (dwType != REG_MULTI_SZ) { // convert space delimited list to msz for (szThisChar = mszObjectList; *szThisChar != 0; szThisChar++) { if (*szThisChar == L' ') *szThisChar = L'\0'; } ++szThisChar; *szThisChar = 0; // add MSZ term Null } for (szThisObject = mszObjectList, dwObjIndex = 0; (*szThisObject != 0) && (dwObjIndex < MAX_PERF_OBJECTS_IN_QUERY_FUNCTION); szThisObject += lstrlenW(szThisObject) + 1) { dwObjectArray[dwObjIndex] = wcstoul(szThisObject, NULL, 10); dwObjIndex++; } if (*szThisObject != 0) { DWORD dwDataIndex = 0; WORD wStringIndex = 0; DWORD dwRawDataDwords[8]; LPWSTR szMessageArray[8]; dwRawDataDwords[dwDataIndex++] = (DWORD) ERROR_SUCCESS; szMessageArray[wStringIndex++] = (LPWSTR) cszObjListValue; szMessageArray[wStringIndex++] = szLibraryString; szMessageArray[wStringIndex++] = szServicePath; ReportEventW(hEventLog, EVENTLOG_WARNING_TYPE, 0, (DWORD) WBEMPERF_TOO_MANY_OBJECT_IDS, NULL, wStringIndex, dwDataIndex * sizeof(DWORD), (LPCWSTR *) szMessageArray, (LPVOID) & dwRawDataDwords[0]); } } else { // reset status since not having this is // not a showstopper Status = ERROR_SUCCESS; } if (Status == ERROR_SUCCESS) { dwType = 0; dwKeep = 0; dwSize = sizeof(dwKeep); Status = RegQueryValueExW (hPerfKey, cszKeepResident, NULL, &dwType, (LPBYTE)&dwKeep, &dwSize); if ((Status == ERROR_SUCCESS) && (dwType == REG_DWORD)) { if (dwKeep == 1) { dwFlags |= PERF_EO_KEEP_RESIDENT; } else { // no change. } } else { // not fatal, just use the defaults. Status = ERROR_SUCCESS; } } } } if (Status == ERROR_SUCCESS) { memset (szLinkageString, 0, sizeof(szLinkageString)); lstrcpyW (szLinkageKeyPath, szServiceName); lstrcatW (szLinkageKeyPath, cszLinkageKey); Status = RegOpenKeyExW ( hServicesKey, szLinkageKeyPath, 0L, KEY_READ, &hKeyLinkage); if (Status == ERROR_SUCCESS) { // look up export value string dwSize = sizeof(szLinkageString); dwType = 0; Status = RegQueryValueExW ( hKeyLinkage, cszExportValue, NULL, &dwType, (LPBYTE)&szLinkageString, &dwSize); if ((Status != ERROR_SUCCESS) || ((dwType != REG_SZ) && (dwType != REG_MULTI_SZ))) { // clear buffer memset (szLinkageString, 0, sizeof(szLinkageString)); dwLinkageStringLen = 0; // not finding a linkage key is not fatal so correct // status Status = ERROR_SUCCESS; } else { // add size of linkage string to buffer // the size value includes the Term. NULL dwLinkageStringLen = dwSize; dwMemBlockSize += DWORD_MULTIPLE(dwSize); } RegCloseKey (hKeyLinkage); } else { // not finding a linkage key is not fatal so correct // status Status = ERROR_SUCCESS; } } if (Status == ERROR_SUCCESS) { // add in size of service name dwSize = lstrlenW (szServiceName); dwSize += 1; dwSize *= sizeof(WCHAR); dwMemBlockSize += DWORD_MULTIPLE(dwSize); // allocate and initialize a new ext. object block pReturnObject = (pExtObject)ALLOCMEM(m_hObjectHeap, HEAP_ZERO_MEMORY, dwMemBlockSize); if (pReturnObject != NULL) { // copy values to new buffer (all others are NULL) pNextStringA = (LPSTR)&pReturnObject[1]; // copy Open Procedure Name pReturnObject->szOpenProcName = pNextStringA; lstrcpyA (pNextStringA, szOpenProcName); pNextStringA += lstrlenA (pNextStringA) + 1; pNextStringA = (LPSTR)ALIGN_ON_DWORD(pNextStringA); pReturnObject->dwOpenTimeout = dwOpenTimeout; // copy collect function or query function, depending pReturnObject->szCollectProcName = pNextStringA; lstrcpyA (pNextStringA, szCollectProcName); pNextStringA += lstrlenA (pNextStringA) + 1; pNextStringA = (LPSTR)ALIGN_ON_DWORD(pNextStringA); pReturnObject->dwCollectTimeout = dwCollectTimeout; // copy Close Procedure Name pReturnObject->szCloseProcName = pNextStringA; lstrcpyA (pNextStringA, szCloseProcName); pNextStringA += lstrlenA (pNextStringA) + 1; pNextStringA = (LPSTR)ALIGN_ON_DWORD(pNextStringA); // copy Library path pNextStringW = (LPWSTR)pNextStringA; pReturnObject->szLibraryName = pNextStringW; lstrcpyW (pNextStringW, szLibraryExpPath); pNextStringW += lstrlenW (pNextStringW) + 1; pNextStringW = (LPWSTR)ALIGN_ON_DWORD(pNextStringW); // copy Linkage String if there is one if (*szLinkageString != 0) { pReturnObject->szLinkageString = pNextStringW; memcpy (pNextStringW, szLinkageString, dwLinkageStringLen); // length includes extra NULL char and is in BYTES pNextStringW += (dwLinkageStringLen / sizeof (WCHAR)); pNextStringW = (LPWSTR)ALIGN_ON_DWORD(pNextStringW); } // copy Service name pReturnObject->szServiceName = pNextStringW; lstrcpyW (pNextStringW, szServiceName); pNextStringW += lstrlenW (pNextStringW) + 1; pNextStringW = (LPWSTR)ALIGN_ON_DWORD(pNextStringW); // load flags if (bUseQueryFn) { dwFlags |= PERF_EO_QUERY_FUNC; } pReturnObject->dwFlags = dwFlags; pReturnObject->hPerfKey = hPerfKey; // load Object array if (dwObjIndex > 0) { pReturnObject->dwNumObjects = dwObjIndex; memcpy (pReturnObject->dwObjList, dwObjectArray, (dwObjIndex * sizeof(dwObjectArray[0]))); } pReturnObject->llLastUsedTime = 0; // create Mutex name lstrcpyW (szMutexName, szRegistryKey); lstrcatW (szMutexName, (LPCWSTR)L"_Perf_Library_Lock_PID_"); _ultow ((ULONG)GetCurrentProcessId(), szPID, 16); lstrcatW (szMutexName, szPID); // pReturnObject->hMutex = CreateMutexW (NULL, FALSE, szMutexName); pReturnObject->hMutex = CreateMutexAsProcess(szMutexName); } else { Status = ERROR_OUTOFMEMORY; } } if (Status != ERROR_SUCCESS) { SetLastError (Status); if (pReturnObject != NULL) { // release the new block FREEMEM (m_hObjectHeap, 0, pReturnObject); } } else { if (pReturnObject != NULL) { Status = OpenExtObjectLibrary (pReturnObject); if (Status == ERROR_SUCCESS) { if (dwPerfIndex != 0) { // initialize the perf index string _ultow (dwPerfIndex, pLibEntry->szQueryString, 10); } else { lstrcpyW (pLibEntry->szQueryString, cszGlobal); } // save the pointer to the initialize structure pLibEntry->pLibInfo = pReturnObject; m_aLibraries.Add(pLibEntry); pLibEntry->dwRefCount++; assert(pLibEntry->dwRefCount == 1); } else { // release the new block FREEMEM (m_hObjectHeap, 0, pReturnObject); } } } if (hServicesKey != NULL) RegCloseKey (hServicesKey); } else { // gets here if pLibEntry == NULL and/or szRegistryKey == NULL if (pLibEntry == NULL) { Status = ERROR_OUTOFMEMORY; } if (szRegistryKey == NULL) { Status = ERROR_INVALID_PARAMETER; } } if ((Status != ERROR_SUCCESS) && (pLibEntry != NULL)) delete pLibEntry; return Status; } //*************************************************************************** // // CPerfObjectAccess::AddClass (IWbemClassObject *pClass, BOOL bCatalogQuery) // // Adds the specified WBEM performance object class and any required library // entries to the access object. // //*************************************************************************** // DWORD CPerfObjectAccess::AddClass (IWbemClassObject *pClass, BOOL bCatalogQuery) { CPerfDataLibrary *pLibEntry = NULL; CPerfDataLibrary *pThisLibEntry = NULL; DWORD dwIndex, dwEnd; LPWSTR szRegistryKey = NULL; IWbemQualifierSet *pClassQualifiers = NULL; VARIANT vRegistryKey; HRESULT hRes; DWORD dwReturn = ERROR_SUCCESS; DWORD dwPerfIndex = 0; VariantInit (&vRegistryKey); // get the Qualifier Set for this class hRes = pClass->GetQualifierSet(&pClassQualifiers); assert (hRes == 0); // now get the library and procedure names hRes = pClassQualifiers->Get(CBSTR(cszRegistryKey), 0, &vRegistryKey, 0); if ((hRes == 0) && (vRegistryKey.vt == VT_BSTR)) { szRegistryKey = Macro_CloneLPWSTR(V_BSTR(&vRegistryKey)); if (szRegistryKey == NULL) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; } else { // now also get the perf index if (bCatalogQuery) { // then insert 0 for the perf index to indicate a "GLOBAL" // query dwPerfIndex = 0; } else { VariantClear (&vRegistryKey); hRes = pClassQualifiers->Get(CBSTR(cszPerfIndex), 0, &vRegistryKey, 0); if (hRes == 0) { dwPerfIndex = (DWORD)V_UI4(&vRegistryKey); } else { // unable to find NtPerfLibrary entry dwReturn = ERROR_FILE_NOT_FOUND; } } } } else { // unable to find NtPerfLibrary entry dwReturn = ERROR_FILE_NOT_FOUND; } if (pClassQualifiers != NULL) pClassQualifiers->Release(); if (dwReturn == ERROR_SUCCESS) { // find matching library in the array dwEnd = m_aLibraries.Size(); if (dwEnd > 0) { // walk down the list of libraries for (dwIndex = 0; dwIndex < dwEnd; dwIndex++) { // see if this library entry is good enough to keep // The library is assumed to be a match if the // lib. name and all proc's are the same. pThisLibEntry = (CPerfDataLibrary *)m_aLibraries[dwIndex]; assert (pThisLibEntry != NULL); // it should have been removed! // make sure it's complete assert (pThisLibEntry->pLibInfo->szServiceName != NULL); if (lstrcmpiW (szRegistryKey, pThisLibEntry->pLibInfo->szServiceName) == 0) { pLibEntry = pThisLibEntry; break; } else { // wrong library // so continue } } } if (pLibEntry == NULL) { // add this class & it's library to the list dwReturn = AddLibrary (pClass, pClassQualifiers, szRegistryKey, dwPerfIndex); } else { WCHAR wszNewIndex[MAX_PATH]; pLibEntry->dwRefCount++; _ultow (dwPerfIndex, wszNewIndex, 10); if (!IsNumberInUnicodeList (dwPerfIndex, pLibEntry->szQueryString)) { // then add it to the list lstrcatW (pLibEntry->szQueryString, cszSpace); lstrcatW (pLibEntry->szQueryString, wszNewIndex); } } } if (szRegistryKey != NULL) delete szRegistryKey; VariantClear(&vRegistryKey); return dwReturn; } //*************************************************************************** // // CPerfObjectAccess::CollectData (LPBYTE pBuffer, // LPDWORD pdwBufferSize, LPWSTR pszItemList) // // Collects data from the perf objects and libraries added to the access // object // // Inputs: // // pBuffer - pointer to start of data block // where data is being collected // // pdwBufferSize - pointer to size of data buffer // // pszItemList - string to pass to ext DLL // // Outputs: // // *lppDataDefinition - set to location for next Type // Definition if successful // // Returns: // // 0 if successful, else Win 32 error code of failure // // //*************************************************************************** // DWORD CPerfObjectAccess::CollectData (LPBYTE pBuffer, LPDWORD pdwBufferSize, LPWSTR pszItemList) { LPWSTR lpValueName = NULL; LPBYTE lpData = pBuffer; LPDWORD lpcbData = pdwBufferSize; LPVOID lpDataDefinition = NULL; DWORD Win32Error=ERROR_SUCCESS; // Failure code DWORD BytesLeft; DWORD NumObjectTypes; LPVOID lpExtDataBuffer = NULL; LPVOID lpCallBuffer = NULL; LPVOID lpLowGuardPage = NULL; LPVOID lpHiGuardPage = NULL; LPVOID lpEndPointer = NULL; LPVOID lpBufferBefore = NULL; LPVOID lpBufferAfter = NULL; LPDWORD lpCheckPointer; LARGE_INTEGER liStartTime, liEndTime, liWaitTime; pExtObject pThisExtObj = NULL; BOOL bGuardPageOK; BOOL bBufferOK; BOOL bException; BOOL bUseSafeBuffer; BOOL bUnlockObjData = FALSE; LPWSTR szMessageArray[8]; DWORD dwRawDataDwords[8]; // raw data buffer DWORD dwDataIndex; WORD wStringIndex; LONG lReturnValue = ERROR_SUCCESS; LONG lInstIndex; PERF_OBJECT_TYPE *pObject, *pNextObject; PERF_INSTANCE_DEFINITION *pInstance; PERF_DATA_BLOCK *pPerfData; BOOL bForeignDataBuffer; DWORD dwItemsInList = 0; DWORD dwIndex, dwEntry; CPerfDataLibrary *pThisLib; liStartTime.QuadPart = 0; liEndTime.QuadPart = 0; if (lExtCounterTestLevel < EXT_TEST_NOMEMALLOC) { bUseSafeBuffer = TRUE; } else { bUseSafeBuffer = FALSE; } lReturnValue = RegisterExtObjListAccess(); if (lReturnValue == ERROR_SUCCESS) { if (*pdwBufferSize > (sizeof(PERF_DATA_BLOCK) *2)) { MonBuildPerfDataBlock( (PERF_DATA_BLOCK *)pBuffer, &lpDataDefinition, 0,0); dwItemsInList = m_aLibraries.Size(); } else { lReturnValue = ERROR_MORE_DATA; dwItemsInList = 0; } if (dwItemsInList > 0) { for (dwEntry = 0; dwEntry < dwItemsInList; dwEntry++) { pThisLib = (CPerfDataLibrary *)m_aLibraries[dwEntry]; assert (pThisLib != NULL); pThisExtObj = pThisLib->pLibInfo; if (pszItemList == NULL) { // use the one for this library lpValueName = pThisLib->szQueryString; } else { // use the one passed by the caller lpValueName = pszItemList; } // convert timeout value liWaitTime.QuadPart = MakeTimeOutValue (pThisExtObj->dwCollectTimeout); // initialize values to pass to the extensible counter function NumObjectTypes = 0; BytesLeft = (DWORD) (*lpcbData - ((LPBYTE)lpDataDefinition - lpData)); bException = FALSE; if (pThisExtObj->hLibrary == NULL) { // lock library object if (pThisExtObj->hMutex != NULL) { Win32Error = WaitForSingleObject ( pThisExtObj->hMutex, pThisExtObj->dwCollectTimeout); if (Win32Error != WAIT_TIMEOUT) { // if necessary, open the library if (pThisExtObj->hLibrary == NULL) { // make sure the library is open Win32Error = OpenExtObjectLibrary(pThisExtObj); if (Win32Error != ERROR_SUCCESS) { // assume error has been posted ReleaseMutex (pThisExtObj->hMutex); continue; // to next entry } } ReleaseMutex (pThisExtObj->hMutex); } else { pThisExtObj->dwLockoutCount++; } } else { Win32Error = ERROR_LOCK_FAILED; } } else { // library should be ready to use } // allocate a local block of memory to pass to the // extensible counter function. if (bUseSafeBuffer) { lpExtDataBuffer = ALLOCMEM (m_hObjectHeap, HEAP_ZERO_MEMORY, BytesLeft + (2*GUARD_PAGE_SIZE)); } else { lpExtDataBuffer = lpCallBuffer = lpDataDefinition; } if (lpExtDataBuffer != NULL) { if (bUseSafeBuffer) { // set buffer pointers lpLowGuardPage = lpExtDataBuffer; lpCallBuffer = (LPBYTE)lpExtDataBuffer + GUARD_PAGE_SIZE; lpHiGuardPage = (LPBYTE)lpCallBuffer + BytesLeft; lpEndPointer = (LPBYTE)lpHiGuardPage + GUARD_PAGE_SIZE; lpBufferBefore = lpCallBuffer; lpBufferAfter = NULL; // initialize GuardPage Data memset (lpLowGuardPage, GUARD_PAGE_CHAR, GUARD_PAGE_SIZE); memset (lpHiGuardPage, GUARD_PAGE_CHAR, GUARD_PAGE_SIZE); } __try { // // Collect data from extesible objects // bUnlockObjData = FALSE; if (pThisExtObj->hMutex != NULL) { Win32Error = WaitForSingleObject ( pThisExtObj->hMutex, pThisExtObj->dwCollectTimeout); // BUG!!A-DCREWS: If CollectProc is uninitialized, then the condition will // result in the lockoutcount being incremented if ((Win32Error != WAIT_TIMEOUT) && (pThisExtObj->CollectProc != NULL)) { bUnlockObjData = TRUE; QueryPerformanceCounter (&liStartTime); Win32Error = (*pThisExtObj->CollectProc) ( lpValueName, &lpCallBuffer, &BytesLeft, &NumObjectTypes); QueryPerformanceCounter (&liEndTime); pThisExtObj->llLastUsedTime = GetTimeAsLongLong(); ReleaseMutex (pThisExtObj->hMutex); bUnlockObjData = FALSE; } else { pThisExtObj->dwLockoutCount++; } } else { Win32Error = ERROR_LOCK_FAILED; } if ((Win32Error == ERROR_SUCCESS) && (BytesLeft > 0)) { // increment perf counters InterlockedIncrement ((LONG *)&pThisExtObj->dwCollectCount); pThisExtObj->llElapsedTime += liEndTime.QuadPart - liStartTime.QuadPart; if (bUseSafeBuffer) { // a data buffer was returned and // the function returned OK so see how things // turned out... // lpBufferAfter = lpCallBuffer; // // check for buffer corruption here // bBufferOK = TRUE; // assume it's ok until a check fails // if (lExtCounterTestLevel <= EXT_TEST_BASIC) { // // check 1: bytes left should be the same as // new data buffer ptr - orig data buffer ptr // if (BytesLeft != (DWORD)((LPBYTE)lpBufferAfter - (LPBYTE)lpBufferBefore)) { if (lEventLogLevel >= LOG_DEBUG) { // issue WARNING, that bytes left param is incorrect // load data for eventlog message // since this error is correctable (though with // some risk) this won't be reported at LOG_USER // level dwDataIndex = wStringIndex = 0; dwRawDataDwords[dwDataIndex++] = BytesLeft; dwRawDataDwords[dwDataIndex++] = (DWORD)((LPBYTE)lpBufferAfter - (LPBYTE)lpBufferBefore); szMessageArray[wStringIndex++] = pThisExtObj->szServiceName; szMessageArray[wStringIndex++] = pThisExtObj->szLibraryName; ReportEventW (hEventLog, EVENTLOG_WARNING_TYPE, // error type 0, // category (not used) (DWORD)WBEMPERF_BUFFER_POINTER_MISMATCH, // event, NULL, // SID (not used), wStringIndex, // number of strings dwDataIndex*sizeof(DWORD), // sizeof raw data (LPCWSTR *)szMessageArray, // message text array (LPVOID)&dwRawDataDwords[0]); // raw data } // we'll keep the buffer, since the returned bytes left // value is ignored anyway, in order to make the // rest of this function work, we'll fix it here BytesLeft = (DWORD)((LPBYTE)lpBufferAfter - (LPBYTE)lpBufferBefore); } // // check 2: buffer after ptr should be < hi Guard page ptr // if (((LPBYTE)lpBufferAfter >= (LPBYTE)lpHiGuardPage) && bBufferOK) { // see if they exceeded the allocated memory if ((LPBYTE)lpBufferAfter >= (LPBYTE)lpEndPointer) { // this is very serious since they've probably trashed // the heap by overwriting the heap sig. block // issue ERROR, buffer overrun if (lEventLogLevel >= LOG_USER) { // load data for eventlog message dwDataIndex = wStringIndex = 0; dwRawDataDwords[dwDataIndex++] = (DWORD)((LPBYTE)lpBufferAfter - (LPBYTE)lpHiGuardPage); szMessageArray[wStringIndex++] = pThisExtObj->szLibraryName; szMessageArray[wStringIndex++] = pThisExtObj->szServiceName; ReportEventW (hEventLog, EVENTLOG_ERROR_TYPE, // error type 0, // category (not used) (DWORD)WBEMPERF_HEAP_ERROR, // event, NULL, // SID (not used), wStringIndex, // number of strings dwDataIndex*sizeof(DWORD), // sizeof raw data (LPCWSTR *)szMessageArray, // message text array (LPVOID)&dwRawDataDwords[0]); // raw data } } else { // issue ERROR, buffer overrun if (lEventLogLevel >= LOG_USER) { // load data for eventlog message dwDataIndex = wStringIndex = 0; dwRawDataDwords[dwDataIndex++] = (DWORD)((LPBYTE)lpBufferAfter - (LPBYTE)lpHiGuardPage); szMessageArray[wStringIndex++] = pThisExtObj->szLibraryName; szMessageArray[wStringIndex++] = pThisExtObj->szServiceName; ReportEventW (hEventLog, EVENTLOG_ERROR_TYPE, // error type 0, // category (not used) (DWORD)WBEMPERF_BUFFER_OVERFLOW, // event, NULL, // SID (not used), wStringIndex, // number of strings dwDataIndex*sizeof(DWORD), // sizeof raw data (LPCWSTR *)szMessageArray, // message text array (LPVOID)&dwRawDataDwords[0]); // raw data } } bBufferOK = FALSE; // since the DLL overran the buffer, the buffer // must be too small (no comments about the DLL // will be made here) so the status will be // changed to ERROR_MORE_DATA and the function // will return. Win32Error = ERROR_MORE_DATA; } // // check 3: check lo guard page for corruption // if (bBufferOK) { bGuardPageOK = TRUE; for (lpCheckPointer = (LPDWORD)lpLowGuardPage; lpCheckPointer < (LPDWORD)lpBufferBefore; lpCheckPointer++) { if (*lpCheckPointer != GUARD_PAGE_DWORD) { bGuardPageOK = FALSE; break; } } if (!bGuardPageOK) { // issue ERROR, Lo Guard Page corrupted if (lEventLogLevel >= LOG_USER) { // load data for eventlog message dwDataIndex = wStringIndex = 0; szMessageArray[wStringIndex++] = pThisExtObj->szLibraryName; szMessageArray[wStringIndex++] = pThisExtObj->szServiceName; ReportEventW (hEventLog, EVENTLOG_ERROR_TYPE, // error type 0, // category (not used) (DWORD)WBEMPERF_GUARD_PAGE_VIOLATION, // event NULL, // SID (not used), wStringIndex, // number of strings dwDataIndex*sizeof(DWORD), // sizeof raw data (LPCWSTR *)szMessageArray, // message text array (LPVOID)&dwRawDataDwords[0]); // raw data } bBufferOK = FALSE; } } // // check 4: check hi guard page for corruption // if (bBufferOK) { bGuardPageOK = TRUE; for (lpCheckPointer = (LPDWORD)lpHiGuardPage; lpCheckPointer < (LPDWORD)lpEndPointer; lpCheckPointer++) { if (*lpCheckPointer != GUARD_PAGE_DWORD) { bGuardPageOK = FALSE; break; } } if (!bGuardPageOK) { // issue ERROR, Hi Guard Page corrupted if (lEventLogLevel >= LOG_USER) { // load data for eventlog message dwDataIndex = wStringIndex = 0; szMessageArray[wStringIndex++] = pThisExtObj->szLibraryName; szMessageArray[wStringIndex++] = pThisExtObj->szServiceName; ReportEventW (hEventLog, EVENTLOG_ERROR_TYPE, // error type 0, // category (not used) (DWORD)WBEMPERF_GUARD_PAGE_VIOLATION, // event, NULL, // SID (not used), wStringIndex, // number of strings dwDataIndex*sizeof(DWORD), // sizeof raw data (LPCWSTR *)szMessageArray, // message text array (LPVOID)&dwRawDataDwords[0]); // raw data } bBufferOK = FALSE; } } // if ((lExtCounterTestLevel <= EXT_TEST_ALL) && bBufferOK) { // // Internal consistency checks // // // Check 5: Check object length field values // // first test to see if this is a foreign // computer data block or not // pPerfData = (PERF_DATA_BLOCK *)lpBufferBefore; if ((pPerfData->Signature[0] == (WCHAR)'P') && (pPerfData->Signature[1] == (WCHAR)'E') && (pPerfData->Signature[2] == (WCHAR)'R') && (pPerfData->Signature[3] == (WCHAR)'F')) { // if this is a foreign computer data block, then the // first object is after the header pObject = (PERF_OBJECT_TYPE *) ( (LPBYTE)pPerfData + pPerfData->HeaderLength); bForeignDataBuffer = TRUE; } else { // otherwise, if this is just a buffer from // an extensible counter, the object starts // at the beginning of the buffer pObject = (PERF_OBJECT_TYPE *)lpBufferBefore; bForeignDataBuffer = FALSE; } // go to where the pointers say the end of the // buffer is and then see if it's where it // should be for (dwIndex = 0; dwIndex < NumObjectTypes; dwIndex++) { pObject = (PERF_OBJECT_TYPE *)((LPBYTE)pObject + pObject->TotalByteLength); } if ((LPBYTE)pObject != (LPBYTE)lpCallBuffer) { // then a length field is incorrect. This is FATAL // since it can corrupt the rest of the buffer // and render the buffer unusable. if (lEventLogLevel >= LOG_USER) { // load data for eventlog message dwDataIndex = wStringIndex = 0; dwRawDataDwords[dwDataIndex++] = NumObjectTypes; szMessageArray[wStringIndex++] = pThisExtObj->szLibraryName; szMessageArray[wStringIndex++] = pThisExtObj->szServiceName; ReportEventW (hEventLog, EVENTLOG_ERROR_TYPE, // error type 0, // category (not used) (DWORD)WBEMPERF_INCORRECT_OBJECT_LENGTH, // event, NULL, // SID (not used), wStringIndex, // number of strings dwDataIndex*sizeof(DWORD), // sizeof raw data (LPCWSTR *)szMessageArray, // message text array (LPVOID)&dwRawDataDwords[0]); // raw data } bBufferOK = FALSE; } // // Test 6: Test instance field size values // if (bBufferOK) { // set object pointer if (bForeignDataBuffer) { pObject = (PERF_OBJECT_TYPE *) ( (LPBYTE)pPerfData + pPerfData->HeaderLength); } else { // otherwise, if this is just a buffer from // an extensible counter, the object starts // at the beginning of the buffer pObject = (PERF_OBJECT_TYPE *)lpBufferBefore; } for (dwIndex = 0; dwIndex < NumObjectTypes; dwIndex++) { pNextObject = (PERF_OBJECT_TYPE *)((LPBYTE)pObject + pObject->TotalByteLength); if (pObject->NumInstances != PERF_NO_INSTANCES) { pInstance = (PERF_INSTANCE_DEFINITION *) ((LPBYTE)pObject + pObject->DefinitionLength); lInstIndex = 0; while (lInstIndex < pObject->NumInstances) { PERF_COUNTER_BLOCK *pCounterBlock; pCounterBlock = (PERF_COUNTER_BLOCK *) ((PCHAR) pInstance + pInstance->ByteLength); pInstance = (PERF_INSTANCE_DEFINITION *) ((PCHAR) pCounterBlock + pCounterBlock->ByteLength); lInstIndex++; } if ((LPBYTE)pInstance > (LPBYTE)pNextObject) { bBufferOK = FALSE; } } if (!bBufferOK) { break; } else { pObject = pNextObject; } } if (!bBufferOK) { if (lEventLogLevel >= LOG_USER) { // load data for eventlog message dwDataIndex = wStringIndex = 0; dwRawDataDwords[dwDataIndex++] = pObject->ObjectNameTitleIndex; szMessageArray[wStringIndex++] = pThisExtObj->szLibraryName; szMessageArray[wStringIndex++] = pThisExtObj->szServiceName; ReportEventW (hEventLog, EVENTLOG_ERROR_TYPE, // error type 0, // category (not used) (DWORD)WBEMPERF_INCORRECT_INSTANCE_LENGTH, // event, NULL, // SID (not used), wStringIndex, // number of strings dwDataIndex*sizeof(DWORD), // sizeof raw data (LPCWSTR *)szMessageArray, // message text array (LPVOID)&dwRawDataDwords[0]); // raw data } } } } } // // if all the tests pass,then copy the data to the // original buffer and update the pointers if (bBufferOK) { RtlMoveMemory (lpDataDefinition, lpBufferBefore, BytesLeft); // returned buffer size } else { NumObjectTypes = 0; // since this buffer was tossed BytesLeft = 0; // reset the size value since the buffer wasn't used } } else { // function already copied data to caller's buffer // so no further action is necessary } lpDataDefinition = (LPVOID)((LPBYTE)(lpDataDefinition) + BytesLeft); // update data pointer } else { if (Win32Error != ERROR_SUCCESS) { InterlockedIncrement ((LONG *)&pThisExtObj->dwErrorCount); } if (bUnlockObjData) { ReleaseMutex (pThisExtObj->hMutex); } NumObjectTypes = 0; // clear counter }// end if function returned successfully } __except (EXCEPTION_EXECUTE_HANDLER) { Win32Error = GetExceptionCode(); InterlockedIncrement ((LONG *)&pThisExtObj->dwErrorCount); bException = TRUE; if (bUnlockObjData) { ReleaseMutex (pThisExtObj->hMutex); bUnlockObjData = FALSE; } } if (bUseSafeBuffer) { FREEMEM (m_hObjectHeap, 0, lpExtDataBuffer); } } else { // unable to allocate memory so set error value Win32Error = ERROR_OUTOFMEMORY; } // end if temp buffer allocated successfully // // Update the count of the number of object types // ((PPERF_DATA_BLOCK) lpData)->NumObjectTypes += NumObjectTypes; if ( Win32Error != ERROR_SUCCESS) { if (bException || !((Win32Error == ERROR_MORE_DATA) || (Win32Error == WAIT_TIMEOUT))) { // inform on exceptions & illegal error status only if (lEventLogLevel >= LOG_USER) { // load data for eventlog message dwDataIndex = wStringIndex = 0; dwRawDataDwords[dwDataIndex++] = Win32Error; szMessageArray[wStringIndex++] = pThisExtObj->szServiceName; szMessageArray[wStringIndex++] = pThisExtObj->szLibraryName; ReportEventW (hEventLog, EVENTLOG_ERROR_TYPE, // error type 0, // category (not used) (DWORD)WBEMPERF_COLLECT_PROC_EXCEPTION, // event, NULL, // SID (not used), wStringIndex, // number of strings dwDataIndex*sizeof(DWORD), // sizeof raw data (LPCWSTR *)szMessageArray, // message text array (LPVOID)&dwRawDataDwords[0]); // raw data } else { // don't report } } // the ext. dll is only supposed to return: // ERROR_SUCCESS even if it encountered a problem, OR // ERROR_MODE_DATA if the buffer was too small. // if it's ERROR_MORE_DATA, then break and return the // error now, since it'll just be returned again and again. if (Win32Error == ERROR_MORE_DATA) { lReturnValue = Win32Error; break; } } } // end for each object } // else an error occurred so unable to call functions Win32Error = DeRegisterExtObjListAccess(); ((PPERF_DATA_BLOCK) lpData)->TotalByteLength = (DWORD) ((LPBYTE)lpDataDefinition - (LPBYTE)lpData); } return lReturnValue; } //*************************************************************************** // // CPerfObjectAccess::RemoveClass(IWbemClassObject *pClass) // // removes the class from the access object // //*************************************************************************** // DWORD CPerfObjectAccess::RemoveClass(IWbemClassObject *pClass) { CPerfDataLibrary *pLibEntry = NULL; CPerfDataLibrary *pThisLibEntry = NULL; DWORD dwIndex = 0; DWORD dwEnd; LPWSTR szRegistryKey = NULL; IWbemQualifierSet *pClassQualifiers = NULL; VARIANT vRegistryKey; HRESULT hRes; DWORD dwReturn = ERROR_SUCCESS; DWORD dwPerfIndex; VariantInit (&vRegistryKey); // get the Qualifier Set for this class hRes = pClass->GetQualifierSet(&pClassQualifiers); assert (hRes == 0); // now get the library and procedure names hRes = pClassQualifiers->Get(CBSTR(cszRegistryKey), 0, &vRegistryKey, 0); if ((hRes == 0) && (vRegistryKey.vt == VT_BSTR)) { szRegistryKey = Macro_CloneLPWSTR(V_BSTR(&vRegistryKey)); if (szRegistryKey == NULL) { dwReturn = ERROR_NOT_ENOUGH_MEMORY; } else { // now also get the perf index VariantClear (&vRegistryKey); hRes = pClassQualifiers->Get(CBSTR(cszPerfIndex), 0, &vRegistryKey, 0); if (hRes == 0) { dwPerfIndex = (DWORD)V_UI4(&vRegistryKey); } else { // unable to find NtPerfLibrary entry dwReturn = ERROR_FILE_NOT_FOUND; } } } else { // unable to find NtPerfLibrary entry dwReturn = ERROR_FILE_NOT_FOUND; } if (pClassQualifiers != NULL) pClassQualifiers->Release(); if (dwReturn == ERROR_SUCCESS) { // find matching library in the array dwEnd = m_aLibraries.Size(); if (dwEnd > 0) { // walk down the list of libraries for (dwIndex = 0; dwIndex < dwEnd; dwIndex++) { // see if this library entry is good enough to keep // The library is assumed to be a match if the // lib. name and all proc's are the same. pThisLibEntry = (CPerfDataLibrary *)m_aLibraries[dwIndex]; assert (pThisLibEntry != NULL); // it should have been removed! // make sure it's complete assert (pThisLibEntry->pLibInfo->szServiceName != NULL); if (lstrcmpiW (szRegistryKey, pThisLibEntry->pLibInfo->szServiceName) == 0) { pLibEntry = pThisLibEntry; break; } else { // wrong library // so continue } } } if (pLibEntry != NULL) { // close this class & it's library dwReturn = CloseLibrary(pLibEntry); if (dwReturn == 0) { // then no one wants it FREEMEM(m_hObjectHeap, 0, pLibEntry->pLibInfo); pLibEntry->pLibInfo = NULL; m_aLibraries.RemoveAt(dwIndex); m_aLibraries.Compress(); delete pLibEntry; } dwReturn = ERROR_SUCCESS; } else { dwReturn = ERROR_FILE_NOT_FOUND; } } if (szRegistryKey != NULL) delete szRegistryKey; VariantClear(&vRegistryKey); return dwReturn; }