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