| 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 | |
| 6 | /******************************************************************** |
| 7 | WcaProcessMessage() - sends a message from the CustomAction |
| 8 | |
| 9 | ********************************************************************/ |
| 10 | extern "C" UINT WIXAPI WcaProcessMessage( |
| 11 | __in INSTALLMESSAGE eMessageType, |
| 12 | __in MSIHANDLE hRecord |
| 13 | ) |
| 14 | { |
| 15 | UINT er = ::MsiProcessMessage(WcaGetInstallHandle(), eMessageType, hRecord); |
| 16 | if (ERROR_INSTALL_USEREXIT == er || IDCANCEL == er) |
| 17 | { |
| 18 | WcaSetReturnValue(ERROR_INSTALL_USEREXIT); |
| 19 | } |
| 20 | |
| 21 | return er; |
| 22 | } |
| 23 | |
| 24 | |
| 25 | /******************************************************************** |
| 26 | WcaErrorMessage() - sends an error message from the CustomAction using |
| 27 | the Error table |
| 28 | |
| 29 | NOTE: Any and all var_args (...) must be WCHAR* |
| 30 | If you pass -1 to cArgs, the count will be determined by |
| 31 | looking for a trailing NULL argment. If you omit a terminating |
| 32 | NULL, the results are undefined and probably crashy. |
| 33 | ********************************************************************/ |
| 34 | extern "C" UINT __cdecl WcaErrorMessage( |
| 35 | __in int iError, |
| 36 | __in HRESULT hrError, |
| 37 | __in UINT uiType, |
| 38 | __in INT cArgs, |
| 39 | ... |
| 40 | ) |
| 41 | { |
| 42 | UINT er; |
| 43 | MSIHANDLE hRec = NULL; |
| 44 | va_list args = NULL; |
| 45 | |
| 46 | if (-1 == cArgs) |
| 47 | { |
| 48 | LPCWSTR wzArg = NULL; |
| 49 | va_list iter = NULL; |
| 50 | |
| 51 | va_start(iter, cArgs); |
| 52 | cArgs = 0; |
| 53 | |
| 54 | while (NULL != (wzArg = va_arg(iter, WCHAR*)) && L'\0' != *wzArg) |
| 55 | { |
| 56 | ++cArgs; |
| 57 | } |
| 58 | |
| 59 | va_end(iter); |
| 60 | } |
| 61 | |
| 62 | uiType |= INSTALLMESSAGE_ERROR; // ensure error type is set |
| 63 | hRec = ::MsiCreateRecord(cArgs + 2); |
| 64 | if (!hRec) |
| 65 | { |
| 66 | er = ERROR_OUTOFMEMORY; |
| 67 | ExitOnFailure(HRESULT_FROM_WIN32(er), "failed to create record when sending error message"); |
| 68 | } |
| 69 | |
| 70 | er = ::MsiRecordSetInteger(hRec, 1, iError); |
| 71 | ExitOnFailure(HRESULT_FROM_WIN32(er), "failed to set error code into error message"); |
| 72 | |
| 73 | er = ::MsiRecordSetInteger(hRec, 2, hrError); |
| 74 | ExitOnFailure(HRESULT_FROM_WIN32(er), "failed to set hresult code into error message"); |
| 75 | |
| 76 | va_start(args, cArgs); |
| 77 | for (INT i = 0; i < cArgs; i++) |
| 78 | { |
| 79 | er = ::MsiRecordSetStringW(hRec, i + 3, va_arg(args, WCHAR*)); |
| 80 | ExitOnFailure(HRESULT_FROM_WIN32(er), "failed to set string string into error message"); |
| 81 | } |
| 82 | va_end(args); |
| 83 | |
| 84 | er = WcaProcessMessage(static_cast<INSTALLMESSAGE>(uiType), hRec); |
| 85 | |
| 86 | LExit: |
| 87 | if (args) |
| 88 | { |
| 89 | va_end(args); |
| 90 | } |
| 91 | |
| 92 | if (hRec) |
| 93 | { |
| 94 | ::MsiCloseHandle(hRec); |
| 95 | } |
| 96 | |
| 97 | return er; |
| 98 | } |
| 99 | |
| 100 | |
| 101 | /******************************************************************** |
| 102 | WcaProgressMessage() - extends the progress bar or sends a progress |
| 103 | update from the CustomAction |
| 104 | |
| 105 | ********************************************************************/ |
| 106 | extern "C" HRESULT WIXAPI WcaProgressMessage( |
| 107 | __in UINT uiCost, |
| 108 | __in BOOL fExtendProgressBar |
| 109 | ) |
| 110 | { |
| 111 | static BOOL fExplicitProgressMessages = FALSE; |
| 112 | |
| 113 | HRESULT hr = S_OK; |
| 114 | UINT er = ERROR_SUCCESS; |
| 115 | MSIHANDLE hRec = ::MsiCreateRecord(3); |
| 116 | |
| 117 | // if aren't extending the progress bar and we haven't switched into explicit message mode |
| 118 | if (!fExtendProgressBar && !fExplicitProgressMessages) |
| 119 | { |
| 120 | AssertSz(::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_SCHEDULED) || |
| 121 | ::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_COMMIT) || |
| 122 | ::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_ROLLBACK), "can only send progress bar messages in a deferred CustomAction"); |
| 123 | |
| 124 | // tell Darwin to use explicit progress messages |
| 125 | ::MsiRecordSetInteger(hRec, 1, 1); |
| 126 | ::MsiRecordSetInteger(hRec, 2, 1); |
| 127 | ::MsiRecordSetInteger(hRec, 3, 0); |
| 128 | |
| 129 | er = WcaProcessMessage(INSTALLMESSAGE_PROGRESS, hRec); |
| 130 | if (0 == er || IDOK == er || IDYES == er) |
| 131 | { |
| 132 | hr = S_OK; |
| 133 | } |
| 134 | else if (IDABORT == er || IDCANCEL == er) |
| 135 | { |
| 136 | WcaSetReturnValue(ERROR_INSTALL_USEREXIT); // note that the user said exit |
| 137 | ExitFunction1(hr = S_FALSE); |
| 138 | } |
| 139 | else |
| 140 | { |
| 141 | hr = E_UNEXPECTED; |
| 142 | } |
| 143 | ExitOnFailure(hr, "failed to tell Darwin to use explicit progress messages"); |
| 144 | |
| 145 | fExplicitProgressMessages = TRUE; |
| 146 | } |
| 147 | #if DEBUG |
| 148 | else if (fExtendProgressBar) // if we are extending the progress bar, make sure we're not deferred |
| 149 | { |
| 150 | AssertSz(!::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_SCHEDULED), "cannot add ticks to progress bar length from deferred CustomAction"); |
| 151 | } |
| 152 | #endif |
| 153 | |
| 154 | // send the progress message |
| 155 | ::MsiRecordSetInteger(hRec, 1, (fExtendProgressBar) ? 3 : 2); |
| 156 | ::MsiRecordSetInteger(hRec, 2, uiCost); |
| 157 | ::MsiRecordSetInteger(hRec, 3, 0); |
| 158 | |
| 159 | er = WcaProcessMessage(INSTALLMESSAGE_PROGRESS, hRec); |
| 160 | if (0 == er || IDOK == er || IDYES == er) |
| 161 | { |
| 162 | hr = S_OK; |
| 163 | } |
| 164 | else if (IDABORT == er || IDCANCEL == er) |
| 165 | { |
| 166 | WcaSetReturnValue(ERROR_INSTALL_USEREXIT); // note that the user said exit |
| 167 | hr = S_FALSE; |
| 168 | } |
| 169 | else |
| 170 | { |
| 171 | hr = E_UNEXPECTED; |
| 172 | } |
| 173 | |
| 174 | LExit: |
| 175 | if (hRec) |
| 176 | { |
| 177 | ::MsiCloseHandle(hRec); |
| 178 | } |
| 179 | |
| 180 | return hr; |
| 181 | } |
| 182 | |
| 183 | |
| 184 | /******************************************************************** |
| 185 | WcaIsInstalling() - determines if a pair of installstates means install |
| 186 | |
| 187 | ********************************************************************/ |
| 188 | extern "C" BOOL WIXAPI WcaIsInstalling( |
| 189 | __in INSTALLSTATE isInstalled, |
| 190 | __in INSTALLSTATE isAction |
| 191 | ) |
| 192 | { |
| 193 | return (INSTALLSTATE_LOCAL == isAction || |
| 194 | INSTALLSTATE_SOURCE == isAction || |
| 195 | (INSTALLSTATE_DEFAULT == isAction && |
| 196 | (INSTALLSTATE_LOCAL == isInstalled || |
| 197 | INSTALLSTATE_SOURCE == isInstalled))); |
| 198 | } |
| 199 | |
| 200 | /******************************************************************** |
| 201 | WcaIsReInstalling() - determines if a pair of installstates means reinstall |
| 202 | |
| 203 | ********************************************************************/ |
| 204 | extern "C" BOOL WIXAPI WcaIsReInstalling( |
| 205 | __in INSTALLSTATE isInstalled, |
| 206 | __in INSTALLSTATE isAction |
| 207 | ) |
| 208 | { |
| 209 | return ((INSTALLSTATE_LOCAL == isAction || |
| 210 | INSTALLSTATE_SOURCE == isAction || |
| 211 | INSTALLSTATE_DEFAULT == isAction) && |
| 212 | (INSTALLSTATE_LOCAL == isInstalled || |
| 213 | INSTALLSTATE_SOURCE == isInstalled)); |
| 214 | } |
| 215 | |
| 216 | |
| 217 | /******************************************************************** |
| 218 | WcaIsUninstalling() - determines if a pair of installstates means uninstall |
| 219 | |
| 220 | ********************************************************************/ |
| 221 | extern "C" BOOL WIXAPI WcaIsUninstalling( |
| 222 | __in INSTALLSTATE isInstalled, |
| 223 | __in INSTALLSTATE isAction |
| 224 | ) |
| 225 | { |
| 226 | return ((INSTALLSTATE_ABSENT == isAction || |
| 227 | INSTALLSTATE_REMOVED == isAction) && |
| 228 | (INSTALLSTATE_LOCAL == isInstalled || |
| 229 | INSTALLSTATE_SOURCE == isInstalled)); |
| 230 | } |
| 231 | |
| 232 | |
| 233 | /******************************************************************** |
| 234 | WcaGetComponentToDo() - gets a component's install states and |
| 235 | determines if they mean install, uninstall, or reinstall. |
| 236 | ********************************************************************/ |
| 237 | extern "C" WCA_TODO WIXAPI WcaGetComponentToDo( |
| 238 | __in_z LPCWSTR wzComponentId |
| 239 | ) |
| 240 | { |
| 241 | INSTALLSTATE isInstalled = INSTALLSTATE_UNKNOWN; |
| 242 | INSTALLSTATE isAction = INSTALLSTATE_UNKNOWN; |
| 243 | if (ERROR_SUCCESS != ::MsiGetComponentStateW(WcaGetInstallHandle(), wzComponentId, &isInstalled, &isAction)) |
| 244 | { |
| 245 | return WCA_TODO_UNKNOWN; |
| 246 | } |
| 247 | |
| 248 | if (WcaIsReInstalling(isInstalled, isAction)) |
| 249 | { |
| 250 | return WCA_TODO_REINSTALL; |
| 251 | } |
| 252 | else if (WcaIsUninstalling(isInstalled, isAction)) |
| 253 | { |
| 254 | return WCA_TODO_UNINSTALL; |
| 255 | } |
| 256 | else if (WcaIsInstalling(isInstalled, isAction)) |
| 257 | { |
| 258 | return WCA_TODO_INSTALL; |
| 259 | } |
| 260 | else |
| 261 | { |
| 262 | return WCA_TODO_UNKNOWN; |
| 263 | } |
| 264 | } |
| 265 | |
| 266 | |
| 267 | /******************************************************************** |
| 268 | WcaSetComponentState() - sets the install state of a Component |
| 269 | |
| 270 | ********************************************************************/ |
| 271 | extern "C" HRESULT WIXAPI WcaSetComponentState( |
| 272 | __in_z LPCWSTR wzComponent, |
| 273 | __in INSTALLSTATE isState |
| 274 | ) |
| 275 | { |
| 276 | UINT er = ::MsiSetComponentStateW(WcaGetInstallHandle(), wzComponent, isState); |
| 277 | if (ERROR_INSTALL_USEREXIT == er) |
| 278 | { |
| 279 | WcaSetReturnValue(er); |
| 280 | } |
| 281 | |
| 282 | return HRESULT_FROM_WIN32(er); |
| 283 | } |
| 284 | |
| 285 | |
| 286 | /******************************************************************** |
| 287 | WcaTableExists() - determines if installing database contains a table |
| 288 | |
| 289 | ********************************************************************/ |
| 290 | extern "C" HRESULT WIXAPI WcaTableExists( |
| 291 | __in_z LPCWSTR wzTable |
| 292 | ) |
| 293 | { |
| 294 | HRESULT hr = S_OK; |
| 295 | UINT er = ERROR_SUCCESS; |
| 296 | |
| 297 | // NOTE: The following line of commented out code should work in a |
| 298 | // CustomAction but does not in Windows Installer v1.1 |
| 299 | // er = ::MsiDatabaseIsTablePersistentW(hDatabase, wzTable); |
| 300 | |
| 301 | // a "most elegant" workaround a Darwin v1.1 bug |
| 302 | PMSIHANDLE hRec; |
| 303 | er = ::MsiDatabaseGetPrimaryKeysW(WcaGetDatabaseHandle(), wzTable, &hRec); |
| 304 | |
| 305 | if (ERROR_SUCCESS == er) |
| 306 | { |
| 307 | hr = S_OK; |
| 308 | } |
| 309 | else if (ERROR_INVALID_TABLE == er) |
| 310 | { |
| 311 | hr = S_FALSE; |
| 312 | } |
| 313 | else |
| 314 | { |
| 315 | hr = E_FAIL; |
| 316 | } |
| 317 | Assert(SUCCEEDED(hr)); |
| 318 | |
| 319 | return hr; |
| 320 | } |
| 321 | |
| 322 | |
| 323 | /******************************************************************** |
| 324 | WcaOpenView() - opens a view on the installing database |
| 325 | |
| 326 | ********************************************************************/ |
| 327 | extern "C" HRESULT WIXAPI WcaOpenView( |
| 328 | __in_z LPCWSTR wzSql, |
| 329 | __out MSIHANDLE* phView |
| 330 | ) |
| 331 | { |
| 332 | if (!wzSql || !*wzSql|| !phView) |
| 333 | { |
| 334 | return E_INVALIDARG; |
| 335 | } |
| 336 | |
| 337 | HRESULT hr = S_OK; |
| 338 | UINT er = ::MsiDatabaseOpenViewW(WcaGetDatabaseHandle(), wzSql, phView); |
| 339 | ExitOnWin32Error(er, hr, "failed to open view on database with SQL: %ls", wzSql); |
| 340 | |
| 341 | LExit: |
| 342 | return hr; |
| 343 | } |
| 344 | |
| 345 | |
| 346 | /******************************************************************** |
| 347 | WcaExecuteView() - executes a parameterized open view on the installing database |
| 348 | |
| 349 | ********************************************************************/ |
| 350 | extern "C" HRESULT WIXAPI WcaExecuteView( |
| 351 | __in MSIHANDLE hView, |
| 352 | __in MSIHANDLE hRec |
| 353 | ) |
| 354 | { |
| 355 | if (!hView) |
| 356 | { |
| 357 | return E_INVALIDARG; |
| 358 | } |
| 359 | AssertSz(hRec, "Use WcaOpenExecuteView() if you don't need to pass in a record"); |
| 360 | |
| 361 | HRESULT hr = S_OK; |
| 362 | UINT er = ::MsiViewExecute(hView, hRec); |
| 363 | ExitOnWin32Error(er, hr, "failed to execute view"); |
| 364 | |
| 365 | LExit: |
| 366 | return hr; |
| 367 | } |
| 368 | |
| 369 | |
| 370 | /******************************************************************** |
| 371 | WcaOpenExecuteView() - opens and executes a view on the installing database |
| 372 | |
| 373 | ********************************************************************/ |
| 374 | extern "C" HRESULT WIXAPI WcaOpenExecuteView( |
| 375 | __in_z LPCWSTR wzSql, |
| 376 | __out MSIHANDLE* phView |
| 377 | ) |
| 378 | { |
| 379 | if (!wzSql || !*wzSql|| !phView) |
| 380 | { |
| 381 | return E_INVALIDARG; |
| 382 | } |
| 383 | |
| 384 | HRESULT hr = S_OK; |
| 385 | UINT er = ::MsiDatabaseOpenViewW(WcaGetDatabaseHandle(), wzSql, phView); |
| 386 | ExitOnWin32Error(er, hr, "failed to open view on database"); |
| 387 | |
| 388 | er = ::MsiViewExecute(*phView, NULL); |
| 389 | ExitOnWin32Error(er, hr, "failed to execute view"); |
| 390 | |
| 391 | LExit: |
| 392 | return hr; |
| 393 | } |
| 394 | |
| 395 | |
| 396 | /******************************************************************** |
| 397 | WcaFetchRecord() - gets the next record from a view on the installing database |
| 398 | |
| 399 | ********************************************************************/ |
| 400 | extern "C" HRESULT WIXAPI WcaFetchRecord( |
| 401 | __in MSIHANDLE hView, |
| 402 | __out MSIHANDLE* phRec |
| 403 | ) |
| 404 | { |
| 405 | if (!hView|| !phRec) |
| 406 | { |
| 407 | return E_INVALIDARG; |
| 408 | } |
| 409 | |
| 410 | HRESULT hr = S_OK; |
| 411 | UINT er = ::MsiViewFetch(hView, phRec); |
| 412 | hr = HRESULT_FROM_WIN32(er); |
| 413 | if (FAILED(hr) && E_NOMOREITEMS != hr) |
| 414 | { |
| 415 | ExitOnFailure(hr, "failed to fetch record from view"); |
| 416 | } |
| 417 | |
| 418 | LExit: |
| 419 | return hr; |
| 420 | } |
| 421 | |
| 422 | |
| 423 | /******************************************************************** |
| 424 | WcaFetchSingleRecord() - gets a single record from a view on the installing database |
| 425 | |
| 426 | ********************************************************************/ |
| 427 | extern "C" HRESULT WIXAPI WcaFetchSingleRecord( |
| 428 | __in MSIHANDLE hView, |
| 429 | __out MSIHANDLE* phRec |
| 430 | ) |
| 431 | { |
| 432 | if (!hView|| !phRec) |
| 433 | { |
| 434 | return E_INVALIDARG; |
| 435 | } |
| 436 | |
| 437 | HRESULT hr = S_OK; |
| 438 | UINT er = ::MsiViewFetch(hView, phRec); |
| 439 | if (ERROR_NO_MORE_ITEMS == er) |
| 440 | { |
| 441 | hr = S_FALSE; |
| 442 | } |
| 443 | else |
| 444 | { |
| 445 | hr = HRESULT_FROM_WIN32(er); |
| 446 | } |
| 447 | ExitOnFailure(hr, "failed to fetch single record from view"); |
| 448 | |
| 449 | #ifdef DEBUG // only do this in debug to verify that a single record was returned |
| 450 | MSIHANDLE hRecTest; |
| 451 | er = ::MsiViewFetch(hView, &hRecTest); |
| 452 | AssertSz(ERROR_NO_MORE_ITEMS == er && NULL == hRecTest, "WcaSingleFetch() did not fetch a single record"); |
| 453 | ::MsiCloseHandle(hRecTest); |
| 454 | #endif |
| 455 | |
| 456 | LExit: |
| 457 | return hr; |
| 458 | } |
| 459 | |
| 460 | |
| 461 | /******************************************************************** |
| 462 | WcaGetProperty - gets a string property value from the active install |
| 463 | |
| 464 | ********************************************************************/ |
| 465 | extern "C" HRESULT WIXAPI WcaGetProperty( |
| 466 | __in_z LPCWSTR wzProperty, |
| 467 | __inout LPWSTR* ppwzData |
| 468 | ) |
| 469 | { |
| 470 | if (!wzProperty || !*wzProperty || !ppwzData) |
| 471 | { |
| 472 | return E_INVALIDARG; |
| 473 | } |
| 474 | |
| 475 | HRESULT hr = S_OK; |
| 476 | UINT er = ERROR_SUCCESS; |
| 477 | DWORD cch = 0; |
| 478 | SIZE_T cchMax = 0; |
| 479 | |
| 480 | if (!*ppwzData) |
| 481 | { |
| 482 | WCHAR szEmpty[1] = L""; |
| 483 | er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, szEmpty, &cch); |
| 484 | if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er) |
| 485 | { |
| 486 | hr = StrAlloc(ppwzData, ++cch); |
| 487 | } |
| 488 | else |
| 489 | { |
| 490 | hr = HRESULT_FROM_WIN32(er); |
| 491 | } |
| 492 | ExitOnRootFailure(hr, "Failed to allocate string for Property '%ls'", wzProperty); |
| 493 | } |
| 494 | else |
| 495 | { |
| 496 | hr = StrMaxLength(*ppwzData, &cchMax); |
| 497 | ExitOnFailure(hr, "Failed to get previous size of property data string."); |
| 498 | |
| 499 | cch = (DWORD)min(MAXDWORD, cchMax); |
| 500 | } |
| 501 | |
| 502 | er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, *ppwzData, &cch); |
| 503 | if (ERROR_MORE_DATA == er) |
| 504 | { |
| 505 | Assert(*ppwzData); |
| 506 | hr = StrAlloc(ppwzData, ++cch); |
| 507 | ExitOnFailure(hr, "Failed to allocate string for Property '%ls'", wzProperty); |
| 508 | |
| 509 | er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, *ppwzData, &cch); |
| 510 | } |
| 511 | ExitOnWin32Error(er, hr, "Failed to get data for property '%ls'", wzProperty); |
| 512 | |
| 513 | LExit: |
| 514 | return hr; |
| 515 | } |
| 516 | |
| 517 | |
| 518 | /******************************************************************** |
| 519 | WcaGetFormattedProperty - gets a formatted string property value from |
| 520 | the active install |
| 521 | |
| 522 | ********************************************************************/ |
| 523 | extern "C" HRESULT WIXAPI WcaGetFormattedProperty( |
| 524 | __in_z LPCWSTR wzProperty, |
| 525 | __out LPWSTR* ppwzData |
| 526 | ) |
| 527 | { |
| 528 | if (!wzProperty || !*wzProperty || !ppwzData) |
| 529 | { |
| 530 | return E_INVALIDARG; |
| 531 | } |
| 532 | |
| 533 | HRESULT hr = S_OK; |
| 534 | LPWSTR pwzPropertyValue = NULL; |
| 535 | |
| 536 | hr = WcaGetProperty(wzProperty, &pwzPropertyValue); |
| 537 | ExitOnFailure(hr, "failed to get %ls", wzProperty); |
| 538 | |
| 539 | hr = WcaGetFormattedString(pwzPropertyValue, ppwzData); |
| 540 | ExitOnFailure(hr, "failed to get formatted value for property: '%ls' with value: '%ls'", wzProperty, pwzPropertyValue); |
| 541 | |
| 542 | LExit: |
| 543 | ReleaseStr(pwzPropertyValue); |
| 544 | |
| 545 | return hr; |
| 546 | } |
| 547 | |
| 548 | |
| 549 | /******************************************************************** |
| 550 | WcaGetFormattedString - gets a formatted string value from |
| 551 | the active install |
| 552 | |
| 553 | ********************************************************************/ |
| 554 | extern "C" HRESULT WIXAPI WcaGetFormattedString( |
| 555 | __in_z LPCWSTR wzString, |
| 556 | __out LPWSTR* ppwzData |
| 557 | ) |
| 558 | { |
| 559 | if (!wzString || !*wzString || !ppwzData) |
| 560 | { |
| 561 | return E_INVALIDARG; |
| 562 | } |
| 563 | |
| 564 | HRESULT hr = S_OK; |
| 565 | UINT er = ERROR_SUCCESS; |
| 566 | PMSIHANDLE hRecord = ::MsiCreateRecord(1); |
| 567 | DWORD cch = 0; |
| 568 | SIZE_T cchMax = 0; |
| 569 | |
| 570 | er = ::MsiRecordSetStringW(hRecord, 0, wzString); |
| 571 | ExitOnWin32Error(er, hr, "Failed to set record field 0 with '%ls'", wzString); |
| 572 | |
| 573 | if (!*ppwzData) |
| 574 | { |
| 575 | WCHAR szEmpty[1] = L""; |
| 576 | er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecord, szEmpty, &cch); |
| 577 | if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er) |
| 578 | { |
| 579 | hr = StrAlloc(ppwzData, ++cch); |
| 580 | } |
| 581 | else |
| 582 | { |
| 583 | hr = HRESULT_FROM_WIN32(er); |
| 584 | } |
| 585 | ExitOnFailure(hr, "Failed to allocate string for formatted string: '%ls'", wzString); |
| 586 | } |
| 587 | else |
| 588 | { |
| 589 | hr = StrMaxLength(*ppwzData, &cchMax); |
| 590 | ExitOnFailure(hr, "Failed to get previous size of property data string"); |
| 591 | |
| 592 | cch = (DWORD)min(MAXDWORD, cchMax); |
| 593 | } |
| 594 | |
| 595 | er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecord, *ppwzData, &cch); |
| 596 | if (ERROR_MORE_DATA == er) |
| 597 | { |
| 598 | hr = StrAlloc(ppwzData, ++cch); |
| 599 | ExitOnFailure(hr, "Failed to allocate string for formatted string: '%ls'", wzString); |
| 600 | |
| 601 | er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecord, *ppwzData, &cch); |
| 602 | } |
| 603 | ExitOnWin32Error(er, hr, "Failed to get formatted string: '%ls'", wzString); |
| 604 | |
| 605 | LExit: |
| 606 | return hr; |
| 607 | } |
| 608 | |
| 609 | |
| 610 | /******************************************************************** |
| 611 | WcaGetIntProperty - gets an integer property value from the active install |
| 612 | |
| 613 | ********************************************************************/ |
| 614 | extern "C" HRESULT WIXAPI WcaGetIntProperty( |
| 615 | __in_z LPCWSTR wzProperty, |
| 616 | __inout int* piData |
| 617 | ) |
| 618 | { |
| 619 | if (!piData) |
| 620 | return E_INVALIDARG; |
| 621 | |
| 622 | HRESULT hr = S_OK; |
| 623 | UINT er; |
| 624 | |
| 625 | WCHAR wzValue[32]; |
| 626 | DWORD cch = countof(wzValue) - 1; |
| 627 | |
| 628 | er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, wzValue, &cch); |
| 629 | ExitOnWin32Error(er, hr, "Failed to get data for property '%ls'", wzProperty); |
| 630 | |
| 631 | *piData = wcstol(wzValue, NULL, 10); |
| 632 | |
| 633 | LExit: |
| 634 | return hr; |
| 635 | } |
| 636 | |
| 637 | |
| 638 | /******************************************************************** |
| 639 | WcaGetTargetPath - gets the target path for a specified folder |
| 640 | |
| 641 | ********************************************************************/ |
| 642 | extern "C" HRESULT WIXAPI WcaGetTargetPath( |
| 643 | __in_z LPCWSTR wzFolder, |
| 644 | __out LPWSTR* ppwzData |
| 645 | ) |
| 646 | { |
| 647 | if (!wzFolder || !*wzFolder || !ppwzData) |
| 648 | return E_INVALIDARG; |
| 649 | |
| 650 | HRESULT hr = S_OK; |
| 651 | |
| 652 | UINT er = ERROR_SUCCESS; |
| 653 | DWORD cch = 0; |
| 654 | SIZE_T cchMax = 0; |
| 655 | |
| 656 | if (!*ppwzData) |
| 657 | { |
| 658 | WCHAR szEmpty[1] = L""; |
| 659 | er = ::MsiGetTargetPathW(WcaGetInstallHandle(), wzFolder, szEmpty, &cch); |
| 660 | if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er) |
| 661 | { |
| 662 | ++cch; //Add one for the null terminator |
| 663 | hr = StrAlloc(ppwzData, cch); |
| 664 | } |
| 665 | else |
| 666 | { |
| 667 | hr = HRESULT_FROM_WIN32(er); |
| 668 | } |
| 669 | ExitOnFailure(hr, "Failed to allocate string for target path of folder: '%ls'", wzFolder); |
| 670 | } |
| 671 | else |
| 672 | { |
| 673 | hr = StrMaxLength(*ppwzData, &cchMax); |
| 674 | ExitOnFailure(hr, "Failed to get previous size of string"); |
| 675 | |
| 676 | cch = (DWORD)min(MAXDWORD, cchMax); |
| 677 | } |
| 678 | |
| 679 | er = ::MsiGetTargetPathW(WcaGetInstallHandle(), wzFolder, *ppwzData, &cch); |
| 680 | if (ERROR_MORE_DATA == er) |
| 681 | { |
| 682 | ++cch; |
| 683 | hr = StrAlloc(ppwzData, cch); |
| 684 | ExitOnFailure(hr, "Failed to allocate string for target path of folder: '%ls'", wzFolder); |
| 685 | |
| 686 | er = ::MsiGetTargetPathW(WcaGetInstallHandle(), wzFolder, *ppwzData, &cch); |
| 687 | } |
| 688 | ExitOnWin32Error(er, hr, "Failed to get target path for folder '%ls'", wzFolder); |
| 689 | |
| 690 | LExit: |
| 691 | return hr; |
| 692 | } |
| 693 | |
| 694 | |
| 695 | /******************************************************************** |
| 696 | WcaSetProperty - sets a string property value in the active install |
| 697 | |
| 698 | ********************************************************************/ |
| 699 | extern "C" HRESULT WIXAPI WcaSetProperty( |
| 700 | __in_z LPCWSTR wzPropertyName, |
| 701 | __in_z LPCWSTR wzPropertyValue |
| 702 | ) |
| 703 | { |
| 704 | HRESULT hr = S_OK; |
| 705 | |
| 706 | if (!wzPropertyName || !*wzPropertyName || !wzPropertyValue) |
| 707 | return E_INVALIDARG; |
| 708 | |
| 709 | UINT er = ::MsiSetPropertyW(WcaGetInstallHandle(), wzPropertyName, wzPropertyValue); |
| 710 | ExitOnWin32Error(er, hr, "failed to set property: %ls", wzPropertyName); |
| 711 | |
| 712 | LExit: |
| 713 | return hr; |
| 714 | } |
| 715 | |
| 716 | |
| 717 | /******************************************************************** |
| 718 | WcaSetIntProperty - sets a integer property value in the active install |
| 719 | |
| 720 | ********************************************************************/ |
| 721 | extern "C" HRESULT WIXAPI WcaSetIntProperty( |
| 722 | __in_z LPCWSTR wzPropertyName, |
| 723 | __in int nPropertyValue |
| 724 | ) |
| 725 | { |
| 726 | if (!wzPropertyName || !*wzPropertyName) |
| 727 | return E_INVALIDARG; |
| 728 | |
| 729 | // 12 characters should be enough for a 32-bit int: 10 digits, 1 sign, 1 null |
| 730 | WCHAR wzPropertyValue[13]; |
| 731 | HRESULT hr = StringCchPrintfW(wzPropertyValue, countof(wzPropertyValue), L"%d", nPropertyValue); |
| 732 | ExitOnFailure(hr, "failed to convert into string property value: %d", nPropertyValue); |
| 733 | |
| 734 | UINT er = ::MsiSetPropertyW(WcaGetInstallHandle(), wzPropertyName, wzPropertyValue); |
| 735 | ExitOnWin32Error(er, hr, "failed to set property: %ls", wzPropertyName); |
| 736 | |
| 737 | LExit: |
| 738 | return hr; |
| 739 | } |
| 740 | |
| 741 | |
| 742 | /******************************************************************** |
| 743 | WcaIsPropertySet() - returns TRUE if property is set |
| 744 | |
| 745 | ********************************************************************/ |
| 746 | extern "C" BOOL WIXAPI WcaIsPropertySet( |
| 747 | __in LPCSTR szProperty |
| 748 | ) |
| 749 | { |
| 750 | DWORD cchProperty = 0; |
| 751 | char szEmpty[1] = ""; |
| 752 | #ifdef DEBUG |
| 753 | UINT er = |
| 754 | #endif |
| 755 | ::MsiGetPropertyA(WcaGetInstallHandle(), szProperty, szEmpty, &cchProperty); |
| 756 | AssertSz(ERROR_INVALID_PARAMETER != er && ERROR_INVALID_HANDLE != er, "Unexpected return value from ::MsiGetProperty()"); |
| 757 | |
| 758 | return 0 < cchProperty; // property is set if the length is greater than zero |
| 759 | } |
| 760 | |
| 761 | |
| 762 | /******************************************************************** |
| 763 | WcaIsUnicodePropertySet() - returns TRUE if property is set |
| 764 | |
| 765 | ********************************************************************/ |
| 766 | extern "C" BOOL WIXAPI WcaIsUnicodePropertySet( |
| 767 | __in LPCWSTR wzProperty |
| 768 | ) |
| 769 | { |
| 770 | DWORD cchProperty = 0; |
| 771 | wchar_t wzEmpty[1] = L""; |
| 772 | #ifdef DEBUG |
| 773 | UINT er = |
| 774 | #endif |
| 775 | ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, wzEmpty, &cchProperty); |
| 776 | AssertSz(ERROR_INVALID_PARAMETER != er && ERROR_INVALID_HANDLE != er, "Unexpected return value from ::MsiGetProperty()"); |
| 777 | |
| 778 | return 0 < cchProperty; // property is set if the length is greater than zero |
| 779 | } |
| 780 | |
| 781 | |
| 782 | /******************************************************************** |
| 783 | WcaGetRecordInteger() - gets an integer field out of a record |
| 784 | |
| 785 | NOTE: returns S_FALSE if the field was null |
| 786 | ********************************************************************/ |
| 787 | extern "C" HRESULT WIXAPI WcaGetRecordInteger( |
| 788 | __in MSIHANDLE hRec, |
| 789 | __in UINT uiField, |
| 790 | __inout int* piData |
| 791 | ) |
| 792 | { |
| 793 | if (!hRec || !piData) |
| 794 | return E_INVALIDARG; |
| 795 | |
| 796 | HRESULT hr = S_OK; |
| 797 | *piData = ::MsiRecordGetInteger(hRec, uiField); |
| 798 | if (MSI_NULL_INTEGER == *piData) |
| 799 | hr = S_FALSE; |
| 800 | |
| 801 | //LExit: |
| 802 | return hr; |
| 803 | } |
| 804 | |
| 805 | |
| 806 | /******************************************************************** |
| 807 | WcaGetRecordString() - gets a string field out of a record |
| 808 | |
| 809 | ********************************************************************/ |
| 810 | extern "C" HRESULT WIXAPI WcaGetRecordString( |
| 811 | __in MSIHANDLE hRec, |
| 812 | __in UINT uiField, |
| 813 | __inout LPWSTR* ppwzData |
| 814 | ) |
| 815 | { |
| 816 | if (!hRec || !ppwzData) |
| 817 | return E_INVALIDARG; |
| 818 | |
| 819 | HRESULT hr = S_OK; |
| 820 | UINT er; |
| 821 | DWORD cch = 0; |
| 822 | SIZE_T cchMax = 0; |
| 823 | |
| 824 | if (!*ppwzData) |
| 825 | { |
| 826 | WCHAR szEmpty[1] = L""; |
| 827 | er = ::MsiRecordGetStringW(hRec, uiField, szEmpty, &cch); |
| 828 | if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er) |
| 829 | { |
| 830 | hr = StrAlloc(ppwzData, ++cch); |
| 831 | } |
| 832 | else |
| 833 | { |
| 834 | hr = HRESULT_FROM_WIN32(er); |
| 835 | } |
| 836 | ExitOnFailure(hr, "Failed to allocate memory for record string"); |
| 837 | } |
| 838 | else |
| 839 | { |
| 840 | hr = StrMaxLength(*ppwzData, &cchMax); |
| 841 | ExitOnFailure(hr, "Failed to get previous size of string"); |
| 842 | |
| 843 | cch = (DWORD)min(MAXDWORD, cchMax); |
| 844 | } |
| 845 | |
| 846 | er = ::MsiRecordGetStringW(hRec, uiField, *ppwzData, &cch); |
| 847 | if (ERROR_MORE_DATA == er) |
| 848 | { |
| 849 | hr = StrAlloc(ppwzData, ++cch); |
| 850 | ExitOnFailure(hr, "Failed to allocate memory for record string"); |
| 851 | |
| 852 | er = ::MsiRecordGetStringW(hRec, uiField, *ppwzData, &cch); |
| 853 | } |
| 854 | ExitOnWin32Error(er, hr, "Failed to get string from record"); |
| 855 | |
| 856 | LExit: |
| 857 | return hr; |
| 858 | } |
| 859 | |
| 860 | |
| 861 | /******************************************************************** |
| 862 | HideNulls() - internal helper function to escape [~] in formatted strings |
| 863 | |
| 864 | ********************************************************************/ |
| 865 | static void HideNulls( |
| 866 | __inout_z LPWSTR wzData |
| 867 | ) |
| 868 | { |
| 869 | LPWSTR pwz = wzData; |
| 870 | |
| 871 | while(*pwz) |
| 872 | { |
| 873 | if (pwz[0] == L'[' && pwz[1] == L'~' && pwz[2] == L']') // found a null [~] |
| 874 | { |
| 875 | pwz[0] = L'!'; // turn it into !$! |
| 876 | pwz[1] = L'$'; |
| 877 | pwz[2] = L'!'; |
| 878 | pwz += 3; |
| 879 | } |
| 880 | else |
| 881 | { |
| 882 | ++pwz; |
| 883 | } |
| 884 | } |
| 885 | } |
| 886 | |
| 887 | |
| 888 | /******************************************************************** |
| 889 | RevealNulls() - internal helper function to unescape !$! in formatted strings |
| 890 | |
| 891 | ********************************************************************/ |
| 892 | static void RevealNulls( |
| 893 | __inout_z LPWSTR wzData |
| 894 | ) |
| 895 | { |
| 896 | LPWSTR pwz = wzData; |
| 897 | |
| 898 | while(*pwz) |
| 899 | { |
| 900 | if (pwz[0] == L'!' && pwz[1] == L'$' && pwz[2] == L'!') // found the fake null !$! |
| 901 | { |
| 902 | pwz[0] = L'['; // turn it back into [~] |
| 903 | pwz[1] = L'~'; |
| 904 | pwz[2] = L']'; |
| 905 | pwz += 3; |
| 906 | } |
| 907 | else |
| 908 | { |
| 909 | ++pwz; |
| 910 | } |
| 911 | } |
| 912 | } |
| 913 | |
| 914 | |
| 915 | /******************************************************************** |
| 916 | WcaGetRecordFormattedString() - gets formatted string field from record |
| 917 | |
| 918 | ********************************************************************/ |
| 919 | extern "C" HRESULT WIXAPI WcaGetRecordFormattedString( |
| 920 | __in MSIHANDLE hRec, |
| 921 | __in UINT uiField, |
| 922 | __inout LPWSTR* ppwzData |
| 923 | ) |
| 924 | { |
| 925 | if (!hRec || !ppwzData) |
| 926 | { |
| 927 | return E_INVALIDARG; |
| 928 | } |
| 929 | |
| 930 | HRESULT hr = S_OK; |
| 931 | UINT er; |
| 932 | DWORD cch = 0; |
| 933 | SIZE_T cchMax = 0; |
| 934 | PMSIHANDLE hRecFormat; |
| 935 | |
| 936 | // get the format string |
| 937 | hr = WcaGetRecordString(hRec, uiField, ppwzData); |
| 938 | ExitOnFailure(hr, "failed to get string from record"); |
| 939 | |
| 940 | if (!**ppwzData) |
| 941 | { |
| 942 | ExitFunction(); |
| 943 | } |
| 944 | |
| 945 | // hide the nulls '[~]' so we can get them back after formatting |
| 946 | HideNulls(*ppwzData); |
| 947 | |
| 948 | // set up the format record |
| 949 | hRecFormat = ::MsiCreateRecord(1); |
| 950 | ExitOnNull(hRecFormat, hr, E_UNEXPECTED, "Failed to create record to format string"); |
| 951 | hr = WcaSetRecordString(hRecFormat, 0, *ppwzData); |
| 952 | ExitOnFailure(hr, "failed to set string to format record"); |
| 953 | |
| 954 | // format the string |
| 955 | hr = StrMaxLength(*ppwzData, &cchMax); |
| 956 | ExitOnFailure(hr, "failed to get max length of string"); |
| 957 | |
| 958 | cch = (DWORD)min(MAXDWORD, cchMax); |
| 959 | |
| 960 | er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecFormat, *ppwzData, &cch); |
| 961 | if (ERROR_MORE_DATA == er) |
| 962 | { |
| 963 | hr = StrAlloc(ppwzData, ++cch); |
| 964 | ExitOnFailure(hr, "Failed to allocate memory for record string"); |
| 965 | |
| 966 | er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecFormat, *ppwzData, &cch); |
| 967 | } |
| 968 | ExitOnWin32Error(er, hr, "Failed to format string"); |
| 969 | |
| 970 | // put the nulls back |
| 971 | RevealNulls(*ppwzData); |
| 972 | |
| 973 | LExit: |
| 974 | return hr; |
| 975 | } |
| 976 | |
| 977 | |
| 978 | /******************************************************************** |
| 979 | WcaGetRecordFormattedInteger() - gets formatted integer from record |
| 980 | |
| 981 | ********************************************************************/ |
| 982 | extern "C" HRESULT WIXAPI WcaGetRecordFormattedInteger( |
| 983 | __in MSIHANDLE hRec, |
| 984 | __in UINT uiField, |
| 985 | __out int* piData |
| 986 | ) |
| 987 | { |
| 988 | if (!hRec || !piData) |
| 989 | { |
| 990 | return E_INVALIDARG; |
| 991 | } |
| 992 | |
| 993 | HRESULT hr = S_OK; |
| 994 | LPWSTR pwzData = NULL; |
| 995 | |
| 996 | hr = WcaGetRecordFormattedString(hRec, uiField, &pwzData); |
| 997 | ExitOnFailure(hr, "failed to get record field: %u", uiField); |
| 998 | if (pwzData && *pwzData) |
| 999 | { |
| 1000 | LPWSTR wz = NULL; |
| 1001 | *piData = wcstol(pwzData, &wz, 10); |
| 1002 | if (wz && *wz) |
| 1003 | { |
| 1004 | hr = E_INVALIDARG; |
| 1005 | ExitOnFailure(hr, "failed to parse record field: %u as number: %ls", uiField, pwzData); |
| 1006 | } |
| 1007 | } |
| 1008 | else |
| 1009 | { |
| 1010 | *piData = MSI_NULL_INTEGER; |
| 1011 | } |
| 1012 | |
| 1013 | LExit: |
| 1014 | return hr; |
| 1015 | } |
| 1016 | |
| 1017 | |
| 1018 | /******************************************************************** |
| 1019 | WcaAllocStream() - creates a byte stream of the specified size |
| 1020 | |
| 1021 | NOTE: Use WcaFreeStream() to release the byte stream |
| 1022 | ********************************************************************/ |
| 1023 | extern "C" HRESULT WIXAPI WcaAllocStream( |
| 1024 | __deref_out_bcount_part(cbData, 0) BYTE** ppbData, |
| 1025 | __in SIZE_T cbData |
| 1026 | ) |
| 1027 | { |
| 1028 | Assert(ppbData); |
| 1029 | HRESULT hr; |
| 1030 | BYTE* pbNewData; |
| 1031 | |
| 1032 | if (*ppbData) |
| 1033 | pbNewData = static_cast<BYTE*>(MemReAlloc(*ppbData, cbData, TRUE)); |
| 1034 | else |
| 1035 | pbNewData = static_cast<BYTE*>(MemAlloc(cbData, TRUE)); |
| 1036 | |
| 1037 | if (!pbNewData) |
| 1038 | { |
| 1039 | ExitOnLastError(hr, "Failed to allocate string"); |
| 1040 | } |
| 1041 | |
| 1042 | *ppbData = pbNewData; |
| 1043 | pbNewData = NULL; |
| 1044 | |
| 1045 | hr = S_OK; |
| 1046 | LExit: |
| 1047 | ReleaseMem(pbNewData); |
| 1048 | |
| 1049 | return hr; |
| 1050 | } |
| 1051 | |
| 1052 | |
| 1053 | /******************************************************************** |
| 1054 | WcaFreeStream() - frees a byte stream |
| 1055 | |
| 1056 | ********************************************************************/ |
| 1057 | extern "C" HRESULT WIXAPI WcaFreeStream( |
| 1058 | __in BYTE* pbData |
| 1059 | ) |
| 1060 | { |
| 1061 | if (!pbData) |
| 1062 | return E_INVALIDARG; |
| 1063 | |
| 1064 | HRESULT hr = MemFree(pbData); |
| 1065 | return hr; |
| 1066 | } |
| 1067 | |
| 1068 | |
| 1069 | /******************************************************************** |
| 1070 | WcaGetRecordStream() - gets a byte stream field from record |
| 1071 | |
| 1072 | ********************************************************************/ |
| 1073 | extern "C" HRESULT WIXAPI WcaGetRecordStream( |
| 1074 | __in MSIHANDLE hRecBinary, |
| 1075 | __in UINT uiField, |
| 1076 | __deref_out_bcount_full(*pcbData) BYTE** ppbData, |
| 1077 | __out DWORD* pcbData |
| 1078 | ) |
| 1079 | { |
| 1080 | HRESULT hr = S_OK; |
| 1081 | UINT er = ERROR_SUCCESS; |
| 1082 | |
| 1083 | if (!hRecBinary || !ppbData || !pcbData) |
| 1084 | return E_INVALIDARG; |
| 1085 | |
| 1086 | *pcbData = 0; |
| 1087 | er = ::MsiRecordReadStream(hRecBinary, uiField, NULL, pcbData); |
| 1088 | ExitOnWin32Error(er, hr, "failed to get size of stream"); |
| 1089 | |
| 1090 | hr = WcaAllocStream(ppbData, *pcbData); |
| 1091 | ExitOnFailure(hr, "failed to allocate data for stream"); |
| 1092 | |
| 1093 | er = ::MsiRecordReadStream(hRecBinary, uiField, (char*)*ppbData, pcbData); |
| 1094 | ExitOnWin32Error(er, hr, "failed to read from stream"); |
| 1095 | |
| 1096 | LExit: |
| 1097 | return hr; |
| 1098 | } |
| 1099 | |
| 1100 | |
| 1101 | /******************************************************************** |
| 1102 | WcaSetRecordString() - set a string field in record |
| 1103 | |
| 1104 | ********************************************************************/ |
| 1105 | extern "C" HRESULT WIXAPI WcaSetRecordString( |
| 1106 | __in MSIHANDLE hRec, |
| 1107 | __in UINT uiField, |
| 1108 | __in_z LPCWSTR wzData |
| 1109 | ) |
| 1110 | { |
| 1111 | if (!hRec || !wzData) |
| 1112 | return E_INVALIDARG; |
| 1113 | |
| 1114 | HRESULT hr = S_OK; |
| 1115 | UINT er = ::MsiRecordSetStringW(hRec, uiField, wzData); |
| 1116 | ExitOnWin32Error(er, hr, "failed to set string in record"); |
| 1117 | |
| 1118 | LExit: |
| 1119 | return hr; |
| 1120 | } |
| 1121 | |
| 1122 | |
| 1123 | /******************************************************************** |
| 1124 | WcaSetRecordInteger() - set a integer field in record |
| 1125 | |
| 1126 | ********************************************************************/ |
| 1127 | extern "C" HRESULT WIXAPI WcaSetRecordInteger( |
| 1128 | __in MSIHANDLE hRec, |
| 1129 | __in UINT uiField, |
| 1130 | __in int iValue |
| 1131 | ) |
| 1132 | { |
| 1133 | if (!hRec) |
| 1134 | return E_INVALIDARG; |
| 1135 | |
| 1136 | HRESULT hr = S_OK; |
| 1137 | UINT er = ::MsiRecordSetInteger(hRec, uiField, iValue); |
| 1138 | ExitOnWin32Error(er, hr, "failed to set integer in record"); |
| 1139 | |
| 1140 | LExit: |
| 1141 | return hr; |
| 1142 | } |
| 1143 | |
| 1144 | |
| 1145 | /******************************************************************** |
| 1146 | |
| 1147 | WcaDoDeferredAction() - schedules an action at this point in the script |
| 1148 | |
| 1149 | ********************************************************************/ |
| 1150 | extern "C" HRESULT WIXAPI WcaDoDeferredAction( |
| 1151 | __in_z LPCWSTR wzAction, |
| 1152 | __in_z LPCWSTR wzCustomActionData, |
| 1153 | __in UINT uiCost |
| 1154 | ) |
| 1155 | { |
| 1156 | HRESULT hr = S_OK; |
| 1157 | UINT er; |
| 1158 | |
| 1159 | if (wzCustomActionData && *wzCustomActionData) |
| 1160 | { |
| 1161 | er = ::MsiSetPropertyW(WcaGetInstallHandle(), wzAction, wzCustomActionData); |
| 1162 | ExitOnWin32Error(er, hr, "Failed to set CustomActionData for deferred action"); |
| 1163 | } |
| 1164 | |
| 1165 | if (0 < uiCost) |
| 1166 | { |
| 1167 | hr = WcaProgressMessage(uiCost, TRUE); // add ticks to the progress bar |
| 1168 | // TODO: handle the return codes correctly |
| 1169 | } |
| 1170 | |
| 1171 | er = ::MsiDoActionW(WcaGetInstallHandle(), wzAction); |
| 1172 | if (ERROR_INSTALL_USEREXIT == er) |
| 1173 | { |
| 1174 | WcaSetReturnValue(er); |
| 1175 | } |
| 1176 | ExitOnWin32Error(er, hr, "Failed MsiDoAction on deferred action"); |
| 1177 | |
| 1178 | LExit: |
| 1179 | return hr; |
| 1180 | } |
| 1181 | |
| 1182 | |
| 1183 | /******************************************************************** |
| 1184 | WcaCountOfCustomActionDataRecords() - counts the number of records |
| 1185 | passed to a deferred CustomAction |
| 1186 | |
| 1187 | ********************************************************************/ |
| 1188 | extern "C" DWORD WIXAPI WcaCountOfCustomActionDataRecords( |
| 1189 | __in_z LPCWSTR wzData |
| 1190 | ) |
| 1191 | { |
| 1192 | WCHAR delim[] = {MAGIC_MULTISZ_DELIM, 0}; // magic char followed by NULL terminator |
| 1193 | DWORD dwCount = 0; |
| 1194 | |
| 1195 | // Loop through until there are no delimiters, we are at the end of the string, or the delimiter is the last character in the string |
| 1196 | for (LPCWSTR pwzCurrent = wzData; pwzCurrent && *pwzCurrent && *(pwzCurrent + 1); pwzCurrent = wcsstr(pwzCurrent, delim)) |
| 1197 | { |
| 1198 | ++dwCount; |
| 1199 | ++pwzCurrent; |
| 1200 | } |
| 1201 | |
| 1202 | return dwCount; |
| 1203 | } |
| 1204 | |
| 1205 | |
| 1206 | /******************************************************************** |
| 1207 | BreakDownCustomActionData() - internal helper to chop up CustomActionData |
| 1208 | |
| 1209 | NOTE: this modifies the passed in data |
| 1210 | ********************************************************************/ |
| 1211 | static LPWSTR BreakDownCustomActionData( |
| 1212 | __inout LPWSTR* ppwzData |
| 1213 | ) |
| 1214 | { |
| 1215 | if (!ppwzData) |
| 1216 | return NULL; |
| 1217 | if (0 == *ppwzData) |
| 1218 | return NULL; |
| 1219 | |
| 1220 | WCHAR delim[] = {MAGIC_MULTISZ_DELIM, 0}; // magic char followed by Null terminator |
| 1221 | |
| 1222 | LPWSTR pwzReturn = *ppwzData; |
| 1223 | LPWSTR pwz = wcsstr(pwzReturn, delim); |
| 1224 | if (pwz) |
| 1225 | { |
| 1226 | *pwz = 0; |
| 1227 | *ppwzData = pwz + 1; |
| 1228 | } |
| 1229 | else |
| 1230 | *ppwzData = 0; |
| 1231 | |
| 1232 | return pwzReturn; |
| 1233 | } |
| 1234 | |
| 1235 | |
| 1236 | /******************************************************************** |
| 1237 | RevertCustomActionData() - Reverts custom action data changes made |
| 1238 | by BreakDownCustomActionData; |
| 1239 | |
| 1240 | NOTE: this modifies the passed in data |
| 1241 | ********************************************************************/ |
| 1242 | extern "C" void WIXAPI RevertCustomActionData( |
| 1243 | __in LPWSTR wzRevertTo, |
| 1244 | __in LPCWSTR wzRevertFrom |
| 1245 | ) |
| 1246 | { |
| 1247 | if (!wzRevertTo) |
| 1248 | return; |
| 1249 | if (!wzRevertFrom) |
| 1250 | return; |
| 1251 | // start at the revert point and replace all \0 with MAGIC_MULTISZ_DELIM |
| 1252 | for(LPWSTR wzIndex = wzRevertTo; wzIndex < wzRevertFrom; wzIndex++) |
| 1253 | { |
| 1254 | if (0 == *wzIndex) |
| 1255 | { |
| 1256 | *wzIndex = MAGIC_MULTISZ_DELIM; |
| 1257 | } |
| 1258 | } |
| 1259 | return; |
| 1260 | } |
| 1261 | |
| 1262 | /******************************************************************** |
| 1263 | WcaReadStringFromCaData() - reads a string out of the CustomActionData |
| 1264 | |
| 1265 | NOTE: this modifies the passed in ppwzCustomActionData variable |
| 1266 | ********************************************************************/ |
| 1267 | extern "C" HRESULT WIXAPI WcaReadStringFromCaData( |
| 1268 | __deref_in LPWSTR* ppwzCustomActionData, |
| 1269 | __deref_out_z LPWSTR* ppwzString |
| 1270 | ) |
| 1271 | { |
| 1272 | HRESULT hr = S_OK; |
| 1273 | |
| 1274 | LPCWSTR pwz = BreakDownCustomActionData(ppwzCustomActionData); |
| 1275 | if (!pwz) |
| 1276 | return E_NOMOREITEMS; |
| 1277 | |
| 1278 | hr = StrAllocString(ppwzString, pwz, 0); |
| 1279 | ExitOnFailure(hr, "failed to allocate memory for string"); |
| 1280 | |
| 1281 | hr = S_OK; |
| 1282 | LExit: |
| 1283 | return hr; |
| 1284 | } |
| 1285 | |
| 1286 | |
| 1287 | /******************************************************************** |
| 1288 | WcaReadIntegerFromCaData() - reads an integer out of the CustomActionData |
| 1289 | |
| 1290 | NOTE: this modifies the passed in ppwzCustomActionData variable |
| 1291 | ********************************************************************/ |
| 1292 | extern "C" HRESULT WIXAPI WcaReadIntegerFromCaData( |
| 1293 | __deref_in LPWSTR* ppwzCustomActionData, |
| 1294 | __out int* piResult |
| 1295 | ) |
| 1296 | { |
| 1297 | LPCWSTR pwz = BreakDownCustomActionData(ppwzCustomActionData); |
| 1298 | if (!pwz || !*pwz) |
| 1299 | return E_NOMOREITEMS; |
| 1300 | |
| 1301 | *piResult = wcstol(pwz, NULL, 10); |
| 1302 | return S_OK; |
| 1303 | } |
| 1304 | |
| 1305 | |
| 1306 | /******************************************************************** |
| 1307 | WcaReadStreamFromCaData() - reads a stream out of the CustomActionData |
| 1308 | |
| 1309 | NOTE: this modifies the passed in ppwzCustomActionData variable |
| 1310 | NOTE: returned stream should be freed with WcaFreeStream() |
| 1311 | ********************************************************************/ |
| 1312 | extern "C" HRESULT WIXAPI WcaReadStreamFromCaData( |
| 1313 | __deref_in LPWSTR* ppwzCustomActionData, |
| 1314 | __deref_out_bcount(*pcbData) BYTE** ppbData, |
| 1315 | __out DWORD_PTR* pcbData |
| 1316 | ) |
| 1317 | { |
| 1318 | HRESULT hr; |
| 1319 | |
| 1320 | LPCWSTR pwz = BreakDownCustomActionData(ppwzCustomActionData); |
| 1321 | if (!pwz) |
| 1322 | return E_NOMOREITEMS; |
| 1323 | |
| 1324 | hr = StrAllocBase85Decode(pwz, ppbData, pcbData); |
| 1325 | ExitOnFailure(hr, "failed to decode string into stream"); |
| 1326 | |
| 1327 | LExit: |
| 1328 | return hr; |
| 1329 | } |
| 1330 | |
| 1331 | |
| 1332 | /******************************************************************** |
| 1333 | WcaWriteStringToCaData() - adds a string to the CustomActionData to |
| 1334 | feed a deferred CustomAction |
| 1335 | |
| 1336 | ********************************************************************/ |
| 1337 | extern "C" HRESULT WIXAPI WcaWriteStringToCaData( |
| 1338 | __in_z LPCWSTR wzString, |
| 1339 | __deref_inout_z_opt LPWSTR* ppwzCustomActionData |
| 1340 | ) |
| 1341 | { |
| 1342 | HRESULT hr = S_OK; |
| 1343 | WCHAR delim[] = {MAGIC_MULTISZ_DELIM, 0}; // magic char followed by NULL terminator |
| 1344 | SIZE_T cchString = 0; |
| 1345 | SIZE_T cchCustomActionData = 0; |
| 1346 | SIZE_T cchMax = 0; |
| 1347 | |
| 1348 | if (!ppwzCustomActionData) |
| 1349 | { |
| 1350 | ExitFunction1(hr = E_INVALIDARG); |
| 1351 | } |
| 1352 | |
| 1353 | hr = ::StringCchLengthW(wzString, STRSAFE_MAX_LENGTH, reinterpret_cast<size_t*>(&cchString)); |
| 1354 | ExitOnRootFailure(hr, "failed to get length of ca data string"); |
| 1355 | |
| 1356 | ++cchString; // assume we'll be adding the delim |
| 1357 | |
| 1358 | if (*ppwzCustomActionData) |
| 1359 | { |
| 1360 | hr = StrMaxLength(*ppwzCustomActionData, &cchCustomActionData); |
| 1361 | ExitOnFailure(hr, "failed to get max length of custom action data"); |
| 1362 | |
| 1363 | hr = ::StringCchLengthW(*ppwzCustomActionData, STRSAFE_MAX_LENGTH, reinterpret_cast<size_t*>(&cchMax)); |
| 1364 | ExitOnRootFailure(hr, "failed to get length of custom action data"); |
| 1365 | } |
| 1366 | |
| 1367 | if ((cchCustomActionData - cchMax) < cchString + 1) |
| 1368 | { |
| 1369 | cchCustomActionData += cchString + 1 + 255; // add 255 for good measure |
| 1370 | cchCustomActionData = min(STRSAFE_MAX_LENGTH, cchCustomActionData); |
| 1371 | |
| 1372 | hr = StrAlloc(ppwzCustomActionData, cchCustomActionData); |
| 1373 | ExitOnFailure(hr, "Failed to allocate memory for CustomActionData string"); |
| 1374 | } |
| 1375 | |
| 1376 | if (**ppwzCustomActionData) // if data exists toss the delimiter on before adding more to the end |
| 1377 | { |
| 1378 | hr = ::StringCchCatW(*ppwzCustomActionData, cchCustomActionData, delim); |
| 1379 | ExitOnRootFailure(hr, "Failed to concatenate CustomActionData string"); |
| 1380 | } |
| 1381 | |
| 1382 | hr = ::StringCchCatW(*ppwzCustomActionData, cchCustomActionData, wzString); |
| 1383 | ExitOnRootFailure(hr, "Failed to concatenate CustomActionData string"); |
| 1384 | |
| 1385 | LExit: |
| 1386 | return hr; |
| 1387 | } |
| 1388 | |
| 1389 | |
| 1390 | /******************************************************************** |
| 1391 | WcaWriteIntegerToCaData() - adds an integer to the CustomActionData to |
| 1392 | feed a deferred CustomAction |
| 1393 | |
| 1394 | ********************************************************************/ |
| 1395 | extern "C" HRESULT WIXAPI WcaWriteIntegerToCaData( |
| 1396 | __in int i, |
| 1397 | __deref_out_z_opt LPWSTR* ppwzCustomActionData |
| 1398 | ) |
| 1399 | { |
| 1400 | WCHAR wzBuffer[13]; |
| 1401 | HRESULT hr = StringCchPrintfW(wzBuffer, countof(wzBuffer), L"%d", i); |
| 1402 | ExitOnFailure(hr, "failed to write integer to ca data"); |
| 1403 | |
| 1404 | hr = WcaWriteStringToCaData(wzBuffer, ppwzCustomActionData); |
| 1405 | ExitOnFailure(hr, "failed to write integer to ca data"); |
| 1406 | |
| 1407 | LExit: |
| 1408 | return hr; |
| 1409 | } |
| 1410 | |
| 1411 | |
| 1412 | /******************************************************************** |
| 1413 | WcaWriteStreamToCaData() - adds a byte stream to the CustomActionData to |
| 1414 | feed a deferred CustomAction |
| 1415 | |
| 1416 | ********************************************************************/ |
| 1417 | extern "C" HRESULT WIXAPI WcaWriteStreamToCaData( |
| 1418 | __in_bcount(cbData) const BYTE* pbData, |
| 1419 | __in SIZE_T cbData, |
| 1420 | __deref_inout_z_opt LPWSTR* ppwzCustomActionData |
| 1421 | ) |
| 1422 | { |
| 1423 | HRESULT hr; |
| 1424 | LPWSTR pwzData = NULL; |
| 1425 | |
| 1426 | hr = StrAllocBase85Encode(pbData, cbData, &pwzData); |
| 1427 | ExitOnFailure(hr, "failed to encode data into string"); |
| 1428 | |
| 1429 | hr = WcaWriteStringToCaData(pwzData, ppwzCustomActionData); |
| 1430 | |
| 1431 | LExit: |
| 1432 | ReleaseStr(pwzData); |
| 1433 | return hr; |
| 1434 | } |
| 1435 | |
| 1436 | |
| 1437 | /******************************************************************** |
| 1438 | WcaAddTempRecord - adds a temporary record to the active database |
| 1439 | |
| 1440 | NOTE: you cannot use PMSIHANDLEs for the __in/__out parameters |
| 1441 | NOTE: uiUniquifyColumn can be 0 if no column needs to be made unique |
| 1442 | ********************************************************************/ |
| 1443 | extern "C" HRESULT __cdecl WcaAddTempRecord( |
| 1444 | __inout MSIHANDLE* phTableView, |
| 1445 | __inout MSIHANDLE* phColumns, |
| 1446 | __in_z LPCWSTR wzTable, |
| 1447 | __out_opt MSIDBERROR* pdbError, |
| 1448 | __in UINT uiUniquifyColumn, |
| 1449 | __in UINT cColumns, |
| 1450 | ... |
| 1451 | ) |
| 1452 | { |
| 1453 | Assert(phTableView && phColumns); |
| 1454 | |
| 1455 | static DWORD dwUniquifyValue = ::GetTickCount(); |
| 1456 | |
| 1457 | HRESULT hr = S_OK; |
| 1458 | UINT er = ERROR_SUCCESS; |
| 1459 | |
| 1460 | LPWSTR pwzQuery = NULL; |
| 1461 | PMSIHANDLE hTempRec; |
| 1462 | DWORD i; |
| 1463 | va_list args; |
| 1464 | |
| 1465 | LPWSTR pwzData = NULL; |
| 1466 | LPWSTR pwzUniquify = NULL; |
| 1467 | |
| 1468 | // |
| 1469 | // if we don't have a table and its columns already |
| 1470 | // |
| 1471 | if (NULL == *phTableView) |
| 1472 | { |
| 1473 | // set the query |
| 1474 | hr = StrAllocFormatted(&pwzQuery, L"SELECT * FROM `%s`",wzTable); |
| 1475 | ExitOnFailure(hr, "failed to allocate string for query"); |
| 1476 | |
| 1477 | // Open and Execute the temp View |
| 1478 | hr = WcaOpenExecuteView(pwzQuery, phTableView); |
| 1479 | ExitOnFailure(hr, "failed to openexecute temp view with query %ls", pwzQuery); |
| 1480 | } |
| 1481 | |
| 1482 | if (NULL == *phColumns) |
| 1483 | { |
| 1484 | // use GetColumnInfo to populate the datatype record |
| 1485 | er = ::MsiViewGetColumnInfo(*phTableView, MSICOLINFO_TYPES, phColumns); |
| 1486 | ExitOnWin32Error(er, hr, "failed to columns for table: %ls", wzTable); |
| 1487 | } |
| 1488 | AssertSz(::MsiRecordGetFieldCount(*phColumns) == cColumns, "passed in argument does not match number of columns in table"); |
| 1489 | |
| 1490 | // |
| 1491 | // create the temp record |
| 1492 | // |
| 1493 | hTempRec = ::MsiCreateRecord(cColumns); |
| 1494 | ExitOnNull(hTempRec, hr, E_UNEXPECTED, "could not create temp record for table: %ls", wzTable); |
| 1495 | |
| 1496 | // |
| 1497 | // loop through all the columns filling in the data |
| 1498 | // |
| 1499 | va_start(args, cColumns); |
| 1500 | for (i = 1; i <= cColumns; i++) |
| 1501 | { |
| 1502 | hr = WcaGetRecordString(*phColumns, i, &pwzData); |
| 1503 | ExitOnFailure(hr, "failed to get the data type for %d", i); |
| 1504 | |
| 1505 | // if data type is string write string |
| 1506 | if (L's' == *pwzData || L'S' == *pwzData || L'g' == *pwzData || L'G' == *pwzData || L'l' == *pwzData || L'L' == *pwzData) |
| 1507 | { |
| 1508 | LPCWSTR wz = va_arg(args, WCHAR*); |
| 1509 | |
| 1510 | // if this is the column that is supposed to be unique add the time stamp on the end |
| 1511 | if (uiUniquifyColumn == i) |
| 1512 | { |
| 1513 | hr = StrAllocFormatted(&pwzUniquify, L"%s%u", wz, ++dwUniquifyValue); // up the count so we have no collisions on the unique name |
| 1514 | ExitOnFailure(hr, "failed to allocate string for unique column: %d", uiUniquifyColumn); |
| 1515 | |
| 1516 | wz = pwzUniquify; |
| 1517 | } |
| 1518 | |
| 1519 | er = ::MsiRecordSetStringW(hTempRec, i, wz); |
| 1520 | ExitOnWin32Error(er, hr, "failed to set string value at position %d", i); |
| 1521 | } |
| 1522 | // if data type is integer write integer |
| 1523 | else if (L'i' == *pwzData || L'I' == *pwzData || L'j' == *pwzData || L'J' == *pwzData) |
| 1524 | { |
| 1525 | AssertSz(uiUniquifyColumn != i, "Cannot uniquify an integer column"); |
| 1526 | int iData = va_arg(args, int); |
| 1527 | |
| 1528 | er = ::MsiRecordSetInteger(hTempRec, i, iData); |
| 1529 | ExitOnWin32Error(er, hr, "failed to set integer value at position %d", i); |
| 1530 | } |
| 1531 | else |
| 1532 | { |
| 1533 | // not supporting binary streams so error out |
| 1534 | hr = HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH); |
| 1535 | ExitOnRootFailure(hr, "unsupported data type '%ls' in column: %d", pwzData, i); |
| 1536 | } |
| 1537 | } |
| 1538 | va_end(args); |
| 1539 | |
| 1540 | // |
| 1541 | // add the temporary record to the MSI |
| 1542 | // |
| 1543 | er = ::MsiViewModify(*phTableView, MSIMODIFY_INSERT_TEMPORARY, hTempRec); |
| 1544 | hr = HRESULT_FROM_WIN32(er); |
| 1545 | if (FAILED(hr)) |
| 1546 | { |
| 1547 | if (pdbError) |
| 1548 | { |
| 1549 | // MSI provides only a generic ERROR_FUNCTION_FAILED if a temporary row |
| 1550 | // can't be inserted; if we're being asked to provide the detailed error, |
| 1551 | // get it using the MSIMODIFY_VALIDATE_NEW flag |
| 1552 | er = ::MsiViewModify(*phTableView, MSIMODIFY_VALIDATE_NEW, hTempRec); |
| 1553 | hr = HRESULT_FROM_WIN32(er); |
| 1554 | } |
| 1555 | |
| 1556 | WCHAR wzBuf[MAX_PATH]; |
| 1557 | DWORD cchBuf = countof(wzBuf); |
| 1558 | MSIDBERROR dbErr = ::MsiViewGetErrorW(*phTableView, wzBuf, &cchBuf); |
| 1559 | if (pdbError) |
| 1560 | { |
| 1561 | *pdbError = dbErr; |
| 1562 | } |
| 1563 | ExitOnFailure(hr, "failed to add temporary row, dberr: %d, err: %ls", dbErr, wzBuf); |
| 1564 | } |
| 1565 | |
| 1566 | LExit: |
| 1567 | ReleaseStr(pwzUniquify); |
| 1568 | ReleaseStr(pwzData); |
| 1569 | ReleaseStr(pwzQuery); |
| 1570 | |
| 1571 | return hr; |
| 1572 | } |
| 1573 | |
| 1574 | |
| 1575 | /******************************************************************** |
| 1576 | WcaDumpTable - dumps a table to the log file |
| 1577 | |
| 1578 | ********************************************************************/ |
| 1579 | extern "C" HRESULT WIXAPI WcaDumpTable( |
| 1580 | __in_z LPCWSTR wzTable |
| 1581 | ) |
| 1582 | { |
| 1583 | HRESULT hr = S_OK; |
| 1584 | UINT er = ERROR_SUCCESS; |
| 1585 | |
| 1586 | LPWSTR pwzQuery = NULL; |
| 1587 | PMSIHANDLE hView; |
| 1588 | PMSIHANDLE hColumns; |
| 1589 | DWORD cColumns = 0; |
| 1590 | PMSIHANDLE hRec; |
| 1591 | |
| 1592 | LPWSTR pwzData = NULL; |
| 1593 | LPWSTR pwzPrint = NULL; |
| 1594 | |
| 1595 | hr = StrAllocFormatted(&pwzQuery, L"SELECT * FROM `%s`",wzTable); |
| 1596 | ExitOnFailure(hr, "failed to allocate string for query"); |
| 1597 | |
| 1598 | // Open and Execute the temp View |
| 1599 | hr = WcaOpenExecuteView(pwzQuery, &hView); |
| 1600 | ExitOnFailure(hr, "failed to openexecute temp view with query %ls", pwzQuery); |
| 1601 | |
| 1602 | // Use GetColumnInfo to populate the names of the columns. |
| 1603 | er = ::MsiViewGetColumnInfo(hView, MSICOLINFO_NAMES, &hColumns); |
| 1604 | hr = HRESULT_FROM_WIN32(er); |
| 1605 | ExitOnFailure(hr, "failed to get column info for table: %ls", wzTable); |
| 1606 | |
| 1607 | cColumns = ::MsiRecordGetFieldCount(hColumns); |
| 1608 | |
| 1609 | WcaLog(LOGMSG_STANDARD, "--- Begin Table Dump %ls ---", wzTable); |
| 1610 | |
| 1611 | // Loop through all the columns filling in the data. |
| 1612 | for (DWORD i = 1; i <= cColumns; i++) |
| 1613 | { |
| 1614 | hr = WcaGetRecordString(hColumns, i, &pwzData); |
| 1615 | ExitOnFailure(hr, "failed to get the column name for %d", i); |
| 1616 | |
| 1617 | hr = StrAllocConcat(&pwzPrint, pwzData, 0); |
| 1618 | ExitOnFailure(hr, "Failed to add column name."); |
| 1619 | |
| 1620 | hr = StrAllocConcat(&pwzPrint, L"\t", 1); |
| 1621 | ExitOnFailure(hr, "Failed to add column name."); |
| 1622 | } |
| 1623 | |
| 1624 | WcaLog(LOGMSG_STANDARD, "%ls", pwzPrint); |
| 1625 | |
| 1626 | // Now dump the actual rows. |
| 1627 | while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) |
| 1628 | { |
| 1629 | if (pwzPrint && *pwzPrint) |
| 1630 | { |
| 1631 | *pwzPrint = L'\0'; |
| 1632 | } |
| 1633 | |
| 1634 | for (DWORD i = 1; i <= cColumns; i++) |
| 1635 | { |
| 1636 | hr = WcaGetRecordString(hRec, i, &pwzData); |
| 1637 | ExitOnFailure(hr, "failed to get the column name for %d", i); |
| 1638 | |
| 1639 | hr = StrAllocConcat(&pwzPrint, pwzData, 0); |
| 1640 | ExitOnFailure(hr, "Failed to add column name."); |
| 1641 | |
| 1642 | hr = StrAllocConcat(&pwzPrint, L"\t", 1); |
| 1643 | ExitOnFailure(hr, "Failed to add column name."); |
| 1644 | } |
| 1645 | |
| 1646 | WcaLog(LOGMSG_STANDARD, "%ls", pwzPrint); |
| 1647 | } |
| 1648 | |
| 1649 | WcaLog(LOGMSG_STANDARD, "--- End Table Dump %ls ---", wzTable); |
| 1650 | |
| 1651 | LExit: |
| 1652 | ReleaseStr(pwzPrint); |
| 1653 | ReleaseStr(pwzData); |
| 1654 | ReleaseStr(pwzQuery); |
| 1655 | |
| 1656 | return hr; |
| 1657 | } |
| 1658 | |
| 1659 | |
| 1660 | HRESULT WIXAPI WcaDeferredActionRequiresReboot() |
| 1661 | { |
| 1662 | HRESULT hr = S_OK; |
| 1663 | ATOM atomReboot = 0; |
| 1664 | |
| 1665 | atomReboot = ::GlobalAddAtomW(L"WcaDeferredActionRequiresReboot"); |
| 1666 | ExitOnNullWithLastError(atomReboot, hr, "Failed to create WcaDeferredActionRequiresReboot global atom."); |
| 1667 | |
| 1668 | LExit: |
| 1669 | return hr; |
| 1670 | } |
| 1671 | |
| 1672 | |
| 1673 | BOOL WIXAPI WcaDidDeferredActionRequireReboot() |
| 1674 | { |
| 1675 | // NOTE: This function does not delete the global atom. That is done |
| 1676 | // purposefully so that any other installs that occur after this point also |
| 1677 | // require a reboot. |
| 1678 | ATOM atomReboot = ::GlobalFindAtomW(L"WcaDeferredActionRequiresReboot"); |
| 1679 | return 0 != atomReboot; |
| 1680 | } |