/*++ BUILD Version: 0001 // Increment this if a change has global effects Copyright (c) 2000 Microsoft Corporation Module Name: extinit.c Abstract: This file implements all the initialization library routines operating on extensible performance libraries. Author: JeePang Revision History: 09/27/2000 - JeePang - Moved from perflib.c --*/ #define UNICODE // // Include files // #pragma warning(disable:4306) #include #include #include #include #include #include #include #include #include #include #include "regrpc.h" #include "ntconreg.h" #include "prflbmsg.h" // event log messages #include "perflib.h" #pragma warning(default:4306) // // used for error logging control #define DEFAULT_ERROR_LIMIT 1000 DWORD dwExtCtrOpenProcWaitMs = OPEN_PROC_WAIT_TIME; LONG lExtCounterTestLevel = EXT_TEST_UNDEFINED; PEXT_OBJECT AllocateAndInitializeExtObject ( HKEY hServicesKey, HKEY hPerfKey, PUNICODE_STRING usServiceName ) /*++ AllocateAndInitializeExtObject allocates and initializes an extensible object information entry for use by the performance library. a pointer to the initialized block is returned if all goes well, otherwise no memory is allocated and a null pointer is returned. The calling function must close the open handles and free this memory block when it is no longer needed. Arguments: hServicesKey -- open registry handle to the HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services hey hPerfKey -- the open registry key to the Performance sub-key under the selected service szServiceName -- The name of the service --*/ { LONG Status; HKEY hKeyLinkage; BOOL bUseQueryFn = FALSE; PEXT_OBJECT pReturnObject = NULL; DWORD dwType; DWORD dwSize; DWORD dwFlags = 0; DWORD dwKeep; DWORD dwObjectArray[MAX_PERF_OBJECTS_IN_QUERY_FUNCTION]; DWORD dwObjIndex = 0; DWORD dwMemBlockSize = sizeof(EXT_OBJECT); DWORD dwLinkageStringLen = 0; DWORD dwErrorLimit; 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]; LPWSTR szLinkageString = NULL; // max path wasn't enough for some paths DLL_VALIDATION_DATA DllVD; FILETIME LocalftLastGoodDllFileDate; DWORD dwOpenTimeout; DWORD dwCollectTimeout; LPWSTR szThisObject; LPWSTR szThisChar; LPSTR pNextStringA; LPWSTR pNextStringW; WCHAR szMutexName[MAX_PATH]; WCHAR szPID[32]; WORD wStringIndex; LPWSTR szMessageArray[2]; BOOL bDisable = FALSE; LPWSTR szServiceName; // read the performance DLL name szServiceName = (LPWSTR) usServiceName->Buffer; dwType = 0; dwSize = sizeof(szLibraryString); memset (szLibraryString, 0, sizeof(szLibraryString)); memset (szLibraryString, 0, sizeof(szLibraryExpPath)); LocalftLastGoodDllFileDate.dwLowDateTime = 0; LocalftLastGoodDllFileDate.dwHighDateTime = 0; memset (&DllVD, 0, sizeof(DllVD)); dwErrorLimit = DEFAULT_ERROR_LIMIT; dwCollectTimeout = dwExtCtrOpenProcWaitMs; dwOpenTimeout = dwExtCtrOpenProcWaitMs; Status = PrivateRegQueryValueExW (hPerfKey, DLLValue, 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 += QWORD_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 += QWORD_MULTIPLE(dwSize); } } else { Status = ERROR_INVALID_DLL; TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_ALLOC_INIT_EXT, 0, Status, NULL)); } 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 = PrivateRegQueryValueExA (hPerfKey, OpenValue, NULL, &dwType, (LPBYTE)szOpenProcName, &dwSize); if (((Status != ERROR_SUCCESS) || (szOpenProcName[0] == 0)) && (lEventLogLevel >= LOG_USER)) { if (szServiceName != NULL) { TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_ALLOC_INIT_EXT, ARG_TYPE_WSTR, Status, szServiceName, usServiceName->MaximumLength, NULL)); } else { TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_ALLOC_INIT_EXT, 0, Status, NULL)); } // DebugPrint((1, "No open procedure for %ws %d\n", // szServiceName, Status)); bDisable = TRUE; wStringIndex = 0; szMessageArray[wStringIndex++] = (LPWSTR) L"Open"; szMessageArray[wStringIndex++] = szServiceName; ReportEvent(hEventLog, EVENTLOG_ERROR_TYPE, 0, (DWORD)PERFLIB_PROC_NAME_NOT_FOUND, NULL, wStringIndex, 0, szMessageArray, NULL); } #ifdef DBG else { DebugPrint((2, "Found %s for %ws\n", szOpenProcName, szServiceName)); } #endif } #ifdef DBG else { DebugPrint((1, "Invalid DLL found for %ws\n", szServiceName)); } #endif if (Status == ERROR_SUCCESS) { // add in size of previous string // the size value includes the Term. NULL dwMemBlockSize += QWORD_MULTIPLE(dwSize); // we have the procedure name so get the timeout value dwType = 0; dwSize = sizeof(dwOpenTimeout); Status = PrivateRegQueryValueExW (hPerfKey, OpenTimeout, 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 = PrivateRegQueryValueExA (hPerfKey, CloseValue, NULL, &dwType, (LPBYTE)szCloseProcName, &dwSize); if (((Status != ERROR_SUCCESS) || (szCloseProcName[0] == 0)) && (lEventLogLevel >= LOG_USER)) { if (szServiceName != NULL) { TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_ALLOC_INIT_EXT, ARG_TYPE_WSTR, Status, szServiceName, usServiceName->MaximumLength, NULL)); } else { TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_ALLOC_INIT_EXT, 0, Status, NULL)); } // DebugPrint((1, "No close procedure for %ws\n", // szServiceName)); wStringIndex = 0; szMessageArray[wStringIndex++] = (LPWSTR) L"Close"; szMessageArray[wStringIndex++] = szServiceName; ReportEvent(hEventLog, EVENTLOG_ERROR_TYPE, 0, (DWORD)PERFLIB_PROC_NAME_NOT_FOUND, NULL, wStringIndex, 0, szMessageArray, NULL); bDisable = TRUE; } #ifdef DBG else { DebugPrint((2, "Found %s for %ws\n", szCloseProcName, szServiceName)); } #endif } if (Status == ERROR_SUCCESS) { // add in size of previous string // the size value includes the Term. NULL dwMemBlockSize += QWORD_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 = PrivateRegQueryValueExA (hPerfKey, QueryValue, 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 += QWORD_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 = PrivateRegQueryValueExA (hPerfKey, CollectValue, NULL, &dwType, (LPBYTE)szCollectProcName, &dwSize); if (Status == ERROR_SUCCESS) { // add in size of Collect Function Name // the size value includes the Term. NULL dwMemBlockSize += QWORD_MULTIPLE(dwSize); } } if (((Status != ERROR_SUCCESS) || (szCollectProcName[0] == 0)) && (lEventLogLevel >= LOG_USER)) { if (szServiceName != NULL) { TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_ALLOC_INIT_EXT, ARG_TYPE_WSTR, Status, szServiceName, usServiceName->MaximumLength, NULL)); } else { TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_ALLOC_INIT_EXT, 0, Status, NULL)); } DebugPrint((1, "No collect procedure for %ws\n", szServiceName)); wStringIndex = 0; bDisable = TRUE; szMessageArray[wStringIndex++] = (LPWSTR) L"Collect"; szMessageArray[wStringIndex++] = szServiceName; ReportEvent(hEventLog, EVENTLOG_ERROR_TYPE, 0, (DWORD)PERFLIB_PROC_NAME_NOT_FOUND, NULL, wStringIndex, 0, szMessageArray, NULL); } #ifdef DBG else { DebugPrint((2, "Found %s for %ws\n", szCollectProcName, szServiceName)); } #endif if (Status == ERROR_SUCCESS) { // we have the procedure name so get the timeout value dwType = 0; dwSize = sizeof(dwCollectTimeout); Status = PrivateRegQueryValueExW (hPerfKey, CollectTimeout, 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 = PrivateRegQueryValueExW (hPerfKey, ObjListValue, 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) && (lEventLogLevel >= LOG_USER)) { TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_ALLOC_INIT_EXT, 0, 0, NULL)); ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE, // error type 0, // category (not used (DWORD)PERFLIB_TOO_MANY_OBJECTS, // event, NULL, // SID (not used), 0, // number of strings 0, // sizeof raw data NULL, // message text array NULL); // raw data } } 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 = PrivateRegQueryValueExW (hPerfKey, KeepResident, 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) { dwType = REG_DWORD; dwSize = sizeof(DWORD); PrivateRegQueryValueExW( hPerfKey, cszFailureLimit, NULL, &dwType, (LPBYTE)&dwErrorLimit, &dwSize); } } } else { if (szLibraryString != NULL) { TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_ALLOC_INIT_EXT, ARG_TYPE_WSTR, Status, szLibraryString, WSTRSIZE(szLibraryString), NULL)); } else { TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_ALLOC_INIT_EXT, 0, Status, NULL)); } // DebugPrint((1, "Cannot key for %ws. Error=%d\n", // szLibraryString, Status)); } if (Status == ERROR_SUCCESS) { // get Library validation time dwType = 0; dwSize = sizeof(DllVD); Status = PrivateRegQueryValueExW (hPerfKey, cszLibraryValidationData, NULL, &dwType, (LPBYTE)&DllVD, &dwSize); if ((Status != ERROR_SUCCESS) || (dwType != REG_BINARY) || (dwSize != sizeof (DllVD))){ // then set this entry to be 0 TRACE((WINPERF_DBG_TRACE_INFO), (&PerflibGuid, __LINE__, PERF_ALLOC_INIT_EXT, 0, Status, &dwType, sizeof(dwType), &dwSize, sizeof(dwSize), NULL)); memset (&DllVD, 0, sizeof(DllVD)); // and clear the error Status = ERROR_SUCCESS; } } if (Status == ERROR_SUCCESS) { // get the file timestamp of the last successfully accessed file dwType = 0; dwSize = sizeof(LocalftLastGoodDllFileDate); memset (&LocalftLastGoodDllFileDate, 0, sizeof(LocalftLastGoodDllFileDate)); Status = PrivateRegQueryValueExW (hPerfKey, cszSuccessfulFileData, NULL, &dwType, (LPBYTE)&LocalftLastGoodDllFileDate, &dwSize); if ((Status != ERROR_SUCCESS) || (dwType != REG_BINARY) || (dwSize != sizeof (LocalftLastGoodDllFileDate))) { // then set this entry to be Invalid memset (&LocalftLastGoodDllFileDate, 0xFF, sizeof(LocalftLastGoodDllFileDate)); // and clear the error TRACE((WINPERF_DBG_TRACE_INFO), (&PerflibGuid, __LINE__, PERF_ALLOC_INIT_EXT, 0, Status, &dwType, sizeof(dwType), &dwSize, sizeof(dwSize), NULL)); Status = ERROR_SUCCESS; } } if (Status == ERROR_SUCCESS) { lstrcpyW (szLinkageKeyPath, szServiceName); lstrcatW (szLinkageKeyPath, LinkageKey); Status = RegOpenKeyExW ( hServicesKey, szLinkageKeyPath, 0L, KEY_READ, &hKeyLinkage); if (Status == ERROR_SUCCESS) { // look up export value string dwSize = 0; dwType = 0; Status = PrivateRegQueryValueExW ( hKeyLinkage, ExportValue, NULL, &dwType, NULL, &dwSize); // get size of string if (((Status != ERROR_SUCCESS) && (Status != ERROR_MORE_DATA)) || ((dwType != REG_SZ) && (dwType != REG_MULTI_SZ))) { dwLinkageStringLen = 0; szLinkageString = NULL; // not finding a linkage key is not fatal so correct // status Status = ERROR_SUCCESS; } else { // allocate buffer szLinkageString = (LPWSTR)ALLOCMEM(dwSize); if (szLinkageString != NULL) { // read string into buffer dwType = 0; Status = PrivateRegQueryValueExW ( hKeyLinkage, ExportValue, NULL, &dwType, (LPBYTE)szLinkageString, &dwSize); if ((Status != ERROR_SUCCESS) || ((dwType != REG_SZ) && (dwType != REG_MULTI_SZ))) { // clear & release buffer FREEMEM (szLinkageString); szLinkageString = NULL; 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 += QWORD_MULTIPLE(dwSize); } } else { // clear & release buffer FREEMEM (szLinkageString); szLinkageString = NULL; dwLinkageStringLen = 0; Status = ERROR_OUTOFMEMORY; TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_ALLOC_INIT_EXT, 0, Status, &dwSize, sizeof(dwSize), NULL)); } } RegCloseKey (hKeyLinkage); } else { // not finding a linkage key is not fatal so correct // status // clear & release buffer szLinkageString = NULL; dwLinkageStringLen = 0; Status = ERROR_SUCCESS; } } if (Status == ERROR_SUCCESS) { // add in size of service name /* dwSize = lstrlenW (szServiceName); dwSize += 1; dwSize *= sizeof(WCHAR); */ dwSize = usServiceName->MaximumLength; dwMemBlockSize += QWORD_MULTIPLE(dwSize); // allocate and initialize a new ext. object block pReturnObject = ALLOCMEM (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 = ALIGN_ON_QWORD(pNextStringA); pReturnObject->dwOpenTimeout = dwOpenTimeout; // copy collect function or query function, depending pReturnObject->szCollectProcName = pNextStringA; lstrcpyA (pNextStringA, szCollectProcName); pNextStringA += lstrlenA (pNextStringA) + 1; pNextStringA = ALIGN_ON_QWORD(pNextStringA); pReturnObject->dwCollectTimeout = dwCollectTimeout; // copy Close Procedure Name pReturnObject->szCloseProcName = pNextStringA; lstrcpyA (pNextStringA, szCloseProcName); pNextStringA += lstrlenA (pNextStringA) + 1; pNextStringA = ALIGN_ON_QWORD(pNextStringA); // copy Library path pNextStringW = (LPWSTR)pNextStringA; pReturnObject->szLibraryName = pNextStringW; lstrcpyW (pNextStringW, szLibraryExpPath); pNextStringW += lstrlenW (pNextStringW) + 1; pNextStringW = ALIGN_ON_QWORD(pNextStringW); // copy Linkage String if there is one if (szLinkageString != NULL) { pReturnObject->szLinkageString = pNextStringW; memcpy (pNextStringW, szLinkageString, dwLinkageStringLen); // length includes extra NULL char and is in BYTES pNextStringW += (dwLinkageStringLen / sizeof (WCHAR)); pNextStringW = ALIGN_ON_QWORD(pNextStringW); // release the buffer now that it's been copied FREEMEM (szLinkageString); szLinkageString = NULL; } // copy Service name pReturnObject->szServiceName = pNextStringW; lstrcpyW (pNextStringW, szServiceName); pNextStringW += lstrlenW (pNextStringW) + 1; pNextStringW = ALIGN_ON_QWORD(pNextStringW); // load flags if (bUseQueryFn) { dwFlags |= PERF_EO_QUERY_FUNC; } pReturnObject->dwFlags = dwFlags; pReturnObject->hPerfKey = hPerfKey; pReturnObject->LibData = DllVD; // validation data pReturnObject->ftLastGoodDllFileDate = LocalftLastGoodDllFileDate; // the default test level is "all tests" // if the file and timestamp work out OK, this can // be reset to the system test level pReturnObject->dwValidationLevel = EXT_TEST_ALL; // 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, szServiceName); lstrcatW (szMutexName, (LPCWSTR)L"_Perf_Library_Lock_PID_"); _ultow ((ULONG)GetCurrentProcessId(), szPID, 16); lstrcatW (szMutexName, szPID); pReturnObject->hMutex = CreateMutexW(NULL, FALSE, szMutexName); pReturnObject->dwErrorLimit = dwErrorLimit; if ( pReturnObject->hMutex != NULL && GetLastError() == ERROR_ALREADY_EXISTS) { Status = ERROR_SUCCESS; } else { Status = GetLastError(); } } else { Status = ERROR_OUTOFMEMORY; TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_ALLOC_INIT_EXT, 0, dwMemBlockSize, NULL)); } } if ((Status == ERROR_SUCCESS) && (lpPerflibSectionAddr != NULL)) { PPERFDATA_SECTION_HEADER pHead; DWORD dwEntry; PPERFDATA_SECTION_RECORD pEntry; // init perf data section pHead = (PPERFDATA_SECTION_HEADER)lpPerflibSectionAddr; pEntry = (PPERFDATA_SECTION_RECORD)lpPerflibSectionAddr; // get the entry first // the "0" entry is the header if (pHead->dwEntriesInUse < pHead->dwMaxEntries) { dwEntry = ++pHead->dwEntriesInUse; pReturnObject->pPerfSectionEntry = &pEntry[dwEntry]; lstrcpynW (pReturnObject->pPerfSectionEntry->szServiceName, pReturnObject->szServiceName, PDSR_SERVICE_NAME_LEN); } else { // the list is full so bump the missing entry count pHead->dwMissingEntries++; pReturnObject->pPerfSectionEntry = NULL; } } if (Status != ERROR_SUCCESS) { SetLastError (Status); TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_ALLOC_INIT_EXT, 0, Status, NULL)); if (bDisable) { DisableLibrary(hPerfKey, szServiceName); } if (pReturnObject) { FREEMEM(pReturnObject); pReturnObject = NULL; } if (szLinkageString) { FREEMEM(szLinkageString); } } return pReturnObject; } void OpenExtensibleObjects ( ) /*++ Routine Description: This routine will search the Configuration Registry for modules which will return data at data collection time. If any are found, and successfully opened, data structures are allocated to hold handles to them. The global data access in this section is protected by the hGlobalDataMutex acquired by the calling function. Arguments: None. successful open. Return Value: None. --*/ { DWORD dwIndex; // index for enumerating services ULONG KeyBufferLength; // length of buffer for reading key data ULONG ValueBufferLength; // length of buffer for reading value data ULONG ResultLength; // length of data returned by Query call HANDLE hPerfKey; // Root of queries for performance info HANDLE hServicesKey; // Root of services REGSAM samDesired; // access needed to query NTSTATUS Status; // generally used for Nt call result status ANSI_STRING AnsiValueData; // Ansi version of returned strings UNICODE_STRING ServiceName; // name of service returned by enumeration UNICODE_STRING PathName; // path name to services UNICODE_STRING PerformanceName; // name of key holding performance data UNICODE_STRING ValueDataName; // result of query of value is this name OBJECT_ATTRIBUTES ObjectAttributes; // general use for opening keys PKEY_BASIC_INFORMATION KeyInformation; // data from query key goes here WCHAR szServiceName[MAX_PATH]; UNICODE_STRING usServiceName; LPTSTR szMessageArray[8]; DWORD dwRawDataDwords[8]; // raw data buffer DWORD dwDataIndex; WORD wStringIndex; DWORD dwDefaultValue; HANDLE hTimeOutEvent; PEXT_OBJECT pLastObject = NULL; PEXT_OBJECT pThisObject = NULL; // Initialize do failure can deallocate if allocated ServiceName.Buffer = NULL; KeyInformation = NULL; ValueDataName.Buffer = NULL; AnsiValueData.Buffer = NULL; dwIndex = 0; RtlInitUnicodeString(&PathName, ExtPath); RtlInitUnicodeString(&PerformanceName, PerfSubKey); try { // get current event log level dwDefaultValue = LOG_NONE; Status = GetPerflibKeyValue ( EventLogLevel, REG_DWORD, sizeof(DWORD), (LPVOID)&lEventLogLevel, sizeof(DWORD), (LPVOID)&dwDefaultValue); dwDefaultValue = EXT_TEST_ALL; Status = GetPerflibKeyValue ( ExtCounterTestLevel, REG_DWORD, sizeof(DWORD), (LPVOID)&lExtCounterTestLevel, sizeof(DWORD), (LPVOID)&dwDefaultValue); dwDefaultValue = OPEN_PROC_WAIT_TIME; Status = GetPerflibKeyValue ( OpenProcedureWaitTime, REG_DWORD, sizeof(DWORD), (LPVOID)&dwExtCtrOpenProcWaitMs, sizeof(DWORD), (LPVOID)&dwDefaultValue); dwDefaultValue = PERFLIB_TIMING_THREAD_TIMEOUT; Status = GetPerflibKeyValue ( LibraryUnloadTime, REG_DWORD, sizeof(DWORD), (LPVOID)&dwThreadAndLibraryTimeout, sizeof(DWORD), (LPVOID)&dwDefaultValue); // register as an event log source if not already done. if (hEventLog == NULL) { hEventLog = RegisterEventSource (NULL, (LPCWSTR)TEXT("Perflib")); } if (ExtensibleObjects == NULL) { // create a list of the known performance data objects ServiceName.Length = ServiceName.MaximumLength = (WORD)(MAX_KEY_NAME_LENGTH + PerformanceName.MaximumLength + sizeof(UNICODE_NULL)); ServiceName.Buffer = ALLOCMEM(ServiceName.MaximumLength); InitializeObjectAttributes(&ObjectAttributes, &PathName, OBJ_CASE_INSENSITIVE, NULL, NULL); samDesired = KEY_READ; Status = NtOpenKey(&hServicesKey, samDesired, &ObjectAttributes); KeyBufferLength = sizeof(KEY_BASIC_INFORMATION) + MAX_KEY_NAME_LENGTH; KeyInformation = ALLOCMEM(KeyBufferLength); ValueBufferLength = sizeof(KEY_VALUE_FULL_INFORMATION) + MAX_VALUE_NAME_LENGTH + MAX_VALUE_DATA_LENGTH; ValueDataName.MaximumLength = MAX_VALUE_DATA_LENGTH; ValueDataName.Buffer = ALLOCMEM(ValueDataName.MaximumLength); AnsiValueData.MaximumLength = MAX_VALUE_DATA_LENGTH/sizeof(WCHAR); AnsiValueData.Buffer = ALLOCMEM(AnsiValueData.MaximumLength); // // Check for successful NtOpenKey and allocation of dynamic buffers // if ( NT_SUCCESS(Status) && ServiceName.Buffer != NULL && KeyInformation != NULL && ValueDataName.Buffer != NULL && AnsiValueData.Buffer != NULL ) { dwIndex = 0; hTimeOutEvent = CreateEvent(NULL,TRUE,TRUE,NULL); // wait longer than the thread to give the timing thread // a chance to finish on it's own. This is really just a // failsafe step. while (NT_SUCCESS(Status)) { Status = NtEnumerateKey(hServicesKey, dwIndex, KeyBasicInformation, KeyInformation, KeyBufferLength, &ResultLength); dwIndex++; // next time, get the next key if( !NT_SUCCESS(Status) ) { // This is the normal exit: Status should be // STATUS_NO_MORE_VALUES break; } // Concatenate Service name with "\\Performance" to form Subkey if ( ServiceName.MaximumLength >= (USHORT)( KeyInformation->NameLength + sizeof(UNICODE_NULL) ) ) { ServiceName.Length = (USHORT) KeyInformation->NameLength; RtlMoveMemory(ServiceName.Buffer, KeyInformation->Name, ServiceName.Length); ServiceName.Buffer[(ServiceName.Length/sizeof(WCHAR))] = 0; // null term lstrcpyW (szServiceName, ServiceName.Buffer); RtlInitUnicodeString(&usServiceName, szServiceName); // zero terminate the buffer if space allows RtlAppendUnicodeStringToString(&ServiceName, &PerformanceName); // Open Service\Performance Subkey InitializeObjectAttributes(&ObjectAttributes, &ServiceName, OBJ_CASE_INSENSITIVE, hServicesKey, NULL); samDesired = KEY_WRITE | KEY_READ; // to be able to disable perf DLL's Status = NtOpenKey(&hPerfKey, samDesired, &ObjectAttributes); if(! NT_SUCCESS(Status) ) { samDesired = KEY_READ; // try read only access Status = NtOpenKey(&hPerfKey, samDesired, &ObjectAttributes); } if( NT_SUCCESS(Status) ) { // this has a performance key so read the info // and add the entry to the list pThisObject = AllocateAndInitializeExtObject ( hServicesKey, hPerfKey, &usServiceName); if (pThisObject != NULL) { if (ExtensibleObjects == NULL) { // set head pointer pLastObject = ExtensibleObjects = pThisObject; NumExtensibleObjects = 1; } else { pLastObject->pNext = pThisObject; pLastObject = pThisObject; NumExtensibleObjects++; } } else { if (szServiceName != NULL) { TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_OPEN_EXT_OBJS, ARG_TYPE_WSTR, 0, szServiceName, usServiceName.MaximumLength, NULL)); } else { TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_OPEN_EXT_OBJS, 0, 0, NULL)); } // the object wasn't initialized so toss // the perf subkey handle. // otherwise keep it open for later // use and it will be closed when // this extensible object is closed NtClose (hPerfKey); } } else { if (szServiceName != NULL) { TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_OPEN_EXT_OBJS, ARG_TYPE_WSTR, Status, szServiceName, usServiceName.MaximumLength, NULL)); } else { TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_OPEN_EXT_OBJS, 0, Status, NULL)); } // *** NEW FEATURE CODE *** // unable to open the performance subkey if (((Status != STATUS_OBJECT_NAME_NOT_FOUND) && (lEventLogLevel >= LOG_USER)) || (lEventLogLevel >= LOG_DEBUG)) { // an error other than OBJECT_NOT_FOUND should be // displayed if error logging is enabled // if DEBUG level is selected, then write all // non-success status returns to the event log // dwDataIndex = wStringIndex = 0; dwRawDataDwords[dwDataIndex++] = PerfpDosError(Status); if (lEventLogLevel >= LOG_DEBUG) { // if this is DEBUG mode, then log // the NT status as well. dwRawDataDwords[dwDataIndex++] = (DWORD)Status; } szMessageArray[wStringIndex++] = szServiceName; ReportEvent (hEventLog, EVENTLOG_WARNING_TYPE, // error type 0, // category (not used) (DWORD)PERFLIB_NO_PERFORMANCE_SUBKEY, // event, NULL, // SID (not used), wStringIndex, // number of strings dwDataIndex*sizeof(DWORD), // sizeof raw data szMessageArray, // message text array (LPVOID)&dwRawDataDwords[0]); // raw data } } } Status = STATUS_SUCCESS; // allow loop to continue } if (hTimeOutEvent != NULL) NtClose (hTimeOutEvent); NtClose (hServicesKey); } } } finally { if ( ServiceName.Buffer ) FREEMEM(ServiceName.Buffer); if ( KeyInformation ) FREEMEM(KeyInformation); if ( ValueDataName.Buffer ) FREEMEM(ValueDataName.Buffer); if ( AnsiValueData.Buffer ) FREEMEM(AnsiValueData.Buffer); } }