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