aboutsummaryrefslogtreecommitdiff
path: root/src/wcautil
diff options
context:
space:
mode:
authorSean Hall <rseanhall@gmail.com>2017-09-03 17:25:02 -0500
committerSean Hall <rseanhall@gmail.com>2017-09-03 18:10:41 -0500
commit7df142a4586bcede1442feb38029ff15556ccb46 (patch)
tree4a52b55f2f0ff7bd511bd01cf08b67479d64cd76 /src/wcautil
parenta3073f2da5160b71dc4b89fd6c4cef1008521aa4 (diff)
downloadwix-7df142a4586bcede1442feb38029ff15556ccb46.tar.gz
wix-7df142a4586bcede1442feb38029ff15556ccb46.tar.bz2
wix-7df142a4586bcede1442feb38029ff15556ccb46.zip
Initialize repo
Diffstat (limited to 'src/wcautil')
-rw-r--r--src/wcautil/build/WixToolset.WcaUtil.props23
-rw-r--r--src/wcautil/custommsierrors.h130
-rw-r--r--src/wcautil/exbinary.cpp142
-rw-r--r--src/wcautil/inc/wcalog.h14
-rw-r--r--src/wcautil/inc/wcautil.h384
-rw-r--r--src/wcautil/inc/wcawow64.h20
-rw-r--r--src/wcautil/inc/wcawrapquery.h130
-rw-r--r--src/wcautil/packages.config5
-rw-r--r--src/wcautil/precomp.h19
-rw-r--r--src/wcautil/qtexec.cpp340
-rw-r--r--src/wcautil/wcalog.cpp251
-rw-r--r--src/wcautil/wcascript.cpp447
-rw-r--r--src/wcautil/wcautil.cpp216
-rw-r--r--src/wcautil/wcautil.nuspec23
-rw-r--r--src/wcautil/wcautil.vcxproj79
-rw-r--r--src/wcautil/wcawow64.cpp169
-rw-r--r--src/wcautil/wcawrap.cpp1643
-rw-r--r--src/wcautil/wcawrapquery.cpp717
18 files changed, 4752 insertions, 0 deletions
diff --git a/src/wcautil/build/WixToolset.WcaUtil.props b/src/wcautil/build/WixToolset.WcaUtil.props
new file mode 100644
index 00000000..71a9743e
--- /dev/null
+++ b/src/wcautil/build/WixToolset.WcaUtil.props
@@ -0,0 +1,23 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- 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. -->
3
4<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
5 <ItemDefinitionGroup>
6 <ClCompile>
7 <AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)native\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
8 </ClCompile>
9 <ResourceCompile>
10 <AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)native\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
11 </ResourceCompile>
12 </ItemDefinitionGroup>
13 <ItemDefinitionGroup Condition=" $(PlatformToolset.ToLower().StartsWith('v140')) ">
14 <Link>
15 <AdditionalDependencies>$(MSBuildThisFileDirectory)native\lib\v140\$(PlatformTarget)\wcautil.lib;%(AdditionalDependencies)</AdditionalDependencies>
16 </Link>
17 </ItemDefinitionGroup>
18 <ItemDefinitionGroup Condition=" $(PlatformToolset.ToLower().StartsWith('v141')) ">
19 <Link>
20 <AdditionalDependencies>$(MSBuildThisFileDirectory)native\lib\v141\$(PlatformTarget)\wcautil.lib;%(AdditionalDependencies)</AdditionalDependencies>
21 </Link>
22 </ItemDefinitionGroup>
23</Project>
diff --git a/src/wcautil/custommsierrors.h b/src/wcautil/custommsierrors.h
new file mode 100644
index 00000000..f149fb31
--- /dev/null
+++ b/src/wcautil/custommsierrors.h
@@ -0,0 +1,130 @@
1#pragma once
2// 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.
3
4
5#define GLOBAL_ERROR_BASE 25501
6
7#define msierrSecureObjectsFailedCreateSD 25520
8#define msierrSecureObjectsFailedSet 25521
9#define msierrSecureObjectsUnknownType 25522
10
11#define msierrXmlFileFailedRead 25530
12#define msierrXmlFileFailedOpen 25531
13#define msierrXmlFileFailedSelect 25532
14#define msierrXmlFileFailedSave 25533
15
16#define msierrXmlConfigFailedRead 25540
17#define msierrXmlConfigFailedOpen 25541
18#define msierrXmlConfigFailedSelect 25542
19#define msierrXmlConfigFailedSave 25543
20
21#define msierrFirewallCannotConnect 25580
22
23//---------------------------------------------------------------------------
24// Server CustomAction Errors
25// SERVER range: 26001-26100
26#define SERVER_ERROR_BASE 26000
27
28#define msierrIISCannotConnect 26001
29#define msierrIISFailedReadWebSite 26002
30#define msierrIISFailedReadWebDirs 26003
31#define msierrIISFailedReadVDirs 26004
32#define msierrIISFailedReadFilters 26005
33#define msierrIISFailedReadAppPool 26006
34#define msierrIISFailedReadMimeMap 26007
35#define msierrIISFailedReadProp 26008
36#define msierrIISFailedReadWebSvcExt 26009
37#define msierrIISFailedReadWebError 26010
38#define msierrIISFailedReadHttpHeader 26011
39
40#define msierrIISFailedSchedTransaction 26031
41#define msierrIISFailedSchedInstallWebs 26032
42#define msierrIISFailedSchedInstallWebDirs 26033
43#define msierrIISFailedSchedInstallVDirs 26034
44#define msierrIISFailedSchedInstallFilters 26035
45#define msierrIISFailedSchedInstallAppPool 26036
46#define msierrIISFailedSchedInstallProp 26037
47#define msierrIISFailedSchedInstallWebSvcExt 26038
48
49#define msierrIISFailedSchedUninstallWebs 26051
50#define msierrIISFailedSchedUninstallWebDirs 26052
51#define msierrIISFailedSchedUninstallVDirs 26053
52#define msierrIISFailedSchedUninstallFilters 26054
53#define msierrIISFailedSchedUninstallAppPool 26055
54#define msierrIISFailedSchedUninstallProp 26056
55#define msierrIISFailedSchedUninstallWebSvcExt 26057
56
57#define msierrIISFailedStartTransaction 26101
58#define msierrIISFailedOpenKey 26102
59#define msierrIISFailedCreateKey 26103
60#define msierrIISFailedWriteData 26104
61#define msierrIISFailedCreateApp 26105
62#define msierrIISFailedDeleteKey 26106
63#define msierrIISFailedDeleteApp 26107
64#define msierrIISFailedDeleteValue 26108
65#define msierrIISFailedCommitInUse 26109
66
67#define msierrSQLFailedCreateDatabase 26201
68#define msierrSQLFailedDropDatabase 26202
69#define msierrSQLFailedConnectDatabase 26203
70#define msierrSQLFailedExecString 26204
71#define msierrSQLDatabaseAlreadyExists 26205
72
73#define msierrPERFMONFailedRegisterDLL 26251
74#define msierrPERFMONFailedUnregisterDLL 26252
75#define msierrInstallPerfCounterData 26253
76#define msierrUninstallPerfCounterData 26254
77
78#define msierrSMBFailedCreate 26301
79#define msierrSMBFailedDrop 26302
80
81#define msierrCERTFailedOpen 26351
82#define msierrCERTFailedAdd 26352
83
84#define msierrUSRFailedUserCreate 26401
85#define msierrUSRFailedUserCreatePswd 26402
86#define msierrUSRFailedUserGroupAdd 26403
87#define msierrUSRFailedUserCreateExists 26404
88#define msierrUSRFailedGrantLogonAsService 26405
89
90#define msierrDependencyMissingDependencies 26451
91#define msierrDependencyHasDependents 26452
92
93//--------------------------------------------------------------------------
94// Managed code CustomAction Errors
95// MANAGED range: 27000-27100
96#define MANAGED_ERROR_BASE 27000
97
98#define msierrDotNetRuntimeRequired 27000
99//---------------------------------------------------------------------------
100// Public CustomAction Errors
101// PUBLIC range: 28001-28100
102#define PUBLIC_ERROR_BASE 28000
103
104#define msierrComPlusCannotConnect 28001
105#define msierrComPlusPartitionReadFailed 28002
106#define msierrComPlusPartitionRoleReadFailed 28003
107#define msierrComPlusUserInPartitionRoleReadFailed 28004
108#define msierrComPlusPartitionUserReadFailed 28005
109#define msierrComPlusApplicationReadFailed 28006
110#define msierrComPlusApplicationRoleReadFailed 28007
111#define msierrComPlusUserInApplicationRoleReadFailed 28008
112#define msierrComPlusAssembliesReadFailed 28009
113#define msierrComPlusSubscriptionReadFailed 28010
114#define msierrComPlusPartitionDependency 28011
115#define msierrComPlusPartitionNotFound 28012
116#define msierrComPlusPartitionIdConflict 28013
117#define msierrComPlusPartitionNameConflict 28014
118#define msierrComPlusApplicationDependency 28015
119#define msierrComPlusApplicationNotFound 28016
120#define msierrComPlusApplicationIdConflict 28017
121#define msierrComPlusApplicationNameConflict 28018
122#define msierrComPlusApplicationRoleDependency 28019
123#define msierrComPlusApplicationRoleNotFound 28020
124#define msierrComPlusApplicationRoleConflict 28021
125#define msierrComPlusAssemblyDependency 28022
126#define msierrComPlusSubscriptionIdConflict 28023
127#define msierrComPlusSubscriptionNameConflict 28024
128#define msierrComPlusFailedLookupNames 28025
129
130#define msierrMsmqCannotConnect 28101
diff --git a/src/wcautil/exbinary.cpp b/src/wcautil/exbinary.cpp
new file mode 100644
index 00000000..5ff24212
--- /dev/null
+++ b/src/wcautil/exbinary.cpp
@@ -0,0 +1,142 @@
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// Extracts the data from the Binary table row with the given ID into a buffer.
7//
8HRESULT WIXAPI WcaExtractBinaryToBuffer(
9 __in LPCWSTR wzBinaryId,
10 __out BYTE** pbData,
11 __out DWORD* pcbData
12 )
13{
14 HRESULT hr = S_OK;
15 LPWSTR pwzSql = NULL;
16 PMSIHANDLE hView;
17 PMSIHANDLE hRec;
18
19 // make sure we're not horked from the get-go
20 hr = WcaTableExists(L"Binary");
21 if (S_OK != hr)
22 {
23 if (SUCCEEDED(hr))
24 {
25 hr = E_UNEXPECTED;
26 }
27 ExitOnFailure(hr, "There is no Binary table.");
28 }
29
30 ExitOnNull(wzBinaryId, hr, E_INVALIDARG, "Binary ID cannot be null");
31 ExitOnNull(*wzBinaryId, hr, E_INVALIDARG, "Binary ID cannot be empty string");
32
33 hr = StrAllocFormatted(&pwzSql, L"SELECT `Data` FROM `Binary` WHERE `Name`=\'%ls\'", wzBinaryId);
34 ExitOnFailure(hr, "Failed to allocate Binary table query.");
35
36 hr = WcaOpenExecuteView(pwzSql, &hView);
37 ExitOnFailure(hr, "Failed to open view on Binary table");
38
39 hr = WcaFetchSingleRecord(hView, &hRec);
40 ExitOnFailure(hr, "Failed to retrieve request from Binary table");
41
42 hr = WcaGetRecordStream(hRec, 1, pbData, pcbData);
43 ExitOnFailure(hr, "Failed to read Binary.Data.");
44
45LExit:
46 ReleaseStr(pwzSql);
47
48 return hr;
49}
50
51//
52// Extracts the data from the Binary table row with the given ID into a file.
53//
54HRESULT WIXAPI WcaExtractBinaryToFile(
55 __in LPCWSTR wzBinaryId,
56 __in LPCWSTR wzPath
57 )
58{
59 HRESULT hr = S_OK;
60 BYTE* pbData = NULL;
61 DWORD cbData = 0;
62 HANDLE hFile = INVALID_HANDLE_VALUE;
63
64 // grab the bits
65 hr = WcaExtractBinaryToBuffer(wzBinaryId, &pbData, &cbData);
66 ExitOnFailure(hr, "Failed to extract binary data: %ls", wzBinaryId);
67
68 // write 'em to the file
69 hFile = ::CreateFileW(wzPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
70 if (INVALID_HANDLE_VALUE == hFile)
71 {
72 ExitWithLastError(hr, "Failed to create file: %ls", wzPath);
73 }
74
75 DWORD cbWritten = 0;
76 if (!::WriteFile(hFile, pbData, cbData, &cbWritten, NULL))
77 {
78 ExitWithLastError(hr, "Failed to write data to file: %ls", wzPath);
79 }
80
81LExit:
82 ReleaseFile(hFile);
83 ReleaseMem(pbData);
84
85 return hr;
86}
87
88//
89// Extracts the data from the Binary table row with the given ID into a string.
90//
91HRESULT WIXAPI WcaExtractBinaryToString(
92 __in LPCWSTR wzBinaryId,
93 __deref_out_z LPWSTR* psczOutput,
94 __out WCA_ENCODING* encoding
95 )
96{
97 HRESULT hr = S_OK;
98 BYTE* pbData = NULL;
99 DWORD cbData = 0;
100
101 // grab the bits
102 hr = WcaExtractBinaryToBuffer(wzBinaryId, &pbData, &cbData);
103 ExitOnFailure(hr, "Failed to extract binary data: %ls", wzBinaryId);
104
105 // expand by a NULL character (or two) to make sure the buffer is null-terminated
106 cbData += 2;
107 pbData = reinterpret_cast<LPBYTE>(MemReAlloc(pbData, cbData, TRUE));
108 ExitOnNull(pbData, hr, E_OUTOFMEMORY, "Failed to expand binary buffer");
109
110 // Check for BOMs.
111 if (2 < cbData)
112 {
113 if ((0xFF == *pbData) && (0xFE == *(pbData + 1)))
114 {
115 *encoding = WCA_ENCODING_UTF_16;
116 hr = StrAllocString(psczOutput, reinterpret_cast<LPWSTR>(pbData), 0);
117 }
118 else if ((0xEF == *pbData) && (0xBB == *(pbData + 1)) && (0xBF == *(pbData + 2)))
119 {
120 *encoding = WCA_ENCODING_UTF_8;
121 hr = StrAllocStringAnsi(psczOutput, reinterpret_cast<LPCSTR>(pbData), 0, CP_UTF8);
122 }
123 else
124 {
125 *encoding = WCA_ENCODING_ANSI;
126 hr = StrAllocStringAnsi(psczOutput, reinterpret_cast<LPCSTR>(pbData), 0, CP_ACP);
127 }
128 ExitOnFailure(hr, "Failed to allocate string for binary buffer.");
129 }
130
131 // Free the byte buffer since it has been converted to a new UNICODE string, one way or another.
132 if (pbData)
133 {
134 WcaFreeStream(pbData);
135 pbData = NULL;
136 }
137
138LExit:
139 ReleaseMem(pbData);
140
141 return hr;
142}
diff --git a/src/wcautil/inc/wcalog.h b/src/wcautil/inc/wcalog.h
new file mode 100644
index 00000000..ffa3fa03
--- /dev/null
+++ b/src/wcautil/inc/wcalog.h
@@ -0,0 +1,14 @@
1#pragma once
2// 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.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9BOOL WIXAPI IsVerboseLogging();
10HRESULT WIXAPI SetVerboseLoggingAtom(BOOL bValue);
11
12#ifdef __cplusplus
13}
14#endif
diff --git a/src/wcautil/inc/wcautil.h b/src/wcautil/inc/wcautil.h
new file mode 100644
index 00000000..8139a7ca
--- /dev/null
+++ b/src/wcautil/inc/wcautil.h
@@ -0,0 +1,384 @@
1#pragma once
2// 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.
3
4
5#ifdef __cplusplus
6extern "C" {
7#endif
8
9#define WIXAPI __stdcall
10#define ExitTrace WcaLogError
11
12#include "dutil.h"
13
14#define MessageExitOnLastError(x, e, s, ...) { x = ::GetLastError(); x = HRESULT_FROM_WIN32(x); if (FAILED(x)) { ExitTrace(x, "%s", s, __VA_ARGS__); WcaErrorMessage(e, x, MB_OK, -1, __VA_ARGS__); goto LExit; } }
15#define MessageExitOnFailure(x, e, s, ...) if (FAILED(x)) { ExitTrace(x, "%s", s, __VA_ARGS__); WcaErrorMessage(e, x, INSTALLMESSAGE_ERROR | MB_OK, -1, __VA_ARGS__); goto LExit; }
16#define MessageExitOnNullWithLastError(p, x, e, s, ...) if (NULL == p) { x = ::GetLastError(); x = HRESULT_FROM_WIN32(x); if (!FAILED(x)) { x = E_FAIL; } ExitTrace(x, "%s", s, __VA_ARGS__); WcaErrorMessage(e, x, MB_OK, -1, __VA_ARGS__); goto LExit; }
17
18// Generic action enum.
19typedef enum WCA_ACTION
20{
21 WCA_ACTION_NONE,
22 WCA_ACTION_INSTALL,
23 WCA_ACTION_UNINSTALL,
24} WCA_ACTION;
25
26typedef enum WCA_CASCRIPT
27{
28 WCA_CASCRIPT_SCHEDULED,
29 WCA_CASCRIPT_ROLLBACK,
30} WCA_CASCRIPT;
31
32typedef enum WCA_CASCRIPT_CLOSE
33{
34 WCA_CASCRIPT_CLOSE_PRESERVE,
35 WCA_CASCRIPT_CLOSE_DELETE,
36} WCA_CASCRIPT_CLOSE;
37
38typedef enum WCA_TODO
39{
40 WCA_TODO_UNKNOWN,
41 WCA_TODO_INSTALL,
42 WCA_TODO_UNINSTALL,
43 WCA_TODO_REINSTALL,
44} WCA_TODO;
45
46typedef struct WCA_CASCRIPT_STRUCT
47{
48 LPWSTR pwzScriptPath;
49 HANDLE hScriptFile;
50} *WCA_CASCRIPT_HANDLE;
51
52typedef enum WCA_ENCODING
53{
54 WCA_ENCODING_UNKNOWN,
55 WCA_ENCODING_UTF_16,
56 WCA_ENCODING_UTF_8,
57 WCA_ENCODING_ANSI,
58} WCA_ENCODING;
59
60void WIXAPI WcaGlobalInitialize(
61 __in HINSTANCE hInst
62 );
63void WIXAPI WcaGlobalFinalize();
64
65HRESULT WIXAPI WcaInitialize(
66 __in MSIHANDLE hInstall,
67 __in_z PCSTR szCustomActionLogName
68 );
69UINT WIXAPI WcaFinalize(
70 __in UINT iReturnValue
71 );
72BOOL WIXAPI WcaIsInitialized();
73
74MSIHANDLE WIXAPI WcaGetInstallHandle();
75MSIHANDLE WIXAPI WcaGetDatabaseHandle();
76
77const char* WIXAPI WcaGetLogName();
78
79void WIXAPI WcaSetReturnValue(
80 __in UINT iReturnValue
81 );
82BOOL WIXAPI WcaCancelDetected();
83
84#define LOG_BUFFER 2048
85typedef enum LOGLEVEL
86{
87 LOGMSG_TRACEONLY, // Never written to the log file (except in DEBUG builds)
88 LOGMSG_VERBOSE, // Written to log when LOGVERBOSE
89 LOGMSG_STANDARD // Written to log whenever informational logging is enabled
90} LOGLEVEL;
91
92void __cdecl WcaLog(
93 __in LOGLEVEL llv,
94 __in_z __format_string PCSTR fmt, ...
95 );
96BOOL WIXAPI WcaDisplayAssert(
97 __in LPCSTR sz
98 );
99void __cdecl WcaLogError(
100 __in HRESULT hr,
101 __in LPCSTR szMessage,
102 ...
103 );
104
105UINT WIXAPI WcaProcessMessage(
106 __in INSTALLMESSAGE eMessageType,
107 __in MSIHANDLE hRecord
108 );
109UINT __cdecl WcaErrorMessage(
110 __in int iError,
111 __in HRESULT hrError,
112 __in UINT uiType,
113 __in INT cArgs,
114 ...
115 );
116HRESULT WIXAPI WcaProgressMessage(
117 __in UINT uiCost,
118 __in BOOL fExtendProgressBar
119 );
120
121BOOL WIXAPI WcaIsInstalling(
122 __in INSTALLSTATE isInstalled,
123 __in INSTALLSTATE isAction
124 );
125BOOL WIXAPI WcaIsReInstalling(
126 __in INSTALLSTATE isInstalled,
127 __in INSTALLSTATE isAction
128 );
129BOOL WIXAPI WcaIsUninstalling(
130 __in INSTALLSTATE isInstalled,
131 __in INSTALLSTATE isAction
132 );
133
134HRESULT WIXAPI WcaSetComponentState(
135 __in_z LPCWSTR wzComponent,
136 __in INSTALLSTATE isState
137 );
138
139HRESULT WIXAPI WcaTableExists(
140 __in_z LPCWSTR wzTable
141 );
142
143HRESULT WIXAPI WcaOpenView(
144 __in_z LPCWSTR wzSql,
145 __out MSIHANDLE* phView
146 );
147HRESULT WIXAPI WcaExecuteView(
148 __in MSIHANDLE hView,
149 __in MSIHANDLE hRec
150 );
151HRESULT WIXAPI WcaOpenExecuteView(
152 __in_z LPCWSTR wzSql,
153 __out MSIHANDLE* phView
154 );
155HRESULT WIXAPI WcaFetchRecord(
156 __in MSIHANDLE hView,
157 __out MSIHANDLE* phRec
158 );
159HRESULT WIXAPI WcaFetchSingleRecord(
160 __in MSIHANDLE hView,
161 __out MSIHANDLE* phRec
162 );
163
164HRESULT WIXAPI WcaGetProperty(
165 __in_z LPCWSTR wzProperty,
166 __inout LPWSTR* ppwzData
167 );
168HRESULT WIXAPI WcaGetFormattedProperty(
169 __in_z LPCWSTR wzProperty,
170 __out LPWSTR* ppwzData
171 );
172HRESULT WIXAPI WcaGetFormattedString(
173 __in_z LPCWSTR wzString,
174 __out LPWSTR* ppwzData
175 );
176HRESULT WIXAPI WcaGetIntProperty(
177 __in_z LPCWSTR wzProperty,
178 __inout int* piData
179 );
180HRESULT WIXAPI WcaGetTargetPath(
181 __in_z LPCWSTR wzFolder,
182 __out LPWSTR* ppwzData
183 );
184HRESULT WIXAPI WcaSetProperty(
185 __in_z LPCWSTR wzPropertyName,
186 __in_z LPCWSTR wzPropertyValue
187 );
188HRESULT WIXAPI WcaSetIntProperty(
189 __in_z LPCWSTR wzPropertyName,
190 __in int nPropertyValue
191 );
192BOOL WIXAPI WcaIsPropertySet(
193 __in LPCSTR szProperty
194 );
195BOOL WIXAPI WcaIsUnicodePropertySet(
196 __in LPCWSTR wzProperty
197 );
198
199HRESULT WIXAPI WcaGetRecordInteger(
200 __in MSIHANDLE hRec,
201 __in UINT uiField,
202 __inout int* piData
203 );
204HRESULT WIXAPI WcaGetRecordString(
205 __in MSIHANDLE hRec,
206 __in UINT uiField,
207 __inout LPWSTR* ppwzData
208 );
209HRESULT WIXAPI WcaGetRecordFormattedInteger(
210 __in MSIHANDLE hRec,
211 __in UINT uiField,
212 __out int* piData
213 );
214HRESULT WIXAPI WcaGetRecordFormattedString(
215 __in MSIHANDLE hRec,
216 __in UINT uiField,
217 __inout LPWSTR* ppwzData
218 );
219
220HRESULT WIXAPI WcaAllocStream(
221 __deref_out_bcount_part(cbData, 0) BYTE** ppbData,
222 __in DWORD cbData
223 );
224HRESULT WIXAPI WcaFreeStream(
225 __in BYTE* pbData
226 );
227
228HRESULT WIXAPI WcaGetRecordStream(
229 __in MSIHANDLE hRecBinary,
230 __in UINT uiField,
231 __deref_out_bcount_full(*pcbData) BYTE** ppbData,
232 __out DWORD* pcbData
233 );
234HRESULT WIXAPI WcaSetRecordString(
235 __in MSIHANDLE hRec,
236 __in UINT uiField,
237 __in_z LPCWSTR wzData
238 );
239HRESULT WIXAPI WcaSetRecordInteger(
240 __in MSIHANDLE hRec,
241 __in UINT uiField,
242 __in int iValue
243 );
244
245HRESULT WIXAPI WcaDoDeferredAction(
246 __in_z LPCWSTR wzAction,
247 __in_z LPCWSTR wzCustomActionData,
248 __in UINT uiCost
249 );
250DWORD WIXAPI WcaCountOfCustomActionDataRecords(
251 __in_z LPCWSTR wzData
252 );
253
254HRESULT WIXAPI WcaReadStringFromCaData(
255 __deref_in LPWSTR* ppwzCustomActionData,
256 __deref_out_z LPWSTR* ppwzString
257 );
258HRESULT WIXAPI WcaReadIntegerFromCaData(
259 __deref_in LPWSTR* ppwzCustomActionData,
260 __out int* piResult
261 );
262HRESULT WIXAPI WcaReadStreamFromCaData(
263 __deref_in LPWSTR* ppwzCustomActionData,
264 __deref_out_bcount(*pcbData) BYTE** ppbData,
265 __out DWORD_PTR* pcbData
266 );
267HRESULT WIXAPI WcaWriteStringToCaData(
268 __in_z LPCWSTR wzString,
269 __deref_inout_z LPWSTR* ppwzCustomActionData
270 );
271HRESULT WIXAPI WcaWriteIntegerToCaData(
272 __in int i,
273 __deref_out_z_opt LPWSTR* ppwzCustomActionData
274 );
275HRESULT WIXAPI WcaWriteStreamToCaData(
276 __in_bcount(cbData) const BYTE* pbData,
277 __in DWORD cbData,
278 __deref_inout_z_opt LPWSTR* ppwzCustomActionData
279 );
280
281HRESULT __cdecl WcaAddTempRecord(
282 __inout MSIHANDLE* phTableView,
283 __inout MSIHANDLE* phColumns,
284 __in_z LPCWSTR wzTable,
285 __out_opt MSIDBERROR* pdbError,
286 __in UINT uiUniquifyColumn,
287 __in UINT cColumns,
288 ...
289 );
290
291HRESULT WIXAPI WcaDumpTable(
292 __in_z LPCWSTR wzTable
293 );
294
295HRESULT WIXAPI WcaDeferredActionRequiresReboot();
296BOOL WIXAPI WcaDidDeferredActionRequireReboot();
297
298HRESULT WIXAPI WcaCaScriptCreateKey(
299 __out LPWSTR* ppwzScriptKey
300 );
301
302HRESULT WIXAPI WcaCaScriptCreate(
303 __in WCA_ACTION action,
304 __in WCA_CASCRIPT script,
305 __in BOOL fImpersonated,
306 __in_z LPCWSTR wzScriptKey,
307 __in BOOL fAppend,
308 __out WCA_CASCRIPT_HANDLE* phScript
309 );
310
311HRESULT WIXAPI WcaCaScriptOpen(
312 __in WCA_ACTION action,
313 __in WCA_CASCRIPT script,
314 __in BOOL fImpersonated,
315 __in_z LPCWSTR wzScriptKey,
316 __out WCA_CASCRIPT_HANDLE* phScript
317 );
318
319void WIXAPI WcaCaScriptClose(
320 __in_opt WCA_CASCRIPT_HANDLE hScript,
321 __in WCA_CASCRIPT_CLOSE closeOperation
322 );
323
324HRESULT WIXAPI WcaCaScriptReadAsCustomActionData(
325 __in WCA_CASCRIPT_HANDLE hScript,
326 __out LPWSTR* ppwzCustomActionData
327 );
328
329HRESULT WIXAPI WcaCaScriptWriteString(
330 __in WCA_CASCRIPT_HANDLE hScript,
331 __in_z LPCWSTR wzValue
332 );
333
334HRESULT WIXAPI WcaCaScriptWriteNumber(
335 __in WCA_CASCRIPT_HANDLE hScript,
336 __in DWORD dwValue
337 );
338
339void WIXAPI WcaCaScriptFlush(
340 __in WCA_CASCRIPT_HANDLE hScript
341 );
342
343void WIXAPI WcaCaScriptCleanup(
344 __in_z LPCWSTR wzProductCode,
345 __in BOOL fImpersonated
346 );
347
348HRESULT WIXAPI QuietExec(
349 __inout_z LPWSTR wzCommand,
350 __in DWORD dwTimeout,
351 __in BOOL fLogCommand,
352 __in BOOL fLogOutput
353 );
354
355HRESULT WIXAPI QuietExecCapture(
356 __inout_z LPWSTR wzCommand,
357 __in DWORD dwTimeout,
358 __in BOOL fLogCommand,
359 __in BOOL fLogOutput,
360 __out_z_opt LPWSTR* psczOutput
361 );
362
363WCA_TODO WIXAPI WcaGetComponentToDo(
364 __in_z LPCWSTR wzComponentId
365 );
366
367HRESULT WIXAPI WcaExtractBinaryToBuffer(
368 __in LPCWSTR wzBinaryId,
369 __out BYTE** pbData,
370 __out DWORD* pcbData
371 );
372HRESULT WIXAPI WcaExtractBinaryToFile(
373 __in LPCWSTR wzBinaryId,
374 __in LPCWSTR wzPath
375 );
376HRESULT WIXAPI WcaExtractBinaryToString(
377 __in LPCWSTR wzBinaryId,
378 __deref_out_z LPWSTR* psczOutput,
379 __out WCA_ENCODING* encoding
380 );
381
382#ifdef __cplusplus
383}
384#endif
diff --git a/src/wcautil/inc/wcawow64.h b/src/wcautil/inc/wcawow64.h
new file mode 100644
index 00000000..dd55f3fd
--- /dev/null
+++ b/src/wcautil/inc/wcawow64.h
@@ -0,0 +1,20 @@
1#pragma once
2// 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.
3
4
5#include "wcautil.h"
6
7#ifdef __cplusplus
8extern "C" {
9#endif
10
11HRESULT WIXAPI WcaInitializeWow64();
12BOOL WIXAPI WcaIsWow64Process();
13BOOL WIXAPI WcaIsWow64Initialized();
14HRESULT WIXAPI WcaDisableWow64FSRedirection();
15HRESULT WIXAPI WcaRevertWow64FSRedirection();
16HRESULT WIXAPI WcaFinalizeWow64();
17
18#ifdef __cplusplus
19}
20#endif
diff --git a/src/wcautil/inc/wcawrapquery.h b/src/wcautil/inc/wcawrapquery.h
new file mode 100644
index 00000000..e08f1c3f
--- /dev/null
+++ b/src/wcautil/inc/wcawrapquery.h
@@ -0,0 +1,130 @@
1#pragma once
2// 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.
3
4
5#include "wcautil.h"
6
7// Enumerations
8typedef enum eWrapQueryAction
9{
10 wqaTableBegin = 1,
11 wqaTableFinish,
12 wqaRowBegin,
13 wqaRowFinish
14} eWrapQueryAction;
15
16typedef enum eColumnDataType
17{
18 cdtString = 1,
19 cdtInt,
20 cdtStream,
21 cdtUnknown
22} eColumnDataType;
23
24typedef enum eFormatMaskColumn
25{
26 efmcColumn1 = 1,
27 efmcColumn2 = 1 << 1,
28 efmcColumn3 = 1 << 2,
29 efmcColumn4 = 1 << 3,
30 efmcColumn5 = 1 << 4,
31 efmcColumn6 = 1 << 5,
32 efmcColumn7 = 1 << 6,
33 efmcColumn8 = 1 << 7,
34 efmcColumn9 = 1 << 8,
35 efmcColumn10 = 1 << 9,
36 efmcColumn11 = 1 << 10,
37 efmcColumn12 = 1 << 11,
38 efmcColumn13 = 1 << 12,
39 efmcColumn14 = 1 << 13,
40 efmcColumn15 = 1 << 14,
41 efmcColumn16 = 1 << 15,
42 efmcColumn17 = 1 << 16,
43 efmcColumn18 = 1 << 17,
44 efmcColumn19 = 1 << 18,
45 efmcColumn20 = 1 << 19,
46 efmcColumn21 = 1 << 20,
47 efmcColumn22 = 1 << 21,
48 efmcColumn23 = 1 << 22,
49 efmcColumn24 = 1 << 23,
50 efmcColumn25 = 1 << 24,
51 efmcColumn26 = 1 << 25,
52 efmcColumn27 = 1 << 26,
53 efmcColumn28 = 1 << 27,
54 efmcColumn29 = 1 << 28,
55 efmcColumn30 = 1 << 29,
56 efmcColumn31 = 1 << 30,
57 efmcColumn32 = 1 << 31,
58} eFormatMaskColumn;
59
60// Keeps track of the query instance for the reading CA (deferred CA)
61typedef struct WCA_WRAPQUERY_STRUCT
62{
63 // These are used to size our dynamic arrays below
64 DWORD dwColumns, dwRows, dwNextIndex;
65
66 // Dynamic arrays of column schema information
67 eColumnDataType *pcdtColumnType;
68 LPWSTR *ppwzColumnNames;
69
70 // Dynamic array of raw record data
71 MSIHANDLE *phRecords;
72} *WCA_WRAPQUERY_HANDLE;
73
74// Wrap a query
75// Setting the pfFormatMask enables control over which fields will be formatted, and which will be left unchanged
76// Setting dwComponentColumn to something other than 0xFFFFFFFF tells WcaWrapQuery to add two additional columns to the right side of the table
77// - ISInstalled and ISAction - which map to the ComponentState of the component (the component is found in the column specified)
78// Note that if a component is NULL, the component state columns will also be left null, and it will be up to the deferred CA to fail or ignore the case appropriately
79// Setting dwDirectoryColumn to something other than 0xFFFFFFFF tells WcaWrapQuery to add two more additional columns to the right side of the table
80// - SourcePath and TargetPath - which map to the Directory's Source and Target Path (the directory is found in the column specified)
81// Note that if a directory is NULL, the directory source/target path columns will also be left null, and it will be up to the deferred CA to fail or ignore the case appropriately
82HRESULT WIXAPI WcaWrapQuery(
83 __in_z LPCWSTR pwzQuery,
84 __inout LPWSTR * ppwzCustomActionData,
85 __in_opt DWORD dwFormatMask,
86 __in_opt DWORD dwComponentColumn,
87 __in_opt DWORD dwDirectoryColumn
88 );
89// This wraps an empty table query into the custom action data - this is a way to indicate to the deferred custom action that a necessary table doesn't exist, or its query returned no results
90HRESULT WIXAPI WcaWrapEmptyQuery(
91 __inout LPWSTR * ppwzCustomActionData
92 );
93
94// Open a new unwrap query operation, with data from the ppwzCustomActionData string
95HRESULT WIXAPI WcaBeginUnwrapQuery(
96 __out WCA_WRAPQUERY_HANDLE * phWrapQuery,
97 __inout LPWSTR * ppwzCustomActionData
98 );
99
100// Get the number of records in a query being unwrapped
101DWORD WIXAPI WcaGetQueryRecords(
102 __in const WCA_WRAPQUERY_HANDLE hWrapQuery
103 );
104
105// This function resets a query back to its first row, so that the next fetch returns the first record
106void WIXAPI WcaFetchWrappedReset(
107 __in WCA_WRAPQUERY_HANDLE hWrapQuery
108 );
109// Fetch the next record in this query
110// NOTE: the MSIHANDLE returned by this function should not be released, as it is the same handle used by the query object to maintain the item.
111// so, don't use this function with PMSIHANDLE objects!
112HRESULT WIXAPI WcaFetchWrappedRecord(
113 __in WCA_WRAPQUERY_HANDLE hWrapQuery,
114 __out MSIHANDLE* phRec
115 );
116
117// Fetch the next record in the query where the string value in column dwComparisonColumn equals the value pwzExpectedValue
118// NOTE: the MSIHANDLE returned by this function should not be released, as it is the same handle used by the query object to maintain the item.
119// so, don't use this function with PMSIHANDLE objects!
120HRESULT WIXAPI WcaFetchWrappedRecordWhereString(
121 __in WCA_WRAPQUERY_HANDLE hWrapQuery,
122 __in DWORD dwComparisonColumn,
123 __in_z LPCWSTR pwzExpectedValue,
124 __out MSIHANDLE* phRec
125 );
126
127// Release a query ID (frees memory, and frees the ID for a new query)
128void WIXAPI WcaFinishUnwrapQuery(
129 __in_opt WCA_WRAPQUERY_HANDLE hWrapQuery
130 );
diff --git a/src/wcautil/packages.config b/src/wcautil/packages.config
new file mode 100644
index 00000000..b11fe210
--- /dev/null
+++ b/src/wcautil/packages.config
@@ -0,0 +1,5 @@
1<?xml version="1.0" encoding="utf-8"?>
2<packages>
3 <package id="Nerdbank.GitVersioning" version="2.0.37-beta" targetFramework="native" developmentDependency="true" />
4 <package id="WixToolset.DUtil" version="4.0.3" targetFramework="native" />
5</packages> \ No newline at end of file
diff --git a/src/wcautil/precomp.h b/src/wcautil/precomp.h
new file mode 100644
index 00000000..1d41337a
--- /dev/null
+++ b/src/wcautil/precomp.h
@@ -0,0 +1,19 @@
1#pragma once
2// 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.
3
4
5#include <windows.h>
6#include <msiquery.h>
7#include <wchar.h>
8#include <strsafe.h>
9
10const WCHAR MAGIC_MULTISZ_DELIM = 128;
11
12#include "wcautil.h"
13#include "inc\wcalog.h"
14#include "inc\wcawow64.h"
15#include "inc\wcawrapquery.h"
16#include "wiutil.h"
17#include "fileutil.h"
18#include "memutil.h"
19#include "strutil.h"
diff --git a/src/wcautil/qtexec.cpp b/src/wcautil/qtexec.cpp
new file mode 100644
index 00000000..19abfaf8
--- /dev/null
+++ b/src/wcautil/qtexec.cpp
@@ -0,0 +1,340 @@
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#define OUTPUT_BUFFER 1024
6#define ONEMINUTE 60000
7
8static HRESULT CreatePipes(
9 __out HANDLE *phOutRead,
10 __out HANDLE *phOutWrite,
11 __out HANDLE *phErrWrite,
12 __out HANDLE *phInRead,
13 __out HANDLE *phInWrite
14 )
15{
16 Assert(phOutRead);
17 Assert(phOutWrite);
18 Assert(phErrWrite);
19 Assert(phInRead);
20 Assert(phInWrite);
21
22 HRESULT hr = S_OK;
23 SECURITY_ATTRIBUTES sa;
24 HANDLE hOutTemp = INVALID_HANDLE_VALUE;
25 HANDLE hInTemp = INVALID_HANDLE_VALUE;
26
27 HANDLE hOutRead = INVALID_HANDLE_VALUE;
28 HANDLE hOutWrite = INVALID_HANDLE_VALUE;
29 HANDLE hErrWrite = INVALID_HANDLE_VALUE;
30 HANDLE hInRead = INVALID_HANDLE_VALUE;
31 HANDLE hInWrite = INVALID_HANDLE_VALUE;
32
33 // Fill out security structure so we can inherit handles
34 ::ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
35 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
36 sa.bInheritHandle = TRUE;
37 sa.lpSecurityDescriptor = NULL;
38
39 // Create pipes
40 if (!::CreatePipe(&hOutTemp, &hOutWrite, &sa, 0))
41 {
42 ExitOnLastError(hr, "Failed to create output pipe");
43 }
44
45 if (!::CreatePipe(&hInRead, &hInTemp, &sa, 0))
46 {
47 ExitOnLastError(hr, "Failed to create input pipe");
48 }
49
50
51 // Duplicate output pipe so standard error and standard output write to
52 // the same pipe
53 if (!::DuplicateHandle(::GetCurrentProcess(), hOutWrite, ::GetCurrentProcess(), &hErrWrite, 0, TRUE, DUPLICATE_SAME_ACCESS))
54 {
55 ExitOnLastError(hr, "Failed to duplicate write handle");
56 }
57
58 // We need to create new output read and input write handles that are
59 // non inheritable. Otherwise it creates handles that can't be closed.
60 if (!::DuplicateHandle(::GetCurrentProcess(), hOutTemp, ::GetCurrentProcess(), &hOutRead, 0, FALSE, DUPLICATE_SAME_ACCESS))
61 {
62 ExitOnLastError(hr, "Failed to duplicate output pipe");
63 }
64
65 if (!::DuplicateHandle(::GetCurrentProcess(), hInTemp, ::GetCurrentProcess(), &hInWrite, 0, FALSE, DUPLICATE_SAME_ACCESS))
66 {
67 ExitOnLastError(hr, "Failed to duplicate input pipe");
68 }
69
70 // now that everything has succeeded, assign to the outputs
71 *phOutRead = hOutRead;
72 hOutRead = INVALID_HANDLE_VALUE;
73
74 *phOutWrite = hOutWrite;
75 hOutWrite = INVALID_HANDLE_VALUE;
76
77 *phErrWrite = hErrWrite;
78 hErrWrite = INVALID_HANDLE_VALUE;
79
80 *phInRead = hInRead;
81 hInRead = INVALID_HANDLE_VALUE;
82
83 *phInWrite = hInWrite;
84 hInWrite = INVALID_HANDLE_VALUE;
85
86LExit:
87 ReleaseFile(hOutRead);
88 ReleaseFile(hOutWrite);
89 ReleaseFile(hErrWrite);
90 ReleaseFile(hInRead);
91 ReleaseFile(hInWrite);
92 ReleaseFile(hOutTemp);
93 ReleaseFile(hInTemp);
94
95 return hr;
96}
97
98static HRESULT HandleOutput(
99 __in BOOL fLogOutput,
100 __in HANDLE hRead,
101 __out_z_opt LPWSTR* psczOutput
102 )
103{
104 BYTE* pBuffer = NULL;
105 LPWSTR szLog = NULL;
106 LPWSTR szTemp = NULL;
107 LPWSTR pEnd = NULL;
108 LPWSTR pNext = NULL;
109 LPWSTR sczEscaped = NULL;
110 LPSTR szWrite = NULL;
111 DWORD dwBytes = OUTPUT_BUFFER;
112 BOOL bFirst = TRUE;
113 BOOL bUnicode = TRUE;
114 HRESULT hr = S_OK;
115
116 // Get buffer for output
117 pBuffer = static_cast<BYTE *>(MemAlloc(OUTPUT_BUFFER, FALSE));
118 ExitOnNull(pBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for output.");
119
120 while (0 != dwBytes)
121 {
122 ::ZeroMemory(pBuffer, OUTPUT_BUFFER);
123 if (!::ReadFile(hRead, pBuffer, OUTPUT_BUFFER - 1, &dwBytes, NULL) && GetLastError() != ERROR_BROKEN_PIPE)
124 {
125 ExitOnLastError(hr, "Failed to read from handle.");
126 }
127
128 if (fLogOutput)
129 {
130 // Check for UNICODE or ANSI output
131 if (bFirst)
132 {
133 if ((isgraph(pBuffer[0]) && isgraph(pBuffer[1])) ||
134 (isgraph(pBuffer[0]) && isspace(pBuffer[1])) ||
135 (isspace(pBuffer[0]) && isgraph(pBuffer[1])) ||
136 (isspace(pBuffer[0]) && isspace(pBuffer[1])))
137 {
138 bUnicode = FALSE;
139 }
140
141 bFirst = FALSE;
142 }
143
144 // Keep track of output
145 if (bUnicode)
146 {
147 hr = StrAllocConcat(&szLog, (LPCWSTR)pBuffer, 0);
148 ExitOnFailure(hr, "Failed to concatenate output strings.");
149
150 if (psczOutput)
151 {
152 hr = StrAllocConcat(psczOutput, (LPCWSTR)pBuffer, 0);
153 ExitOnFailure(hr, "Failed to concatenate output to return string.");
154 }
155 }
156 else
157 {
158 hr = StrAllocStringAnsi(&szTemp, (LPCSTR)pBuffer, 0, CP_OEMCP);
159 ExitOnFailure(hr, "Failed to allocate output string.");
160 hr = StrAllocConcat(&szLog, szTemp, 0);
161 ExitOnFailure(hr, "Failed to concatenate output strings.");
162
163 if (psczOutput)
164 {
165 hr = StrAllocConcat(psczOutput, szTemp, 0);
166 ExitOnFailure(hr, "Failed to concatenate output to return string.");
167 }
168 }
169
170 // Log each line of the output
171 pNext = szLog;
172 pEnd = wcschr(szLog, L'\r');
173 if (NULL == pEnd)
174 {
175 pEnd = wcschr(szLog, L'\n');
176 }
177 while (pEnd && *pEnd)
178 {
179 // Find beginning of next line
180 pEnd[0] = 0;
181 ++pEnd;
182 if ((pEnd[0] == L'\r') || (pEnd[0] == L'\n'))
183 {
184 ++pEnd;
185 }
186
187 // Log output
188 hr = StrAllocString(&sczEscaped, pNext, 0);
189 ExitOnFailure(hr, "Failed to allocate copy of string");
190
191 hr = StrReplaceStringAll(&sczEscaped, L"%", L"%%");
192 ExitOnFailure(hr, "Failed to escape percent signs in string");
193
194 hr = StrAnsiAllocString(&szWrite, sczEscaped, 0, CP_OEMCP);
195 ExitOnFailure(hr, "Failed to convert output to ANSI");
196 WcaLog(LOGMSG_STANDARD, szWrite);
197
198 // Next line
199 pNext = pEnd;
200 pEnd = wcschr(pNext, L'\r');
201 if (NULL == pEnd)
202 {
203 pEnd = wcschr(pNext, L'\n');
204 }
205 }
206
207 hr = StrAllocString(&szTemp, pNext, 0);
208 ExitOnFailure(hr, "Failed to allocate string");
209
210 hr = StrAllocString(&szLog, szTemp, 0);
211 ExitOnFailure(hr, "Failed to allocate string");
212 }
213 }
214
215 // Print any text that didn't end with a new line
216 if (szLog && *szLog)
217 {
218 hr = StrReplaceStringAll(&szLog, L"%", L"%%");
219 ExitOnFailure(hr, "Failed to escape percent signs in string");
220
221 hr = StrAnsiAllocString(&szWrite, szLog, 0, CP_OEMCP);
222 ExitOnFailure(hr, "Failed to convert output to ANSI");
223
224 WcaLog(LOGMSG_VERBOSE, szWrite);
225 }
226
227LExit:
228 ReleaseMem(pBuffer);
229
230 ReleaseStr(szLog);
231 ReleaseStr(szTemp);
232 ReleaseStr(szWrite);
233 ReleaseStr(sczEscaped);
234
235 return hr;
236}
237
238static HRESULT QuietExecImpl(
239 __inout_z LPWSTR wzCommand,
240 __in DWORD dwTimeout,
241 __in BOOL fLogCommand,
242 __in BOOL fLogOutput,
243 __out_z_opt LPWSTR* psczOutput
244 )
245{
246 HRESULT hr = S_OK;
247 PROCESS_INFORMATION oProcInfo;
248 STARTUPINFOW oStartInfo;
249 DWORD dwExitCode = ERROR_SUCCESS;
250 HANDLE hOutRead = INVALID_HANDLE_VALUE;
251 HANDLE hOutWrite = INVALID_HANDLE_VALUE;
252 HANDLE hErrWrite = INVALID_HANDLE_VALUE;
253 HANDLE hInRead = INVALID_HANDLE_VALUE;
254 HANDLE hInWrite = INVALID_HANDLE_VALUE;
255
256 memset(&oProcInfo, 0, sizeof(oProcInfo));
257 memset(&oStartInfo, 0, sizeof(oStartInfo));
258
259 // Create output redirect pipes
260 hr = CreatePipes(&hOutRead, &hOutWrite, &hErrWrite, &hInRead, &hInWrite);
261 ExitOnFailure(hr, "Failed to create output pipes");
262
263 // Set up startup structure
264 oStartInfo.cb = sizeof(STARTUPINFOW);
265 oStartInfo.dwFlags = STARTF_USESTDHANDLES;
266 oStartInfo.hStdInput = hInRead;
267 oStartInfo.hStdOutput = hOutWrite;
268 oStartInfo.hStdError = hErrWrite;
269
270 // Log command if we were asked to do so
271 if (fLogCommand)
272 {
273 WcaLog(LOGMSG_VERBOSE, "%ls", wzCommand);
274 }
275
276#pragma prefast(suppress:25028)
277 if (::CreateProcessW(NULL,
278 wzCommand, // command line
279 NULL, // security info
280 NULL, // thread info
281 TRUE, // inherit handles
282 ::GetPriorityClass(::GetCurrentProcess()) | CREATE_NO_WINDOW, // creation flags
283 NULL, // environment
284 NULL, // cur dir
285 &oStartInfo,
286 &oProcInfo))
287 {
288 ReleaseFile(oProcInfo.hThread);
289
290 // Close child output/input handles so it doesn't hang
291 ReleaseFile(hOutWrite);
292 ReleaseFile(hErrWrite);
293 ReleaseFile(hInRead);
294
295 // Log output if we were asked to do so; otherwise just read the output handle
296 HandleOutput(fLogOutput, hOutRead, psczOutput);
297
298 // Wait for everything to finish
299 ::WaitForSingleObject(oProcInfo.hProcess, dwTimeout);
300 if (!::GetExitCodeProcess(oProcInfo.hProcess, &dwExitCode))
301 {
302 dwExitCode = ERROR_SEM_IS_SET;
303 }
304
305 ReleaseFile(hOutRead);
306 ReleaseFile(hInWrite);
307 ReleaseFile(oProcInfo.hProcess);
308 }
309 else
310 {
311 ExitOnLastError(hr, "Command failed to execute.");
312 }
313
314 ExitOnWin32Error(dwExitCode, hr, "Command line returned an error.");
315
316LExit:
317 return hr;
318}
319
320
321HRESULT WIXAPI QuietExec(
322 __inout_z LPWSTR wzCommand,
323 __in DWORD dwTimeout,
324 __in BOOL fLogCommand,
325 __in BOOL fLogOutput
326 )
327{
328 return QuietExecImpl(wzCommand, dwTimeout, fLogCommand, fLogOutput, NULL);
329}
330
331HRESULT WIXAPI QuietExecCapture(
332 __inout_z LPWSTR wzCommand,
333 __in DWORD dwTimeout,
334 __in BOOL fLogCommand,
335 __in BOOL fLogOutput,
336 __out_z_opt LPWSTR* psczOutput
337 )
338{
339 return QuietExecImpl(wzCommand, dwTimeout, fLogCommand, fLogOutput, psczOutput);
340}
diff --git a/src/wcautil/wcalog.cpp b/src/wcautil/wcalog.cpp
new file mode 100644
index 00000000..fa969bff
--- /dev/null
+++ b/src/wcautil/wcalog.cpp
@@ -0,0 +1,251 @@
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 IsVerboseLoggingPolicy() - internal helper function to detect if
7 policy is set for verbose logging. Does
8 not require database access.
9********************************************************************/
10static BOOL IsVerboseLoggingPolicy()
11{
12 BOOL fVerbose = FALSE;
13 HKEY hkey = NULL;
14 WCHAR rgwc[16] = { 0 };
15 DWORD cb = sizeof(rgwc);
16 if (ERROR_SUCCESS == ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Policies\\Microsoft\\Windows\\Installer", 0, KEY_QUERY_VALUE, &hkey))
17 {
18 if (ERROR_SUCCESS == ::RegQueryValueExW(hkey, L"Logging", 0, NULL, reinterpret_cast<BYTE*>(rgwc), &cb))
19 {
20 for (LPCWSTR pwc = rgwc; (cb / sizeof(WCHAR)) > static_cast<DWORD>(pwc - rgwc) && *pwc; pwc++)
21 {
22 if (L'v' == *pwc || L'V' == *pwc)
23 {
24 fVerbose = TRUE;
25 break;
26 }
27 }
28 }
29
30 ::RegCloseKey(hkey);
31 }
32 return fVerbose;
33}
34
35/********************************************************************
36 IsVerboseLogging() - internal helper function to detect if doing
37 verbose logging. Checks:
38 1. LOGVERBOSE property.
39 2. MsiLogging property contains 'v'
40 3. Policy from registry.
41
42 Requires database access.
43********************************************************************/
44BOOL WIXAPI IsVerboseLogging()
45{
46 static int iVerbose = -1;
47 LPWSTR pwzMsiLogging = NULL;
48
49 if (0 > iVerbose)
50 {
51 iVerbose = WcaIsPropertySet("LOGVERBOSE");
52 if (0 == iVerbose)
53 {
54 // if the property wasn't set, check the MsiLogging property (MSI 4.0+)
55 HRESULT hr = WcaGetProperty(L"MsiLogging", &pwzMsiLogging);
56 ExitOnFailure(hr, "failed to get MsiLogging property");
57 int cchMsiLogging = lstrlenW(pwzMsiLogging);
58 if (0 < cchMsiLogging)
59 {
60 for (int i = 0; i < cchMsiLogging; i++)
61 {
62 if (L'v' == pwzMsiLogging[i] || L'V' == pwzMsiLogging[i])
63 {
64 iVerbose = 1;
65 break;
66 }
67 }
68 }
69
70 // last chance: Check the registry to see if the logging policy was turned on
71 if (0 == iVerbose && IsVerboseLoggingPolicy())
72 {
73 iVerbose = 1;
74 }
75 }
76 }
77
78LExit:
79 ReleaseStr(pwzMsiLogging);
80 Assert(iVerbose >= 0);
81 return (BOOL)iVerbose;
82}
83
84/********************************************************************
85 SetVerboseLoggingAtom() - Sets one of two global Atoms to specify
86 if the install should do verbose logging.
87 Communicates the verbose setting to
88 deferred CAs.
89 Set a negative case atom so that we can
90 distinguish between an unset atom and the
91 non-verbose case. This helps prevent the
92 expensive regkey lookup for non-verbose.
93********************************************************************/
94HRESULT WIXAPI SetVerboseLoggingAtom(BOOL bValue)
95{
96 HRESULT hr = S_OK;
97 ATOM atomVerbose = 0;
98
99 atomVerbose = ::GlobalFindAtomW(L"WcaVerboseLogging");
100 if (0 == atomVerbose && bValue)
101 {
102 atomVerbose = ::GlobalAddAtomW(L"WcaVerboseLogging");
103 ExitOnNullWithLastError(atomVerbose, hr, "Failed to create WcaVerboseLogging global atom.");
104 }
105 else if (0 != atomVerbose && !bValue)
106 {
107 ::SetLastError(ERROR_SUCCESS);
108 ::GlobalDeleteAtom(atomVerbose);
109 ExitOnLastError(hr, "Failed to delete WcaVerboseLogging global atom.");
110 }
111
112 atomVerbose = ::GlobalFindAtomW(L"WcaNotVerboseLogging");
113 if (0 == atomVerbose && !bValue)
114 {
115 atomVerbose = ::GlobalAddAtomW(L"WcaNotVerboseLogging");
116 ExitOnNullWithLastError(atomVerbose, hr, "Failed to create WcaNotVerboseLogging global atom.");
117 }
118 else if (0 != atomVerbose && bValue)
119 {
120 ::SetLastError(ERROR_SUCCESS);
121 ::GlobalDeleteAtom(atomVerbose);
122 ExitOnLastError(hr, "Failed to delete WcaNotVerboseLogging global atom.");
123 }
124
125LExit:
126 return hr;
127}
128
129/********************************************************************
130 IsVerboseLoggingLite() - internal helper function to detect if atom was
131 previously set to specify verbose logging.
132 Falls back on policy for an installer that is
133 unable to set the atom (no immediate CAs).
134
135 Does not require database access.
136********************************************************************/
137static BOOL IsVerboseLoggingLite()
138{
139 ATOM atomVerbose = ::GlobalFindAtomW(L"WcaVerboseLogging");
140 if (0 != atomVerbose)
141 {
142 return TRUE;
143 }
144
145 atomVerbose = ::GlobalFindAtomW(L"WcaNotVerboseLogging");
146 if (0 != atomVerbose)
147 {
148 return FALSE;
149 }
150
151 return IsVerboseLoggingPolicy();
152}
153
154/********************************************************************
155 WcaLog() - outputs trace and log info
156
157*******************************************************************/
158extern "C" void __cdecl WcaLog(
159 __in LOGLEVEL llv,
160 __in_z __format_string PCSTR fmt,
161 ...
162 )
163{
164 static char szFmt[LOG_BUFFER];
165 static char szBuf[LOG_BUFFER];
166 static bool fInLogPrint = false;
167
168 // prevent re-entrant logprints. (recursion issues between assert/logging code)
169 if (fInLogPrint)
170 return;
171 fInLogPrint = true;
172
173 if (LOGMSG_STANDARD == llv ||
174 (LOGMSG_VERBOSE == llv && IsVerboseLoggingLite())
175#ifdef DEBUG
176 || LOGMSG_TRACEONLY == llv
177#endif
178 )
179 {
180 va_list args;
181 va_start(args, fmt);
182
183 LPCSTR szLogName = WcaGetLogName();
184 if (szLogName[0] != 0)
185 StringCchPrintfA(szFmt, countof(szFmt), "%s: %s", szLogName, fmt);
186 else
187 StringCchCopyA(szFmt, countof(szFmt), fmt);
188
189 StringCchVPrintfA(szBuf, countof(szBuf), szFmt, args);
190 va_end(args);
191
192#ifdef DEBUG
193 // always write to the log in debug
194#else
195 if (llv == LOGMSG_STANDARD || (llv == LOGMSG_VERBOSE && IsVerboseLoggingLite()))
196#endif
197 {
198 PMSIHANDLE hrec = MsiCreateRecord(1);
199
200 ::MsiRecordSetStringA(hrec, 0, szBuf);
201 // TODO: Recursion on failure. May not be safe to assert from here.
202 WcaProcessMessage(INSTALLMESSAGE_INFO, hrec);
203 }
204
205#if DEBUG
206 StringCchCatA(szBuf, countof(szBuf), "\n");
207 OutputDebugStringA(szBuf);
208#endif
209 }
210
211 fInLogPrint = false;
212 return;
213}
214
215
216/********************************************************************
217 WcaDisplayAssert() - called before Assert() dialog shows
218
219 NOTE: writes the assert string to the MSI log
220********************************************************************/
221extern "C" BOOL WIXAPI WcaDisplayAssert(
222 __in LPCSTR sz
223 )
224{
225 WcaLog(LOGMSG_STANDARD, "Debug Assert Message: %s", sz);
226 return TRUE;
227}
228
229
230/********************************************************************
231 WcaLogError() - called before ExitOnXXX() macro exists the function
232
233 NOTE: writes the hresult and error string to the MSI log
234********************************************************************/
235extern "C" void WcaLogError(
236 __in HRESULT hr,
237 __in LPCSTR szMessage,
238 ...
239 )
240{
241 char szBuffer[LOG_BUFFER];
242 va_list dots;
243
244 va_start(dots, szMessage);
245 StringCchVPrintfA(szBuffer, countof(szBuffer), szMessage, dots);
246 va_end(dots);
247
248 // log the message if using Wca common layer
249 if (WcaIsInitialized())
250 WcaLog(LOGMSG_STANDARD, "Error 0x%x: %s", hr, szBuffer);
251}
diff --git a/src/wcautil/wcascript.cpp b/src/wcautil/wcascript.cpp
new file mode 100644
index 00000000..b6629850
--- /dev/null
+++ b/src/wcautil/wcascript.cpp
@@ -0,0 +1,447 @@
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
6static HRESULT CaScriptFileName(
7 __in WCA_ACTION action,
8 __in WCA_CASCRIPT script,
9 __in BOOL fImpersonated,
10 __in_z LPCWSTR wzScriptKey,
11 __out LPWSTR* pwzScriptName
12 );
13
14
15/********************************************************************
16 WcaCaScriptCreateKey() - creates a unique script key for this
17 CustomAction.
18
19********************************************************************/
20extern "C" HRESULT WIXAPI WcaCaScriptCreateKey(
21 __out LPWSTR* ppwzScriptKey
22 )
23{
24 AssertSz(WcaIsInitialized(), "WcaInitialize() should have been called before calling this function.");
25 HRESULT hr = S_OK;
26
27 hr = StrAllocStringAnsi(ppwzScriptKey, WcaGetLogName(), 0, CP_ACP);
28 ExitOnFailure(hr, "Failed to create script key.");
29
30LExit:
31 return hr;
32}
33
34
35/********************************************************************
36 WcaCaScriptCreate() - creates the appropriate script for this
37 CustomAction Script Key.
38
39********************************************************************/
40extern "C" HRESULT WIXAPI WcaCaScriptCreate(
41 __in WCA_ACTION action,
42 __in WCA_CASCRIPT script,
43 __in BOOL fImpersonated,
44 __in_z LPCWSTR wzScriptKey,
45 __in BOOL fAppend,
46 __out WCA_CASCRIPT_HANDLE* phScript
47 )
48{
49 HRESULT hr = S_OK;
50 LPWSTR pwzScriptPath = NULL;
51 HANDLE hScriptFile = INVALID_HANDLE_VALUE;
52
53 hr = CaScriptFileName(action, script, fImpersonated, wzScriptKey, &pwzScriptPath);
54 ExitOnFailure(hr, "Failed to calculate script file name.");
55
56 hScriptFile = ::CreateFileW(pwzScriptPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, fAppend ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
57 if (INVALID_HANDLE_VALUE == hScriptFile)
58 {
59 ExitWithLastError(hr, "Failed to open CaScript: %ls", pwzScriptPath);
60 }
61
62 if (fAppend && INVALID_SET_FILE_POINTER == ::SetFilePointer(hScriptFile, 0, NULL, FILE_END))
63 {
64 ExitWithLastError(hr, "Failed to seek to end of file.");
65 }
66
67 *phScript = static_cast<WCA_CASCRIPT_HANDLE>(MemAlloc(sizeof(WCA_CASCRIPT_STRUCT), TRUE));
68 ExitOnNull(*phScript, hr, E_OUTOFMEMORY, "Failed to allocate space for cascript handle.");
69
70 (*phScript)->pwzScriptPath = pwzScriptPath;
71 pwzScriptPath = NULL;
72 (*phScript)->hScriptFile = hScriptFile;
73 hScriptFile = INVALID_HANDLE_VALUE;
74
75LExit:
76 if (INVALID_HANDLE_VALUE != hScriptFile)
77 {
78 ::CloseHandle(hScriptFile);
79 }
80
81 ReleaseStr(pwzScriptPath);
82 return hr;
83}
84
85
86/********************************************************************
87 WcaCaScriptOpen() - opens the appropriate script for this CustomAction
88 Script Key.
89
90********************************************************************/
91extern "C" HRESULT WIXAPI WcaCaScriptOpen(
92 __in WCA_ACTION action,
93 __in WCA_CASCRIPT script,
94 __in BOOL fImpersonated,
95 __in_z LPCWSTR wzScriptKey,
96 __out WCA_CASCRIPT_HANDLE* phScript
97 )
98{
99 HRESULT hr = S_OK;
100 LPWSTR pwzScriptPath = NULL;
101 HANDLE hScriptFile = INVALID_HANDLE_VALUE;
102
103 hr = CaScriptFileName(action, script, fImpersonated, wzScriptKey, &pwzScriptPath);
104 ExitOnFailure(hr, "Failed to calculate script file name.");
105
106 hScriptFile = ::CreateFileW(pwzScriptPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
107 if (INVALID_HANDLE_VALUE == hScriptFile)
108 {
109 ExitWithLastError(hr, "Failed to open CaScript: %ls", pwzScriptPath);
110 }
111
112 *phScript = static_cast<WCA_CASCRIPT_HANDLE>(MemAlloc(sizeof(WCA_CASCRIPT_STRUCT), TRUE));
113 ExitOnNull(*phScript, hr, E_OUTOFMEMORY, "Failed to allocate space for cascript handle.");
114
115 (*phScript)->pwzScriptPath = pwzScriptPath;
116 pwzScriptPath = NULL;
117 (*phScript)->hScriptFile = hScriptFile;
118 hScriptFile = INVALID_HANDLE_VALUE;
119
120LExit:
121 if (INVALID_HANDLE_VALUE != hScriptFile)
122 {
123 ::CloseHandle(hScriptFile);
124 }
125
126 ReleaseStr(pwzScriptPath);
127 return hr;
128}
129
130
131/********************************************************************
132 WcaCaScriptClose() - closes an open script handle.
133
134********************************************************************/
135extern "C" void WIXAPI WcaCaScriptClose(
136 __in_opt WCA_CASCRIPT_HANDLE hScript,
137 __in WCA_CASCRIPT_CLOSE closeOperation
138 )
139{
140 if (hScript)
141 {
142 if (INVALID_HANDLE_VALUE != hScript->hScriptFile)
143 {
144 ::CloseHandle(hScript->hScriptFile);
145 }
146
147 if (hScript->pwzScriptPath)
148 {
149 if (WCA_CASCRIPT_CLOSE_DELETE == closeOperation)
150 {
151 ::DeleteFileW(hScript->pwzScriptPath);
152 }
153
154 StrFree(hScript->pwzScriptPath);
155 }
156
157 MemFree(hScript);
158 }
159}
160
161
162/********************************************************************
163 WcaCaScriptReadAsCustomActionData() - read the ca script into a format
164 that is useable by other CA data
165 functions.
166
167********************************************************************/
168extern "C" HRESULT WIXAPI WcaCaScriptReadAsCustomActionData(
169 __in WCA_CASCRIPT_HANDLE hScript,
170 __out LPWSTR* ppwzCustomActionData
171 )
172{
173 HRESULT hr = S_OK;
174 LARGE_INTEGER liScriptSize = { 0 };
175 BYTE* pbData = NULL;
176 DWORD cbData = 0;
177
178 if (!::GetFileSizeEx(hScript->hScriptFile, &liScriptSize))
179 {
180 ExitWithLastError(hr, "Failed to get size of ca script file.");
181 }
182
183 if (0 != liScriptSize.HighPart || 0 != (liScriptSize.LowPart % sizeof(WCHAR)))
184 {
185 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
186 ExitOnRootFailure(hr, "Invalid data read from ca script.");
187 }
188
189 cbData = liScriptSize.LowPart;
190 if (cbData)
191 {
192 pbData = static_cast<BYTE*>(MemAlloc(cbData, TRUE));
193 ExitOnNull(pbData, hr, E_OUTOFMEMORY, "Failed to allocate memory to read in ca script.");
194
195 if (INVALID_SET_FILE_POINTER == ::SetFilePointer(hScript->hScriptFile, 0, NULL, FILE_BEGIN))
196 {
197 ExitWithLastError(hr, "Failed to reset to beginning of ca script.");
198 }
199
200 DWORD cbTotalRead = 0;
201 DWORD cbRead = 0;
202 do
203 {
204 if (!::ReadFile(hScript->hScriptFile, pbData + cbTotalRead, cbData - cbTotalRead, &cbRead, NULL))
205 {
206 ExitWithLastError(hr, "Failed to read from ca script.");
207 }
208
209 cbTotalRead += cbRead;
210 } while (cbRead && cbTotalRead < cbData);
211
212 if (cbTotalRead != cbData)
213 {
214 hr = E_UNEXPECTED;
215 ExitOnFailure(hr, "Failed to completely read ca script.");
216 }
217 }
218
219 // Add one to the allocated space because the data stored in the script is not
220 // null terminated. After copying the memory over, we'll ensure the string is
221 // null terminated.
222 DWORD cchData = cbData / sizeof(WCHAR) + 1;
223 hr = StrAlloc(ppwzCustomActionData, cchData);
224 ExitOnFailure(hr, "Failed to copy ca script.");
225
226 if (cbData)
227 {
228 CopyMemory(*ppwzCustomActionData, pbData, cbData);
229 }
230
231 (*ppwzCustomActionData)[cchData - 1] = L'\0';
232
233LExit:
234 ReleaseMem(pbData);
235 return hr;
236}
237
238
239/********************************************************************
240 WcaCaScriptWriteString() - writes a string to the ca script.
241
242********************************************************************/
243extern "C" HRESULT WIXAPI WcaCaScriptWriteString(
244 __in WCA_CASCRIPT_HANDLE hScript,
245 __in_z LPCWSTR wzValue
246 )
247{
248 HRESULT hr = S_OK;
249 DWORD cbFile = 0;
250 DWORD cbWrite = 0;
251 DWORD cbTotalWritten = 0;
252 WCHAR delim[] = { MAGIC_MULTISZ_DELIM }; // magic char followed by NULL terminator
253
254 cbFile = ::SetFilePointer(hScript->hScriptFile, 0, NULL, FILE_END);
255 if (INVALID_SET_FILE_POINTER == cbFile)
256 {
257 ExitWithLastError(hr, "Failed to move file pointer to end of file.");
258 }
259
260 // If there is existing data in the file, append on the magic delimeter
261 // before adding our new data on the end of the file.
262 if (0 < cbFile)
263 {
264 cbWrite = sizeof(delim);
265 cbTotalWritten = 0;
266 while (cbTotalWritten < cbWrite)
267 {
268 DWORD cbWritten = 0;
269 if (!::WriteFile(hScript->hScriptFile, reinterpret_cast<BYTE*>(delim) + cbTotalWritten, cbWrite - cbTotalWritten, &cbWritten, NULL))
270 {
271 ExitWithLastError(hr, "Failed to write data to ca script.");
272 }
273
274 cbTotalWritten += cbWritten;
275 }
276 }
277
278 cbWrite = lstrlenW(wzValue) * sizeof(WCHAR);
279 cbTotalWritten = 0;
280 while (cbTotalWritten < cbWrite)
281 {
282 DWORD cbWritten = 0;
283 if (!::WriteFile(hScript->hScriptFile, reinterpret_cast<const BYTE*>(wzValue) + cbTotalWritten, cbWrite - cbTotalWritten, &cbWritten, NULL))
284 {
285 ExitWithLastError(hr, "Failed to write data to ca script.");
286 }
287
288 cbTotalWritten += cbWritten;
289 }
290
291LExit:
292 return hr;
293}
294
295
296/********************************************************************
297 WcaCaScriptWriteNumber() - writes a number to the ca script.
298
299********************************************************************/
300extern "C" HRESULT WIXAPI WcaCaScriptWriteNumber(
301 __in WCA_CASCRIPT_HANDLE hScript,
302 __in DWORD dwValue
303 )
304{
305 HRESULT hr = S_OK;
306 WCHAR wzBuffer[13] = { 0 };
307
308 hr = ::StringCchPrintfW(wzBuffer, countof(wzBuffer), L"%u", dwValue);
309 ExitOnFailure(hr, "Failed to convert number into string.");
310
311 hr = WcaCaScriptWriteString(hScript, wzBuffer);
312 ExitOnFailure(hr, "Failed to write number to script.");
313
314LExit:
315 return hr;
316}
317
318
319/********************************************************************
320 WcaCaScriptFlush() - best effort function to get script written to
321 disk.
322
323********************************************************************/
324extern "C" void WIXAPI WcaCaScriptFlush(
325 __in WCA_CASCRIPT_HANDLE hScript
326 )
327{
328 ::FlushFileBuffers(hScript->hScriptFile);
329}
330
331
332/********************************************************************
333 WcaCaScriptCleanup() - best effort clean-up of any cascripts left
334 over from this install/uninstall.
335
336********************************************************************/
337extern "C" void WIXAPI WcaCaScriptCleanup(
338 __in_z LPCWSTR wzProductCode,
339 __in BOOL fImpersonated
340 )
341{
342 HRESULT hr = S_OK;
343 WCHAR wzTempPath[MAX_PATH];
344 LPWSTR pwzWildCardPath = NULL;
345 WIN32_FIND_DATAW fd = { 0 };
346 HANDLE hff = INVALID_HANDLE_VALUE;
347 LPWSTR pwzDeletePath = NULL;
348
349 if (fImpersonated)
350 {
351 if (!::GetTempPathW(countof(wzTempPath), wzTempPath))
352 {
353 ExitWithLastError(hr, "Failed to get temp path.");
354 }
355 }
356 else
357 {
358 if (!::GetWindowsDirectoryW(wzTempPath, countof(wzTempPath)))
359 {
360 ExitWithLastError(hr, "Failed to get windows path.");
361 }
362
363 hr = ::StringCchCatW(wzTempPath, countof(wzTempPath), L"\\Installer\\");
364 ExitOnFailure(hr, "Failed to concat Installer directory on windows path string.");
365 }
366
367 hr = StrAllocFormatted(&pwzWildCardPath, L"%swix%s.*.???", wzTempPath, wzProductCode);
368 ExitOnFailure(hr, "Failed to allocate wildcard path to ca scripts.");
369
370 hff = ::FindFirstFileW(pwzWildCardPath, &fd);
371 if (INVALID_HANDLE_VALUE == hff)
372 {
373 ExitWithLastError(hr, "Failed to find files with pattern: %ls", pwzWildCardPath);
374 }
375
376 do
377 {
378 hr = StrAllocFormatted(&pwzDeletePath, L"%s%s", wzTempPath, fd.cFileName);
379 if (SUCCEEDED(hr))
380 {
381 if (!::DeleteFileW(pwzDeletePath))
382 {
383 DWORD er = ::GetLastError();
384 WcaLog(LOGMSG_VERBOSE, "Failed to clean up CAScript file: %ls, er: %d", fd.cFileName, er);
385 }
386 }
387 else
388 {
389 WcaLog(LOGMSG_VERBOSE, "Failed to allocate path to clean up CAScript file: %ls, hr: 0x%x", fd.cFileName, hr);
390 }
391 } while(::FindNextFileW(hff, &fd));
392
393LExit:
394 if (INVALID_HANDLE_VALUE == hff)
395 {
396 ::FindClose(hff);
397 }
398
399 ReleaseStr(pwzDeletePath);
400 ReleaseStr(pwzWildCardPath);
401 return;
402}
403
404
405static HRESULT CaScriptFileName(
406 __in WCA_ACTION action,
407 __in WCA_CASCRIPT script,
408 __in BOOL fImpersonated,
409 __in_z LPCWSTR wzScriptKey,
410 __out LPWSTR* ppwzScriptName
411 )
412{
413 HRESULT hr = S_OK;
414 WCHAR wzTempPath[MAX_PATH];
415 LPWSTR pwzProductCode = NULL;
416 WCHAR chInstallOrUninstall = action == WCA_ACTION_INSTALL ? L'i' : L'u';
417 WCHAR chScheduledOrRollback = script == WCA_CASCRIPT_SCHEDULED ? L's' : L'r';
418 WCHAR chUserOrMachine = fImpersonated ? L'u' : L'm';
419
420 if (fImpersonated)
421 {
422 if (!::GetTempPathW(countof(wzTempPath), wzTempPath))
423 {
424 ExitWithLastError(hr, "Failed to get temp path.");
425 }
426 }
427 else
428 {
429 if (!::GetWindowsDirectoryW(wzTempPath, countof(wzTempPath)))
430 {
431 ExitWithLastError(hr, "Failed to get windows path.");
432 }
433
434 hr = ::StringCchCatW(wzTempPath, countof(wzTempPath), L"\\Installer\\");
435 ExitOnFailure(hr, "Failed to concat Installer directory on windows path string.");
436 }
437
438 hr = WcaGetProperty(L"ProductCode", &pwzProductCode);
439 ExitOnFailure(hr, "Failed to get ProductCode.");
440
441 hr = StrAllocFormatted(ppwzScriptName, L"%swix%s.%s.%c%c%c", wzTempPath, pwzProductCode, wzScriptKey, chScheduledOrRollback, chUserOrMachine, chInstallOrUninstall);
442 ExitOnFailure(hr, "Failed to allocate path to ca script.");
443
444LExit:
445 ReleaseStr(pwzProductCode);
446 return hr;
447}
diff --git a/src/wcautil/wcautil.cpp b/src/wcautil/wcautil.cpp
new file mode 100644
index 00000000..ce5ef151
--- /dev/null
+++ b/src/wcautil/wcautil.cpp
@@ -0,0 +1,216 @@
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// globals
6HMODULE g_hInstCADLL;
7
8// statics
9static BOOL s_fInitialized;
10static MSIHANDLE s_hInstall;
11static MSIHANDLE s_hDatabase;
12static char s_szCustomActionLogName[32];
13static UINT s_iRetVal;
14
15
16/********************************************************************
17 WcaGlobalInitialize() - initializes the Wca library, should be
18 called once per custom action Dll during
19 DllMain on DLL_PROCESS_ATTACH
20
21********************************************************************/
22extern "C" void WIXAPI WcaGlobalInitialize(
23 __in HINSTANCE hInst
24 )
25{
26 g_hInstCADLL = hInst;
27 MemInitialize();
28
29 AssertSetModule(g_hInstCADLL);
30 AssertSetDisplayFunction(WcaDisplayAssert);
31}
32
33
34/********************************************************************
35 WcaGlobalFinalize() - finalizes the Wca library, should be the
36 called once per custom action Dll during
37 DllMain on DLL_PROCESS_DETACH
38
39********************************************************************/
40extern "C" void WIXAPI WcaGlobalFinalize()
41{
42#ifdef DEBUG
43 if (WcaIsInitialized())
44 {
45 CHAR szBuf[2048];
46 StringCchPrintfA(szBuf, countof(szBuf), "CustomAction %s called WcaInitialize() but not WcaFinalize()", WcaGetLogName());
47
48 AssertSz(FALSE, szBuf);
49 }
50#endif
51 MemUninitialize();
52 g_hInstCADLL = NULL;
53}
54
55
56/********************************************************************
57 WcaInitialize() - initializes the Wca framework, should be the first
58 thing called by all CustomActions
59
60********************************************************************/
61extern "C" HRESULT WIXAPI WcaInitialize(
62 __in MSIHANDLE hInstall,
63 __in_z PCSTR szCustomActionLogName
64 )
65{
66 WCHAR wzCAFileName[MAX_PATH] = {0};
67 DWORD dwMajorVersion = 0;
68 DWORD dwMinorVersion = 0;
69
70 // these statics should be called once per CustomAction invocation.
71 // Darwin does doesn't preserve DLL state across CustomAction calls so
72 // these should always be initialized to NULL. If that behavior changes
73 // we would need to do a careful review of all of our module/global data.
74 AssertSz(!s_fInitialized, "WcaInitialize() should only be called once per CustomAction");
75 Assert(NULL == s_hInstall);
76 Assert(NULL == s_hDatabase);
77 Assert(0 == *s_szCustomActionLogName);
78
79 HRESULT hr = S_OK;
80
81 s_fInitialized = TRUE;
82 s_iRetVal = ERROR_SUCCESS; // assume all will go well
83
84 s_hInstall = hInstall;
85 s_hDatabase = ::MsiGetActiveDatabase(s_hInstall); // may return null if deferred CustomAction
86
87 hr = ::StringCchCopy(s_szCustomActionLogName, countof(s_szCustomActionLogName), szCustomActionLogName);
88 ExitOnFailure(hr, "Failed to copy CustomAction log name: %s", szCustomActionLogName);
89
90 // If we got the database handle IE: immediate CA
91 if (s_hDatabase)
92 {
93 hr = SetVerboseLoggingAtom(IsVerboseLogging());
94 ExitOnFailure(hr, "Failed to set verbose logging global atom");
95 }
96
97 if (!::GetModuleFileNameW(g_hInstCADLL, wzCAFileName, countof(wzCAFileName)))
98 {
99 ExitWithLastError(hr, "Failed to get module filename");
100 }
101
102 FileVersion(wzCAFileName, &dwMajorVersion, &dwMinorVersion); // Ignore failure, just log 0.0.0.0
103
104 WcaLog(LOGMSG_VERBOSE, "Entering %s in %ls, version %u.%u.%u.%u", szCustomActionLogName, wzCAFileName, (DWORD)HIWORD(dwMajorVersion), (DWORD)LOWORD(dwMajorVersion), (DWORD)HIWORD(dwMinorVersion), (DWORD)LOWORD(dwMinorVersion));
105
106 Assert(s_hInstall);
107LExit:
108 if (FAILED(hr))
109 {
110 if (s_hDatabase)
111 {
112 ::MsiCloseHandle(s_hDatabase);
113 s_hDatabase = NULL;
114 }
115
116 s_hInstall = NULL;
117 s_fInitialized = FALSE;
118 }
119
120 return hr;
121}
122
123
124/********************************************************************
125 WcaFinalize() - cleans up after the Wca framework, should be the last
126 thing called by all CustomActions
127
128********************************************************************/
129extern "C" UINT WIXAPI WcaFinalize(
130 __in UINT iReturnValue
131 )
132{
133 AssertSz(!WcaIsWow64Initialized(), "WcaFinalizeWow64() should be called before calling WcaFinalize()");
134
135 // clean up after our initialization
136 if (s_hDatabase)
137 {
138 ::MsiCloseHandle(s_hDatabase);
139 s_hDatabase = NULL;
140 }
141
142 s_hInstall = NULL;
143 s_fInitialized = FALSE;
144
145 // if no error occurred during the processing of the CusotmAction return the passed in return value
146 // otherwise return the previous failure
147 return (ERROR_SUCCESS == s_iRetVal) ? iReturnValue : s_iRetVal;
148}
149
150
151/********************************************************************
152 WcaIsInitialized() - determines if WcaInitialize() has been called
153
154********************************************************************/
155extern "C" BOOL WIXAPI WcaIsInitialized()
156{
157 return s_fInitialized;
158}
159
160
161/********************************************************************
162 WcaGetInstallHandle() - gets the handle to the active install session
163
164********************************************************************/
165extern "C" MSIHANDLE WIXAPI WcaGetInstallHandle()
166{
167 AssertSz(s_hInstall, "WcaInitialize() should be called before attempting to access the install handle.");
168 return s_hInstall;
169}
170
171
172/********************************************************************
173 WcaGetDatabaseHandle() - gets the handle to the active database
174
175 NOTE: this function can only be used in immediate CustomActions.
176 Deferred CustomActions do not have access to the active
177 database.
178********************************************************************/
179extern "C" MSIHANDLE WIXAPI WcaGetDatabaseHandle()
180{
181 AssertSz(s_hDatabase, "WcaInitialize() should be called before attempting to access the install handle. Also note that deferred CustomActions do not have access to the active database.");
182 return s_hDatabase;
183}
184
185
186/********************************************************************
187 WcaGetLogName() - gets the name of the CustomAction used in logging
188
189********************************************************************/
190extern "C" const char* WIXAPI WcaGetLogName()
191{
192 return s_szCustomActionLogName;
193}
194
195
196/********************************************************************
197 WcaSetReturnValue() - sets the value to return from the CustomAction
198
199********************************************************************/
200extern "C" void WIXAPI WcaSetReturnValue(
201 __in UINT iReturnValue
202 )
203{
204 s_iRetVal = iReturnValue;
205}
206
207
208/********************************************************************
209 WcaCancelDetected() - determines if the user has canceled yet
210
211 NOTE: returns true when WcaSetReturnValue() is set to ERROR_INSTALL_USEREXIT
212********************************************************************/
213extern "C" BOOL WIXAPI WcaCancelDetected()
214{
215 return ERROR_INSTALL_USEREXIT == s_iRetVal;
216}
diff --git a/src/wcautil/wcautil.nuspec b/src/wcautil/wcautil.nuspec
new file mode 100644
index 00000000..3d1d2722
--- /dev/null
+++ b/src/wcautil/wcautil.nuspec
@@ -0,0 +1,23 @@
1<?xml version="1.0"?>
2<package >
3 <metadata>
4 <id>$id$</id>
5 <version>$version$</version>
6 <authors>$authors$</authors>
7 <owners>$authors$</owners>
8 <licenseUrl>https://github.com/wixtoolset/wcautil/blob/master/LICENSE.TXT</licenseUrl>
9 <projectUrl>https://github.com/wixtoolset/wcautil</projectUrl>
10 <requireLicenseAcceptance>false</requireLicenseAcceptance>
11 <description>$description$</description>
12 <copyright>$copyright$</copyright>
13 </metadata>
14
15 <files>
16 <file src="build\$id$.props" target="build\" />
17 <file src="inc\*" target="build\native\include" />
18 <file src="..\..\build\$configuration$\v140_xp\x64\wcautil.lib" target="build\native\lib\v140\x64" />
19 <file src="..\..\build\$configuration$\v140_xp\x86\wcautil.lib" target="build\native\lib\v140\x86" />
20 <file src="..\..\build\$configuration$\v141_xp\x64\wcautil.lib" target="build\native\lib\v141\x64" />
21 <file src="..\..\build\$configuration$\v141_xp\x86\wcautil.lib" target="build\native\lib\v141\x86" />
22 </files>
23</package>
diff --git a/src/wcautil/wcautil.vcxproj b/src/wcautil/wcautil.vcxproj
new file mode 100644
index 00000000..61bd9437
--- /dev/null
+++ b/src/wcautil/wcautil.vcxproj
@@ -0,0 +1,79 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- 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. -->
3<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
4 <Import Project="..\..\packages\WixToolset.DUtil.4.0.3\build\WixToolset.DUtil.props" Condition="Exists('..\..\packages\WixToolset.DUtil.4.0.3\build\WixToolset.DUtil.props')" />
5 <ItemGroup Label="ProjectConfigurations">
6 <ProjectConfiguration Include="Debug|Win32">
7 <Configuration>Debug</Configuration>
8 <Platform>Win32</Platform>
9 </ProjectConfiguration>
10 <ProjectConfiguration Include="Release|Win32">
11 <Configuration>Release</Configuration>
12 <Platform>Win32</Platform>
13 </ProjectConfiguration>
14 <ProjectConfiguration Include="Debug|x64">
15 <Configuration>Debug</Configuration>
16 <Platform>x64</Platform>
17 </ProjectConfiguration>
18 <ProjectConfiguration Include="Release|x64">
19 <Configuration>Release</Configuration>
20 <Platform>x64</Platform>
21 </ProjectConfiguration>
22 </ItemGroup>
23 <PropertyGroup Label="Globals">
24 <ProjectGuid>{5B3714B6-3A76-463E-8595-D48DA276C512}</ProjectGuid>
25 <ConfigurationType>StaticLibrary</ConfigurationType>
26 <TargetName>wcautil</TargetName>
27 <MultiTargetLibrary>true</MultiTargetLibrary>
28 <PlatformToolset>v141_xp</PlatformToolset>
29 <CharacterSet>MultiByte</CharacterSet>
30 <Description>WiX Toolset Custom Action native utility library</Description>
31 </PropertyGroup>
32 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
33 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
34 <ImportGroup Label="Shared">
35 <Import Project="..\..\packages\Nerdbank.GitVersioning.2.0.37-beta\build\Nerdbank.GitVersioning.targets" Condition="Exists('..\..\packages\Nerdbank.GitVersioning.2.0.37-beta\build\Nerdbank.GitVersioning.targets')" />
36 </ImportGroup>
37 <Import Project="..\NativeMultiTargeting.Build.props" />
38 <ItemGroup>
39 <ClCompile Include="exbinary.cpp" />
40 <ClCompile Include="wcalog.cpp" />
41 <ClCompile Include="wcascript.cpp" />
42 <ClCompile Include="wcautil.cpp">
43 <PrecompiledHeader>Create</PrecompiledHeader>
44 </ClCompile>
45 <ClCompile Include="wcawrapquery.cpp" />
46 <ClCompile Include="wcawow64.cpp" />
47 <ClCompile Include="wcawrap.cpp" />
48 <ClCompile Include="qtexec.cpp" />
49 </ItemGroup>
50 <ItemGroup Condition="'$(Platform)' == 'Win32'">
51 <ClInclude Include="custommsierrors.h">
52 <GenerateWixInclude>caerr.wxi</GenerateWixInclude>
53 </ClInclude>
54 </ItemGroup>
55 <ItemGroup Condition="'$(Platform)' != 'Win32'">
56 <ClInclude Include="custommsierrors.h" />
57 </ItemGroup>
58 <ItemGroup>
59 <ClInclude Include="precomp.h" />
60 <ClInclude Include="inc\wcalog.h" />
61 <ClInclude Include="inc\wcautil.h" />
62 <ClInclude Include="inc\wcawow64.h" />
63 <ClInclude Include="inc\wcawrapquery.h" />
64 </ItemGroup>
65 <ItemGroup>
66 <None Include="packages.config" />
67 </ItemGroup>
68 <Target Name="PackNativeNuget" DependsOnTargets="GetBuildVersion">
69 <Exec Command="nuget pack wcautil.nuspec -OutputDirectory $(BaseOutputPath) -Properties Configuration=$(Configuration);Id=WixToolset.WcaUtil;Version=&quot;$(BuildVersionSimple)&quot;;Authors=&quot;$(Authors)&quot;;Copyright=&quot;$(Copyright)&quot;;Description=&quot;$(Description)&quot;;Title=&quot;$(Title)&quot;" />
70 </Target>
71 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
72 <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
73 <PropertyGroup>
74 <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
75 </PropertyGroup>
76 <Error Condition="!Exists('..\..\packages\Nerdbank.GitVersioning.2.0.37-beta\build\Nerdbank.GitVersioning.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Nerdbank.GitVersioning.2.0.37-beta\build\Nerdbank.GitVersioning.targets'))" />
77 <Error Condition="!Exists('..\..\packages\WixToolset.DUtil.4.0.3\build\WixToolset.DUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.DUtil.4.0.3\build\WixToolset.DUtil.props'))" />
78 </Target>
79</Project> \ No newline at end of file
diff --git a/src/wcautil/wcawow64.cpp b/src/wcautil/wcawow64.cpp
new file mode 100644
index 00000000..8174c43e
--- /dev/null
+++ b/src/wcautil/wcawow64.cpp
@@ -0,0 +1,169 @@
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
5static HMODULE s_hKernel32;
6static BOOL s_fWow64Initialized;
7static BOOL (*s_pfnDisableWow64)(__out PVOID* );
8static BOOL (*s_pfnRevertWow64)(__in PVOID );
9static BOOL (*s_pfnIsWow64Process) (HANDLE, PBOOL);
10static PVOID s_Wow64FSRevertState;
11static BOOL s_fWow64FSDisabled;
12
13/********************************************************************
14 WcaInitializeWow64() - Initializes the Wow64 API
15
16********************************************************************/
17extern "C" HRESULT WIXAPI WcaInitializeWow64()
18{
19 AssertSz(WcaIsInitialized(), "WcaInitialize() should be called before calling WcaInitializeWow64()");
20 AssertSz(!WcaIsWow64Initialized(), "WcaInitializeWow64() should not be called twice without calling WcaFinalizeWow64()");
21
22 s_fWow64Initialized = FALSE;
23 HRESULT hr = S_OK;
24 s_Wow64FSRevertState = NULL;
25 s_fWow64FSDisabled = false;
26
27 // Test if we have access to the Wow64 API, and store the result in bWow64APIPresent
28 s_hKernel32 = ::GetModuleHandleW(L"kernel32.dll");
29 if (!s_hKernel32)
30 {
31 ExitWithLastError(hr, "failed to get handle to kernel32.dll");
32 }
33
34 // This will test if we have access to the Wow64 API
35 s_pfnIsWow64Process = (BOOL (*)(HANDLE, PBOOL))::GetProcAddress(s_hKernel32, "IsWow64Process");
36 if (NULL != s_pfnIsWow64Process)
37 {
38 s_pfnDisableWow64 = (BOOL (*)(PVOID *))::GetProcAddress(s_hKernel32, "Wow64DisableWow64FsRedirection");
39 // If we fail, log the error but proceed, because we may not need a particular function, or the Wow64 API at all
40 if (!s_pfnDisableWow64)
41 {
42 return S_FALSE;
43 }
44
45 s_pfnRevertWow64 = (BOOL (*)(PVOID))::GetProcAddress(s_hKernel32, "Wow64RevertWow64FsRedirection");
46 if (!s_pfnRevertWow64)
47 {
48 return S_FALSE;
49 }
50
51 if (s_pfnDisableWow64 && s_pfnRevertWow64)
52 {
53 s_fWow64Initialized = TRUE;
54 }
55 }
56 else
57 {
58 return S_FALSE;
59 }
60
61LExit:
62
63 return hr;
64}
65
66/********************************************************************
67 WcaIsWow64Process() - determines if the current process is running
68 in WOW
69
70********************************************************************/
71extern "C" BOOL WIXAPI WcaIsWow64Process()
72{
73 BOOL fIsWow64Process = FALSE;
74 if (s_fWow64Initialized)
75 {
76 if (!s_pfnIsWow64Process(GetCurrentProcess(), &fIsWow64Process))
77 {
78 // clear out the value since call failed
79 fIsWow64Process = FALSE;
80 }
81 }
82 return fIsWow64Process;
83}
84
85/********************************************************************
86 WcaIsWow64Initialized() - determines if WcaInitializeWow64() has
87 been successfully called
88
89********************************************************************/
90extern "C" BOOL WIXAPI WcaIsWow64Initialized()
91{
92 return s_fWow64Initialized;
93}
94
95/********************************************************************
96 WcaDisableWow64FSRedirection() - Disables Wow64 FS Redirection
97
98********************************************************************/
99extern "C" HRESULT WIXAPI WcaDisableWow64FSRedirection()
100{
101 AssertSz(s_fWow64Initialized && s_pfnDisableWow64 != NULL, "WcaDisableWow64FSRedirection() called, but Wow64 API was not initialized");
102
103#ifdef DEBUG
104 AssertSz(!s_fWow64FSDisabled, "You must call WcaRevertWow64FSRedirection() before calling WcaDisableWow64FSRedirection() again");
105#endif
106
107 HRESULT hr = S_OK;
108 if (s_pfnDisableWow64(&s_Wow64FSRevertState))
109 {
110 s_fWow64FSDisabled = TRUE;
111 }
112 else
113 {
114 ExitWithLastError(hr, "Failed to disable WOW64.");
115 }
116
117LExit:
118 return hr;
119}
120
121/********************************************************************
122 WcaRevertWow64FSRedirection() - Reverts Wow64 FS Redirection to its
123 pre-disabled state
124
125********************************************************************/
126extern "C" HRESULT WIXAPI WcaRevertWow64FSRedirection()
127{
128 AssertSz(s_fWow64Initialized && s_pfnDisableWow64 != NULL, "WcaRevertWow64FSRedirection() called, but Wow64 API was not initialized");
129
130#ifdef DEBUG
131 AssertSz(s_fWow64FSDisabled, "You must call WcaDisableWow64FSRedirection() before calling WcaRevertWow64FSRedirection()");
132#endif
133
134 HRESULT hr = S_OK;
135 if (s_pfnRevertWow64(s_Wow64FSRevertState))
136 {
137 s_fWow64FSDisabled = FALSE;
138 }
139 else
140 {
141 ExitWithLastError(hr, "Failed to revert WOW64.");
142 }
143
144LExit:
145 return hr;
146}
147
148/********************************************************************
149 WcaFinalizeWow64() - Cleans up after Wow64 API Initialization
150
151********************************************************************/
152extern "C" HRESULT WIXAPI WcaFinalizeWow64()
153{
154 if (s_fWow64FSDisabled)
155 {
156#ifdef DEBUG
157 AssertSz(FALSE, "WcaFinalizeWow64() called while Filesystem redirection was disabled.");
158#else
159 // If we aren't in debug mode, let's do our best to recover gracefully
160 WcaRevertWow64FSRedirection();
161#endif
162 }
163
164 s_fWow64Initialized = FALSE;
165 s_pfnDisableWow64 = NULL;
166 s_pfnRevertWow64 = NULL;
167
168 return S_OK;
169}
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}
diff --git a/src/wcautil/wcawrapquery.cpp b/src/wcautil/wcawrapquery.cpp
new file mode 100644
index 00000000..f04da10d
--- /dev/null
+++ b/src/wcautil/wcawrapquery.cpp
@@ -0,0 +1,717 @@
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#include "wcawrapquery.h"
5
6static const LPWSTR ISINSTALLEDCOLUMNNAME = L"ISInstalled";
7static const LPWSTR ISACTIONCOLUMNNAME = L"ISAction";
8static const LPWSTR SOURCEPATHCOLUMNNAME = L"SourcePath";
9static const LPWSTR TARGETPATHCOLUMNNAME = L"TargetPath";
10
11// This instantiates a new query object in the deferred CA, and returns the handle to the query
12WCA_WRAPQUERY_HANDLE WIXAPI GetNewQueryInstance(
13 DWORD dwInColumns,
14 DWORD dwInRows
15 )
16{
17 HRESULT hr = S_OK;
18
19 WCA_WRAPQUERY_HANDLE hNewHandle = NULL;
20
21 hNewHandle = static_cast<WCA_WRAPQUERY_HANDLE>(MemAlloc(sizeof(WCA_WRAPQUERY_STRUCT), TRUE));
22 if (NULL == hNewHandle)
23 {
24 hr = E_OUTOFMEMORY;
25 ExitOnFailure(hr, "Failed to allocate Query Instance");
26 }
27
28 // Initialize non-array members
29 hNewHandle->dwColumns = dwInColumns;
30 hNewHandle->dwRows = dwInRows;
31 hNewHandle->dwNextIndex = 0;
32
33 // Initialize arrays
34 if (0 != hNewHandle->dwColumns)
35 {
36 hNewHandle->pcdtColumnType = static_cast<eColumnDataType *>(MemAlloc(hNewHandle->dwColumns * sizeof(eColumnDataType), TRUE));
37 if (NULL == hNewHandle->pcdtColumnType)
38 {
39 hr = E_OUTOFMEMORY;
40 ExitOnFailure(hr, "Failed to allocate column type array");
41 }
42
43 hNewHandle->ppwzColumnNames = static_cast<LPWSTR *>(MemAlloc(hNewHandle->dwColumns * sizeof(LPWSTR), TRUE));
44 if (NULL == hNewHandle->ppwzColumnNames)
45 {
46 hr = E_OUTOFMEMORY;
47 ExitOnFailure(hr, "Failed to allocate column names array");
48 }
49 }
50
51 for (DWORD i=0;i<hNewHandle->dwColumns;i++)
52 {
53 hNewHandle->pcdtColumnType[i] = cdtUnknown;
54 hNewHandle->ppwzColumnNames[i] = NULL;
55 }
56
57 if (0 != hNewHandle->dwRows)
58 {
59 hNewHandle->phRecords = static_cast<MSIHANDLE *>(MemAlloc(hNewHandle->dwRows * sizeof(MSIHANDLE), TRUE));
60 if (NULL == hNewHandle->phRecords)
61 {
62 hr = E_OUTOFMEMORY;
63 ExitOnFailure(hr, "Failed to allocate records array");
64 }
65 }
66
67 for (DWORD i=0;i<hNewHandle->dwRows;i++)
68 {
69 hNewHandle->phRecords[i] = NULL;
70 }
71
72 return hNewHandle;
73
74LExit:
75 // The handle isn't complete, so destroy any memory it allocated before returning NULL
76 if (NULL != hNewHandle)
77 {
78 WcaFinishUnwrapQuery(hNewHandle);
79 }
80
81 return NULL;
82}
83
84// This function takes in the column type string from MsiViewGetColumnInfo, and returns
85// whether the column stores strings, ints, binary streams, or
86// cdtUnknown if this information couldn't be determined.
87eColumnDataType WIXAPI GetDataTypeFromString(
88 LPCWSTR pwzTypeString
89 )
90{
91 if (NULL == pwzTypeString || 0 == wcslen(pwzTypeString))
92 {
93 return cdtUnknown;
94 }
95
96 switch (pwzTypeString[0])
97 {
98 case 'v':
99 case 'V':
100 case 'o':
101 case 'O':
102 return cdtStream;
103
104 case 'g':
105 case 'G':
106 case 's':
107 case 'S':
108 case 'l':
109 case 'L':
110 return cdtString;
111
112 case 'i':
113 case 'I':
114 case 'j':
115 case 'J':
116 return cdtInt;
117
118 default:
119 return cdtUnknown;
120 }
121}
122
123HRESULT WIXAPI WcaWrapEmptyQuery(
124 __inout LPWSTR * ppwzCustomActionData
125 )
126{
127 HRESULT hr = S_OK;
128
129 WcaLog(LOGMSG_TRACEONLY, "Wrapping result of empty query");
130
131 hr = WcaWriteIntegerToCaData(static_cast<int>(wqaTableBegin), ppwzCustomActionData);
132 ExitOnFailure(hr, "Failed to write table begin marker to custom action data");
133
134 hr = WcaWriteIntegerToCaData(0, ppwzCustomActionData);
135 ExitOnFailure(hr, "Failed to write number of columns to custom action data");
136
137 hr = WcaWriteIntegerToCaData(0, ppwzCustomActionData);
138 ExitOnFailure(hr, "Failed to write number of rows to custom action data");
139
140 hr = WcaWriteIntegerToCaData(static_cast<int>(wqaTableFinish), ppwzCustomActionData);
141 ExitOnFailure(hr, "Failed to write table finish marker to custom action data");
142
143// WcaLog(LOGMSG_TRACEONLY, "Finished wrapping result of empty query");
144
145LExit:
146 return hr;
147}
148
149/********************************************************************
150WcaWrapQuery() - wraps a view and transmits it through the
151 CustomActionData property
152
153********************************************************************/
154HRESULT WIXAPI WcaWrapQuery(
155 __in_z LPCWSTR pwzQuery,
156 __inout LPWSTR * ppwzCustomActionData,
157 __in_opt DWORD dwFormatMask,
158 __in_opt DWORD dwComponentColumn,
159 __in_opt DWORD dwDirectoryColumn
160 )
161{
162 HRESULT hr = S_OK;
163 HRESULT hrTemp = S_OK;
164 UINT er = ERROR_SUCCESS;
165 UINT cViewColumns;
166 eColumnDataType *pcdtColumnTypeList = NULL;
167 LPWSTR pwzData = NULL;
168 LPWSTR pwzColumnData = NULL;
169 LPWSTR pwzRecordData = NULL;
170 BYTE* pbData = NULL;
171 DWORD dwNumRecords = 0;
172 BOOL fAddComponentState = FALSE; // Add two integer columns to the right side of the query - ISInstalled, and ISAction
173 BOOL fAddDirectoryPath = FALSE; // Add two string columns to the right side of the query - SourcePath, and TargetPath
174 int iTempInteger = 0;
175
176 WCHAR wzPath[MAX_PATH + 1];
177 DWORD dwLen;
178 INSTALLSTATE isInstalled = INSTALLSTATE_UNKNOWN;
179 INSTALLSTATE isAction = INSTALLSTATE_UNKNOWN;
180
181 PMSIHANDLE hColumnTypes, hColumnNames;
182 PMSIHANDLE hView, hRec;
183
184 WcaLog(LOGMSG_TRACEONLY, "Wrapping result of query: \"%ls\"", pwzQuery);
185
186 // open the view
187 hr = WcaOpenExecuteView(pwzQuery, &hView);
188 ExitOnFailure(hr, "Failed to execute view");
189
190 hr = WcaWriteIntegerToCaData(static_cast<int>(wqaTableBegin), ppwzCustomActionData);
191 ExitOnFailure(hr, "Failed to write table begin marker to custom action data");
192
193// WcaLog(LOGMSG_TRACEONLY, "Starting to wrap table's column information", pwzQuery);
194
195 // Use GetColumnInfo to populate the names of the columns.
196 er = ::MsiViewGetColumnInfo(hView, MSICOLINFO_TYPES, &hColumnTypes);
197 ExitOnWin32Error(er, hr, "Failed to get column types");
198
199 er = ::MsiViewGetColumnInfo(hView, MSICOLINFO_NAMES, &hColumnNames);
200 ExitOnWin32Error(er, hr, "Failed to get column names");
201
202 cViewColumns = ::MsiRecordGetFieldCount(hColumnTypes);
203
204 if (0xFFFFFFFF == cViewColumns)
205 {
206 // According to MSDN, this return value only happens when the handle is invalid
207 hr = E_HANDLE;
208 ExitOnFailure(hr, "Failed to get number of fields in record");
209 }
210
211 if (cViewColumns >= dwComponentColumn)
212 {
213 fAddComponentState = TRUE;
214 }
215 else if (0xFFFFFFFF != dwComponentColumn)
216 {
217 hr = E_INVALIDARG;
218 ExitOnFailure(hr, "Component column %d out of range", dwComponentColumn);
219 }
220
221 if (cViewColumns >= dwDirectoryColumn)
222 {
223 fAddDirectoryPath = TRUE;
224 }
225 else if (0xFFFFFFFF != dwDirectoryColumn)
226 {
227 hr = E_INVALIDARG;
228 ExitOnFailure(hr, "Directory column %d out of range", dwDirectoryColumn);
229 }
230
231 hr = WcaWriteIntegerToCaData(static_cast<int>(cViewColumns) + 2 * static_cast<int>(fAddComponentState) + 2 * static_cast<int>(fAddDirectoryPath), ppwzCustomActionData);
232 ExitOnFailure(hr, "Failed to write number of columns to custom action data");
233
234 pcdtColumnTypeList = new eColumnDataType[cViewColumns];
235 ExitOnNull(pcdtColumnTypeList, hr, E_OUTOFMEMORY, "Failed to allocate memory to store column info types");
236
237 // Loop through all the columns reporting information about each one
238 for (DWORD i = 0; i < cViewColumns; i++)
239 {
240 hr = WcaGetRecordString(hColumnNames, i+1, &pwzData);
241 ExitOnFailure(hr, "Failed to get the column %d name", i+1);
242
243 hr = WcaWriteStringToCaData(pwzData, &pwzColumnData);
244 ExitOnFailure(hr, "Failed to write column %d name %ls to custom action data", i+1, pwzData);
245
246 hr = WcaGetRecordString(hColumnTypes, i+1, &pwzData);
247 ExitOnFailure(hr, "Failed to get the column type string for column %d", i+1);
248
249 pcdtColumnTypeList[i] = GetDataTypeFromString(pwzData);
250
251 if (cdtUnknown == pcdtColumnTypeList[i])
252 {
253 hr = E_INVALIDARG;
254 ExitOnFailure(hr, "Failed to recognize column %d type string: %ls", i+1, pwzData);
255 }
256
257 hr = WcaWriteIntegerToCaData(pcdtColumnTypeList[i], &pwzColumnData);
258 ExitOnFailure(hr, "Failed to write column %d type enumeration to custom action data", i+1);
259 }
260
261 // Add two integer columns to the right side of the query - ISInstalled, and ISAction
262 if (fAddComponentState)
263 {
264 hr = WcaWriteStringToCaData(ISINSTALLEDCOLUMNNAME, &pwzColumnData);
265 ExitOnFailure(hr, "Failed to write extra column %d name %ls to custom action data", cViewColumns + 1, ISINSTALLEDCOLUMNNAME);
266
267 hr = WcaWriteIntegerToCaData(cdtInt, &pwzColumnData);
268 ExitOnFailure(hr, "Failed to write extra column %d type to custom action data", cViewColumns + 1);
269
270 hr = WcaWriteStringToCaData(ISACTIONCOLUMNNAME, &pwzColumnData);
271 ExitOnFailure(hr, "Failed to write extra column %d name %ls to custom action data", cViewColumns + 1, ISACTIONCOLUMNNAME);
272
273 hr = WcaWriteIntegerToCaData(cdtInt, &pwzColumnData);
274 ExitOnFailure(hr, "Failed to write extra column %d type to custom action data", cViewColumns + 1);
275 }
276
277 if (fAddDirectoryPath)
278 {
279 hr = WcaWriteStringToCaData(SOURCEPATHCOLUMNNAME, &pwzColumnData);
280 ExitOnFailure(hr, "Failed to write extra column %d name %ls to custom action data", cViewColumns + 1, SOURCEPATHCOLUMNNAME);
281
282 hr = WcaWriteIntegerToCaData(cdtString, &pwzColumnData);
283 ExitOnFailure(hr, "Failed to write extra column %d type to custom action data", cViewColumns + 1);
284
285 hr = WcaWriteStringToCaData(TARGETPATHCOLUMNNAME, &pwzColumnData);
286 ExitOnFailure(hr, "Failed to write extra column %d name %ls to custom action data", cViewColumns + 1, TARGETPATHCOLUMNNAME);
287
288 hr = WcaWriteIntegerToCaData(cdtString, &pwzColumnData);
289 ExitOnFailure(hr, "Failed to write extra column %d type to custom action data", cViewColumns + 1);
290 }
291
292 // Begin wrapping actual table data
293 //WcaLog(LOGMSG_TRACEONLY, "Starting to wrap table data", pwzQuery);
294 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
295 {
296 hr = WcaWriteIntegerToCaData(static_cast<int>(wqaRowBegin), &pwzRecordData);
297 ExitOnFailure(hr, "Failed to write row begin marker to custom action data");
298
299 for (DWORD i = 0; i < cViewColumns; i++)
300 {
301 switch (pcdtColumnTypeList[i])
302 {
303 case cdtString:
304 // If we were given a format mask, we're not past the index, and it's set to true for this column, then format the string
305 if (i < (sizeof(dwFormatMask) * 8) && (dwFormatMask & (1 << i)))
306 {
307 hr = WcaGetRecordFormattedString(hRec, i + 1, &pwzData);
308 }
309 else
310 {
311 hr = WcaGetRecordString(hRec, i + 1, &pwzData);
312 }
313 ExitOnFailure(hr, "Failed to get string for column %d", i + 1);
314
315 hr = WcaWriteStringToCaData(pwzData, &pwzRecordData);
316 ExitOnFailure(hr, "Failed to write string to temporary record custom action data for column %d", i + 1);
317 break;
318
319 case cdtInt:
320 if (i < (sizeof(dwFormatMask) * 8) && (dwFormatMask & (1 << i)))
321 {
322 hr = WcaGetRecordFormattedInteger(hRec, i + 1, &iTempInteger);
323 }
324 else
325 {
326 hr = WcaGetRecordInteger(hRec, i + 1, &iTempInteger);
327 }
328 ExitOnFailure(hr, "Failed to get integer for column %d", i + 1);
329
330 hr = WcaWriteIntegerToCaData(iTempInteger, &pwzRecordData);
331 ExitOnFailure(hr, "Failed to write integer to temporary record custom action data for column %d", i + 1);
332 break;
333
334 case cdtStream:
335 hr = E_NOTIMPL;
336 ExitOnFailure(hr, "A query was wrapped which contained a binary stream data field in column %d - however, the ability to wrap stream data fields is not implemented at this time", i);
337 break;
338
339 case cdtUnknown:
340 default:
341 hr = E_INVALIDARG;
342 ExitOnFailure(hr, "Failed to recognize column type enumeration %d for column %d", pcdtColumnTypeList[i], i + 1);
343 }
344 }
345
346 // Add two integer columns to the right side of the query - ISInstalled, and ISAction
347 if (fAddComponentState)
348 {
349 // Get the component ID
350 hr = WcaGetRecordString(hRec, dwComponentColumn, &pwzData);
351 ExitOnFailure(hr, "Failed to get component from column %d while adding extra columns", dwComponentColumn);
352
353 if (0 == lstrlenW(pwzData))
354 {
355 // If no component was provided, set these both to zero as though a structure to store them were allocated with memory zero'd out
356 isInstalled = (INSTALLSTATE)0;
357 isAction = (INSTALLSTATE)0;
358 }
359 else
360 {
361 er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &isInstalled, &isAction);
362 // If we don't get the component state, that may be because the component ID was invalid, but isn't necessarily an error, so write NULL's
363 if (FAILED(HRESULT_FROM_WIN32(er)))
364 {
365 ExitOnFailure(hr, "Failed to get component state for component %ls", pwzData);
366 }
367 }
368
369 hr = WcaWriteIntegerToCaData(isInstalled, &pwzRecordData);
370 ExitOnFailure(hr, "Failed to write extra ISInstalled column to custom action data");
371
372 hr = WcaWriteIntegerToCaData(isAction, &pwzRecordData);
373 ExitOnFailure(hr, "Failed to write extra ISAction column to custom action data");
374 }
375
376 // Add two string columns to the right side of the query - SourcePath, and TargetPath
377 if (fAddDirectoryPath)
378 {
379 hr = WcaGetRecordString(hRec, dwDirectoryColumn, &pwzData);
380 // If this fails, ignore it, and just leave those columns blank
381 if (SUCCEEDED(hr))
382 {
383 // Only get source path if the component state is INSTALLSTATE_SOURCE, or if we have no component to check the installstate of
384 if (INSTALLSTATE_SOURCE == isAction || !fAddComponentState)
385 {
386 dwLen = countof(wzPath);
387 er = ::MsiGetSourcePathW(WcaGetInstallHandle(), pwzData, wzPath, &dwLen);
388 hrTemp = HRESULT_FROM_WIN32(er);
389 if (dwLen > countof(wzPath))
390 {
391 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
392 ExitOnRootFailure(hr, "Failed to record entire Source Path for Directory %ls because its length was greater than MAX_PATH.", pwzData);
393 }
394
395 if (SUCCEEDED(hrTemp))
396 {
397 hr = WcaWriteStringToCaData(wzPath, &pwzRecordData);
398 ExitOnFailure(hr, "Failed to write source path string to record data string");
399 }
400 else
401 {
402 hr = WcaWriteStringToCaData(L"", &pwzRecordData);
403 ExitOnFailure(hr, "Failed to write empty source path string to record data string");
404 }
405 }
406 else
407 {
408 hr = WcaWriteStringToCaData(L"", &pwzRecordData);
409 ExitOnFailure(hr, "Failed to write empty source path string before writing target path string to record data string");
410 }
411
412 dwLen = countof(wzPath);
413 er = ::MsiGetTargetPathW(WcaGetInstallHandle(), pwzData, wzPath, &dwLen);
414 hrTemp = HRESULT_FROM_WIN32(er);
415 if (dwLen > countof(wzPath))
416 {
417 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
418 ExitOnRootFailure(hr, "Failed to record entire Source Path for Directory %ls because its length was greater than MAX_PATH.", pwzData);
419 }
420 if (SUCCEEDED(hrTemp))
421 {
422 hr = WcaWriteStringToCaData(wzPath, &pwzRecordData);
423 ExitOnFailure(hr, "Failed to write target path string to record data string");
424 }
425 else
426 {
427 hr = WcaWriteStringToCaData(L"", &pwzRecordData);
428 ExitOnFailure(hr, "Failed to write empty target path string to record data string");
429 }
430 }
431 else
432 {
433 // Write both fields as blank
434 hr = WcaWriteStringToCaData(L"", &pwzRecordData);
435 hr = WcaWriteStringToCaData(L"", &pwzRecordData);
436 }
437 }
438
439 hr = WcaWriteIntegerToCaData(static_cast<int>(wqaRowFinish), &pwzRecordData);
440 ExitOnFailure(hr, "Failed to write row finish marker to custom action data");
441
442 ++dwNumRecords;
443 }
444
445 hr = WcaWriteIntegerToCaData(dwNumRecords, ppwzCustomActionData);
446 ExitOnFailure(hr, "Failed to write number of records to custom action data");
447
448 if (NULL != pwzColumnData)
449 {
450 hr = WcaWriteStringToCaData(pwzColumnData, ppwzCustomActionData);
451 ExitOnFailure(hr, "Failed to write column data to custom action data");
452 }
453
454 if (NULL != pwzRecordData)
455 {
456 hr = WcaWriteStringToCaData(pwzRecordData, ppwzCustomActionData);
457 ExitOnFailure(hr, "Failed to write record data to custom action data");
458 }
459
460 hr = WcaWriteIntegerToCaData(static_cast<int>(wqaTableFinish), ppwzCustomActionData);
461 ExitOnFailure(hr, "Failed to write table finish marker to custom action data");
462
463// WcaLog(LOGMSG_TRACEONLY, "Finished wrapping result of query: \"%ls\"", pwzQuery);
464
465LExit:
466 ReleaseStr(pwzData);
467 ReleaseStr(pwzColumnData);
468 ReleaseStr(pwzRecordData);
469
470 ReleaseMem(pbData);
471
472 return hr;
473}
474
475
476/********************************************************************
477WcaBeginUnwrapQuery() - unwraps a view for direct access from the
478 CustomActionData property
479
480********************************************************************/
481HRESULT WIXAPI WcaBeginUnwrapQuery(
482 __out WCA_WRAPQUERY_HANDLE * phWrapQuery,
483 __inout LPWSTR * ppwzCustomActionData
484 )
485{
486 HRESULT hr = S_OK;
487 int iTempInteger = 0;
488 int iColumns = 0;
489 int iRows = 0;
490 BYTE* pbData = NULL;
491 LPWSTR pwzData = NULL;
492 WCA_WRAPQUERY_HANDLE hWrapQuery = NULL;
493
494 WcaLog(LOGMSG_TRACEONLY, "Unwrapping a query from custom action data");
495
496 hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iTempInteger);
497 if (wqaTableBegin != iTempInteger)
498 {
499 hr = E_INVALIDARG;
500 }
501 ExitOnFailure(hr, "Failed to read table begin marker from custom action data (read %d instead)", iTempInteger);
502
503 hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iColumns);
504 ExitOnFailure(hr, "Failed to read number of columns from custom action data");
505
506 hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iRows);
507 ExitOnFailure(hr, "Failed to read number of rows from custom action data");
508
509 hWrapQuery = GetNewQueryInstance(iColumns, iRows);
510 if (NULL == hWrapQuery)
511 {
512 hr = E_POINTER;
513 }
514 ExitOnFailure(hr, "Failed to get a query instance with %d columns and %d rows", iColumns, iRows);
515
516 for (int i = 0; i < iColumns; i++)
517 {
518 hr = WcaReadStringFromCaData(ppwzCustomActionData, &(hWrapQuery->ppwzColumnNames[i]));
519 ExitOnFailure(hr, "Failed to read column %d's name from custom action data", i+1);
520
521 hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iTempInteger);
522 if (cdtString != iTempInteger && cdtInt != iTempInteger && cdtStream != iTempInteger)
523 {
524 hr = E_INVALIDARG;
525 }
526 ExitOnFailure(hr, "Failed to read column %d's type from custom action data", i+1);
527
528 // Set the column type into the actual data structure
529 hWrapQuery->pcdtColumnType[i] = (eColumnDataType)iTempInteger;
530 }
531
532 for (int i = 0; i < iRows; i++)
533 {
534 hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iTempInteger);
535 if (wqaRowBegin != iTempInteger)
536 {
537 hr = E_INVALIDARG;
538 }
539 ExitOnFailure(hr, "Failed to read begin row marker from custom action data (read %d instead)", iTempInteger);
540
541 hWrapQuery->phRecords[i] = ::MsiCreateRecord((unsigned int)iColumns);
542
543 for (int j = 0; j < iColumns; j++)
544 {
545 switch (hWrapQuery->pcdtColumnType[j])
546 {
547 case cdtString:
548 hr = WcaReadStringFromCaData(ppwzCustomActionData, &pwzData);
549 ExitOnFailure(hr, "Failed to read string from custom action data");
550
551 hr = WcaSetRecordString(hWrapQuery->phRecords[i], j+1, pwzData);
552 ExitOnFailure(hr, "Failed to write string %ls to record in column %d", pwzData, j+1);
553 break;
554
555 case cdtInt:
556 WcaReadIntegerFromCaData(ppwzCustomActionData, &iTempInteger);
557 ExitOnFailure(hr, "Failed to read integer from custom action data");
558
559 WcaSetRecordInteger(hWrapQuery->phRecords[i], j+1, iTempInteger);
560 ExitOnFailure(hr, "Failed to write integer %d to record in column %d", iTempInteger, j+1);
561 break;
562
563 case cdtStream:
564 hr = E_NOTIMPL;
565 ExitOnFailure(hr, "A query was wrapped which contained a stream data field - however, the ability to wrap stream data fields is not implemented at this time");
566 break;
567
568 case cdtUnknown:
569 default:
570 hr = E_INVALIDARG;
571 ExitOnFailure(hr, "Failed to recognize column type enumeration %d for column %d", hWrapQuery->pcdtColumnType[j+1], i+1);
572 }
573 }
574
575 hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iTempInteger);
576 if (wqaRowFinish != iTempInteger)
577 {
578 hr = E_INVALIDARG;
579 }
580 ExitOnFailure(hr, "Failed to read row finish marker from custom action data (read %d instead)", iTempInteger);
581 }
582
583 hr = WcaReadIntegerFromCaData(ppwzCustomActionData, &iTempInteger);
584 if (wqaTableFinish != iTempInteger)
585 {
586 hr = E_INVALIDARG;
587 }
588 ExitOnFailure(hr, "Failed to read table finish marker from custom action data (read %d instead)", iTempInteger);
589
590 *phWrapQuery = hWrapQuery;
591
592// WcaLog(LOGMSG_TRACEONLY, "Successfully finished unwrapping a query from custom action data");
593
594LExit:
595 ReleaseStr(pwzData);
596 ReleaseMem(pbData);
597
598 return hr;
599}
600
601// This function returns the total number of records in a query
602DWORD WIXAPI WcaGetQueryRecords(
603 __in const WCA_WRAPQUERY_HANDLE hWrapQuery
604 )
605{
606 return hWrapQuery->dwRows;
607}
608
609// This function resets a query back to its first row, so that the next fetch returns the first record
610void WIXAPI WcaFetchWrappedReset(
611 __in WCA_WRAPQUERY_HANDLE hWrapQuery
612 )
613{
614 hWrapQuery->dwNextIndex = 0;
615}
616
617// Fetches the next record in the query
618// NOTE: the MSIHANDLE returned by this function should not be released, as it is the same handle used by the query object to maintain the item.
619// so, don't use this function with PMSIHANDLE objects!
620HRESULT WIXAPI WcaFetchWrappedRecord(
621 __in WCA_WRAPQUERY_HANDLE hWrapQuery,
622 __out MSIHANDLE* phRec
623 )
624{
625 DWORD dwNextIndex = hWrapQuery->dwNextIndex;
626
627 if (dwNextIndex >= hWrapQuery->dwRows)
628 {
629 return E_NOMOREITEMS;
630 }
631
632 if (NULL == hWrapQuery->phRecords[dwNextIndex])
633 {
634 return E_HANDLE;
635 }
636
637 *phRec = hWrapQuery->phRecords[hWrapQuery->dwNextIndex];
638
639 // Increment our next index variable
640 ++hWrapQuery->dwNextIndex;
641
642 return S_OK;
643}
644
645// Fetch the next record in the query where the string value in column dwComparisonColumn equals the value pwzExpectedValue
646// NOTE: the MSIHANDLE returned by this function should not be released, as it is the same handle used by the query object to maintain the item.
647// so, don't use this function with PMSIHANDLE objects!
648HRESULT WIXAPI WcaFetchWrappedRecordWhereString(
649 __in WCA_WRAPQUERY_HANDLE hWrapQuery,
650 __in DWORD dwComparisonColumn,
651 __in_z LPCWSTR pwzExpectedValue,
652 __out MSIHANDLE* phRec
653 )
654{
655 HRESULT hr = S_OK;
656 MSIHANDLE hRec = NULL;
657 LPWSTR pwzData = NULL;
658
659 while (S_OK == (hr = WcaFetchWrappedRecord(hWrapQuery, &hRec)))
660 {
661 ExitOnFailure(hr, "Failed to fetch a wrapped record");
662
663 hr = WcaGetRecordString(hRec, dwComparisonColumn, &pwzData);
664 ExitOnFailure(hr, "Failed to get record string in column %d", dwComparisonColumn);
665
666 if (0 == lstrcmpW(pwzData, pwzExpectedValue))
667 {
668 *phRec = hRec;
669 ExitFunction1(hr = S_OK);
670 }
671 }
672 // If we errored here but not because there were no records left, write an error to the log
673 if (hr != E_NOMOREITEMS)
674 {
675 ExitOnFailure(hr, "Failed while searching for a wrapped record where column %d is set to %ls", dwComparisonColumn, pwzExpectedValue);
676 }
677
678LExit:
679 ReleaseStr(pwzData);
680
681 return hr;
682}
683
684/********************************************************************
685WcaBeginUnwrapQuery() - Finishes unwrapping a view for direct access
686 from the CustomActionData property
687
688********************************************************************/
689void WIXAPI WcaFinishUnwrapQuery(
690 __in_opt WCA_WRAPQUERY_HANDLE hWrapQuery
691 )
692{
693 if (NULL == hWrapQuery)
694 {
695 WcaLog(LOGMSG_TRACEONLY, "Failed to finish an unwrap query - ignoring");
696 return;
697 }
698
699 ReleaseMem(hWrapQuery->pcdtColumnType);
700
701 for (DWORD i=0;i<hWrapQuery->dwColumns;i++)
702 {
703 ReleaseStr(hWrapQuery->ppwzColumnNames[i]);
704 }
705 ReleaseMem(hWrapQuery->ppwzColumnNames);
706
707 for (DWORD i=0;i<hWrapQuery->dwRows;i++)
708 {
709 if (NULL != hWrapQuery->phRecords[i])
710 {
711 ::MsiCloseHandle(hWrapQuery->phRecords[i]);
712 }
713 }
714 ReleaseMem(hWrapQuery->phRecords);
715
716 ReleaseMem(hWrapQuery);
717}