main
cpp 1,680 lines 47.1 KB
Raw
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 }