aboutsummaryrefslogtreecommitdiff
path: root/src/libs/wcautil/WixToolset.WcaUtil/wcawrap.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/wcautil/WixToolset.WcaUtil/wcawrap.cpp')
-rw-r--r--src/libs/wcautil/WixToolset.WcaUtil/wcawrap.cpp1668
1 files changed, 1668 insertions, 0 deletions
diff --git a/src/libs/wcautil/WixToolset.WcaUtil/wcawrap.cpp b/src/libs/wcautil/WixToolset.WcaUtil/wcawrap.cpp
new file mode 100644
index 00000000..2b68f36f
--- /dev/null
+++ b/src/libs/wcautil/WixToolset.WcaUtil/wcawrap.cpp
@@ -0,0 +1,1668 @@
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 cch = 0;
471 SIZE_T cchMax = 0;
472
473 if (!*ppwzData)
474 {
475 WCHAR szEmpty[1] = L"";
476 er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, szEmpty, &cch);
477 if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er)
478 {
479 hr = StrAlloc(ppwzData, ++cch);
480 }
481 else
482 {
483 hr = HRESULT_FROM_WIN32(er);
484 }
485 ExitOnRootFailure(hr, "Failed to allocate string for Property '%ls'", wzProperty);
486 }
487 else
488 {
489 hr = StrMaxLength(*ppwzData, &cchMax);
490 ExitOnFailure(hr, "Failed to get previous size of property data string.");
491
492 cch = (DWORD)min(MAXDWORD, cchMax);
493 }
494
495 er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, *ppwzData, &cch);
496 if (ERROR_MORE_DATA == er)
497 {
498 Assert(*ppwzData);
499 hr = StrAlloc(ppwzData, ++cch);
500 ExitOnFailure(hr, "Failed to allocate string for Property '%ls'", wzProperty);
501
502 er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, *ppwzData, &cch);
503 }
504 ExitOnWin32Error(er, hr, "Failed to get data for property '%ls'", wzProperty);
505
506LExit:
507 return hr;
508}
509
510
511/********************************************************************
512WcaGetFormattedProperty - gets a formatted string property value from
513the active install
514
515********************************************************************/
516extern "C" HRESULT WIXAPI WcaGetFormattedProperty(
517 __in_z LPCWSTR wzProperty,
518 __out LPWSTR* ppwzData
519 )
520{
521 if (!wzProperty || !*wzProperty || !ppwzData)
522 {
523 return E_INVALIDARG;
524 }
525
526 HRESULT hr = S_OK;
527 LPWSTR pwzPropertyValue = NULL;
528
529 hr = WcaGetProperty(wzProperty, &pwzPropertyValue);
530 ExitOnFailure(hr, "failed to get %ls", wzProperty);
531
532 hr = WcaGetFormattedString(pwzPropertyValue, ppwzData);
533 ExitOnFailure(hr, "failed to get formatted value for property: '%ls' with value: '%ls'", wzProperty, pwzPropertyValue);
534
535LExit:
536 ReleaseStr(pwzPropertyValue);
537
538 return hr;
539}
540
541
542/********************************************************************
543WcaGetFormattedString - gets a formatted string value from
544the active install
545
546********************************************************************/
547extern "C" HRESULT WIXAPI WcaGetFormattedString(
548 __in_z LPCWSTR wzString,
549 __out LPWSTR* ppwzData
550 )
551{
552 if (!wzString || !*wzString || !ppwzData)
553 {
554 return E_INVALIDARG;
555 }
556
557 HRESULT hr = S_OK;
558 UINT er = ERROR_SUCCESS;
559 PMSIHANDLE hRecord = ::MsiCreateRecord(1);
560 DWORD cch = 0;
561 SIZE_T cchMax = 0;
562
563 er = ::MsiRecordSetStringW(hRecord, 0, wzString);
564 ExitOnWin32Error(er, hr, "Failed to set record field 0 with '%ls'", wzString);
565
566 if (!*ppwzData)
567 {
568 WCHAR szEmpty[1] = L"";
569 er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecord, szEmpty, &cch);
570 if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er)
571 {
572 hr = StrAlloc(ppwzData, ++cch);
573 }
574 else
575 {
576 hr = HRESULT_FROM_WIN32(er);
577 }
578 ExitOnFailure(hr, "Failed to allocate string for formatted string: '%ls'", wzString);
579 }
580 else
581 {
582 hr = StrMaxLength(*ppwzData, &cchMax);
583 ExitOnFailure(hr, "Failed to get previous size of property data string");
584
585 cch = (DWORD)min(MAXDWORD, cchMax);
586 }
587
588 er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecord, *ppwzData, &cch);
589 if (ERROR_MORE_DATA == er)
590 {
591 hr = StrAlloc(ppwzData, ++cch);
592 ExitOnFailure(hr, "Failed to allocate string for formatted string: '%ls'", wzString);
593
594 er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecord, *ppwzData, &cch);
595 }
596 ExitOnWin32Error(er, hr, "Failed to get formatted string: '%ls'", wzString);
597
598LExit:
599 return hr;
600}
601
602
603/********************************************************************
604WcaGetIntProperty - gets an integer property value from the active install
605
606********************************************************************/
607extern "C" HRESULT WIXAPI WcaGetIntProperty(
608 __in_z LPCWSTR wzProperty,
609 __inout int* piData
610 )
611{
612 if (!piData)
613 return E_INVALIDARG;
614
615 HRESULT hr = S_OK;
616 UINT er;
617
618 WCHAR wzValue[32];
619 DWORD cch = countof(wzValue) - 1;
620
621 er = ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, wzValue, &cch);
622 ExitOnWin32Error(er, hr, "Failed to get data for property '%ls'", wzProperty);
623
624 *piData = wcstol(wzValue, NULL, 10);
625
626LExit:
627 return hr;
628}
629
630
631/********************************************************************
632WcaGetTargetPath - gets the target path for a specified folder
633
634********************************************************************/
635extern "C" HRESULT WIXAPI WcaGetTargetPath(
636 __in_z LPCWSTR wzFolder,
637 __out LPWSTR* ppwzData
638 )
639{
640 if (!wzFolder || !*wzFolder || !ppwzData)
641 return E_INVALIDARG;
642
643 HRESULT hr = S_OK;
644
645 UINT er = ERROR_SUCCESS;
646 DWORD cch = 0;
647 SIZE_T cchMax = 0;
648
649 if (!*ppwzData)
650 {
651 WCHAR szEmpty[1] = L"";
652 er = ::MsiGetTargetPathW(WcaGetInstallHandle(), wzFolder, szEmpty, &cch);
653 if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er)
654 {
655 ++cch; //Add one for the null terminator
656 hr = StrAlloc(ppwzData, cch);
657 }
658 else
659 {
660 hr = HRESULT_FROM_WIN32(er);
661 }
662 ExitOnFailure(hr, "Failed to allocate string for target path of folder: '%ls'", wzFolder);
663 }
664 else
665 {
666 hr = StrMaxLength(*ppwzData, &cchMax);
667 ExitOnFailure(hr, "Failed to get previous size of string");
668
669 cch = (DWORD)min(MAXDWORD, cchMax);
670 }
671
672 er = ::MsiGetTargetPathW(WcaGetInstallHandle(), wzFolder, *ppwzData, &cch);
673 if (ERROR_MORE_DATA == er)
674 {
675 ++cch;
676 hr = StrAlloc(ppwzData, cch);
677 ExitOnFailure(hr, "Failed to allocate string for target path of folder: '%ls'", wzFolder);
678
679 er = ::MsiGetTargetPathW(WcaGetInstallHandle(), wzFolder, *ppwzData, &cch);
680 }
681 ExitOnWin32Error(er, hr, "Failed to get target path for folder '%ls'", wzFolder);
682
683LExit:
684 return hr;
685}
686
687
688/********************************************************************
689WcaSetProperty - sets a string property value in the active install
690
691********************************************************************/
692extern "C" HRESULT WIXAPI WcaSetProperty(
693 __in_z LPCWSTR wzPropertyName,
694 __in_z LPCWSTR wzPropertyValue
695 )
696{
697 HRESULT hr = S_OK;
698
699 if (!wzPropertyName || !*wzPropertyName || !wzPropertyValue)
700 return E_INVALIDARG;
701
702 UINT er = ::MsiSetPropertyW(WcaGetInstallHandle(), wzPropertyName, wzPropertyValue);
703 ExitOnWin32Error(er, hr, "failed to set property: %ls", wzPropertyName);
704
705LExit:
706 return hr;
707}
708
709
710/********************************************************************
711WcaSetIntProperty - sets a integer property value in the active install
712
713********************************************************************/
714extern "C" HRESULT WIXAPI WcaSetIntProperty(
715 __in_z LPCWSTR wzPropertyName,
716 __in int nPropertyValue
717 )
718{
719 if (!wzPropertyName || !*wzPropertyName)
720 return E_INVALIDARG;
721
722 // 12 characters should be enough for a 32-bit int: 10 digits, 1 sign, 1 null
723 WCHAR wzPropertyValue[13];
724 HRESULT hr = StringCchPrintfW(wzPropertyValue, countof(wzPropertyValue), L"%d", nPropertyValue);
725 ExitOnFailure(hr, "failed to convert into string property value: %d", nPropertyValue);
726
727 UINT er = ::MsiSetPropertyW(WcaGetInstallHandle(), wzPropertyName, wzPropertyValue);
728 ExitOnWin32Error(er, hr, "failed to set property: %ls", wzPropertyName);
729
730LExit:
731 return hr;
732}
733
734
735/********************************************************************
736WcaIsPropertySet() - returns TRUE if property is set
737
738********************************************************************/
739extern "C" BOOL WIXAPI WcaIsPropertySet(
740 __in LPCSTR szProperty
741 )
742{
743 DWORD cchProperty = 0;
744 char szEmpty[1] = "";
745#ifdef DEBUG
746 UINT er =
747#endif
748 ::MsiGetPropertyA(WcaGetInstallHandle(), szProperty, szEmpty, &cchProperty);
749 AssertSz(ERROR_INVALID_PARAMETER != er && ERROR_INVALID_HANDLE != er, "Unexpected return value from ::MsiGetProperty()");
750
751 return 0 < cchProperty; // property is set if the length is greater than zero
752}
753
754
755/********************************************************************
756WcaIsUnicodePropertySet() - returns TRUE if property is set
757
758********************************************************************/
759extern "C" BOOL WIXAPI WcaIsUnicodePropertySet(
760 __in LPCWSTR wzProperty
761 )
762{
763 DWORD cchProperty = 0;
764 wchar_t wzEmpty[1] = L"";
765#ifdef DEBUG
766 UINT er =
767#endif
768 ::MsiGetPropertyW(WcaGetInstallHandle(), wzProperty, wzEmpty, &cchProperty);
769 AssertSz(ERROR_INVALID_PARAMETER != er && ERROR_INVALID_HANDLE != er, "Unexpected return value from ::MsiGetProperty()");
770
771 return 0 < cchProperty; // property is set if the length is greater than zero
772}
773
774
775/********************************************************************
776WcaGetRecordInteger() - gets an integer field out of a record
777
778NOTE: returns S_FALSE if the field was null
779********************************************************************/
780extern "C" HRESULT WIXAPI WcaGetRecordInteger(
781 __in MSIHANDLE hRec,
782 __in UINT uiField,
783 __inout int* piData
784 )
785{
786 if (!hRec || !piData)
787 return E_INVALIDARG;
788
789 HRESULT hr = S_OK;
790 *piData = ::MsiRecordGetInteger(hRec, uiField);
791 if (MSI_NULL_INTEGER == *piData)
792 hr = S_FALSE;
793
794 //LExit:
795 return hr;
796}
797
798
799/********************************************************************
800WcaGetRecordString() - gets a string field out of a record
801
802********************************************************************/
803extern "C" HRESULT WIXAPI WcaGetRecordString(
804 __in MSIHANDLE hRec,
805 __in UINT uiField,
806 __inout LPWSTR* ppwzData
807 )
808{
809 if (!hRec || !ppwzData)
810 return E_INVALIDARG;
811
812 HRESULT hr = S_OK;
813 UINT er;
814 DWORD cch = 0;
815 SIZE_T cchMax = 0;
816
817 if (!*ppwzData)
818 {
819 WCHAR szEmpty[1] = L"";
820 er = ::MsiRecordGetStringW(hRec, uiField, szEmpty, &cch);
821 if (ERROR_MORE_DATA == er || ERROR_SUCCESS == er)
822 {
823 hr = StrAlloc(ppwzData, ++cch);
824 }
825 else
826 {
827 hr = HRESULT_FROM_WIN32(er);
828 }
829 ExitOnFailure(hr, "Failed to allocate memory for record string");
830 }
831 else
832 {
833 hr = StrMaxLength(*ppwzData, &cchMax);
834 ExitOnFailure(hr, "Failed to get previous size of string");
835
836 cch = (DWORD)min(MAXDWORD, cchMax);
837 }
838
839 er = ::MsiRecordGetStringW(hRec, uiField, *ppwzData, &cch);
840 if (ERROR_MORE_DATA == er)
841 {
842 hr = StrAlloc(ppwzData, ++cch);
843 ExitOnFailure(hr, "Failed to allocate memory for record string");
844
845 er = ::MsiRecordGetStringW(hRec, uiField, *ppwzData, &cch);
846 }
847 ExitOnWin32Error(er, hr, "Failed to get string from record");
848
849LExit:
850 return hr;
851}
852
853
854/********************************************************************
855HideNulls() - internal helper function to escape [~] in formatted strings
856
857********************************************************************/
858static void HideNulls(
859 __inout_z LPWSTR wzData
860 )
861{
862 LPWSTR pwz = wzData;
863
864 while(*pwz)
865 {
866 if (pwz[0] == L'[' && pwz[1] == L'~' && pwz[2] == L']') // found a null [~]
867 {
868 pwz[0] = L'!'; // turn it into !$!
869 pwz[1] = L'$';
870 pwz[2] = L'!';
871 pwz += 3;
872 }
873 else
874 {
875 ++pwz;
876 }
877 }
878}
879
880
881/********************************************************************
882RevealNulls() - internal helper function to unescape !$! in formatted strings
883
884********************************************************************/
885static void RevealNulls(
886 __inout_z LPWSTR wzData
887 )
888{
889 LPWSTR pwz = wzData;
890
891 while(*pwz)
892 {
893 if (pwz[0] == L'!' && pwz[1] == L'$' && pwz[2] == L'!') // found the fake null !$!
894 {
895 pwz[0] = L'['; // turn it back into [~]
896 pwz[1] = L'~';
897 pwz[2] = L']';
898 pwz += 3;
899 }
900 else
901 {
902 ++pwz;
903 }
904 }
905}
906
907
908/********************************************************************
909WcaGetRecordFormattedString() - gets formatted string filed from record
910
911********************************************************************/
912extern "C" HRESULT WIXAPI WcaGetRecordFormattedString(
913 __in MSIHANDLE hRec,
914 __in UINT uiField,
915 __inout LPWSTR* ppwzData
916 )
917{
918 if (!hRec || !ppwzData)
919 {
920 return E_INVALIDARG;
921 }
922
923 HRESULT hr = S_OK;
924 UINT er;
925 DWORD cch = 0;
926 SIZE_T cchMax = 0;
927 PMSIHANDLE hRecFormat;
928
929 // get the format string
930 hr = WcaGetRecordString(hRec, uiField, ppwzData);
931 ExitOnFailure(hr, "failed to get string from record");
932
933 if (!**ppwzData)
934 {
935 ExitFunction();
936 }
937
938 // hide the nulls '[~]' so we can get them back after formatting
939 HideNulls(*ppwzData);
940
941 // set up the format record
942 hRecFormat = ::MsiCreateRecord(1);
943 ExitOnNull(hRecFormat, hr, E_UNEXPECTED, "Failed to create record to format string");
944 hr = WcaSetRecordString(hRecFormat, 0, *ppwzData);
945 ExitOnFailure(hr, "failed to set string to format record");
946
947 // format the string
948 hr = StrMaxLength(*ppwzData, &cchMax);
949 ExitOnFailure(hr, "failed to get max length of string");
950
951 cch = (DWORD)min(MAXDWORD, cchMax);
952
953 er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecFormat, *ppwzData, &cch);
954 if (ERROR_MORE_DATA == er)
955 {
956 hr = StrAlloc(ppwzData, ++cch);
957 ExitOnFailure(hr, "Failed to allocate memory for record string");
958
959 er = ::MsiFormatRecordW(WcaGetInstallHandle(), hRecFormat, *ppwzData, &cch);
960 }
961 ExitOnWin32Error(er, hr, "Failed to format string");
962
963 // put the nulls back
964 RevealNulls(*ppwzData);
965
966LExit:
967 return hr;
968}
969
970
971/********************************************************************
972WcaGetRecordFormattedInteger() - gets formatted integer from record
973
974********************************************************************/
975extern "C" HRESULT WIXAPI WcaGetRecordFormattedInteger(
976 __in MSIHANDLE hRec,
977 __in UINT uiField,
978 __out int* piData
979 )
980{
981 if (!hRec || !piData)
982 {
983 return E_INVALIDARG;
984 }
985
986 HRESULT hr = S_OK;
987 LPWSTR pwzData = NULL;
988
989 hr = WcaGetRecordFormattedString(hRec, uiField, &pwzData);
990 ExitOnFailure(hr, "failed to get record field: %u", uiField);
991 if (pwzData && *pwzData)
992 {
993 LPWSTR wz = NULL;
994 *piData = wcstol(pwzData, &wz, 10);
995 if (wz && *wz)
996 {
997 hr = E_INVALIDARG;
998 ExitOnFailure(hr, "failed to parse record field: %u as number: %ls", uiField, pwzData);
999 }
1000 }
1001 else
1002 {
1003 *piData = MSI_NULL_INTEGER;
1004 }
1005
1006LExit:
1007 return hr;
1008}
1009
1010
1011/********************************************************************
1012WcaAllocStream() - creates a byte stream of the specified size
1013
1014NOTE: Use WcaFreeStream() to release the byte stream
1015********************************************************************/
1016extern "C" HRESULT WIXAPI WcaAllocStream(
1017 __deref_out_bcount_part(cbData, 0) BYTE** ppbData,
1018 __in SIZE_T cbData
1019 )
1020{
1021 Assert(ppbData);
1022 HRESULT hr;
1023 BYTE* pbNewData;
1024
1025 if (*ppbData)
1026 pbNewData = static_cast<BYTE*>(MemReAlloc(*ppbData, cbData, TRUE));
1027 else
1028 pbNewData = static_cast<BYTE*>(MemAlloc(cbData, TRUE));
1029
1030 if (!pbNewData)
1031 {
1032 ExitOnLastError(hr, "Failed to allocate string");
1033 }
1034
1035 *ppbData = pbNewData;
1036 pbNewData = NULL;
1037
1038 hr = S_OK;
1039LExit:
1040 ReleaseMem(pbNewData);
1041
1042 return hr;
1043}
1044
1045
1046/********************************************************************
1047WcaFreeStream() - frees a byte stream
1048
1049********************************************************************/
1050extern "C" HRESULT WIXAPI WcaFreeStream(
1051 __in BYTE* pbData
1052 )
1053{
1054 if (!pbData)
1055 return E_INVALIDARG;
1056
1057 HRESULT hr = MemFree(pbData);
1058 return hr;
1059}
1060
1061
1062/********************************************************************
1063WcaGetRecordStream() - gets a byte stream field from record
1064
1065********************************************************************/
1066extern "C" HRESULT WIXAPI WcaGetRecordStream(
1067 __in MSIHANDLE hRecBinary,
1068 __in UINT uiField,
1069 __deref_out_bcount_full(*pcbData) BYTE** ppbData,
1070 __out DWORD* pcbData
1071 )
1072{
1073 HRESULT hr = S_OK;
1074 UINT er = ERROR_SUCCESS;
1075
1076 if (!hRecBinary || !ppbData || !pcbData)
1077 return E_INVALIDARG;
1078
1079 *pcbData = 0;
1080 er = ::MsiRecordReadStream(hRecBinary, uiField, NULL, pcbData);
1081 ExitOnWin32Error(er, hr, "failed to get size of stream");
1082
1083 hr = WcaAllocStream(ppbData, *pcbData);
1084 ExitOnFailure(hr, "failed to allocate data for stream");
1085
1086 er = ::MsiRecordReadStream(hRecBinary, uiField, (char*)*ppbData, pcbData);
1087 ExitOnWin32Error(er, hr, "failed to read from stream");
1088
1089LExit:
1090 return hr;
1091}
1092
1093
1094/********************************************************************
1095WcaSetRecordString() - set a string field in record
1096
1097********************************************************************/
1098extern "C" HRESULT WIXAPI WcaSetRecordString(
1099 __in MSIHANDLE hRec,
1100 __in UINT uiField,
1101 __in_z LPCWSTR wzData
1102 )
1103{
1104 if (!hRec || !wzData)
1105 return E_INVALIDARG;
1106
1107 HRESULT hr = S_OK;
1108 UINT er = ::MsiRecordSetStringW(hRec, uiField, wzData);
1109 ExitOnWin32Error(er, hr, "failed to set string in record");
1110
1111LExit:
1112 return hr;
1113}
1114
1115
1116/********************************************************************
1117WcaSetRecordInteger() - set a integer field in record
1118
1119********************************************************************/
1120extern "C" HRESULT WIXAPI WcaSetRecordInteger(
1121 __in MSIHANDLE hRec,
1122 __in UINT uiField,
1123 __in int iValue
1124 )
1125{
1126 if (!hRec)
1127 return E_INVALIDARG;
1128
1129 HRESULT hr = S_OK;
1130 UINT er = ::MsiRecordSetInteger(hRec, uiField, iValue);
1131 ExitOnWin32Error(er, hr, "failed to set integer in record");
1132
1133LExit:
1134 return hr;
1135}
1136
1137
1138/********************************************************************
1139
1140WcaDoDeferredAction() - schedules an action at this point in the script
1141
1142********************************************************************/
1143extern "C" HRESULT WIXAPI WcaDoDeferredAction(
1144 __in_z LPCWSTR wzAction,
1145 __in_z LPCWSTR wzCustomActionData,
1146 __in UINT uiCost
1147 )
1148{
1149 HRESULT hr = S_OK;
1150 UINT er;
1151
1152 if (wzCustomActionData && *wzCustomActionData)
1153 {
1154 er = ::MsiSetPropertyW(WcaGetInstallHandle(), wzAction, wzCustomActionData);
1155 ExitOnWin32Error(er, hr, "Failed to set CustomActionData for deferred action");
1156 }
1157
1158 if (0 < uiCost)
1159 {
1160 hr = WcaProgressMessage(uiCost, TRUE); // add ticks to the progress bar
1161 // TODO: handle the return codes correctly
1162 }
1163
1164 er = ::MsiDoActionW(WcaGetInstallHandle(), wzAction);
1165 if (ERROR_INSTALL_USEREXIT == er)
1166 {
1167 WcaSetReturnValue(er);
1168 }
1169 ExitOnWin32Error(er, hr, "Failed MsiDoAction on deferred action");
1170
1171LExit:
1172 return hr;
1173}
1174
1175
1176/********************************************************************
1177WcaCountOfCustomActionDataRecords() - counts the number of records
1178passed to a deferred CustomAction
1179
1180********************************************************************/
1181extern "C" DWORD WIXAPI WcaCountOfCustomActionDataRecords(
1182 __in_z LPCWSTR wzData
1183 )
1184{
1185 WCHAR delim[] = {MAGIC_MULTISZ_DELIM, 0}; // magic char followed by NULL terminator
1186 DWORD dwCount = 0;
1187
1188 // 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
1189 for (LPCWSTR pwzCurrent = wzData; pwzCurrent && *pwzCurrent && *(pwzCurrent + 1); pwzCurrent = wcsstr(pwzCurrent, delim))
1190 {
1191 ++dwCount;
1192 ++pwzCurrent;
1193 }
1194
1195 return dwCount;
1196}
1197
1198
1199/********************************************************************
1200BreakDownCustomActionData() - internal helper to chop up CustomActionData
1201
1202NOTE: this modifies the passed in data
1203********************************************************************/
1204static LPWSTR BreakDownCustomActionData(
1205 __inout LPWSTR* ppwzData
1206 )
1207{
1208 if (!ppwzData)
1209 return NULL;
1210 if (0 == *ppwzData)
1211 return NULL;
1212
1213 WCHAR delim[] = {MAGIC_MULTISZ_DELIM, 0}; // magic char followed by Null terminator
1214
1215 LPWSTR pwzReturn = *ppwzData;
1216 LPWSTR pwz = wcsstr(pwzReturn, delim);
1217 if (pwz)
1218 {
1219 *pwz = 0;
1220 *ppwzData = pwz + 1;
1221 }
1222 else
1223 *ppwzData = 0;
1224
1225 return pwzReturn;
1226}
1227
1228
1229/********************************************************************
1230RevertCustomActionData() - Reverts custom action data changes made
1231 by BreakDownCustomActionData;
1232
1233NOTE: this modifies the passed in data
1234********************************************************************/
1235extern "C" void WIXAPI RevertCustomActionData(
1236 __in LPWSTR wzRevertTo,
1237 __in LPCWSTR wzRevertFrom
1238 )
1239{
1240 if (!wzRevertTo)
1241 return;
1242 if (!wzRevertFrom)
1243 return;
1244 // start at the revert point and replace all \0 with MAGIC_MULTISZ_DELIM
1245 for(LPWSTR wzIndex = wzRevertTo; wzIndex < wzRevertFrom; wzIndex++)
1246 {
1247 if (0 == *wzIndex)
1248 {
1249 *wzIndex = MAGIC_MULTISZ_DELIM;
1250 }
1251 }
1252 return;
1253}
1254
1255/********************************************************************
1256WcaReadStringFromCaData() - reads a string out of the CustomActionData
1257
1258NOTE: this modifies the passed in ppwzCustomActionData variable
1259********************************************************************/
1260extern "C" HRESULT WIXAPI WcaReadStringFromCaData(
1261 __deref_in LPWSTR* ppwzCustomActionData,
1262 __deref_out_z LPWSTR* ppwzString
1263 )
1264{
1265 HRESULT hr = S_OK;
1266
1267 LPCWSTR pwz = BreakDownCustomActionData(ppwzCustomActionData);
1268 if (!pwz)
1269 return E_NOMOREITEMS;
1270
1271 hr = StrAllocString(ppwzString, pwz, 0);
1272 ExitOnFailure(hr, "failed to allocate memory for string");
1273
1274 hr = S_OK;
1275LExit:
1276 return hr;
1277}
1278
1279
1280/********************************************************************
1281WcaReadIntegerFromCaData() - reads an integer out of the CustomActionData
1282
1283NOTE: this modifies the passed in ppwzCustomActionData variable
1284********************************************************************/
1285extern "C" HRESULT WIXAPI WcaReadIntegerFromCaData(
1286 __deref_in LPWSTR* ppwzCustomActionData,
1287 __out int* piResult
1288 )
1289{
1290 LPCWSTR pwz = BreakDownCustomActionData(ppwzCustomActionData);
1291 if (!pwz || !*pwz)
1292 return E_NOMOREITEMS;
1293
1294 *piResult = wcstol(pwz, NULL, 10);
1295 return S_OK;
1296}
1297
1298
1299/********************************************************************
1300WcaReadStreamFromCaData() - reads a stream out of the CustomActionData
1301
1302NOTE: this modifies the passed in ppwzCustomActionData variable
1303NOTE: returned stream should be freed with WcaFreeStream()
1304********************************************************************/
1305extern "C" HRESULT WIXAPI WcaReadStreamFromCaData(
1306 __deref_in LPWSTR* ppwzCustomActionData,
1307 __deref_out_bcount(*pcbData) BYTE** ppbData,
1308 __out DWORD_PTR* pcbData
1309 )
1310{
1311 HRESULT hr;
1312
1313 LPCWSTR pwz = BreakDownCustomActionData(ppwzCustomActionData);
1314 if (!pwz)
1315 return E_NOMOREITEMS;
1316
1317 hr = StrAllocBase85Decode(pwz, ppbData, pcbData);
1318 ExitOnFailure(hr, "failed to decode string into stream");
1319
1320LExit:
1321 return hr;
1322}
1323
1324
1325/********************************************************************
1326WcaWriteStringToCaData() - adds a string to the CustomActionData to
1327feed a deferred CustomAction
1328
1329********************************************************************/
1330extern "C" HRESULT WIXAPI WcaWriteStringToCaData(
1331 __in_z LPCWSTR wzString,
1332 __deref_inout_z_opt LPWSTR* ppwzCustomActionData
1333 )
1334{
1335 HRESULT hr = S_OK;
1336 WCHAR delim[] = {MAGIC_MULTISZ_DELIM, 0}; // magic char followed by NULL terminator
1337 SIZE_T cchString = 0;
1338 SIZE_T cchCustomActionData = 0;
1339 SIZE_T cchMax = 0;
1340
1341 if (!ppwzCustomActionData)
1342 {
1343 ExitFunction1(hr = E_INVALIDARG);
1344 }
1345
1346 hr = ::StringCchLengthW(wzString, STRSAFE_MAX_LENGTH, reinterpret_cast<size_t*>(&cchString));
1347 ExitOnRootFailure(hr, "failed to get length of ca data string");
1348
1349 ++cchString; // assume we'll be adding the delim
1350
1351 if (*ppwzCustomActionData)
1352 {
1353 hr = StrMaxLength(*ppwzCustomActionData, &cchCustomActionData);
1354 ExitOnFailure(hr, "failed to get max length of custom action data");
1355
1356 hr = ::StringCchLengthW(*ppwzCustomActionData, STRSAFE_MAX_LENGTH, reinterpret_cast<size_t*>(&cchMax));
1357 ExitOnRootFailure(hr, "failed to get length of custom action data");
1358 }
1359
1360 if ((cchCustomActionData - cchMax) < cchString + 1)
1361 {
1362 cchCustomActionData += cchString + 1 + 255; // add 255 for good measure
1363 cchCustomActionData = min(STRSAFE_MAX_LENGTH, cchCustomActionData);
1364
1365 hr = StrAlloc(ppwzCustomActionData, cchCustomActionData);
1366 ExitOnFailure(hr, "Failed to allocate memory for CustomActionData string");
1367 }
1368
1369 if (**ppwzCustomActionData) // if data exists toss the delimiter on before adding more to the end
1370 {
1371 hr = ::StringCchCatW(*ppwzCustomActionData, cchCustomActionData, delim);
1372 ExitOnRootFailure(hr, "Failed to concatenate CustomActionData string");
1373 }
1374
1375 hr = ::StringCchCatW(*ppwzCustomActionData, cchCustomActionData, wzString);
1376 ExitOnRootFailure(hr, "Failed to concatenate CustomActionData string");
1377
1378LExit:
1379 return hr;
1380}
1381
1382
1383/********************************************************************
1384WcaWriteIntegerToCaData() - adds an integer to the CustomActionData to
1385feed a deferred CustomAction
1386
1387********************************************************************/
1388extern "C" HRESULT WIXAPI WcaWriteIntegerToCaData(
1389 __in int i,
1390 __deref_out_z_opt LPWSTR* ppwzCustomActionData
1391 )
1392{
1393 WCHAR wzBuffer[13];
1394 StringCchPrintfW(wzBuffer, countof(wzBuffer), L"%d", i);
1395
1396 return WcaWriteStringToCaData(wzBuffer, ppwzCustomActionData);
1397}
1398
1399
1400/********************************************************************
1401WcaWriteStreamToCaData() - adds a byte stream to the CustomActionData to
1402feed a deferred CustomAction
1403
1404********************************************************************/
1405extern "C" HRESULT WIXAPI WcaWriteStreamToCaData(
1406 __in_bcount(cbData) const BYTE* pbData,
1407 __in SIZE_T cbData,
1408 __deref_inout_z_opt LPWSTR* ppwzCustomActionData
1409 )
1410{
1411 HRESULT hr;
1412 LPWSTR pwzData = NULL;
1413
1414 hr = StrAllocBase85Encode(pbData, cbData, &pwzData);
1415 ExitOnFailure(hr, "failed to encode data into string");
1416
1417 hr = WcaWriteStringToCaData(pwzData, ppwzCustomActionData);
1418
1419LExit:
1420 ReleaseStr(pwzData);
1421 return hr;
1422}
1423
1424
1425/********************************************************************
1426WcaAddTempRecord - adds a temporary record to the active database
1427
1428NOTE: you cannot use PMSIHANDLEs for the __in/__out parameters
1429NOTE: uiUniquifyColumn can be 0 if no column needs to be made unique
1430********************************************************************/
1431extern "C" HRESULT __cdecl WcaAddTempRecord(
1432 __inout MSIHANDLE* phTableView,
1433 __inout MSIHANDLE* phColumns,
1434 __in_z LPCWSTR wzTable,
1435 __out_opt MSIDBERROR* pdbError,
1436 __in UINT uiUniquifyColumn,
1437 __in UINT cColumns,
1438 ...
1439 )
1440{
1441 Assert(phTableView && phColumns);
1442
1443 static DWORD dwUniquifyValue = ::GetTickCount();
1444
1445 HRESULT hr = S_OK;
1446 UINT er = ERROR_SUCCESS;
1447
1448 LPWSTR pwzQuery = NULL;
1449 PMSIHANDLE hTempRec;
1450 DWORD i;
1451 va_list args;
1452
1453 LPWSTR pwzData = NULL;
1454 LPWSTR pwzUniquify = NULL;
1455
1456 //
1457 // if we don't have a table and its columns already
1458 //
1459 if (NULL == *phTableView)
1460 {
1461 // set the query
1462 hr = StrAllocFormatted(&pwzQuery, L"SELECT * FROM `%s`",wzTable);
1463 ExitOnFailure(hr, "failed to allocate string for query");
1464
1465 // Open and Execute the temp View
1466 hr = WcaOpenExecuteView(pwzQuery, phTableView);
1467 ExitOnFailure(hr, "failed to openexecute temp view with query %ls", pwzQuery);
1468 }
1469
1470 if (NULL == *phColumns)
1471 {
1472 // use GetColumnInfo to populate the datatype record
1473 er = ::MsiViewGetColumnInfo(*phTableView, MSICOLINFO_TYPES, phColumns);
1474 ExitOnWin32Error(er, hr, "failed to columns for table: %ls", wzTable);
1475 }
1476 AssertSz(::MsiRecordGetFieldCount(*phColumns) == cColumns, "passed in argument does not match number of columns in table");
1477
1478 //
1479 // create the temp record
1480 //
1481 hTempRec = ::MsiCreateRecord(cColumns);
1482 ExitOnNull(hTempRec, hr, E_UNEXPECTED, "could not create temp record for table: %ls", wzTable);
1483
1484 //
1485 // loop through all the columns filling in the data
1486 //
1487 va_start(args, cColumns);
1488 for (i = 1; i <= cColumns; i++)
1489 {
1490 hr = WcaGetRecordString(*phColumns, i, &pwzData);
1491 ExitOnFailure(hr, "failed to get the data type for %d", i);
1492
1493 // if data type is string write string
1494 if (L's' == *pwzData || L'S' == *pwzData || L'g' == *pwzData || L'G' == *pwzData || L'l' == *pwzData || L'L' == *pwzData)
1495 {
1496 LPCWSTR wz = va_arg(args, WCHAR*);
1497
1498 // if this is the column that is supposed to be unique add the time stamp on the end
1499 if (uiUniquifyColumn == i)
1500 {
1501 hr = StrAllocFormatted(&pwzUniquify, L"%s%u", wz, ++dwUniquifyValue); // up the count so we have no collisions on the unique name
1502 ExitOnFailure(hr, "failed to allocate string for unique column: %d", uiUniquifyColumn);
1503
1504 wz = pwzUniquify;
1505 }
1506
1507 er = ::MsiRecordSetStringW(hTempRec, i, wz);
1508 ExitOnWin32Error(er, hr, "failed to set string value at position %d", i);
1509 }
1510 // if data type is integer write integer
1511 else if (L'i' == *pwzData || L'I' == *pwzData || L'j' == *pwzData || L'J' == *pwzData)
1512 {
1513 AssertSz(uiUniquifyColumn != i, "Cannot uniquify an integer column");
1514 int iData = va_arg(args, int);
1515
1516 er = ::MsiRecordSetInteger(hTempRec, i, iData);
1517 ExitOnWin32Error(er, hr, "failed to set integer value at position %d", i);
1518 }
1519 else
1520 {
1521 // not supporting binary streams so error out
1522 hr = HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH);
1523 ExitOnRootFailure(hr, "unsupported data type '%ls' in column: %d", pwzData, i);
1524 }
1525 }
1526 va_end(args);
1527
1528 //
1529 // add the temporary record to the MSI
1530 //
1531 er = ::MsiViewModify(*phTableView, MSIMODIFY_INSERT_TEMPORARY, hTempRec);
1532 hr = HRESULT_FROM_WIN32(er);
1533 if (FAILED(hr))
1534 {
1535 if (pdbError)
1536 {
1537 // MSI provides only a generic ERROR_FUNCTION_FAILED if a temporary row
1538 // can't be inserted; if we're being asked to provide the detailed error,
1539 // get it using the MSIMODIFY_VALIDATE_NEW flag
1540 er = ::MsiViewModify(*phTableView, MSIMODIFY_VALIDATE_NEW, hTempRec);
1541 hr = HRESULT_FROM_WIN32(er);
1542 }
1543
1544 WCHAR wzBuf[MAX_PATH];
1545 DWORD cchBuf = countof(wzBuf);
1546 MSIDBERROR dbErr = ::MsiViewGetErrorW(*phTableView, wzBuf, &cchBuf);
1547 if (pdbError)
1548 {
1549 *pdbError = dbErr;
1550 }
1551 ExitOnFailure(hr, "failed to add temporary row, dberr: %d, err: %ls", dbErr, wzBuf);
1552 }
1553
1554LExit:
1555 ReleaseStr(pwzUniquify);
1556 ReleaseStr(pwzData);
1557 ReleaseStr(pwzQuery);
1558
1559 return hr;
1560}
1561
1562
1563/********************************************************************
1564WcaDumpTable - dumps a table to the log file
1565
1566********************************************************************/
1567extern "C" HRESULT WIXAPI WcaDumpTable(
1568 __in_z LPCWSTR wzTable
1569 )
1570{
1571 HRESULT hr = S_OK;
1572 UINT er = ERROR_SUCCESS;
1573
1574 LPWSTR pwzQuery = NULL;
1575 PMSIHANDLE hView;
1576 PMSIHANDLE hColumns;
1577 DWORD cColumns = 0;
1578 PMSIHANDLE hRec;
1579
1580 LPWSTR pwzData = NULL;
1581 LPWSTR pwzPrint = NULL;
1582
1583 hr = StrAllocFormatted(&pwzQuery, L"SELECT * FROM `%s`",wzTable);
1584 ExitOnFailure(hr, "failed to allocate string for query");
1585
1586 // Open and Execute the temp View
1587 hr = WcaOpenExecuteView(pwzQuery, &hView);
1588 ExitOnFailure(hr, "failed to openexecute temp view with query %ls", pwzQuery);
1589
1590 // Use GetColumnInfo to populate the names of the columns.
1591 er = ::MsiViewGetColumnInfo(hView, MSICOLINFO_NAMES, &hColumns);
1592 hr = HRESULT_FROM_WIN32(er);
1593 ExitOnFailure(hr, "failed to get column info for table: %ls", wzTable);
1594
1595 cColumns = ::MsiRecordGetFieldCount(hColumns);
1596
1597 WcaLog(LOGMSG_STANDARD, "--- Begin Table Dump %ls ---", wzTable);
1598
1599 // Loop through all the columns filling in the data.
1600 for (DWORD i = 1; i <= cColumns; i++)
1601 {
1602 hr = WcaGetRecordString(hColumns, i, &pwzData);
1603 ExitOnFailure(hr, "failed to get the column name for %d", i);
1604
1605 hr = StrAllocConcat(&pwzPrint, pwzData, 0);
1606 ExitOnFailure(hr, "Failed to add column name.");
1607
1608 hr = StrAllocConcat(&pwzPrint, L"\t", 1);
1609 ExitOnFailure(hr, "Failed to add column name.");
1610 }
1611
1612 WcaLog(LOGMSG_STANDARD, "%ls", pwzPrint);
1613
1614 // Now dump the actual rows.
1615 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
1616 {
1617 if (pwzPrint && *pwzPrint)
1618 {
1619 *pwzPrint = L'\0';
1620 }
1621
1622 for (DWORD i = 1; i <= cColumns; i++)
1623 {
1624 hr = WcaGetRecordString(hRec, i, &pwzData);
1625 ExitOnFailure(hr, "failed to get the column name for %d", i);
1626
1627 hr = StrAllocConcat(&pwzPrint, pwzData, 0);
1628 ExitOnFailure(hr, "Failed to add column name.");
1629
1630 hr = StrAllocConcat(&pwzPrint, L"\t", 1);
1631 ExitOnFailure(hr, "Failed to add column name.");
1632 }
1633
1634 WcaLog(LOGMSG_STANDARD, "%ls", pwzPrint);
1635 }
1636
1637 WcaLog(LOGMSG_STANDARD, "--- End Table Dump %ls ---", wzTable);
1638
1639LExit:
1640 ReleaseStr(pwzPrint);
1641 ReleaseStr(pwzData);
1642 ReleaseStr(pwzQuery);
1643
1644 return hr;
1645}
1646
1647
1648HRESULT WIXAPI WcaDeferredActionRequiresReboot()
1649{
1650 HRESULT hr = S_OK;
1651 ATOM atomReboot = 0;
1652
1653 atomReboot = ::GlobalAddAtomW(L"WcaDeferredActionRequiresReboot");
1654 ExitOnNullWithLastError(atomReboot, hr, "Failed to create WcaDeferredActionRequiresReboot global atom.");
1655
1656LExit:
1657 return hr;
1658}
1659
1660
1661BOOL WIXAPI WcaDidDeferredActionRequireReboot()
1662{
1663 // NOTE: This function does not delete the global atom. That is done
1664 // purposefully so that any other installs that occur after this point also
1665 // require a reboot.
1666 ATOM atomReboot = ::GlobalFindAtomW(L"WcaDeferredActionRequiresReboot");
1667 return 0 != atomReboot;
1668}