aboutsummaryrefslogtreecommitdiff
path: root/src/ca/XmlFile.cpp
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2018-12-15 21:46:30 -0600
committerGitHub <noreply@github.com>2018-12-15 21:46:30 -0600
commitf7020c0d16baf2b960e7123e233e20c519f6a340 (patch)
treed2cd464ee15b2b3f304ff780c531b39bb292d331 /src/ca/XmlFile.cpp
parent6ed8d107e6edf16956c778bda3573f8d7a7690fc (diff)
downloadwix-f7020c0d16baf2b960e7123e233e20c519f6a340.tar.gz
wix-f7020c0d16baf2b960e7123e233e20c519f6a340.tar.bz2
wix-f7020c0d16baf2b960e7123e233e20c519f6a340.zip
Import implementation of UtilCA from old repo's WixCA/scasched/scaexec. (#3)
Diffstat (limited to 'src/ca/XmlFile.cpp')
-rw-r--r--src/ca/XmlFile.cpp942
1 files changed, 942 insertions, 0 deletions
diff --git a/src/ca/XmlFile.cpp b/src/ca/XmlFile.cpp
new file mode 100644
index 00000000..fc6f519b
--- /dev/null
+++ b/src/ca/XmlFile.cpp
@@ -0,0 +1,942 @@
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 XMLFILE_CREATE_ELEMENT 0x00000001
6#define XMLFILE_DELETE_VALUE 0x00000002
7#define XMLFILE_BULKWRITE_VALUE 0x00000004
8
9#define XMLFILE_DONT_UNINSTALL 0x00010000
10#define XMLFILE_PRESERVE_MODIFIED 0x00001000
11#define XMLFILE_USE_XPATH 0x00000100
12
13extern BOOL vfMsxml30;
14
15enum eXmlAction
16{
17 xaOpenFile = 1,
18 xaOpenFilex64,
19 xaWriteValue,
20 xaDeleteValue,
21 xaCreateElement,
22 xaDeleteElement,
23 xaBulkWriteValue,
24};
25
26enum eXmlPreserveDate
27{
28 xdDontPreserve = 0,
29 xdPreserve
30};
31
32enum eXmlSelectionLanguage
33{
34 xsXSLPattern = 0,
35 xsXPath = 1,
36};
37
38LPCWSTR vcsXmlFileQuery =
39 L"SELECT `XmlFile`.`XmlFile`, `XmlFile`.`File`, `XmlFile`.`ElementPath`, `XmlFile`.`Name`, `XmlFile`.`Value`, "
40 L"`XmlFile`.`Flags`, `XmlFile`.`Component_`, `Component`.`Attributes` "
41 L"FROM `XmlFile`,`Component` WHERE `XmlFile`.`Component_`=`Component`.`Component` ORDER BY `File`, `Sequence`";
42enum eXmlFileQuery { xfqXmlFile = 1, xfqFile, xfqXPath, xfqName, xfqValue, xfqXmlFlags, xfqComponent, xfqCompAttributes };
43
44struct XML_FILE_CHANGE
45{
46 WCHAR wzId[MAX_DARWIN_KEY];
47
48 INSTALLSTATE isInstalled;
49 INSTALLSTATE isAction;
50
51 WCHAR wzFile[MAX_PATH];
52 LPWSTR pwzElementPath;
53 WCHAR wzName[MAX_DARWIN_COLUMN];
54 LPWSTR pwzValue;
55
56 int iXmlFlags;
57 int iCompAttributes;
58
59 XML_FILE_CHANGE* pxfcPrev;
60 XML_FILE_CHANGE* pxfcNext;
61};
62
63//static HRESULT FreeXmlFileChangeList(
64// __in XML_FILE_CHANGE* pxfcList
65// )
66//{
67// HRESULT hr = S_OK;
68//
69// XML_FILE_CHANGE* pxfcDelete;
70// while(pxfcList)
71// {
72// pxfcDelete = pxfcList;
73// pxfcList = pxfcList->pxfcNext;
74//
75// ReleaseStr(pxfcDelete->pwzElementPath);
76// ReleaseStr(pxfcDelete->pwzValue);
77//
78// hr = MemFree(pxfcDelete);
79// ExitOnFailure(hr, "failed to free xml file change list item");
80// }
81//
82//LExit:
83// return hr;
84//}
85
86static HRESULT AddXmlFileChangeToList(
87 __inout XML_FILE_CHANGE** ppxfcHead,
88 __inout XML_FILE_CHANGE** ppxfcTail
89 )
90{
91 Assert(ppxfcHead && ppxfcTail);
92
93 HRESULT hr = S_OK;
94
95 XML_FILE_CHANGE* pxfc = static_cast<XML_FILE_CHANGE*>(MemAlloc(sizeof(XML_FILE_CHANGE), TRUE));
96 ExitOnNull(pxfc, hr, E_OUTOFMEMORY, "failed to allocate memory for new xml file change list element");
97
98 // Add it to the end of the list
99 if (NULL == *ppxfcHead)
100 {
101 *ppxfcHead = pxfc;
102 *ppxfcTail = pxfc;
103 }
104 else
105 {
106 Assert(*ppxfcTail && (*ppxfcTail)->pxfcNext == NULL);
107 (*ppxfcTail)->pxfcNext = pxfc;
108 pxfc->pxfcPrev = *ppxfcTail;
109 *ppxfcTail = pxfc;
110 }
111
112LExit:
113 return hr;
114}
115
116
117static HRESULT ReadXmlFileTable(
118 __inout XML_FILE_CHANGE** ppxfcHead,
119 __inout XML_FILE_CHANGE** ppxfcTail
120 )
121{
122 Assert(ppxfcHead && ppxfcTail);
123
124 HRESULT hr = S_OK;
125 UINT er = ERROR_SUCCESS;
126
127 PMSIHANDLE hView = NULL;
128 PMSIHANDLE hRec = NULL;
129
130 LPWSTR pwzData = NULL;
131
132 // check to see if necessary tables are specified
133 if (S_FALSE == WcaTableExists(L"XmlFile"))
134 ExitFunction1(hr = S_FALSE);
135
136 // loop through all the xml configurations
137 hr = WcaOpenExecuteView(vcsXmlFileQuery, &hView);
138 ExitOnFailure(hr, "failed to open view on XmlFile table");
139
140 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
141 {
142 hr = AddXmlFileChangeToList(ppxfcHead, ppxfcTail);
143 ExitOnFailure(hr, "failed to add xml file change to list");
144
145 // Get record Id
146 hr = WcaGetRecordString(hRec, xfqXmlFile, &pwzData);
147 ExitOnFailure(hr, "failed to get XmlFile record Id");
148 hr = StringCchCopyW((*ppxfcTail)->wzId, countof((*ppxfcTail)->wzId), pwzData);
149 ExitOnFailure(hr, "failed to copy XmlFile record Id");
150
151 // Get component name
152 hr = WcaGetRecordString(hRec, xfqComponent, &pwzData);
153 ExitOnFailure(hr, "failed to get component name for XmlFile: %ls", (*ppxfcTail)->wzId);
154
155 // Get the component's state
156 er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &(*ppxfcTail)->isInstalled, &(*ppxfcTail)->isAction);
157 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to get install state for Component: %ls", pwzData);
158
159 // Get the xml file
160 hr = WcaGetRecordFormattedString(hRec, xfqFile, &pwzData);
161 ExitOnFailure(hr, "failed to get xml file for XmlFile: %ls", (*ppxfcTail)->wzId);
162 hr = StringCchCopyW((*ppxfcTail)->wzFile, countof((*ppxfcTail)->wzFile), pwzData);
163 ExitOnFailure(hr, "failed to copy xml file path");
164
165 // Get the XmlFile table flags
166 hr = WcaGetRecordInteger(hRec, xfqXmlFlags, &(*ppxfcTail)->iXmlFlags);
167 ExitOnFailure(hr, "failed to get XmlFile flags for XmlFile: %ls", (*ppxfcTail)->wzId);
168
169 // Get the XPath
170 hr = WcaGetRecordFormattedString(hRec, xfqXPath, &(*ppxfcTail)->pwzElementPath);
171 ExitOnFailure(hr, "failed to get XPath for XmlFile: %ls", (*ppxfcTail)->wzId);
172
173 // Get the name
174 hr = WcaGetRecordFormattedString(hRec, xfqName, &pwzData);
175 ExitOnFailure(hr, "failed to get Name for XmlFile: %ls", (*ppxfcTail)->wzId);
176 hr = StringCchCopyW((*ppxfcTail)->wzName, countof((*ppxfcTail)->wzName), pwzData);
177 ExitOnFailure(hr, "failed to copy name of element");
178
179 // Get the value
180 hr = WcaGetRecordFormattedString(hRec, xfqValue, &pwzData);
181 ExitOnFailure(hr, "failed to get Value for XmlFile: %ls", (*ppxfcTail)->wzId);
182 hr = StrAllocString(&(*ppxfcTail)->pwzValue, pwzData, 0);
183 ExitOnFailure(hr, "failed to allocate buffer for value");
184
185 // Get the component attributes
186 hr = WcaGetRecordInteger(hRec, xfqCompAttributes, &(*ppxfcTail)->iCompAttributes);
187 ExitOnFailure(hr, "failed to get component attributes for XmlFile: %ls", (*ppxfcTail)->wzId);
188 }
189
190 // if we looped through all records all is well
191 if (E_NOMOREITEMS == hr)
192 hr = S_OK;
193 ExitOnFailure(hr, "failed while looping through all objects to secure");
194
195LExit:
196 ReleaseStr(pwzData);
197
198 return hr;
199}
200
201
202static HRESULT BeginChangeFile(
203 __in LPCWSTR pwzFile,
204 __in XML_FILE_CHANGE* pxfc,
205 __inout LPWSTR* ppwzCustomActionData
206 )
207{
208 Assert(pwzFile && *pwzFile && ppwzCustomActionData);
209
210 HRESULT hr = S_OK;
211 BOOL fIs64Bit = pxfc->iCompAttributes & msidbComponentAttributes64bit;
212 BOOL fUseXPath = pxfc->iXmlFlags & XMLFILE_USE_XPATH;
213 LPBYTE pbData = NULL;
214 DWORD cbData = 0;
215
216 LPWSTR pwzRollbackCustomActionData = NULL;
217
218 if (fIs64Bit)
219 {
220 hr = WcaWriteIntegerToCaData((int)xaOpenFilex64, ppwzCustomActionData);
221 ExitOnFailure(hr, "failed to write 64-bit file indicator to custom action data");
222 }
223 else
224 {
225 hr = WcaWriteIntegerToCaData((int)xaOpenFile, ppwzCustomActionData);
226 ExitOnFailure(hr, "failed to write file indicator to custom action data");
227 }
228 if (fUseXPath)
229 {
230 hr = WcaWriteIntegerToCaData((int)xsXPath, ppwzCustomActionData);
231 ExitOnFailure(hr, "failed to write XPath selectionlanguage indicator to custom action data");
232 }
233 else
234 {
235 hr = WcaWriteIntegerToCaData((int)xsXSLPattern, ppwzCustomActionData);
236 ExitOnFailure(hr, "failed to write XSLPattern selectionlanguage indicator to custom action data");
237 }
238 hr = WcaWriteStringToCaData(pwzFile, ppwzCustomActionData);
239 ExitOnFailure(hr, "failed to write file to custom action data: %ls", pwzFile);
240
241 // If the file already exits, then we have to put it back the way it was on failure
242 if (FileExistsEx(pwzFile, NULL))
243 {
244 hr = FileRead(&pbData, &cbData, pwzFile);
245 ExitOnFailure(hr, "failed to read file: %ls", pwzFile);
246
247 // Set up the rollback for this file
248 hr = WcaWriteIntegerToCaData((int)fIs64Bit, &pwzRollbackCustomActionData);
249 ExitOnFailure(hr, "failed to write component bitness to rollback custom action data");
250
251 hr = WcaWriteStringToCaData(pwzFile, &pwzRollbackCustomActionData);
252 ExitOnFailure(hr, "failed to write file name to rollback custom action data: %ls", pwzFile);
253
254 hr = WcaWriteStreamToCaData(pbData, cbData, &pwzRollbackCustomActionData);
255 ExitOnFailure(hr, "failed to write file contents to rollback custom action data.");
256
257 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"ExecXmlFileRollback"), pwzRollbackCustomActionData, COST_XMLFILE);
258 ExitOnFailure(hr, "failed to schedule ExecXmlFileRollback for file: %ls", pwzFile);
259
260 ReleaseStr(pwzRollbackCustomActionData);
261 }
262LExit:
263 ReleaseMem(pbData);
264
265 return hr;
266}
267
268
269static HRESULT WriteChangeData(
270 __in XML_FILE_CHANGE* pxfc,
271 __inout LPWSTR* ppwzCustomActionData
272 )
273{
274 Assert(pxfc && ppwzCustomActionData);
275
276 HRESULT hr = S_OK;
277
278 hr = WcaWriteStringToCaData(pxfc->pwzElementPath, ppwzCustomActionData);
279 ExitOnFailure(hr, "failed to write ElementPath to custom action data: %ls", pxfc->pwzElementPath);
280
281 hr = WcaWriteStringToCaData(pxfc->wzName, ppwzCustomActionData);
282 ExitOnFailure(hr, "failed to write Name to custom action data: %ls", pxfc->wzName);
283
284 hr = WcaWriteStringToCaData(pxfc->pwzValue, ppwzCustomActionData);
285 ExitOnFailure(hr, "failed to write Value to custom action data: %ls", pxfc->pwzValue);
286
287LExit:
288 return hr;
289}
290
291
292/******************************************************************
293 SchedXmlFile - entry point for XmlFile Custom Action
294
295********************************************************************/
296extern "C" UINT __stdcall SchedXmlFile(
297 __in MSIHANDLE hInstall
298 )
299{
300// AssertSz(FALSE, "debug SchedXmlFile");
301
302 HRESULT hr = S_OK;
303 UINT er = ERROR_SUCCESS;
304
305 LPWSTR pwzCurrentFile = NULL;
306 BOOL fCurrentFileChanged = FALSE;
307 BOOL fCurrentUseXPath = FALSE;
308
309 PMSIHANDLE hView = NULL;
310 PMSIHANDLE hRec = NULL;
311
312 XML_FILE_CHANGE* pxfcHead = NULL;
313 XML_FILE_CHANGE* pxfcTail = NULL;
314 XML_FILE_CHANGE* pxfc = NULL;
315 XML_FILE_CHANGE* pxfcUninstall = NULL;
316
317 LPWSTR pwzCustomActionData = NULL;
318
319 DWORD cFiles = 0;
320
321 // initialize
322 hr = WcaInitialize(hInstall, "SchedXmlFile");
323 ExitOnFailure(hr, "failed to initialize");
324
325 hr = ReadXmlFileTable(&pxfcHead, &pxfcTail);
326 if (S_FALSE == hr)
327 {
328 WcaLog(LOGMSG_VERBOSE, "Skipping SchedXmlFile because XmlFile table not present");
329 ExitFunction1(hr = S_OK);
330 }
331
332 MessageExitOnFailure(hr, msierrXmlFileFailedRead, "failed to read XmlFile table");
333
334 // loop through all the xml configurations
335 for (pxfc = pxfcHead; pxfc; pxfc = pxfc->pxfcNext)
336 {
337 // If this is the first file, a different file, the last file, or the SelectionLanguage property changes...
338 if (NULL == pwzCurrentFile || 0 != lstrcmpW(pwzCurrentFile, pxfc->wzFile) || NULL == pxfc->pxfcNext || fCurrentUseXPath != ((XMLFILE_USE_XPATH & pxfc->iXmlFlags)))
339 {
340 // If this isn't the first file
341 if (NULL != pwzCurrentFile)
342 {
343 // Do the uninstall work for the current file by walking backwards through the list (so the sequence is reversed)
344 for (pxfcUninstall = ((NULL != pxfc->pxfcNext) ? pxfc->pxfcPrev : pxfc); pxfcUninstall && 0 == lstrcmpW(pwzCurrentFile, pxfcUninstall->wzFile) && fCurrentUseXPath == ((XMLFILE_USE_XPATH & pxfcUninstall->iXmlFlags)); pxfcUninstall = pxfcUninstall->pxfcPrev)
345 {
346 // If it's being uninstalled
347 if (WcaIsUninstalling(pxfcUninstall->isInstalled, pxfcUninstall->isAction))
348 {
349 // Uninstall the change
350 if (!(XMLFILE_DONT_UNINSTALL & pxfcUninstall->iXmlFlags))
351 {
352 if (!fCurrentFileChanged)
353 {
354 hr = BeginChangeFile(pwzCurrentFile, pxfcUninstall, &pwzCustomActionData);
355 ExitOnFailure(hr, "failed to begin file change for file: %ls", pwzCurrentFile);
356
357 fCurrentFileChanged = TRUE;
358 ++cFiles;
359 }
360 if (XMLFILE_CREATE_ELEMENT & pxfcUninstall->iXmlFlags)
361 {
362 hr = WcaWriteIntegerToCaData((int)xaDeleteElement, &pwzCustomActionData);
363 ExitOnFailure(hr, "failed to write delete element action indicator to custom action data");
364 }
365 else
366 {
367 hr = WcaWriteIntegerToCaData((int)xaDeleteValue, &pwzCustomActionData);
368 ExitOnFailure(hr, "failed to write delete value action indicator to custom action data");
369 }
370
371 if (XMLFILE_PRESERVE_MODIFIED & pxfc->iXmlFlags)
372 {
373 hr = WcaWriteIntegerToCaData((int)xdPreserve, &pwzCustomActionData);
374 ExitOnFailure(hr, "failed to write Preserve Date indicator to custom action data");
375 }
376 else
377 {
378 hr = WcaWriteIntegerToCaData((int)xdDontPreserve, &pwzCustomActionData);
379 ExitOnFailure(hr, "failed to write Don't Preserve Date indicator to custom action data");
380 }
381
382 hr = WriteChangeData(pxfcUninstall, &pwzCustomActionData);
383 ExitOnFailure(hr, "failed to write uninstall change data");
384 }
385 }
386 }
387 }
388
389 // Remember the file we're currently working on
390 hr = StrAllocString(&pwzCurrentFile, pxfc->wzFile, 0);
391 ExitOnFailure(hr, "failed to copy file name");
392 fCurrentUseXPath = (XMLFILE_USE_XPATH & pxfc->iXmlFlags);
393
394 // We haven't changed the current file yet
395 fCurrentFileChanged = FALSE;
396 }
397
398 // If it's being installed
399 if (WcaIsInstalling(pxfc->isInstalled, pxfc->isAction))
400 {
401 if (!fCurrentFileChanged)
402 {
403 hr = BeginChangeFile(pwzCurrentFile, pxfc, &pwzCustomActionData);
404 ExitOnFailure(hr, "failed to begin file change for file: %ls", pwzCurrentFile);
405 fCurrentFileChanged = TRUE;
406 ++cFiles;
407 }
408
409 // Install the change
410 if (XMLFILE_CREATE_ELEMENT & pxfc->iXmlFlags)
411 {
412 hr = WcaWriteIntegerToCaData((int)xaCreateElement, &pwzCustomActionData);
413 ExitOnFailure(hr, "failed to write create element action indicator to custom action data");
414 }
415 else if (XMLFILE_DELETE_VALUE & pxfc->iXmlFlags)
416 {
417 hr = WcaWriteIntegerToCaData((int)xaDeleteValue, &pwzCustomActionData);
418 ExitOnFailure(hr, "failed to write delete value action indicator to custom action data");
419 }
420 else if (XMLFILE_BULKWRITE_VALUE & pxfc->iXmlFlags)
421 {
422 hr = WcaWriteIntegerToCaData((int)xaBulkWriteValue, &pwzCustomActionData);
423 ExitOnFailure(hr, "failed to write builkwrite value action indicator to custom action data");
424 }
425 else
426 {
427 hr = WcaWriteIntegerToCaData((int)xaWriteValue, &pwzCustomActionData);
428 ExitOnFailure(hr, "failed to write file indicator to custom action data");
429 }
430
431 if (XMLFILE_PRESERVE_MODIFIED & pxfc->iXmlFlags)
432 {
433 hr = WcaWriteIntegerToCaData((int)xdPreserve, &pwzCustomActionData);
434 ExitOnFailure(hr, "failed to write Preserve Date indicator to custom action data");
435 }
436 else
437 {
438 hr = WcaWriteIntegerToCaData((int)xdDontPreserve, &pwzCustomActionData);
439 ExitOnFailure(hr, "failed to write Don't Preserve Date indicator to custom action data");
440 }
441
442 hr = WriteChangeData(pxfc, &pwzCustomActionData);
443 ExitOnFailure(hr, "failed to write change data");
444 }
445 }
446
447 // If we looped through all records all is well
448 if (E_NOMOREITEMS == hr)
449 hr = S_OK;
450 ExitOnFailure(hr, "failed while looping through all objects to secure");
451
452 // Schedule the custom action and add to progress bar
453 if (pwzCustomActionData && *pwzCustomActionData)
454 {
455 Assert(0 < cFiles);
456
457 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"ExecXmlFile"), pwzCustomActionData, cFiles * COST_XMLFILE);
458 ExitOnFailure(hr, "failed to schedule ExecXmlFile action");
459 }
460
461LExit:
462 ReleaseStr(pwzCurrentFile);
463 ReleaseStr(pwzCustomActionData);
464
465 if (FAILED(hr))
466 er = ERROR_INSTALL_FAILURE;
467 return WcaFinalize(er);
468}
469
470
471/******************************************************************
472 ExecXmlFile - entry point for XmlFile Custom Action
473
474*******************************************************************/
475extern "C" UINT __stdcall ExecXmlFile(
476 __in MSIHANDLE hInstall
477 )
478{
479// AssertSz(FALSE, "debug ExecXmlFile");
480 HRESULT hr = S_OK;
481 HRESULT hrOpenFailure = S_OK;
482 UINT er = ERROR_SUCCESS;
483
484 BOOL fIsWow64Process = FALSE;
485 BOOL fIsFSRedirectDisabled = FALSE;
486 BOOL fPreserveDate = FALSE;
487
488 int id = IDRETRY;
489
490 LPWSTR pwzCustomActionData = NULL;
491 LPWSTR pwzData = NULL;
492 LPWSTR pwzFile = NULL;
493 LPWSTR pwzXPath = NULL;
494 LPWSTR pwzName = NULL;
495 LPWSTR pwzValue = NULL;
496 LPWSTR pwz = NULL;
497
498 IXMLDOMDocument* pixd = NULL;
499 IXMLDOMNode* pixn = NULL;
500 IXMLDOMNode* pixnNewNode = NULL;
501 IXMLDOMNodeList* pixNodes = NULL;
502 IXMLDOMDocument2 *pixdDocument2 = NULL;
503
504 FILETIME ft;
505
506 BSTR bstrProperty = ::SysAllocString(L"SelectionLanguage");
507 ExitOnNull(bstrProperty, hr, E_OUTOFMEMORY, "failed SysAllocString");
508 VARIANT varValue;
509 ::VariantInit(&varValue);
510 varValue.vt = VT_BSTR;
511 varValue.bstrVal = ::SysAllocString(L"XPath");
512 ExitOnNull(varValue.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString");
513 eXmlAction xa;
514 eXmlPreserveDate xd;
515 eXmlSelectionLanguage xl;
516
517 // initialize
518 hr = WcaInitialize(hInstall, "ExecXmlFile");
519 ExitOnFailure(hr, "failed to initialize");
520
521 hr = XmlInitialize();
522 ExitOnFailure(hr, "failed to initialize xml utilities");
523
524 hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
525 ExitOnFailure(hr, "failed to get CustomActionData");
526
527 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
528
529 pwz = pwzCustomActionData;
530
531 hr = WcaReadIntegerFromCaData(&pwz, (int*) &xa);
532 ExitOnFailure(hr, "failed to process CustomActionData");
533
534 // Initialize the Wow64 API - store the result in fWow64APIPresent
535 // If it fails, this doesn't warrant an error yet, because we only need the Wow64 API in some cases
536 WcaInitializeWow64();
537 fIsWow64Process = WcaIsWow64Process();
538
539 if (xaOpenFile != xa && xaOpenFilex64 != xa)
540 ExitOnFailure(hr = E_INVALIDARG, "invalid custom action data");
541
542 // loop through all the passed in data
543 while (pwz && *pwz)
544 {
545 hr = WcaReadIntegerFromCaData(&pwz, (int*) &xl);
546 ExitOnFailure(hr, "failed to process CustomActionData");
547
548 hr = WcaReadStringFromCaData(&pwz, &pwzFile);
549 ExitOnFailure(hr, "failed to read file name from custom action data");
550
551 // Default to not preserve the modified date
552 fPreserveDate = FALSE;
553
554 // Open the file
555 ReleaseNullObject(pixd);
556
557 if (xaOpenFilex64 == xa)
558 {
559 if (!fIsWow64Process)
560 {
561 hr = E_NOTIMPL;
562 ExitOnFailure(hr, "Custom action was told to act on a 64-bit component, but the custom action process is not running in WOW.");
563 }
564
565 hr = WcaDisableWow64FSRedirection();
566 ExitOnFailure(hr, "Custom action was told to act on a 64-bit component, but was unable to disable filesystem redirection through the Wow64 API.");
567
568 fIsFSRedirectDisabled = TRUE;
569 }
570
571 hr = XmlLoadDocumentFromFileEx(pwzFile, XML_LOAD_PRESERVE_WHITESPACE, &pixd);
572 if (FAILED(hr))
573 {
574 // Ignore the return code for now. If they try to add something, we'll fail the install. If all they do is remove stuff then it doesn't matter.
575 hrOpenFailure = hr;
576 hr = S_OK;
577 }
578 else
579 {
580 hrOpenFailure = S_OK;
581 }
582 WcaLog(LOGMSG_VERBOSE, "Configuring Xml File: %ls", pwzFile);
583
584 if (xsXPath == xl)
585 {
586 if (vfMsxml30)
587 {
588 // If we failed to open the file, don't fail immediately; just skip setting the selection language, and we'll fail later if appropriate
589 if (SUCCEEDED(hrOpenFailure))
590 {
591 hr = pixd->QueryInterface(XmlUtil_IID_IXMLDOMDocument2, (void**)&pixdDocument2);
592 ExitOnFailure(hr, "failed in querying IXMLDOMDocument2 interface");
593 hr = pixdDocument2->setProperty(bstrProperty, varValue);
594 ExitOnFailure(hr, "failed in setting SelectionLanguage");
595 }
596 }
597 else
598 {
599 hr = E_NOTIMPL;
600 ExitTrace(hr, "Error: current MSXML version does not support xpath query.");
601 ExitFunction();
602 }
603 }
604
605 while (pwz && *pwz)
606 {
607 hr = WcaReadIntegerFromCaData(&pwz, (int*) &xa);
608 ExitOnFailure(hr, "failed to process CustomActionData");
609
610 // Break if we need to move on to a different file
611 if (xaOpenFile == xa || xaOpenFilex64 == xa)
612 break;
613
614 hr = WcaReadIntegerFromCaData(&pwz, (int*) &xd);
615 ExitOnFailure(hr, "failed to process CustomActionData");
616
617 if (xdPreserve == xd)
618 {
619 fPreserveDate = TRUE;
620 }
621
622 // Get path, name, and value to be written
623 hr = WcaReadStringFromCaData(&pwz, &pwzXPath);
624 ExitOnFailure(hr, "failed to process CustomActionData");
625 hr = WcaReadStringFromCaData(&pwz, &pwzName);
626 ExitOnFailure(hr, "failed to process CustomActionData");
627 hr = WcaReadStringFromCaData(&pwz, &pwzValue);
628 ExitOnFailure(hr, "failed to process CustomActionData");
629
630 // If we failed to open the file and we're adding something to the file, we've got a problem. Otherwise, just continue on since the file's already gone.
631 if (FAILED(hrOpenFailure))
632 {
633 if (xaCreateElement == xa || xaWriteValue == xa || xaBulkWriteValue == xa)
634 {
635 MessageExitOnFailure(hr = hrOpenFailure, msierrXmlFileFailedOpen, "failed to load XML file: %ls", pwzFile);
636 }
637 else
638 {
639 continue;
640 }
641 }
642
643 // Select the node we're about to modify
644 ReleaseNullObject(pixn);
645
646 if (xaBulkWriteValue == xa)
647 {
648 hr = XmlSelectNodes(pixd, pwzXPath, &pixNodes);
649 if (S_FALSE == hr)
650 {
651 hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND);
652 }
653
654 MessageExitOnFailure(hr, msierrXmlFileFailedSelect, "failed to find any nodes: %ls in XML file: %ls", pwzXPath, pwzFile);
655 for (;;)
656 {
657 pixNodes->nextNode(&pixn);
658 if (NULL == pixn)
659 break;
660
661 if (pwzName && *pwzName)
662 {
663 // We're setting an attribute
664 hr = XmlSetAttribute(pixn, pwzName, pwzValue);
665 ExitOnFailure(hr, "failed to set attribute: %ls to value %ls", pwzName, pwzValue);
666 }
667 else
668 {
669 // We're setting the text of the node
670 hr = XmlSetText(pixn, pwzValue);
671 ExitOnFailure(hr, "failed to set text to: %ls for element %ls. Make sure that XPath points to an element.", pwzValue, pwzXPath);
672 }
673 ReleaseNullObject(pixn);
674 }
675 }
676 else
677 {
678 hr = XmlSelectSingleNode(pixd, pwzXPath, &pixn);
679 if (S_FALSE == hr)
680 hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND);
681 MessageExitOnFailure(hr, msierrXmlFileFailedSelect, "failed to find node: %ls in XML file: %ls", pwzXPath, pwzFile);
682
683 // Make the modification
684 if (xaWriteValue == xa)
685 {
686 if (pwzName && *pwzName)
687 {
688 // We're setting an attribute
689 hr = XmlSetAttribute(pixn, pwzName, pwzValue);
690 ExitOnFailure(hr, "failed to set attribute: %ls to value %ls", pwzName, pwzValue);
691 }
692 else
693 {
694 // We're setting the text of the node
695 hr = XmlSetText(pixn, pwzValue);
696 ExitOnFailure(hr, "failed to set text to: %ls for element %ls. Make sure that XPath points to an element.", pwzValue, pwzXPath);
697 }
698 }
699 else if (xaCreateElement == xa)
700 {
701 hr = XmlCreateChild(pixn, pwzName, &pixnNewNode);
702 ExitOnFailure(hr, "failed to create child element: %ls", pwzName);
703
704 if (pwzValue && *pwzValue)
705 {
706 hr = XmlSetText(pixnNewNode, pwzValue);
707 ExitOnFailure(hr, "failed to set text to: %ls for node: %ls", pwzValue, pwzName);
708 }
709
710 ReleaseNullObject(pixnNewNode);
711 }
712 else if (xaDeleteValue == xa)
713 {
714 if (pwzName && *pwzName)
715 {
716 // Delete the attribute
717 hr = XmlRemoveAttribute(pixn, pwzName);
718 ExitOnFailure(hr, "failed to remove attribute: %ls", pwzName);
719 }
720 else
721 {
722 // Clear the text value for the node
723 hr = XmlSetText(pixn, L"");
724 ExitOnFailure(hr, "failed to clear text value");
725 }
726 }
727 else if (xaDeleteElement == xa)
728 {
729 // TODO: This may be a little heavy handed
730 hr = XmlRemoveChildren(pixn, pwzName);
731 ExitOnFailure(hr, "failed to delete child node: %ls", pwzName);
732 }
733 else
734 {
735 ExitOnFailure(hr = E_UNEXPECTED, "Invalid modification specified in custom action data");
736 }
737 }
738 }
739
740 // Now that we've made all of the changes to this file, save it and move on to the next
741 if (S_OK == hrOpenFailure)
742 {
743 if (fPreserveDate)
744 {
745 hr = FileGetTime(pwzFile, NULL, NULL, &ft);
746 ExitOnFailure(hr, "failed to get modified time of file : %ls", pwzFile);
747 }
748
749 int iSaveAttempt = 0;
750
751 do
752 {
753 hr = XmlSaveDocument(pixd, pwzFile);
754 if (FAILED(hr))
755 {
756 id = WcaErrorMessage(msierrXmlConfigFailedSave, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 1, pwzFile);
757 switch (id)
758 {
759 case IDABORT:
760 ExitOnFailure(hr, "Failed to save changes to XML file: %ls", pwzFile);
761 case IDRETRY:
762 hr = S_FALSE; // hit me, baby, one more time
763 break;
764 case IDIGNORE:
765 hr = S_OK; // pretend everything is okay and bail
766 break;
767 case 0: // No UI case, MsiProcessMessage returns 0
768 if (STIERR_SHARING_VIOLATION == hr)
769 {
770 // Only in case of sharing violation do we retry 30 times, once a second.
771 if (iSaveAttempt < 30)
772 {
773 hr = S_FALSE;
774 ++iSaveAttempt;
775 WcaLog(LOGMSG_VERBOSE, "Unable to save changes to XML file: %ls, retry attempt: %x", pwzFile, iSaveAttempt);
776 Sleep(1000);
777 }
778 else
779 {
780 ExitOnFailure(hr, "Failed to save changes to XML file: %ls", pwzFile);
781 }
782 }
783 break;
784 default: // Unknown error
785 ExitOnFailure(hr, "Failed to save changes to XML file: %ls", pwzFile);
786 }
787 }
788 } while (S_FALSE == hr);
789
790 if (fPreserveDate)
791 {
792 hr = FileSetTime(pwzFile, NULL, NULL, &ft);
793 ExitOnFailure(hr, "failed to set modified time of file : %ls", pwzFile);
794 }
795
796 if (fIsFSRedirectDisabled)
797 {
798 fIsFSRedirectDisabled = FALSE;
799 WcaRevertWow64FSRedirection();
800 }
801 }
802 }
803
804LExit:
805 // Make sure we revert FS Redirection if necessary before exiting
806 if (fIsFSRedirectDisabled)
807 {
808 fIsFSRedirectDisabled = FALSE;
809 WcaRevertWow64FSRedirection();
810 }
811 WcaFinalizeWow64();
812
813 ReleaseStr(pwzCustomActionData);
814 ReleaseStr(pwzData);
815 ReleaseStr(pwzFile);
816 ReleaseStr(pwzXPath);
817 ReleaseStr(pwzName);
818 ReleaseStr(pwzValue);
819 ReleaseBSTR(bstrProperty);
820 ReleaseVariant(varValue);
821
822 ReleaseObject(pixdDocument2);
823 ReleaseObject(pixn);
824 ReleaseObject(pixd);
825 ReleaseObject(pixnNewNode);
826 ReleaseObject(pixNodes);
827
828 XmlUninitialize();
829
830 if (FAILED(hr))
831 er = ERROR_INSTALL_FAILURE;
832 return WcaFinalize(er);
833}
834
835
836/******************************************************************
837 ExecXmlFileRollback - entry point for XmlFile rollback Custom Action
838
839*******************************************************************/
840extern "C" UINT __stdcall ExecXmlFileRollback(
841 __in MSIHANDLE hInstall
842 )
843{
844// AssertSz(FALSE, "debug ExecXmlFileRollback");
845 HRESULT hr = S_OK;
846 UINT er = ERROR_SUCCESS;
847
848 int iIs64Bit;
849 BOOL fIs64Bit = FALSE;
850
851 LPWSTR pwzCustomActionData = NULL;
852 LPWSTR pwz = NULL;
853 LPWSTR pwzFileName = NULL;
854 LPBYTE pbData = NULL;
855 DWORD_PTR cbData = 0;
856 DWORD cbDataWritten = 0;
857
858 FILETIME ft;
859
860 HANDLE hFile = INVALID_HANDLE_VALUE;
861
862 // initialize
863 hr = WcaInitialize(hInstall, "ExecXmlFileRollback");
864 ExitOnFailure(hr, "failed to initialize");
865
866
867 hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
868 ExitOnFailure(hr, "failed to get CustomActionData");
869
870 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
871
872 pwz = pwzCustomActionData;
873
874 hr = WcaReadIntegerFromCaData(&pwz, &iIs64Bit);
875 ExitOnFailure(hr, "failed to read component bitness from custom action data");
876
877 hr = WcaReadStringFromCaData(&pwz, &pwzFileName);
878 ExitOnFailure(hr, "failed to read file name from custom action data");
879
880 hr = WcaReadStreamFromCaData(&pwz, &pbData, &cbData);
881 ExitOnFailure(hr, "failed to read file contents from custom action data");
882
883 fIs64Bit = (BOOL)iIs64Bit;
884
885 if (fIs64Bit)
886 {
887 hr = WcaInitializeWow64();
888 if (S_FALSE == hr)
889 {
890 hr = TYPE_E_DLLFUNCTIONNOTFOUND;
891 }
892 ExitOnFailure(hr, "failed to initialize Wow64 API");
893
894 if (!WcaIsWow64Process())
895 {
896 hr = E_NOTIMPL;
897 ExitOnFailure(hr, "Custom action was told to rollback a 64-bit component, but the custom action process is not running in WOW.");
898 }
899
900 hr = WcaDisableWow64FSRedirection();
901 ExitOnFailure(hr, "Custom action was told to rollback a 64-bit component, but was unable to Disable Filesystem Redirection through the Wow64 API.");
902 }
903
904 // Always preserve the modified date on rollback
905 hr = FileGetTime(pwzFileName, NULL, NULL, &ft);
906 ExitOnFailure(hr, "Failed to get modified date of file %ls.", pwzFileName);
907
908 // Open the file
909 hFile = ::CreateFileW(pwzFileName, GENERIC_WRITE, NULL, NULL, TRUNCATE_EXISTING, NULL, NULL);
910 ExitOnInvalidHandleWithLastError(hFile, hr, "failed to open file: %ls", pwzFileName);
911
912 // Write out the old data
913 if (!::WriteFile(hFile, pbData, (DWORD)cbData, &cbDataWritten, NULL))
914 ExitOnLastError(hr, "failed to write to file: %ls", pwzFileName);
915
916 Assert(cbData == cbDataWritten);
917
918 ReleaseFile(hFile);
919
920 // Always preserve the modified date on rollback
921 hr = FileSetTime(pwzFileName, NULL, NULL, &ft);
922 ExitOnFailure(hr, "Failed to set modified date of file %ls.", pwzFileName);
923
924LExit:
925 ReleaseStr(pwzCustomActionData);
926 ReleaseStr(pwzFileName);
927
928 ReleaseFile(hFile);
929
930 if (fIs64Bit)
931 {
932 WcaRevertWow64FSRedirection();
933 WcaFinalizeWow64();
934 }
935
936 ReleaseMem(pbData);
937
938 if (FAILED(hr))
939 er = ERROR_INSTALL_FAILURE;
940 return WcaFinalize(er);
941}
942