| 1 | // 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. |
| 2 | |
| 3 | #include "precomp.h" |
| 4 | |
| 5 | typedef DWORD (STDAPICALLTYPE *PFNPERFCOUNTERTEXTSTRINGS)(LPWSTR lpCommandLine, BOOL bQuietModeArg); |
| 6 | |
| 7 | static HRESULT ExecutePerfCounterData( |
| 8 | __in MSIHANDLE hInstall, |
| 9 | __in BOOL fInstall |
| 10 | ); |
| 11 | static HRESULT CreateDataFile( |
| 12 | __in LPCWSTR wzTempFolder, |
| 13 | __in LPCWSTR wzData, |
| 14 | __in BOOL fIniData, |
| 15 | __out HANDLE *phFile, |
| 16 | __out_opt LPWSTR *ppwzFile |
| 17 | ); |
| 18 | |
| 19 | |
| 20 | /******************************************************************** |
| 21 | RegisterPerfCounterData - CUSTOM ACTION ENTRY POINT for registering |
| 22 | performance counters |
| 23 | |
| 24 | Input: deferred CustomActionData: wzName\twzIniData\twzConstantData\twzName\twzIniData\twzConstantData\t... |
| 25 | *******************************************************************/ |
| 26 | extern "C" UINT __stdcall RegisterPerfCounterData( |
| 27 | __in MSIHANDLE hInstall |
| 28 | ) |
| 29 | { |
| 30 | // AssertSz(FALSE, "debug RegisterPerfCounterData()"); |
| 31 | HRESULT hr = S_OK; |
| 32 | DWORD er = ERROR_SUCCESS; |
| 33 | |
| 34 | hr = WcaInitialize(hInstall, "RegisterPerfCounterData"); |
| 35 | ExitOnFailure(hr, "Failed to initialize RegisterPerfCounterData."); |
| 36 | |
| 37 | hr = ExecutePerfCounterData(hInstall, TRUE); |
| 38 | MessageExitOnFailure(hr, msierrInstallPerfCounterData, "Failed to execute Wix4PerformanceCategory table."); |
| 39 | |
| 40 | LExit: |
| 41 | er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; |
| 42 | return WcaFinalize(er); |
| 43 | } |
| 44 | |
| 45 | |
| 46 | /******************************************************************** |
| 47 | UnregisterPerfCounterData - CUSTOM ACTION ENTRY POINT for registering |
| 48 | performance counters |
| 49 | |
| 50 | Input: deferred CustomActionData: wzName\twzIniData\twzConstantData\twzName\twzIniData\twzConstantData\t... |
| 51 | *******************************************************************/ |
| 52 | extern "C" UINT __stdcall UnregisterPerfCounterData( |
| 53 | __in MSIHANDLE hInstall |
| 54 | ) |
| 55 | { |
| 56 | // AssertSz(FALSE, "debug UnregisterPerfCounterData()"); |
| 57 | HRESULT hr = S_OK; |
| 58 | DWORD er = ERROR_SUCCESS; |
| 59 | |
| 60 | hr = WcaInitialize(hInstall, "UnregisterPerfCounterData"); |
| 61 | ExitOnFailure(hr, "Failed to initialize UnregisterPerfCounterData."); |
| 62 | |
| 63 | hr = ExecutePerfCounterData(hInstall, FALSE); |
| 64 | MessageExitOnFailure(hr, msierrUninstallPerfCounterData, "Failed to execute Wix4PerformanceCategory table."); |
| 65 | |
| 66 | LExit: |
| 67 | er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; |
| 68 | return WcaFinalize(er); |
| 69 | } |
| 70 | |
| 71 | |
| 72 | /******************************************************************** |
| 73 | RegisterPerfmon - CUSTOM ACTION ENTRY POINT for registering |
| 74 | counters |
| 75 | |
| 76 | Input: deferred CustomActionData - |
| 77 | wzFile or wzName |
| 78 | *******************************************************************/ |
| 79 | extern "C" UINT __stdcall RegisterPerfmon( |
| 80 | __in MSIHANDLE hInstall |
| 81 | ) |
| 82 | { |
| 83 | // Assert(FALSE); |
| 84 | UINT er = ERROR_SUCCESS; |
| 85 | HRESULT hr = S_OK; |
| 86 | LPWSTR pwzData = NULL; |
| 87 | |
| 88 | HMODULE hMod = NULL; |
| 89 | PFNPERFCOUNTERTEXTSTRINGS pfnPerfCounterTextString; |
| 90 | DWORD dwRet; |
| 91 | LPWSTR pwzShortPath = NULL; |
| 92 | DWORD cchShortPath = MAX_PATH; |
| 93 | DWORD cchShortPathLength = 0; |
| 94 | |
| 95 | LPWSTR pwzCommand = NULL; |
| 96 | |
| 97 | hr = WcaInitialize(hInstall, "RegisterPerfmon"); |
| 98 | ExitOnFailure(hr, "failed to initialize"); |
| 99 | |
| 100 | hr = WcaGetProperty(L"CustomActionData", &pwzData); |
| 101 | ExitOnFailure(hr, "failed to get CustomActionData"); |
| 102 | |
| 103 | WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData); |
| 104 | |
| 105 | // do the perfmon registration |
| 106 | if (NULL == hMod) |
| 107 | { |
| 108 | hr = LoadSystemLibrary(L"loadperf.dll", &hMod); |
| 109 | } |
| 110 | ExitOnFailure(hr, "failed to load DLL for PerfMon"); |
| 111 | |
| 112 | pfnPerfCounterTextString = (PFNPERFCOUNTERTEXTSTRINGS)::GetProcAddress(hMod, "LoadPerfCounterTextStringsW"); |
| 113 | ExitOnNullWithLastError(pfnPerfCounterTextString, hr, "failed to get DLL function for PerfMon"); |
| 114 | |
| 115 | hr = StrAlloc(&pwzShortPath, cchShortPath); |
| 116 | ExitOnFailure(hr, "failed to allocate string"); |
| 117 | |
| 118 | WcaLog(LOGMSG_VERBOSE, "Converting DLL path to short format: %ls", pwzData); |
| 119 | cchShortPathLength = ::GetShortPathNameW(pwzData, pwzShortPath, cchShortPath); |
| 120 | if (cchShortPathLength > cchShortPath) |
| 121 | { |
| 122 | cchShortPath = cchShortPathLength + 1; |
| 123 | hr = StrAlloc(&pwzShortPath, cchShortPath); |
| 124 | ExitOnFailure(hr, "failed to allocate string"); |
| 125 | |
| 126 | cchShortPathLength = ::GetShortPathNameW(pwzData, pwzShortPath, cchShortPath); |
| 127 | } |
| 128 | |
| 129 | if (0 == cchShortPathLength) |
| 130 | { |
| 131 | ExitOnLastError(hr, "failed to get short path format of path: %ls", pwzData); |
| 132 | } |
| 133 | |
| 134 | hr = StrAllocFormatted(&pwzCommand, L"lodctr \"%s\"", pwzShortPath); |
| 135 | ExitOnFailure(hr, "failed to format lodctr string"); |
| 136 | |
| 137 | WcaLog(LOGMSG_VERBOSE, "RegisterPerfmon running command: '%ls'", pwzCommand); |
| 138 | dwRet = (*pfnPerfCounterTextString)(pwzCommand, TRUE); |
| 139 | if (dwRet != ERROR_SUCCESS && dwRet != ERROR_ALREADY_EXISTS) |
| 140 | { |
| 141 | hr = HRESULT_FROM_WIN32(dwRet); |
| 142 | MessageExitOnFailure(hr, msierrPERFMONFailedRegisterDLL, "failed to register with PerfMon, DLL: %ls", pwzData); |
| 143 | } |
| 144 | |
| 145 | hr = S_OK; |
| 146 | LExit: |
| 147 | ReleaseStr(pwzData); |
| 148 | |
| 149 | if (FAILED(hr)) |
| 150 | er = ERROR_INSTALL_FAILURE; |
| 151 | return WcaFinalize(er); |
| 152 | } |
| 153 | |
| 154 | |
| 155 | extern "C" UINT __stdcall UnregisterPerfmon( |
| 156 | __in MSIHANDLE hInstall |
| 157 | ) |
| 158 | { |
| 159 | // Assert(FALSE); |
| 160 | UINT er = ERROR_SUCCESS; |
| 161 | HRESULT hr = S_OK; |
| 162 | LPWSTR pwzData = NULL; |
| 163 | |
| 164 | HMODULE hMod = NULL; |
| 165 | PFNPERFCOUNTERTEXTSTRINGS pfnPerfCounterTextString; |
| 166 | DWORD dwRet; |
| 167 | WCHAR wz[255]; |
| 168 | |
| 169 | hr = WcaInitialize(hInstall, "UnregisterPerfmon"); |
| 170 | ExitOnFailure(hr, "failed to initialize"); |
| 171 | |
| 172 | hr = WcaGetProperty(L"CustomActionData", &pwzData); |
| 173 | ExitOnFailure(hr, "failed to get CustomActionData"); |
| 174 | |
| 175 | WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData); |
| 176 | |
| 177 | // do the perfmon unregistration |
| 178 | hr = E_FAIL; |
| 179 | if (hMod == NULL) |
| 180 | { |
| 181 | hr = LoadSystemLibrary(L"loadperf.dll", &hMod); |
| 182 | } |
| 183 | ExitOnFailure(hr, "failed to load DLL for PerfMon"); |
| 184 | |
| 185 | pfnPerfCounterTextString = (PFNPERFCOUNTERTEXTSTRINGS)::GetProcAddress(hMod, "UnloadPerfCounterTextStringsW"); |
| 186 | ExitOnNullWithLastError(pfnPerfCounterTextString, hr, "failed to get DLL function for PerfMon"); |
| 187 | |
| 188 | hr = ::StringCchPrintfW(wz, countof(wz), L"unlodctr \"%s\"", pwzData); |
| 189 | ExitOnFailure(hr, "Failed to format unlodctr string with: %ls", pwzData); |
| 190 | WcaLog(LOGMSG_VERBOSE, "UnregisterPerfmon running command: '%ls'", wz); |
| 191 | dwRet = (*pfnPerfCounterTextString)(wz, TRUE); |
| 192 | // if the counters aren't registered, then OK to continue |
| 193 | if (dwRet != ERROR_SUCCESS && dwRet != ERROR_FILE_NOT_FOUND && dwRet != ERROR_BADKEY) |
| 194 | { |
| 195 | hr = HRESULT_FROM_WIN32(dwRet); |
| 196 | MessageExitOnFailure(hr, msierrPERFMONFailedUnregisterDLL, "failed to unregsister with PerfMon, DLL: %ls", pwzData); |
| 197 | } |
| 198 | |
| 199 | hr = S_OK; |
| 200 | LExit: |
| 201 | ReleaseStr(pwzData); |
| 202 | |
| 203 | if (FAILED(hr)) |
| 204 | er = ERROR_INSTALL_FAILURE; |
| 205 | return WcaFinalize(er); |
| 206 | } |
| 207 | |
| 208 | |
| 209 | static HRESULT ExecutePerfCounterData( |
| 210 | __in MSIHANDLE /*hInstall*/, |
| 211 | __in BOOL fInstall |
| 212 | ) |
| 213 | { |
| 214 | HRESULT hr = S_OK; |
| 215 | DWORD er = ERROR_SUCCESS; |
| 216 | |
| 217 | HMODULE hModule = NULL; |
| 218 | PFNPERFCOUNTERTEXTSTRINGS pfnPerfCounterTextString = NULL; |
| 219 | LPCWSTR wzPrefix = NULL; |
| 220 | |
| 221 | LPWSTR pwzCustomActionData = NULL; |
| 222 | LPWSTR pwz = NULL; |
| 223 | |
| 224 | LPWSTR pwzName = NULL; |
| 225 | LPWSTR pwzIniData = NULL; |
| 226 | LPWSTR pwzConstantData = NULL; |
| 227 | LPWSTR pwzTempFolder = NULL; |
| 228 | LPWSTR pwzIniFile = NULL; |
| 229 | LPWSTR pwzExecute = NULL; |
| 230 | |
| 231 | HANDLE hIniData = INVALID_HANDLE_VALUE; |
| 232 | HANDLE hConstantData = INVALID_HANDLE_VALUE; |
| 233 | |
| 234 | // Load the system performance counter helper DLL then get the appropriate |
| 235 | // entrypoint out of it. Fortunately, they have the same signature so we |
| 236 | // can use one function pointer to point to both. |
| 237 | hr = LoadSystemLibrary(L"loadperf.dll", &hModule); |
| 238 | ExitOnFailure(hr, "failed to load DLL for PerfMon"); |
| 239 | |
| 240 | if (fInstall) |
| 241 | { |
| 242 | wzPrefix = L"lodctr"; |
| 243 | pfnPerfCounterTextString = (PFNPERFCOUNTERTEXTSTRINGS)::GetProcAddress(hModule, "LoadPerfCounterTextStringsW"); |
| 244 | } |
| 245 | else |
| 246 | { |
| 247 | wzPrefix = L"unlodctr"; |
| 248 | pfnPerfCounterTextString = (PFNPERFCOUNTERTEXTSTRINGS)::GetProcAddress(hModule, "UnloadPerfCounterTextStringsW"); |
| 249 | } |
| 250 | ExitOnNullWithLastError(pfnPerfCounterTextString, hr, "Failed to get DLL function for PerfMon"); |
| 251 | |
| 252 | // Now get the CustomActionData and execute it. |
| 253 | hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData); |
| 254 | ExitOnFailure(hr, "Failed to get CustomActionData."); |
| 255 | |
| 256 | WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData); |
| 257 | |
| 258 | pwz = pwzCustomActionData; |
| 259 | |
| 260 | while (S_OK == (hr = WcaReadStringFromCaData(&pwz, &pwzName))) |
| 261 | { |
| 262 | hr = WcaReadStringFromCaData(&pwz, &pwzIniData); |
| 263 | ExitOnFailure(hr, "Failed to read IniData from custom action data."); |
| 264 | |
| 265 | hr = WcaReadStringFromCaData(&pwz, &pwzConstantData); |
| 266 | ExitOnFailure(hr, "Failed to read ConstantData from custom action data."); |
| 267 | |
| 268 | if (fInstall) |
| 269 | { |
| 270 | hr = PathCreateTempDirectory(NULL, L"WIXPF%03x", 999, &pwzTempFolder); |
| 271 | ExitOnFailure(hr, "Failed to create temp directory."); |
| 272 | |
| 273 | hr = CreateDataFile(pwzTempFolder, pwzIniData, TRUE, &hIniData, &pwzIniFile); |
| 274 | ExitOnFailure(hr, "Failed to create .ini file for performance counter category: %ls", pwzName); |
| 275 | |
| 276 | hr = CreateDataFile(pwzTempFolder, pwzConstantData, FALSE, &hConstantData, NULL); |
| 277 | ExitOnFailure(hr, "Failed to create .h file for performance counter category: %ls", pwzName); |
| 278 | |
| 279 | hr = StrAllocFormatted(&pwzExecute, L"%s \"%s\"", wzPrefix, pwzIniFile); |
| 280 | ExitOnFailure(hr, "Failed to allocate string to execute."); |
| 281 | |
| 282 | // Execute the install. |
| 283 | er = (*pfnPerfCounterTextString)(pwzExecute, TRUE); |
| 284 | hr = HRESULT_FROM_WIN32(er); |
| 285 | ExitOnFailure(hr, "Failed to execute install of performance counter category: %ls", pwzName); |
| 286 | |
| 287 | if (INVALID_HANDLE_VALUE != hIniData) |
| 288 | { |
| 289 | ::CloseHandle(hIniData); |
| 290 | hIniData = INVALID_HANDLE_VALUE; |
| 291 | } |
| 292 | |
| 293 | if (INVALID_HANDLE_VALUE != hConstantData) |
| 294 | { |
| 295 | ::CloseHandle(hConstantData); |
| 296 | hConstantData = INVALID_HANDLE_VALUE; |
| 297 | } |
| 298 | |
| 299 | DirEnsureDelete(pwzTempFolder, TRUE, TRUE); |
| 300 | } |
| 301 | else |
| 302 | { |
| 303 | hr = StrAllocFormatted(&pwzExecute, L"%s \"%s\"", wzPrefix, pwzName); |
| 304 | ExitOnFailure(hr, "Failed to allocate string to execute."); |
| 305 | |
| 306 | // Execute the uninstall and if the counter isn't registered then ignore |
| 307 | // the error since it won't hurt anything. |
| 308 | er = (*pfnPerfCounterTextString)(pwzExecute, TRUE); |
| 309 | if (ERROR_FILE_NOT_FOUND == er || ERROR_BADKEY == er) |
| 310 | { |
| 311 | er = ERROR_SUCCESS; |
| 312 | } |
| 313 | hr = HRESULT_FROM_WIN32(er); |
| 314 | ExitOnFailure(hr, "Failed to execute uninstall of performance counter category: %ls", pwzName); |
| 315 | } |
| 316 | } |
| 317 | |
| 318 | if (E_NOMOREITEMS == hr) // If there are no more items, all is well |
| 319 | { |
| 320 | hr = S_OK; |
| 321 | } |
| 322 | ExitOnFailure(hr, "Failed to execute all perf counter data."); |
| 323 | |
| 324 | hr = S_OK; |
| 325 | |
| 326 | LExit: |
| 327 | if (INVALID_HANDLE_VALUE != hIniData) |
| 328 | { |
| 329 | ::CloseHandle(hIniData); |
| 330 | } |
| 331 | |
| 332 | if (INVALID_HANDLE_VALUE != hConstantData) |
| 333 | { |
| 334 | ::CloseHandle(hConstantData); |
| 335 | } |
| 336 | |
| 337 | ReleaseStr(pwzExecute); |
| 338 | ReleaseStr(pwzIniFile); |
| 339 | ReleaseStr(pwzTempFolder); |
| 340 | ReleaseStr(pwzConstantData); |
| 341 | ReleaseStr(pwzIniData); |
| 342 | ReleaseStr(pwzName); |
| 343 | ReleaseStr(pwzCustomActionData); |
| 344 | |
| 345 | if (hModule) |
| 346 | { |
| 347 | ::FreeLibrary(hModule); |
| 348 | } |
| 349 | |
| 350 | return hr; |
| 351 | } |
| 352 | |
| 353 | |
| 354 | static HRESULT CreateDataFile( |
| 355 | __in LPCWSTR wzTempFolder, |
| 356 | __in LPCWSTR wzData, |
| 357 | __in BOOL fIniData, |
| 358 | __out HANDLE *phFile, |
| 359 | __out_opt LPWSTR *ppwzFile |
| 360 | ) |
| 361 | { |
| 362 | HRESULT hr = S_OK; |
| 363 | HANDLE hFile = INVALID_HANDLE_VALUE; |
| 364 | LPWSTR pwzFile = NULL; |
| 365 | LPSTR pszData = NULL; |
| 366 | DWORD cbData = 0; |
| 367 | DWORD cbWritten = 0; |
| 368 | |
| 369 | // Convert the data to UTF-8 because lodctr/unloctr |
| 370 | // doesn't like unicode. |
| 371 | hr = StrAnsiAllocString(&pszData, wzData, 0, CP_UTF8); |
| 372 | ExitOnFailure(hr, "Failed to covert data to ANSI."); |
| 373 | |
| 374 | cbData = lstrlenA(pszData); |
| 375 | |
| 376 | // Concatenate the paths together, open the file data file |
| 377 | // and dump the data in there. |
| 378 | hr = StrAllocString(&pwzFile, wzTempFolder, 0); |
| 379 | ExitOnFailure(hr, "Failed to copy temp directory name."); |
| 380 | |
| 381 | hr = StrAllocConcat(&pwzFile, L"wixperf", 0); |
| 382 | ExitOnFailure(hr, "Failed to add name of file."); |
| 383 | |
| 384 | hr = StrAllocConcat(&pwzFile, fIniData ? L".ini" : L".h", 0); |
| 385 | ExitOnFailure(hr, "Failed to add extension of file."); |
| 386 | |
| 387 | hFile = ::CreateFileW(pwzFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); |
| 388 | if (INVALID_HANDLE_VALUE == hFile) |
| 389 | { |
| 390 | ExitWithLastError(hr, "Failed to open new temp file: %ls", pwzFile); |
| 391 | } |
| 392 | |
| 393 | if (!::WriteFile(hFile, pszData, cbData, &cbWritten, NULL)) |
| 394 | { |
| 395 | ExitWithLastError(hr, "Failed to write data to new temp file: %ls", pwzFile); |
| 396 | } |
| 397 | |
| 398 | if (INVALID_HANDLE_VALUE != hFile) |
| 399 | { |
| 400 | ::CloseHandle(hFile); |
| 401 | hFile = INVALID_HANDLE_VALUE; |
| 402 | } |
| 403 | |
| 404 | // Return the requested values. |
| 405 | *phFile = hFile; |
| 406 | hFile = INVALID_HANDLE_VALUE; |
| 407 | |
| 408 | if (ppwzFile) |
| 409 | { |
| 410 | *ppwzFile = pwzFile; |
| 411 | pwzFile = NULL; |
| 412 | } |
| 413 | |
| 414 | LExit: |
| 415 | if (INVALID_HANDLE_VALUE != hFile) |
| 416 | { |
| 417 | ::CloseHandle(hFile); |
| 418 | } |
| 419 | ReleaseStr(pszData); |
| 420 | ReleaseStr(pwzFile); |
| 421 | |
| 422 | return hr; |
| 423 | } |