// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. #include "precomp.h" typedef DWORD (STDAPICALLTYPE *PFNPERFCOUNTERTEXTSTRINGS)(LPWSTR lpCommandLine, BOOL bQuietModeArg); static HRESULT ExecutePerfCounterData( __in MSIHANDLE hInstall, __in BOOL fInstall ); static HRESULT CreateDataFile( __in LPCWSTR wzTempFolder, __in LPCWSTR wzData, __in BOOL fIniData, __out HANDLE *phFile, __out_opt LPWSTR *ppwzFile ); /******************************************************************** RegisterPerfCounterData - CUSTOM ACTION ENTRY POINT for registering performance counters Input: deferred CustomActionData: wzName\twzIniData\twzConstantData\twzName\twzIniData\twzConstantData\t... *******************************************************************/ extern "C" UINT __stdcall RegisterPerfCounterData( __in MSIHANDLE hInstall ) { // AssertSz(FALSE, "debug RegisterPerfCounterData()"); HRESULT hr = S_OK; DWORD er = ERROR_SUCCESS; hr = WcaInitialize(hInstall, "RegisterPerfCounterData"); ExitOnFailure(hr, "Failed to initialize RegisterPerfCounterData."); hr = ExecutePerfCounterData(hInstall, TRUE); MessageExitOnFailure(hr, msierrInstallPerfCounterData, "Failed to execute Wix4PerformanceCategory table."); LExit: er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); } /******************************************************************** UnregisterPerfCounterData - CUSTOM ACTION ENTRY POINT for registering performance counters Input: deferred CustomActionData: wzName\twzIniData\twzConstantData\twzName\twzIniData\twzConstantData\t... *******************************************************************/ extern "C" UINT __stdcall UnregisterPerfCounterData( __in MSIHANDLE hInstall ) { // AssertSz(FALSE, "debug UnregisterPerfCounterData()"); HRESULT hr = S_OK; DWORD er = ERROR_SUCCESS; hr = WcaInitialize(hInstall, "UnregisterPerfCounterData"); ExitOnFailure(hr, "Failed to initialize UnregisterPerfCounterData."); hr = ExecutePerfCounterData(hInstall, FALSE); MessageExitOnFailure(hr, msierrUninstallPerfCounterData, "Failed to execute Wix4PerformanceCategory table."); LExit: er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); } /******************************************************************** RegisterPerfmon - CUSTOM ACTION ENTRY POINT for registering counters Input: deferred CustomActionData - wzFile or wzName *******************************************************************/ extern "C" UINT __stdcall RegisterPerfmon( __in MSIHANDLE hInstall ) { // Assert(FALSE); UINT er = ERROR_SUCCESS; HRESULT hr = S_OK; LPWSTR pwzData = NULL; HMODULE hMod = NULL; PFNPERFCOUNTERTEXTSTRINGS pfnPerfCounterTextString; DWORD dwRet; LPWSTR pwzShortPath = NULL; DWORD cchShortPath = MAX_PATH; DWORD cchShortPathLength = 0; LPWSTR pwzCommand = NULL; hr = WcaInitialize(hInstall, "RegisterPerfmon"); ExitOnFailure(hr, "failed to initialize"); hr = WcaGetProperty(L"CustomActionData", &pwzData); ExitOnFailure(hr, "failed to get CustomActionData"); WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData); // do the perfmon registration if (NULL == hMod) { hr = LoadSystemLibrary(L"loadperf.dll", &hMod); } ExitOnFailure(hr, "failed to load DLL for PerfMon"); pfnPerfCounterTextString = (PFNPERFCOUNTERTEXTSTRINGS)::GetProcAddress(hMod, "LoadPerfCounterTextStringsW"); ExitOnNullWithLastError(pfnPerfCounterTextString, hr, "failed to get DLL function for PerfMon"); hr = StrAlloc(&pwzShortPath, cchShortPath); ExitOnFailure(hr, "failed to allocate string"); WcaLog(LOGMSG_VERBOSE, "Converting DLL path to short format: %ls", pwzData); cchShortPathLength = ::GetShortPathNameW(pwzData, pwzShortPath, cchShortPath); if (cchShortPathLength > cchShortPath) { cchShortPath = cchShortPathLength + 1; hr = StrAlloc(&pwzShortPath, cchShortPath); ExitOnFailure(hr, "failed to allocate string"); cchShortPathLength = ::GetShortPathNameW(pwzData, pwzShortPath, cchShortPath); } if (0 == cchShortPathLength) { ExitOnLastError(hr, "failed to get short path format of path: %ls", pwzData); } hr = StrAllocFormatted(&pwzCommand, L"lodctr \"%s\"", pwzShortPath); ExitOnFailure(hr, "failed to format lodctr string"); WcaLog(LOGMSG_VERBOSE, "RegisterPerfmon running command: '%ls'", pwzCommand); dwRet = (*pfnPerfCounterTextString)(pwzCommand, TRUE); if (dwRet != ERROR_SUCCESS && dwRet != ERROR_ALREADY_EXISTS) { hr = HRESULT_FROM_WIN32(dwRet); MessageExitOnFailure(hr, msierrPERFMONFailedRegisterDLL, "failed to register with PerfMon, DLL: %ls", pwzData); } hr = S_OK; LExit: ReleaseStr(pwzData); if (FAILED(hr)) er = ERROR_INSTALL_FAILURE; return WcaFinalize(er); } extern "C" UINT __stdcall UnregisterPerfmon( __in MSIHANDLE hInstall ) { // Assert(FALSE); UINT er = ERROR_SUCCESS; HRESULT hr = S_OK; LPWSTR pwzData = NULL; HMODULE hMod = NULL; PFNPERFCOUNTERTEXTSTRINGS pfnPerfCounterTextString; DWORD dwRet; WCHAR wz[255]; hr = WcaInitialize(hInstall, "UnregisterPerfmon"); ExitOnFailure(hr, "failed to initialize"); hr = WcaGetProperty(L"CustomActionData", &pwzData); ExitOnFailure(hr, "failed to get CustomActionData"); WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData); // do the perfmon unregistration hr = E_FAIL; if (hMod == NULL) { hr = LoadSystemLibrary(L"loadperf.dll", &hMod); } ExitOnFailure(hr, "failed to load DLL for PerfMon"); pfnPerfCounterTextString = (PFNPERFCOUNTERTEXTSTRINGS)::GetProcAddress(hMod, "UnloadPerfCounterTextStringsW"); ExitOnNullWithLastError(pfnPerfCounterTextString, hr, "failed to get DLL function for PerfMon"); hr = ::StringCchPrintfW(wz, countof(wz), L"unlodctr \"%s\"", pwzData); ExitOnFailure(hr, "Failed to format unlodctr string with: %ls", pwzData); WcaLog(LOGMSG_VERBOSE, "UnregisterPerfmon running command: '%ls'", wz); dwRet = (*pfnPerfCounterTextString)(wz, TRUE); // if the counters aren't registered, then OK to continue if (dwRet != ERROR_SUCCESS && dwRet != ERROR_FILE_NOT_FOUND && dwRet != ERROR_BADKEY) { hr = HRESULT_FROM_WIN32(dwRet); MessageExitOnFailure(hr, msierrPERFMONFailedUnregisterDLL, "failed to unregsister with PerfMon, DLL: %ls", pwzData); } hr = S_OK; LExit: ReleaseStr(pwzData); if (FAILED(hr)) er = ERROR_INSTALL_FAILURE; return WcaFinalize(er); } static HRESULT ExecutePerfCounterData( __in MSIHANDLE /*hInstall*/, __in BOOL fInstall ) { HRESULT hr = S_OK; DWORD er = ERROR_SUCCESS; HMODULE hModule = NULL; PFNPERFCOUNTERTEXTSTRINGS pfnPerfCounterTextString = NULL; LPCWSTR wzPrefix = NULL; LPWSTR pwzCustomActionData = NULL; LPWSTR pwz = NULL; LPWSTR pwzName = NULL; LPWSTR pwzIniData = NULL; LPWSTR pwzConstantData = NULL; LPWSTR pwzTempFolder = NULL; LPWSTR pwzIniFile = NULL; LPWSTR pwzExecute = NULL; HANDLE hIniData = INVALID_HANDLE_VALUE; HANDLE hConstantData = INVALID_HANDLE_VALUE; // Load the system performance counter helper DLL then get the appropriate // entrypoint out of it. Fortunately, they have the same signature so we // can use one function pointer to point to both. hr = LoadSystemLibrary(L"loadperf.dll", &hModule); ExitOnFailure(hr, "failed to load DLL for PerfMon"); if (fInstall) { wzPrefix = L"lodctr"; pfnPerfCounterTextString = (PFNPERFCOUNTERTEXTSTRINGS)::GetProcAddress(hModule, "LoadPerfCounterTextStringsW"); } else { wzPrefix = L"unlodctr"; pfnPerfCounterTextString = (PFNPERFCOUNTERTEXTSTRINGS)::GetProcAddress(hModule, "UnloadPerfCounterTextStringsW"); } ExitOnNullWithLastError(pfnPerfCounterTextString, hr, "Failed to get DLL function for PerfMon"); // Now get the CustomActionData and execute it. hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData); ExitOnFailure(hr, "Failed to get CustomActionData."); WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData); pwz = pwzCustomActionData; while (S_OK == (hr = WcaReadStringFromCaData(&pwz, &pwzName))) { hr = WcaReadStringFromCaData(&pwz, &pwzIniData); ExitOnFailure(hr, "Failed to read IniData from custom action data."); hr = WcaReadStringFromCaData(&pwz, &pwzConstantData); ExitOnFailure(hr, "Failed to read ConstantData from custom action data."); if (fInstall) { hr = PathCreateTempDirectory(NULL, L"WIXPF%03x", 999, &pwzTempFolder); ExitOnFailure(hr, "Failed to create temp directory."); hr = CreateDataFile(pwzTempFolder, pwzIniData, TRUE, &hIniData, &pwzIniFile); ExitOnFailure(hr, "Failed to create .ini file for performance counter category: %ls", pwzName); hr = CreateDataFile(pwzTempFolder, pwzConstantData, FALSE, &hConstantData, NULL); ExitOnFailure(hr, "Failed to create .h file for performance counter category: %ls", pwzName); hr = StrAllocFormatted(&pwzExecute, L"%s \"%s\"", wzPrefix, pwzIniFile); ExitOnFailure(hr, "Failed to allocate string to execute."); // Execute the install. er = (*pfnPerfCounterTextString)(pwzExecute, TRUE); hr = HRESULT_FROM_WIN32(er); ExitOnFailure(hr, "Failed to execute install of performance counter category: %ls", pwzName); if (INVALID_HANDLE_VALUE != hIniData) { ::CloseHandle(hIniData); hIniData = INVALID_HANDLE_VALUE; } if (INVALID_HANDLE_VALUE != hConstantData) { ::CloseHandle(hConstantData); hConstantData = INVALID_HANDLE_VALUE; } DirEnsureDelete(pwzTempFolder, TRUE, TRUE); } else { hr = StrAllocFormatted(&pwzExecute, L"%s \"%s\"", wzPrefix, pwzName); ExitOnFailure(hr, "Failed to allocate string to execute."); // Execute the uninstall and if the counter isn't registered then ignore // the error since it won't hurt anything. er = (*pfnPerfCounterTextString)(pwzExecute, TRUE); if (ERROR_FILE_NOT_FOUND == er || ERROR_BADKEY == er) { er = ERROR_SUCCESS; } hr = HRESULT_FROM_WIN32(er); ExitOnFailure(hr, "Failed to execute uninstall of performance counter category: %ls", pwzName); } } if (E_NOMOREITEMS == hr) // If there are no more items, all is well { hr = S_OK; } ExitOnFailure(hr, "Failed to execute all perf counter data."); hr = S_OK; LExit: if (INVALID_HANDLE_VALUE != hIniData) { ::CloseHandle(hIniData); } if (INVALID_HANDLE_VALUE != hConstantData) { ::CloseHandle(hConstantData); } ReleaseStr(pwzExecute); ReleaseStr(pwzIniFile); ReleaseStr(pwzTempFolder); ReleaseStr(pwzConstantData); ReleaseStr(pwzIniData); ReleaseStr(pwzName); ReleaseStr(pwzCustomActionData); if (hModule) { ::FreeLibrary(hModule); } return hr; } static HRESULT CreateDataFile( __in LPCWSTR wzTempFolder, __in LPCWSTR wzData, __in BOOL fIniData, __out HANDLE *phFile, __out_opt LPWSTR *ppwzFile ) { HRESULT hr = S_OK; HANDLE hFile = INVALID_HANDLE_VALUE; LPWSTR pwzFile = NULL; LPSTR pszData = NULL; DWORD cbData = 0; DWORD cbWritten = 0; // Convert the data to UTF-8 because lodctr/unloctr // doesn't like unicode. hr = StrAnsiAllocString(&pszData, wzData, 0, CP_UTF8); ExitOnFailure(hr, "Failed to covert data to ANSI."); cbData = lstrlenA(pszData); // Concatenate the paths together, open the file data file // and dump the data in there. hr = StrAllocString(&pwzFile, wzTempFolder, 0); ExitOnFailure(hr, "Failed to copy temp directory name."); hr = StrAllocConcat(&pwzFile, L"wixperf", 0); ExitOnFailure(hr, "Failed to add name of file."); hr = StrAllocConcat(&pwzFile, fIniData ? L".ini" : L".h", 0); ExitOnFailure(hr, "Failed to add extension of file."); hFile = ::CreateFileW(pwzFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hFile) { ExitWithLastError(hr, "Failed to open new temp file: %ls", pwzFile); } if (!::WriteFile(hFile, pszData, cbData, &cbWritten, NULL)) { ExitWithLastError(hr, "Failed to write data to new temp file: %ls", pwzFile); } if (INVALID_HANDLE_VALUE != hFile) { ::CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; } // Return the requested values. *phFile = hFile; hFile = INVALID_HANDLE_VALUE; if (ppwzFile) { *ppwzFile = pwzFile; pwzFile = NULL; } LExit: if (INVALID_HANDLE_VALUE != hFile) { ::CloseHandle(hFile); } ReleaseStr(pszData); ReleaseStr(pwzFile); return hr; }