aboutsummaryrefslogtreecommitdiff
path: root/src/wcautil/wcascript.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wcautil/wcascript.cpp')
-rw-r--r--src/wcautil/wcascript.cpp447
1 files changed, 447 insertions, 0 deletions
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}