aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ca/BroadcastSettingChange.cpp45
-rw-r--r--src/ca/CheckReboot.cpp36
-rw-r--r--src/ca/CloseApps.cpp570
-rw-r--r--src/ca/CustomMsiErrors.h32
-rw-r--r--src/ca/FormatFiles.cpp221
-rw-r--r--src/ca/OsInfo.cpp487
-rw-r--r--src/ca/RemoveFoldersEx.cpp197
-rw-r--r--src/ca/RestartManager.cpp185
-rw-r--r--src/ca/TouchFile.cpp308
-rw-r--r--src/ca/XmlConfig.cpp1099
-rw-r--r--src/ca/XmlFile.cpp942
-rw-r--r--src/ca/caSuffix.h11
-rw-r--r--src/ca/cost.h9
-rw-r--r--src/ca/exitearlywithsuccess.cpp27
-rw-r--r--src/ca/netshortcuts.cpp434
-rw-r--r--src/ca/precomp.h42
-rw-r--r--src/ca/qtexecca.cpp312
-rw-r--r--src/ca/sca.h19
-rw-r--r--src/ca/scacost.h18
-rw-r--r--src/ca/scaexec.cpp807
-rw-r--r--src/ca/scamanifest.cpp377
-rw-r--r--src/ca/scaperf.cpp310
-rw-r--r--src/ca/scaperfexec.cpp423
-rw-r--r--src/ca/scasched.cpp127
-rw-r--r--src/ca/scasmb.h46
-rw-r--r--src/ca/scasmbexec.cpp316
-rw-r--r--src/ca/scasmbexec.h27
-rw-r--r--src/ca/scasmbsched.cpp639
-rw-r--r--src/ca/scauser.cpp676
-rw-r--r--src/ca/scauser.h67
-rw-r--r--src/ca/secureobj.cpp902
-rw-r--r--src/ca/serviceconfig.cpp821
-rw-r--r--src/ca/shellexecca.cpp271
-rw-r--r--src/ca/test.cpp269
-rw-r--r--src/ca/utilca.def83
-rw-r--r--src/ca/utilca.vcxproj35
-rw-r--r--src/wixlib/UtilExtension.wxs40
-rw-r--r--src/wixlib/UtilExtension_Platform.wxi2
38 files changed, 11209 insertions, 23 deletions
diff --git a/src/ca/BroadcastSettingChange.cpp b/src/ca/BroadcastSettingChange.cpp
new file mode 100644
index 00000000..2e153ad3
--- /dev/null
+++ b/src/ca/BroadcastSettingChange.cpp
@@ -0,0 +1,45 @@
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/********************************************************************
7WixBroadcastSettingChange
8
9 Send WM_SETTINGCHANGE message to all top-level windows indicating
10 that unspecified settings have changed.
11********************************************************************/
12extern "C" UINT __stdcall WixBroadcastSettingChange(
13 __in MSIHANDLE hInstall
14 )
15{
16 HRESULT hr = WcaInitialize(hInstall, "WixBroadcastSettingChange");
17 ExitOnFailure(hr, "failed to initialize WixBroadcastSettingChange");
18
19 // best effort; ignore failures
20 ::SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, NULL, NULL, SMTO_ABORTIFHUNG, 1000, NULL);
21
22LExit:
23 return WcaFinalize(ERROR_SUCCESS);
24}
25
26
27/********************************************************************
28WixBroadcastEnvironmentChange
29
30 Send WM_SETTINGCHANGE message to all top-level windows indicating
31 that environment variables have changed.
32********************************************************************/
33extern "C" UINT __stdcall WixBroadcastEnvironmentChange(
34 __in MSIHANDLE hInstall
35 )
36{
37 HRESULT hr = WcaInitialize(hInstall, "WixBroadcastEnvironmentChange");
38 ExitOnFailure(hr, "failed to initialize WixBroadcastEnvironmentChange");
39
40 // best effort; ignore failures
41 ::SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, NULL, reinterpret_cast<LPARAM>(L"Environment"), SMTO_ABORTIFHUNG, 1000, NULL);
42
43LExit:
44 return WcaFinalize(ERROR_SUCCESS);
45}
diff --git a/src/ca/CheckReboot.cpp b/src/ca/CheckReboot.cpp
new file mode 100644
index 00000000..ce056411
--- /dev/null
+++ b/src/ca/CheckReboot.cpp
@@ -0,0 +1,36 @@
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/********************************************************************
7WixCheckRebootRequired - entry point for WixCheckRebootRequired Custom Action
8
9 called as Type 1 CustomAction (binary DLL) from Windows Installer
10 in InstallExecuteSequence after InstallFinalize
11********************************************************************/
12extern "C" UINT __stdcall WixCheckRebootRequired(
13 __in MSIHANDLE hInstall
14 )
15{
16 HRESULT hr = S_OK;
17 DWORD er = ERROR_SUCCESS;
18
19 hr = WcaInitialize(hInstall, "WixCheckRebootRequired");
20 ExitOnFailure(hr, "failed to initialize");
21
22 if (WcaDidDeferredActionRequireReboot())
23 {
24 WcaLog(LOGMSG_STANDARD, "Reboot required by deferred CustomAction.");
25
26 er = ::MsiSetMode(hInstall, MSIRUNMODE_REBOOTATEND, TRUE);
27 hr = HRESULT_FROM_WIN32(er);
28 ExitOnFailure(hr, "Failed to schedule reboot.");
29 }
30
31LExit:
32
33 if (FAILED(hr))
34 er = ERROR_INSTALL_FAILURE;
35 return WcaFinalize(er);
36}
diff --git a/src/ca/CloseApps.cpp b/src/ca/CloseApps.cpp
new file mode 100644
index 00000000..a3f28ed3
--- /dev/null
+++ b/src/ca/CloseApps.cpp
@@ -0,0 +1,570 @@
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 DEFAULT_PROCESS_EXIT_WAIT_TIME 5000
6
7// WixCloseApplication Target Description Condition Attributes Sequence
8
9// structs
10LPCWSTR wzQUERY_CLOSEAPPS = L"SELECT `WixCloseApplication`, `Target`, `Description`, `Condition`, `Attributes`, `Property`, `TerminateExitCode`, `Timeout` FROM `WixCloseApplication` ORDER BY `Sequence`";
11enum eQUERY_CLOSEAPPS { QCA_ID = 1, QCA_TARGET, QCA_DESCRIPTION, QCA_CONDITION, QCA_ATTRIBUTES, QCA_PROPERTY, QCA_TERMINATEEXITCODE, QCA_TIMEOUT };
12
13// CloseApplication.Attributes
14enum CLOSEAPP_ATTRIBUTES
15{
16 CLOSEAPP_ATTRIBUTE_NONE = 0x0,
17 CLOSEAPP_ATTRIBUTE_CLOSEMESSAGE = 0x1,
18 CLOSEAPP_ATTRIBUTE_REBOOTPROMPT = 0x2,
19 CLOSEAPP_ATTRIBUTE_ELEVATEDCLOSEMESSAGE = 0x4,
20 CLOSEAPP_ATTRIBUTE_ENDSESSIONMESSAGE = 0x8,
21 CLOSEAPP_ATTRIBUTE_ELEVATEDENDSESSIONMESSAGE = 0x10,
22 CLOSEAPP_ATTRIBUTE_TERMINATEPROCESS = 0x20,
23 CLOSEAPP_ATTRIBUTE_PROMPTTOCONTINUE = 0x40,
24};
25
26struct PROCESS_AND_MESSAGE
27{
28 DWORD dwProcessId;
29 DWORD dwMessageId;
30 DWORD dwTimeout;
31};
32
33
34/******************************************************************
35 EnumWindowsProc - callback function which sends message if the
36 current window matches the passed in process ID
37
38******************************************************************/
39BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
40{
41 PROCESS_AND_MESSAGE* pPM = reinterpret_cast<PROCESS_AND_MESSAGE*>(lParam);
42 DWORD dwProcessId = 0;
43 DWORD_PTR dwResult = 0;
44 BOOL fQueryEndSession = WM_QUERYENDSESSION == pPM->dwMessageId;
45 BOOL fContinueWindowsInProcess = TRUE; // assume we will send message to all top-level windows in a process.
46
47 ::GetWindowThreadProcessId(hwnd, &dwProcessId);
48
49 // check if the process Id is the one we're looking for
50 if (dwProcessId != pPM->dwProcessId)
51 {
52 return TRUE;
53 }
54
55 WcaLog(LOGMSG_VERBOSE, "Sending message to process id 0x%x", dwProcessId);
56
57 if (::SendMessageTimeoutW(hwnd, pPM->dwMessageId, 0, fQueryEndSession ? ENDSESSION_CLOSEAPP : 0, SMTO_BLOCK, pPM->dwTimeout, &dwResult))
58 {
59 WcaLog(LOGMSG_VERBOSE, "Result 0x%x", dwResult);
60
61 if (fQueryEndSession)
62 {
63 // If application said it was okay to close, do that.
64 if (dwResult)
65 {
66 ::SendMessageTimeoutW(hwnd, WM_ENDSESSION, TRUE, ENDSESSION_CLOSEAPP, SMTO_BLOCK, pPM->dwTimeout, &dwResult);
67 }
68 else // application said don't try to close it, so don't bother sending messages to any other top-level windows.
69 {
70 fContinueWindowsInProcess = FALSE;
71 }
72 }
73 }
74 else // log result message.
75 {
76 WcaLog(LOGMSG_VERBOSE, "Failed to send message id: %u, error: 0x%x", pPM->dwMessageId, ::GetLastError());
77 }
78
79 // so we know we succeeded
80 ::SetLastError(ERROR_SUCCESS);
81
82 return fContinueWindowsInProcess;
83}
84
85/******************************************************************
86 PromptToContinue - displays the prompt if the application is still
87 running.
88
89******************************************************************/
90static HRESULT PromptToContinue(
91 __in_z LPCWSTR wzApplication,
92 __in_z LPCWSTR wzPrompt
93 )
94{
95 HRESULT hr = S_OK;
96 UINT er = ERROR_SUCCESS;
97 PMSIHANDLE hRecMessage = NULL;
98 DWORD *prgProcessIds = NULL;
99 DWORD cProcessIds = 0;
100
101 hRecMessage = ::MsiCreateRecord(1);
102 ExitOnNull(hRecMessage, hr, E_OUTOFMEMORY, "Failed to create record for prompt.");
103
104 er = ::MsiRecordSetStringW(hRecMessage, 0, wzPrompt);
105 ExitOnWin32Error(er, hr, "Failed to set prompt record field string");
106
107 do
108 {
109 hr = ProcFindAllIdsFromExeName(wzApplication, &prgProcessIds, &cProcessIds);
110 if (SUCCEEDED(hr) && 0 < cProcessIds)
111 {
112 er = WcaProcessMessage(static_cast<INSTALLMESSAGE>(INSTALLMESSAGE_WARNING | MB_ABORTRETRYIGNORE | MB_DEFBUTTON3 | MB_ICONWARNING), hRecMessage);
113 if (IDABORT == er)
114 {
115 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
116 }
117 else if (IDRETRY == er)
118 {
119 hr = S_FALSE;
120 }
121 else if (IDIGNORE == er)
122 {
123 hr = S_OK;
124 }
125 else
126 {
127 ExitOnWin32Error(er, hr, "Unexpected return value from prompt to continue.");
128 }
129 }
130
131 ReleaseNullMem(prgProcessIds);
132 cProcessIds = 0;
133 } while (S_FALSE == hr);
134
135LExit:
136 ReleaseMem(prgProcessIds);
137 return hr;
138}
139
140/******************************************************************
141 SendProcessMessage - helper function to enumerate the top-level
142 windows and send to all matching a process ID.
143
144******************************************************************/
145void SendProcessMessage(
146 __in DWORD dwProcessId,
147 __in DWORD dwMessageId,
148 __in DWORD dwTimeout
149 )
150{
151 WcaLog(LOGMSG_VERBOSE, "Attempting to send process id 0x%x message id: %u", dwProcessId, dwMessageId);
152
153 PROCESS_AND_MESSAGE pm = { };
154 pm.dwProcessId = dwProcessId;
155 pm.dwMessageId = dwMessageId;
156 pm.dwTimeout = dwTimeout;
157
158 if (!::EnumWindows(EnumWindowsProc, reinterpret_cast<LPARAM>(&pm)))
159 {
160 DWORD dwLastError = ::GetLastError();
161 if (ERROR_SUCCESS != dwLastError)
162 {
163 WcaLog(LOGMSG_VERBOSE, "CloseApp enumeration error: 0x%x", dwLastError);
164 }
165 }
166}
167
168/******************************************************************
169 SendApplicationMessage - helper function to iterate through the
170 processes for the specified application and send all
171 applicable process Ids a message and give them time to process
172 the message.
173
174******************************************************************/
175void SendApplicationMessage(
176 __in LPCWSTR wzApplication,
177 __in DWORD dwMessageId,
178 __in DWORD dwTimeout
179 )
180{
181 DWORD *prgProcessIds = NULL;
182 DWORD cProcessIds = 0, iProcessId;
183 HRESULT hr = S_OK;
184
185 WcaLog(LOGMSG_VERBOSE, "Checking App: %ls ", wzApplication);
186
187 hr = ProcFindAllIdsFromExeName(wzApplication, &prgProcessIds, &cProcessIds);
188
189 if (SUCCEEDED(hr) && 0 < cProcessIds)
190 {
191 WcaLog(LOGMSG_VERBOSE, "App: %ls found running, %d processes, attempting to send message.", wzApplication, cProcessIds);
192
193 for (iProcessId = 0; iProcessId < cProcessIds; ++iProcessId)
194 {
195 SendProcessMessage(prgProcessIds[iProcessId], dwMessageId, dwTimeout);
196 }
197
198 ProcWaitForIds(prgProcessIds, cProcessIds, dwTimeout);
199 }
200
201 ReleaseMem(prgProcessIds);
202}
203
204/******************************************************************
205 SetRunningProcessProperty - helper function that sets the specified
206 property if there are any instances of the specified executable
207 running. Useful to show custom UI to ask for shutdown.
208******************************************************************/
209void SetRunningProcessProperty(
210 __in LPCWSTR wzApplication,
211 __in LPCWSTR wzProperty
212 )
213{
214 DWORD *prgProcessIds = NULL;
215 DWORD cProcessIds = 0;
216 HRESULT hr = S_OK;
217
218 WcaLog(LOGMSG_VERBOSE, "Checking App: %ls ", wzApplication);
219
220 hr = ProcFindAllIdsFromExeName(wzApplication, &prgProcessIds, &cProcessIds);
221
222 if (SUCCEEDED(hr) && 0 < cProcessIds)
223 {
224 WcaLog(LOGMSG_VERBOSE, "App: %ls found running, %d processes, setting '%ls' property.", wzApplication, cProcessIds, wzProperty);
225 WcaSetIntProperty(wzProperty, cProcessIds);
226 }
227
228 ReleaseMem(prgProcessIds);
229}
230
231/******************************************************************
232 TerminateProcesses - helper function that kills the provided set of
233 process ids such that they return a particular exit code.
234******************************************************************/
235void TerminateProcesses(
236 __in_ecount(cProcessIds) DWORD rgdwProcessIds[],
237 __in DWORD cProcessIds,
238 __in DWORD dwExitCode
239 )
240{
241 for (DWORD i = 0; i < cProcessIds; ++i)
242 {
243 HANDLE hProcess = ::OpenProcess(PROCESS_TERMINATE, FALSE, rgdwProcessIds[i]);
244 if (hProcess)
245 {
246 ::TerminateProcess(hProcess, dwExitCode);
247 ::CloseHandle(hProcess);
248 }
249 }
250}
251
252/******************************************************************
253 WixCloseApplications - entry point for WixCloseApplications Custom Action
254
255 called as Type 1 CustomAction (binary DLL) from Windows Installer
256 in InstallExecuteSequence before InstallFiles
257******************************************************************/
258extern "C" UINT __stdcall WixCloseApplications(
259 __in MSIHANDLE hInstall
260 )
261{
262 //AssertSz(FALSE, "debug WixCloseApplications");
263 HRESULT hr = S_OK;
264 UINT er = ERROR_SUCCESS;
265
266 LPWSTR pwzData = NULL;
267 LPWSTR pwzId = NULL;
268 LPWSTR pwzTarget = NULL;
269 LPWSTR pwzDescription = NULL;
270 LPWSTR pwzCondition = NULL;
271 LPWSTR pwzProperty = NULL;
272 DWORD dwAttributes = 0;
273 DWORD dwTimeout = 0;
274 DWORD dwTerminateExitCode = 0;
275 MSICONDITION condition = MSICONDITION_NONE;
276
277 DWORD cCloseApps = 0;
278
279 PMSIHANDLE hView = NULL;
280 PMSIHANDLE hRec = NULL;
281 MSIHANDLE hListboxTable = NULL;
282 MSIHANDLE hListboxColumns = NULL;
283
284 LPWSTR pwzCustomActionData = NULL;
285 //DWORD cchCustomActionData = 0;
286
287 //
288 // initialize
289 //
290 hr = WcaInitialize(hInstall, "WixCloseApplications");
291 ExitOnFailure(hr, "failed to initialize");
292
293 //
294 // loop through all the objects to be secured
295 //
296 hr = WcaOpenExecuteView(wzQUERY_CLOSEAPPS, &hView);
297 ExitOnFailure(hr, "failed to open view on WixCloseApplication table");
298 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
299 {
300 hr = WcaGetRecordString(hRec, QCA_ID, &pwzId);
301 ExitOnFailure(hr, "failed to get id from WixCloseApplication table");
302
303 hr = WcaGetRecordString(hRec, QCA_CONDITION, &pwzCondition);
304 ExitOnFailure(hr, "failed to get condition from WixCloseApplication table");
305
306 if (pwzCondition && *pwzCondition)
307 {
308 condition = ::MsiEvaluateConditionW(hInstall, pwzCondition);
309 if (MSICONDITION_ERROR == condition)
310 {
311 hr = E_INVALIDARG;
312 ExitOnFailure(hr, "failed to process condition for WixCloseApplication '%ls'", pwzId);
313 }
314 else if (MSICONDITION_FALSE == condition)
315 {
316 continue; // skip processing this target
317 }
318 }
319
320 hr = WcaGetRecordFormattedString(hRec, QCA_TARGET, &pwzTarget);
321 ExitOnFailure(hr, "failed to get target from WixCloseApplication table");
322
323 hr = WcaGetRecordFormattedString(hRec, QCA_DESCRIPTION, &pwzDescription);
324 ExitOnFailure(hr, "failed to get description from WixCloseApplication table");
325
326 hr = WcaGetRecordInteger(hRec, QCA_ATTRIBUTES, reinterpret_cast<int*>(&dwAttributes));
327 ExitOnFailure(hr, "failed to get attributes from WixCloseApplication table");
328
329 hr = WcaGetRecordFormattedString(hRec, QCA_PROPERTY, &pwzProperty);
330 ExitOnFailure(hr, "failed to get property from WixCloseApplication table");
331
332 hr = WcaGetRecordInteger(hRec, QCA_TERMINATEEXITCODE, reinterpret_cast<int*>(&dwTerminateExitCode));
333 if (S_FALSE == hr)
334 {
335 dwTerminateExitCode = 0;
336 hr = S_OK;
337 }
338 ExitOnFailure(hr, "failed to get timeout from WixCloseApplication table");
339
340 hr = WcaGetRecordInteger(hRec, QCA_TIMEOUT, reinterpret_cast<int*>(&dwTimeout));
341 if (S_FALSE == hr)
342 {
343 dwTimeout = DEFAULT_PROCESS_EXIT_WAIT_TIME;
344 hr = S_OK;
345 }
346 ExitOnFailure(hr, "failed to get timeout from WixCloseApplication table");
347
348 // Before trying any changes to the machine, prompt if requested.
349 if (dwAttributes & CLOSEAPP_ATTRIBUTE_PROMPTTOCONTINUE)
350 {
351 hr = PromptToContinue(pwzTarget, pwzDescription ? pwzDescription : L"");
352 if (HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hr)
353 {
354 // Skip error message if user canceled.
355 ExitFunction();
356 }
357 ExitOnFailure(hr, "Failure while prompting user to continue to close application.");
358 }
359
360 //
361 // send WM_CLOSE or WM_QUERYENDSESSION to currently running applications
362 //
363 if (dwAttributes & CLOSEAPP_ATTRIBUTE_CLOSEMESSAGE)
364 {
365 SendApplicationMessage(pwzTarget, WM_CLOSE, dwTimeout);
366 }
367
368 if (dwAttributes & CLOSEAPP_ATTRIBUTE_ENDSESSIONMESSAGE)
369 {
370 SendApplicationMessage(pwzTarget, WM_QUERYENDSESSION, dwTimeout);
371 }
372
373 //
374 // Pass the targets to the deferred action in case the app comes back
375 // even if we close it now.
376 //
377 if (dwAttributes & (CLOSEAPP_ATTRIBUTE_ELEVATEDCLOSEMESSAGE | CLOSEAPP_ATTRIBUTE_ELEVATEDENDSESSIONMESSAGE | CLOSEAPP_ATTRIBUTE_REBOOTPROMPT | CLOSEAPP_ATTRIBUTE_TERMINATEPROCESS))
378 {
379 hr = WcaWriteStringToCaData(pwzTarget, &pwzCustomActionData);
380 ExitOnFailure(hr, "failed to add target data to CustomActionData");
381
382 hr = WcaWriteIntegerToCaData(dwAttributes, &pwzCustomActionData);
383 ExitOnFailure(hr, "failed to add attribute data to CustomActionData");
384
385 hr = WcaWriteIntegerToCaData(dwTimeout, &pwzCustomActionData);
386 ExitOnFailure(hr, "failed to add timeout data to CustomActionData");
387
388 hr = WcaWriteIntegerToCaData(dwTerminateExitCode, &pwzCustomActionData);
389 ExitOnFailure(hr, "failed to add timeout data to CustomActionData");
390 }
391
392 if (pwzProperty && *pwzProperty)
393 {
394 SetRunningProcessProperty(pwzTarget, pwzProperty);
395 }
396
397 ++cCloseApps;
398 }
399
400 // if we looped through all records all is well
401 if (E_NOMOREITEMS == hr)
402 {
403 hr = S_OK;
404 }
405 ExitOnFailure(hr, "failed while looping through all apps to close");
406
407 //
408 // Do the UI dance now.
409 //
410 /*
411
412 TODO: Do this eventually
413
414 if (cCloseApps)
415 {
416 while (TRUE)
417 {
418 for (DWORD i = 0; i < cCloseApps; ++i)
419 {
420 hr = WcaAddTempRecord(&hListboxTable, &hListboxColumns, L"ListBox", NULL, 0, 4, L"FileInUseProcess", i, target, description);
421 if (FAILED(hr))
422 {
423 }
424 }
425 }
426 }
427 */
428
429 //
430 // schedule the custom action and add to progress bar
431 //
432 if (pwzCustomActionData && *pwzCustomActionData)
433 {
434 Assert(0 < cCloseApps);
435
436 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"WixCloseApplicationsDeferred"), pwzCustomActionData, cCloseApps * COST_CLOSEAPP);
437 ExitOnFailure(hr, "failed to schedule WixCloseApplicationsDeferred action");
438 }
439
440LExit:
441 if (hListboxColumns)
442 {
443 ::MsiCloseHandle(hListboxColumns);
444 }
445 if (hListboxTable)
446 {
447 ::MsiCloseHandle(hListboxTable);
448 }
449
450 ReleaseStr(pwzCustomActionData);
451 ReleaseStr(pwzData);
452 ReleaseStr(pwzProperty);
453 ReleaseStr(pwzCondition);
454 ReleaseStr(pwzDescription);
455 ReleaseStr(pwzTarget);
456 ReleaseStr(pwzId);
457
458 if (FAILED(hr))
459 {
460 er = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hr ? ERROR_INSTALL_USEREXIT : ERROR_INSTALL_FAILURE;
461 }
462 return WcaFinalize(er);
463}
464
465
466/******************************************************************
467 WixCloseApplicationsDeferred - entry point for
468 WixCloseApplicationsDeferred Custom Action
469 called as Type 1025 CustomAction
470 (deferred binary DLL)
471
472 NOTE: deferred CustomAction since it modifies the machine
473 NOTE: CustomActionData == wzTarget\tdwAttributes\tdwTimeout\tdwTerminateExitCode\t...
474******************************************************************/
475extern "C" UINT __stdcall WixCloseApplicationsDeferred(
476 __in MSIHANDLE hInstall
477 )
478{
479 //AssertSz(FALSE, "debug WixCloseApplicationsDeferred");
480 HRESULT hr = S_OK;
481 DWORD er = ERROR_SUCCESS;
482
483 LPWSTR pwz = NULL;
484 LPWSTR pwzData = NULL;
485 LPWSTR pwzTarget = NULL;
486 DWORD dwAttributes = 0;
487 DWORD dwTimeout = 0;
488 DWORD dwTerminateExitCode = 0;
489
490 DWORD *prgProcessIds = NULL;
491 DWORD cProcessIds = 0;
492
493 //
494 // initialize
495 //
496 hr = WcaInitialize(hInstall, "WixCloseApplicationsDeferred");
497 ExitOnFailure(hr, "failed to initialize");
498
499 hr = WcaGetProperty(L"CustomActionData", &pwzData);
500 ExitOnFailure(hr, "failed to get CustomActionData");
501
502 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
503
504 pwz = pwzData;
505
506 //
507 // loop through all the passed in data
508 //
509 while (pwz && *pwz)
510 {
511 hr = WcaReadStringFromCaData(&pwz, &pwzTarget);
512 ExitOnFailure(hr, "failed to process target from CustomActionData");
513
514 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwAttributes));
515 ExitOnFailure(hr, "failed to process attributes from CustomActionData");
516
517 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwTimeout));
518 ExitOnFailure(hr, "failed to process timeout from CustomActionData");
519
520 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwTerminateExitCode));
521 ExitOnFailure(hr, "failed to process terminate exit code from CustomActionData");
522
523 WcaLog(LOGMSG_VERBOSE, "Checking for App: %ls Attributes: %d", pwzTarget, dwAttributes);
524
525 //
526 // send WM_CLOSE or WM_QUERYENDSESSION to currently running applications
527 //
528 if (dwAttributes & CLOSEAPP_ATTRIBUTE_ELEVATEDCLOSEMESSAGE)
529 {
530 SendApplicationMessage(pwzTarget, WM_CLOSE, dwTimeout);
531 }
532
533 if (dwAttributes & CLOSEAPP_ATTRIBUTE_ELEVATEDENDSESSIONMESSAGE)
534 {
535 SendApplicationMessage(pwzTarget, WM_QUERYENDSESSION, dwTimeout);
536 }
537
538 // If we find that an app that we need closed is still runing, require a
539 // restart or kill the process as directed.
540 ProcFindAllIdsFromExeName(pwzTarget, &prgProcessIds, &cProcessIds);
541 if (0 < cProcessIds)
542 {
543 if (dwAttributes & CLOSEAPP_ATTRIBUTE_REBOOTPROMPT)
544 {
545 WcaLog(LOGMSG_VERBOSE, "App: %ls found running, requiring a reboot.", pwzTarget);
546
547 WcaDeferredActionRequiresReboot();
548 }
549 else if (dwAttributes & CLOSEAPP_ATTRIBUTE_TERMINATEPROCESS)
550 {
551 TerminateProcesses(prgProcessIds, cProcessIds, dwTerminateExitCode);
552 }
553 }
554
555 hr = WcaProgressMessage(COST_CLOSEAPP, FALSE);
556 ExitOnFailure(hr, "failed to send progress message");
557 }
558
559LExit:
560 ReleaseMem(prgProcessIds);
561
562 ReleaseStr(pwzTarget);
563 ReleaseStr(pwzData);
564
565 if (FAILED(hr))
566 {
567 er = ERROR_INSTALL_FAILURE;
568 }
569 return WcaFinalize(er);
570}
diff --git a/src/ca/CustomMsiErrors.h b/src/ca/CustomMsiErrors.h
new file mode 100644
index 00000000..3218b61b
--- /dev/null
+++ b/src/ca/CustomMsiErrors.h
@@ -0,0 +1,32 @@
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 msierrSecureObjectsFailedCreateSD 25520
6#define msierrSecureObjectsFailedSet 25521
7#define msierrSecureObjectsUnknownType 25522
8
9#define msierrXmlFileFailedRead 25530
10#define msierrXmlFileFailedOpen 25531
11#define msierrXmlFileFailedSelect 25532
12#define msierrXmlFileFailedSave 25533
13
14#define msierrXmlConfigFailedRead 25540
15#define msierrXmlConfigFailedOpen 25541
16#define msierrXmlConfigFailedSelect 25542
17#define msierrXmlConfigFailedSave 25543
18
19#define msierrPERFMONFailedRegisterDLL 26251
20#define msierrPERFMONFailedUnregisterDLL 26252
21#define msierrInstallPerfCounterData 26253
22#define msierrUninstallPerfCounterData 26254
23
24#define msierrSMBFailedCreate 26301
25#define msierrSMBFailedDrop 26302
26#define msierrUSRFailedUserCreate 26401
27#define msierrUSRFailedUserCreatePswd 26402
28#define msierrUSRFailedUserGroupAdd 26403
29#define msierrUSRFailedUserCreateExists 26404
30#define msierrUSRFailedGrantLogonAsService 26405
31
32//Last available is 26450 \ No newline at end of file
diff --git a/src/ca/FormatFiles.cpp b/src/ca/FormatFiles.cpp
new file mode 100644
index 00000000..6a816700
--- /dev/null
+++ b/src/ca/FormatFiles.cpp
@@ -0,0 +1,221 @@
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
5const UINT COST_FILEFORMATTING = 2000;
6
7
8//
9// WixSchedFormatFiles - immediate CA to schedule format files CAs
10//
11extern "C" UINT __stdcall WixSchedFormatFiles(
12 __in MSIHANDLE hInstall
13 )
14{
15 HRESULT hr = S_OK;
16 UINT er = ERROR_SUCCESS;
17 PSCZ sczBinaryKey;
18 PSCZ sczFileKey;
19 PSCZ sczComponentKey;
20 PSCZ sczFormattedFile;
21 PSCZ sczFilePath;
22 PMSIHANDLE hView;
23 PMSIHANDLE hRec;
24 PSCZ sczFileContent;
25 PSCZ sczFormattedContent;
26 PSCZ sczExecCustomActionData;
27 PSCZ sczRollbackCustomActionData;
28
29 LPCWSTR wzQuery =
30 L"SELECT `WixFormatFiles`.`Binary_`, `WixFormatFiles`.`File_`, `File`.`Component_` "
31 L"FROM `WixFormatFiles`, `File` "
32 L"WHERE `WixFormatFiles`.`File_` = `File`.`File`";
33 enum eQuery { eqBinaryKey = 1, eqFileKey, eqComponentKey };
34
35 // initialize
36 hr = WcaInitialize(hInstall, "WixSchedFormatFiles");
37 ExitOnFailure(hr, "Failed to initialize for WixSchedFormatFiles.");
38
39 // query and loop through all the files
40 hr = WcaOpenExecuteView(wzQuery, &hView);
41 ExitOnFailure(hr, "Failed to open view on WixFormatFiles table");
42
43 DWORD cFiles = 0;
44 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
45 {
46 ++cFiles;
47
48 hr = WcaGetRecordString(hRec, eqBinaryKey, &sczBinaryKey);
49 ExitOnFailure(hr, "Failed to get Binary table key.");
50
51 hr = WcaGetRecordString(hRec, eqFileKey, &sczFileKey);
52 ExitOnFailure(hr, "Failed to get File table key.");
53
54 hr = WcaGetRecordString(hRec, eqComponentKey, &sczComponentKey);
55 ExitOnFailure(hr, "Failed to get Component table key.");
56
57 // we need to know if the component's being installed, uninstalled, or reinstalled
58 WCA_TODO todo = WcaGetComponentToDo(sczComponentKey);
59 if (WCA_TODO_INSTALL == todo || WCA_TODO_REINSTALL == todo)
60 {
61 // turn the file key into the path to the target file
62 hr = StrAllocFormatted(&sczFormattedFile, L"[#%ls]", sczFileKey);
63 ExitOnFailure(hr, "Failed to format file string for file: %ls", sczFileKey);
64 hr = WcaGetFormattedString(sczFormattedFile, &sczFilePath);
65 ExitOnFailure(hr, "Failed to get path for file: %ls", sczFileKey);
66
67 // extract binary to string
68 WCA_ENCODING encoding = WCA_ENCODING_UNKNOWN;
69 hr = WcaExtractBinaryToString(sczBinaryKey, &sczFileContent, &encoding);
70 ExitOnFailure(hr, "Failed to extract binary: %ls", sczBinaryKey);
71
72 // format string
73 hr = WcaGetFormattedString(sczFileContent, &sczFormattedContent);
74 ExitOnFailure(hr, "Failed to format file content: %ls", sczFileContent);
75
76 // write to deferred custom action data
77 hr = WcaWriteStringToCaData(sczFilePath, &sczExecCustomActionData);
78 ExitOnFailure(hr, "Failed to write deferred custom action data for file: %ls", sczFilePath);
79
80 hr = WcaWriteIntegerToCaData(encoding, &sczExecCustomActionData);
81 ExitOnFailure(hr, "Failed to write deferred custom action data for encoding: %d", encoding);
82
83 hr = WcaWriteStringToCaData(sczFormattedContent, &sczExecCustomActionData);
84 ExitOnFailure(hr, "Failed to write deferred custom action data for file content: %ls", sczFilePath);
85
86 // write to rollback custom action data
87 hr = WcaWriteStringToCaData(sczFilePath, &sczRollbackCustomActionData);
88 ExitOnFailure(hr, "Failed to write rollback custom action data for file: %ls", sczFilePath);
89
90 hr = WcaWriteIntegerToCaData(encoding, &sczRollbackCustomActionData);
91 ExitOnFailure(hr, "Failed to write deferred custom action data for encoding: %d", encoding);
92
93 hr = WcaWriteStringToCaData(sczFileContent, &sczRollbackCustomActionData);
94 ExitOnFailure(hr, "Failed to write rollback custom action data for file content: %ls", sczFilePath);
95 }
96 }
97
98 // reaching the end of the list is actually a good thing, not an error
99 if (E_NOMOREITEMS == hr)
100 {
101 hr = S_OK;
102 }
103 ExitOnFailure(hr, "Failure occurred while processing WixFormatFiles table");
104
105 // schedule deferred CAs if there's anything to do
106 if (sczRollbackCustomActionData && *sczRollbackCustomActionData)
107 {
108 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"WixRollbackFormatFiles"), sczRollbackCustomActionData, cFiles * COST_FILEFORMATTING);
109 ExitOnFailure(hr, "Failed to schedule WixRollbackFormatFiles");
110 }
111
112 if (sczExecCustomActionData && *sczExecCustomActionData)
113 {
114 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"WixExecFormatFiles"), sczExecCustomActionData, cFiles * COST_FILEFORMATTING);
115 ExitOnFailure(hr, "Failed to schedule WixExecFormatFiles");
116 }
117
118LExit:
119 return WcaFinalize(er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er);
120}
121
122
123//
124// WixExecFormatFiles - deferred and rollback CAs to write formatted files
125//
126extern "C" UINT __stdcall WixExecFormatFiles(
127 __in MSIHANDLE hInstall
128 )
129{
130 HRESULT hr = S_OK;
131 UINT er = ERROR_SUCCESS;
132 PSCZ sczCustomActionData;
133 LPWSTR pwz = NULL;
134 PSCZ sczFilePath;
135 PSCZ sczFileContent;
136 LPSTR psz = NULL;
137
138 // initialize
139 hr = WcaInitialize(hInstall, "WixExecFormatFiles");
140 ExitOnFailure(hr, "Failed to initialize for WixExecFormatFiles.");
141
142 hr = WcaGetProperty(L"CustomActionData", &sczCustomActionData);
143 ExitOnFailure(hr, "Failed to get CustomActionData.");
144#ifdef _DEBUG
145 WcaLog(LOGMSG_STANDARD, "CustomActionData: %ls", sczCustomActionData);
146#endif
147
148 // loop through all the passed in data
149 pwz = sczCustomActionData;
150 while (pwz && *pwz)
151 {
152 // extract the custom action data
153 hr = WcaReadStringFromCaData(&pwz, &sczFilePath);
154 ExitOnFailure(hr, "Failed to read file path from custom action data");
155
156 WCA_ENCODING encoding = WCA_ENCODING_UNKNOWN;
157 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&encoding));
158 ExitOnFailure(hr, "Failed to read encoding from custom action data");
159
160 hr = WcaReadStringFromCaData(&pwz, &sczFileContent);
161 ExitOnFailure(hr, "Failed to read file content from custom action data");
162
163 // re-encode content
164 LPCBYTE pbData = NULL;
165 size_t cbData = 0;
166 switch (encoding)
167 {
168 case WCA_ENCODING_UTF_16:
169 pbData = reinterpret_cast<LPCBYTE>(LPCWSTR(sczFileContent));
170 cbData = lstrlenW(sczFileContent) * sizeof(WCHAR);
171 break;
172
173 case WCA_ENCODING_UTF_8:
174 hr = StrAnsiAllocString(&psz, sczFileContent, 0, CP_UTF8);
175 ExitOnFailure(hr, "Failed to convert Unicode to UTF-8.");
176 pbData = reinterpret_cast<LPCBYTE>(psz);
177
178 hr = ::StringCbLengthA(psz, STRSAFE_MAX_CCH, &cbData);
179 ExitOnFailure(hr, "Failed to count UTF-8 bytes.");
180 break;
181
182 case WCA_ENCODING_ANSI:
183 hr = StrAnsiAllocString(&psz, sczFileContent, 0, CP_ACP);
184 ExitOnFailure(hr, "Failed to convert Unicode to ANSI.");
185 pbData = reinterpret_cast<LPCBYTE>(psz);
186
187 hr = ::StringCbLengthA(psz, STRSAFE_MAX_CCH, &cbData);
188 ExitOnFailure(hr, "Failed to count UTF-8 bytes.");
189 break;
190
191 default:
192 break;
193 }
194
195#ifdef _DEBUG
196 WcaLog(LOGMSG_STANDARD, "File: %ls", sczCustomActionData);
197 WcaLog(LOGMSG_STANDARD, "Content: %ls", sczFileContent);
198#endif
199
200 // write file and preserve modified time
201 FILETIME filetime;
202
203 hr = FileGetTime(sczFilePath, NULL, NULL, &filetime);
204 ExitOnFailure(hr, "Failed to get modified time of file : %ls", sczFilePath);
205
206 hr = FileWrite(sczFilePath, FILE_ATTRIBUTE_NORMAL, pbData, static_cast<DWORD>(cbData), NULL);
207 ExitOnFailure(hr, "Failed to write file content: %ls", sczFilePath);
208
209 hr = FileSetTime(sczFilePath, NULL, NULL, &filetime);
210 ExitOnFailure(hr, "Failed to set modified time of file : %ls", sczFilePath);
211
212 // Tick the progress bar
213 hr = WcaProgressMessage(COST_FILEFORMATTING, FALSE);
214 ExitOnFailure(hr, "Failed to tick progress bar for file: %ls", sczFilePath);
215 }
216
217LExit:
218 ReleaseStr(psz);
219
220 return WcaFinalize(er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er);
221}
diff --git a/src/ca/OsInfo.cpp b/src/ca/OsInfo.cpp
new file mode 100644
index 00000000..4783673e
--- /dev/null
+++ b/src/ca/OsInfo.cpp
@@ -0,0 +1,487 @@
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// constants we'll pick up from later SDKs
6#define SM_TABLETPC 86
7#define SM_MEDIACENTER 87
8#define SM_STARTER 88
9#define SM_SERVERR2 89
10#define VER_SUITE_WH_SERVER 0x00008000
11
12/********************************************************************
13WixQueryOsInfo - entry point for WixQueryOsInfo custom action
14
15 Called as Type 1 custom action (DLL from the Binary table) from
16 Windows Installer to set properties that identify OS information
17 and predefined directories
18********************************************************************/
19extern "C" UINT __stdcall WixQueryOsInfo(
20 __in MSIHANDLE hInstall
21 )
22{
23 HRESULT hr = S_OK;
24 DWORD er = ERROR_SUCCESS;
25 OSVERSIONINFOEXW ovix = {0};
26
27 hr = WcaInitialize(hInstall, "WixQueryOsInfo");
28 ExitOnFailure(hr, "WixQueryOsInfo failed to initialize");
29
30 // identify product suites
31 ovix.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
32 #pragma warning(suppress: 4996) //TODO: use osutil
33 ::GetVersionExW(reinterpret_cast<LPOSVERSIONINFOW>(&ovix));
34
35 if (VER_SUITE_SMALLBUSINESS == (ovix.wSuiteMask & VER_SUITE_SMALLBUSINESS))
36 {
37 WcaSetIntProperty(L"WIX_SUITE_SMALLBUSINESS", 1);
38 }
39
40 if (VER_SUITE_ENTERPRISE == (ovix.wSuiteMask & VER_SUITE_ENTERPRISE))
41 {
42 WcaSetIntProperty(L"WIX_SUITE_ENTERPRISE", 1);
43 }
44
45 if (VER_SUITE_BACKOFFICE == (ovix.wSuiteMask & VER_SUITE_BACKOFFICE))
46 {
47 WcaSetIntProperty(L"WIX_SUITE_BACKOFFICE", 1);
48 }
49
50 if (VER_SUITE_COMMUNICATIONS == (ovix.wSuiteMask & VER_SUITE_COMMUNICATIONS))
51 {
52 WcaSetIntProperty(L"WIX_SUITE_COMMUNICATIONS", 1);
53 }
54
55 if (VER_SUITE_TERMINAL == (ovix.wSuiteMask & VER_SUITE_TERMINAL))
56 {
57 WcaSetIntProperty(L"WIX_SUITE_TERMINAL", 1);
58 }
59
60 if (VER_SUITE_SMALLBUSINESS_RESTRICTED == (ovix.wSuiteMask & VER_SUITE_SMALLBUSINESS_RESTRICTED))
61 {
62 WcaSetIntProperty(L"WIX_SUITE_SMALLBUSINESS_RESTRICTED", 1);
63 }
64
65 if (VER_SUITE_EMBEDDEDNT == (ovix.wSuiteMask & VER_SUITE_EMBEDDEDNT))
66 {
67 WcaSetIntProperty(L"WIX_SUITE_EMBEDDEDNT", 1);
68 }
69
70 if (VER_SUITE_DATACENTER == (ovix.wSuiteMask & VER_SUITE_DATACENTER))
71 {
72 WcaSetIntProperty(L"WIX_SUITE_DATACENTER", 1);
73 }
74
75 if (VER_SUITE_SINGLEUSERTS == (ovix.wSuiteMask & VER_SUITE_SINGLEUSERTS))
76 {
77 WcaSetIntProperty(L"WIX_SUITE_SINGLEUSERTS", 1);
78 }
79
80 if (VER_SUITE_PERSONAL == (ovix.wSuiteMask & VER_SUITE_PERSONAL))
81 {
82 WcaSetIntProperty(L"WIX_SUITE_PERSONAL", 1);
83 }
84
85 if (VER_SUITE_BLADE == (ovix.wSuiteMask & VER_SUITE_BLADE))
86 {
87 WcaSetIntProperty(L"WIX_SUITE_BLADE", 1);
88 }
89
90 if (VER_SUITE_EMBEDDED_RESTRICTED == (ovix.wSuiteMask & VER_SUITE_EMBEDDED_RESTRICTED))
91 {
92 WcaSetIntProperty(L"WIX_SUITE_EMBEDDED_RESTRICTED", 1);
93 }
94
95 if (VER_SUITE_SECURITY_APPLIANCE == (ovix.wSuiteMask & VER_SUITE_SECURITY_APPLIANCE))
96 {
97 WcaSetIntProperty(L"WIX_SUITE_SECURITY_APPLIANCE", 1);
98 }
99
100 if (VER_SUITE_STORAGE_SERVER == (ovix.wSuiteMask & VER_SUITE_STORAGE_SERVER))
101 {
102 WcaSetIntProperty(L"WIX_SUITE_STORAGE_SERVER", 1);
103 }
104
105 if (VER_SUITE_COMPUTE_SERVER == (ovix.wSuiteMask & VER_SUITE_COMPUTE_SERVER))
106 {
107 WcaSetIntProperty(L"WIX_SUITE_COMPUTE_SERVER", 1);
108 }
109
110 if (VER_SUITE_WH_SERVER == (ovix.wSuiteMask & VER_SUITE_WH_SERVER))
111 {
112 WcaSetIntProperty(L"WIX_SUITE_WH_SERVER", 1);
113 }
114
115 // only for XP and later
116 if (5 < ovix.dwMajorVersion || (5 == ovix.dwMajorVersion && 0 < ovix.dwMinorVersion))
117 {
118 if (::GetSystemMetrics(SM_SERVERR2))
119 {
120 WcaSetIntProperty(L"WIX_SUITE_SERVERR2", 1);
121 }
122
123 if (::GetSystemMetrics(SM_MEDIACENTER))
124 {
125 WcaSetIntProperty(L"WIX_SUITE_MEDIACENTER", 1);
126 }
127
128 if (::GetSystemMetrics(SM_STARTER))
129 {
130 WcaSetIntProperty(L"WIX_SUITE_STARTER", 1);
131 }
132
133 if (::GetSystemMetrics(SM_TABLETPC))
134 {
135 WcaSetIntProperty(L"WIX_SUITE_TABLETPC", 1);
136 }
137 }
138
139LExit:
140 if (FAILED(hr))
141 er = ERROR_INSTALL_FAILURE;
142 return WcaFinalize(er);
143}
144
145/********************************************************************
146WixQueryOsDirs - entry point for WixQueryOsDirs custom action
147
148 Called as Type 1 custom action (DLL from the Binary table) from
149 Windows Installer to set properties that identify predefined directories
150********************************************************************/
151extern "C" UINT __stdcall WixQueryOsDirs(
152 __in MSIHANDLE hInstall
153 )
154{
155 HRESULT hr = S_OK;
156 DWORD er = ERROR_SUCCESS;
157
158 hr = WcaInitialize(hInstall, "WixQueryOsDirs");
159 ExitOnFailure(hr, "WixQueryOsDirs failed to initialize");
160
161 // get the paths of the CSIDLs that represent real paths and for which MSI
162 // doesn't yet have standard folder properties
163 WCHAR path[MAX_PATH];
164 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_ADMINTOOLS, NULL, SHGFP_TYPE_CURRENT, path))
165 {
166 WcaSetProperty(L"WIX_DIR_ADMINTOOLS", path);
167 }
168
169 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_ALTSTARTUP, NULL, SHGFP_TYPE_CURRENT, path))
170 {
171 WcaSetProperty(L"WIX_DIR_ALTSTARTUP", path);
172 }
173
174 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_CDBURN_AREA, NULL, SHGFP_TYPE_CURRENT, path))
175 {
176 WcaSetProperty(L"WIX_DIR_CDBURN_AREA", path);
177 }
178
179 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_COMMON_ADMINTOOLS, NULL, SHGFP_TYPE_CURRENT, path))
180 {
181 WcaSetProperty(L"WIX_DIR_COMMON_ADMINTOOLS", path);
182 }
183
184 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_COMMON_ALTSTARTUP, NULL, SHGFP_TYPE_CURRENT, path))
185 {
186 WcaSetProperty(L"WIX_DIR_COMMON_ALTSTARTUP", path);
187 }
188
189 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_COMMON_DOCUMENTS, NULL, SHGFP_TYPE_CURRENT, path))
190 {
191 WcaSetProperty(L"WIX_DIR_COMMON_DOCUMENTS", path);
192 }
193
194 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_COMMON_FAVORITES, NULL, SHGFP_TYPE_CURRENT, path))
195 {
196 WcaSetProperty(L"WIX_DIR_COMMON_FAVORITES", path);
197 }
198
199 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_COMMON_MUSIC, NULL, SHGFP_TYPE_CURRENT, path))
200 {
201 WcaSetProperty(L"WIX_DIR_COMMON_MUSIC", path);
202 }
203
204 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_COMMON_PICTURES, NULL, SHGFP_TYPE_CURRENT, path))
205 {
206 WcaSetProperty(L"WIX_DIR_COMMON_PICTURES", path);
207 }
208
209 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_COMMON_VIDEO, NULL, SHGFP_TYPE_CURRENT, path))
210 {
211 WcaSetProperty(L"WIX_DIR_COMMON_VIDEO", path);
212 }
213
214 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_COOKIES, NULL, SHGFP_TYPE_CURRENT, path))
215 {
216 WcaSetProperty(L"WIX_DIR_COOKIES", path);
217 }
218
219 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_DESKTOP, NULL, SHGFP_TYPE_CURRENT, path))
220 {
221 WcaSetProperty(L"WIX_DIR_DESKTOP", path);
222 }
223
224 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_HISTORY, NULL, SHGFP_TYPE_CURRENT, path))
225 {
226 WcaSetProperty(L"WIX_DIR_HISTORY", path);
227 }
228
229 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_INTERNET_CACHE, NULL, SHGFP_TYPE_CURRENT, path))
230 {
231 WcaSetProperty(L"WIX_DIR_INTERNET_CACHE", path);
232 }
233
234 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_MYMUSIC, NULL, SHGFP_TYPE_CURRENT, path))
235 {
236 WcaSetProperty(L"WIX_DIR_MYMUSIC", path);
237 }
238
239 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_MYPICTURES, NULL, SHGFP_TYPE_CURRENT, path))
240 {
241 WcaSetProperty(L"WIX_DIR_MYPICTURES", path);
242 }
243
244 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_MYVIDEO, NULL, SHGFP_TYPE_CURRENT, path))
245 {
246 WcaSetProperty(L"WIX_DIR_MYVIDEO", path);
247 }
248
249 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_NETHOOD, NULL, SHGFP_TYPE_CURRENT, path))
250 {
251 WcaSetProperty(L"WIX_DIR_NETHOOD", path);
252 }
253
254 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, path))
255 {
256 WcaSetProperty(L"WIX_DIR_PERSONAL", path);
257 }
258
259 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_PRINTHOOD, NULL, SHGFP_TYPE_CURRENT, path))
260 {
261 WcaSetProperty(L"WIX_DIR_PRINTHOOD", path);
262 }
263
264 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, path))
265 {
266 WcaSetProperty(L"WIX_DIR_PROFILE", path);
267 }
268
269 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_RECENT, NULL, SHGFP_TYPE_CURRENT, path))
270 {
271 WcaSetProperty(L"WIX_DIR_RECENT", path);
272 }
273
274 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_RESOURCES, NULL, SHGFP_TYPE_CURRENT, path))
275 {
276 WcaSetProperty(L"WIX_DIR_RESOURCES", path);
277 }
278
279LExit:
280 if (FAILED(hr))
281 er = ERROR_INSTALL_FAILURE;
282 return WcaFinalize(er);
283}
284
285
286/********************************************************************
287SetPropertyWellKnownSID
288
289 Set a property with the localized name of a well known windows SID
290********************************************************************/
291static HRESULT SetPropertyWellKnownSID(
292 __in WELL_KNOWN_SID_TYPE sidType,
293 __in LPCWSTR wzPropertyName,
294 __in BOOL fIncludeDomainName
295 )
296{
297 HRESULT hr = S_OK;
298 PSID psid = NULL;
299 WCHAR wzRefDomain[MAX_PATH] = {0};
300 SID_NAME_USE nameUse;
301 DWORD refSize = MAX_PATH;
302 WCHAR wzName[MAX_PATH] = {0};
303 LPWSTR pwzPropertyValue = NULL;
304 DWORD size = MAX_PATH;
305
306 hr = AclGetWellKnownSid(sidType, &psid);
307 ExitOnFailure(hr, "Failed to get SID; skipping account %ls", wzPropertyName);
308
309 if (!::LookupAccountSidW(NULL, psid, wzName, &size, wzRefDomain, &refSize, &nameUse))
310 {
311 ExitWithLastError(hr, "Failed to look up account for SID; skipping account %ls.", wzPropertyName);
312 }
313
314 if (fIncludeDomainName)
315 {
316 hr = StrAllocFormatted(&pwzPropertyValue, L"%s\\%s", wzRefDomain, wzName);
317 ExitOnFailure(hr, "Failed to format property value");
318
319 hr = WcaSetProperty(wzPropertyName, pwzPropertyValue);
320 ExitOnFailure(hr, "Failed write domain\\name property");
321 }
322 else
323 {
324 hr = WcaSetProperty(wzPropertyName, wzName);
325 ExitOnFailure(hr, "Failed write to name-only property");
326 }
327
328LExit:
329 if (NULL != psid)
330 {
331 ::LocalFree(psid);
332 }
333 ReleaseStr(pwzPropertyValue);
334 return hr;
335}
336
337/********************************************************************
338WixQueryOsWellKnownSID - entry point for WixQueryOsWellKnownSID custom action
339
340 Called as Type 1 custom action (DLL from the Binary table) from
341 Windows Installer to set properties with the localized names of built-in
342 Windows Security IDs
343********************************************************************/
344extern "C" UINT __stdcall WixQueryOsWellKnownSID(
345 __in MSIHANDLE hInstall
346 )
347{
348 HRESULT hr = S_OK;
349 DWORD er = ERROR_SUCCESS;
350
351 hr = WcaInitialize(hInstall, "WixQueryOsWellKnownSID");
352 ExitOnFailure(hr, "WixQueryOsWellKnownSID failed to initialize");
353
354 SetPropertyWellKnownSID(WinLocalSystemSid, L"WIX_ACCOUNT_LOCALSYSTEM", TRUE);
355 SetPropertyWellKnownSID(WinLocalServiceSid, L"WIX_ACCOUNT_LOCALSERVICE", TRUE);
356 SetPropertyWellKnownSID(WinNetworkServiceSid, L"WIX_ACCOUNT_NETWORKSERVICE", TRUE);
357 SetPropertyWellKnownSID(WinBuiltinAdministratorsSid, L"WIX_ACCOUNT_ADMINISTRATORS", TRUE);
358 SetPropertyWellKnownSID(WinBuiltinUsersSid, L"WIX_ACCOUNT_USERS", TRUE);
359 SetPropertyWellKnownSID(WinBuiltinGuestsSid, L"WIX_ACCOUNT_GUESTS", TRUE);
360 SetPropertyWellKnownSID(WinBuiltinPerfLoggingUsersSid, L"WIX_ACCOUNT_PERFLOGUSERS", TRUE);
361 SetPropertyWellKnownSID(WinBuiltinPerfLoggingUsersSid, L"WIX_ACCOUNT_PERFLOGUSERS_NODOMAIN", FALSE);
362
363LExit:
364 if (FAILED(hr))
365 {
366 er = ERROR_INSTALL_FAILURE;
367 }
368 return WcaFinalize(er);
369}
370
371
372/********************************************************************
373DetectWDDMDriver
374
375 Set a property if the driver on the machine is a WDDM driver. One
376 reliable way to detect the presence of a WDDM driver is to try and
377 use the Direct3DCreate9Ex() function. This method attempts that
378 then sets the property appropriately.
379********************************************************************/
380static HRESULT DetectWDDMDriver()
381{
382 HRESULT hr = S_OK;
383 HMODULE hModule = NULL;
384
385 // Manually load the d3d9.dll library. If the library couldn't be loaded then we obviously won't be able
386 // to try calling the function so just return.
387 hr = LoadSystemLibrary(L"d3d9.dll", &hModule);
388 if (E_MODNOTFOUND == hr)
389 {
390 TraceError(hr, "Unable to load DirectX APIs, skipping WDDM driver check.");
391 ExitFunction1(hr = S_OK);
392 }
393 ExitOnFailure(hr, "Failed to the load the existing DirectX APIs.");
394
395 // Obtain the address of the Direct3DCreate9Ex function. If this fails we know it isn't a WDDM
396 // driver so just exit.
397 const void* Direct3DCreate9ExPtr = ::GetProcAddress(hModule, "Direct3DCreate9Ex");
398 ExitOnNull(Direct3DCreate9ExPtr, hr, S_OK, "Unable to load Direct3DCreateEx function, so the driver is not a WDDM driver.");
399
400 // At this point we know it's a WDDM driver so set the property.
401 hr = WcaSetIntProperty(L"WIX_WDDM_DRIVER_PRESENT", 1);
402 ExitOnFailure(hr, "Failed write property");
403
404LExit:
405 if (NULL != hModule)
406 {
407 FreeLibrary(hModule);
408 }
409
410 return hr;
411}
412
413/********************************************************************
414DetectIsCompositionEnabled
415
416 Set a property based on the return value of DwmIsCompositionEnabled().
417********************************************************************/
418static HRESULT DetectIsCompositionEnabled()
419{
420 HRESULT hr = S_OK;
421 HMODULE hModule = NULL;
422 BOOL compositionState = false;
423
424 // Manually load the d3d9.dll library. If the library can't load it's likely because we are not on a Vista
425 // OS. Just return ok, and the property won't get set.
426 hr = LoadSystemLibrary(L"dwmapi.dll", &hModule);
427 if (E_MODNOTFOUND == hr)
428 {
429 TraceError(hr, "Unable to load Vista desktop window manager APIs, skipping Composition Enabled check.");
430 ExitFunction1(hr = S_OK);
431 }
432 ExitOnFailure(hr, "Failed to load the existing window manager APIs.");
433
434 // If for some reason we can't get the function pointer that's ok, just return.
435 typedef HRESULT (WINAPI *DWMISCOMPOSITIONENABLEDPTR)(BOOL*);
436 DWMISCOMPOSITIONENABLEDPTR DwmIsCompositionEnabledPtr = (DWMISCOMPOSITIONENABLEDPTR)::GetProcAddress(hModule, "DwmIsCompositionEnabled");
437 ExitOnNull(hModule, hr, S_OK, "Unable to obtain function information, skipping Composition Enabled check.");
438
439 hr = DwmIsCompositionEnabledPtr(&compositionState);
440 ExitOnFailure(hr, "Failed to retrieve Composition state");
441
442 if (compositionState)
443 {
444 hr = WcaSetIntProperty(L"WIX_DWM_COMPOSITION_ENABLED", 1);
445 ExitOnFailure(hr, "Failed write property");
446 }
447
448LExit:
449 if (NULL != hModule)
450 {
451 FreeLibrary(hModule);
452 }
453 return hr;
454}
455
456/********************************************************************
457WixQueryOsDriverInfo - entry point for WixQueryOsDriverInfo custom action
458
459 Called as Type 1 custom action (DLL from the Binary table) from
460 Windows Installer to set properties about drivers installed on
461 the target machine
462********************************************************************/
463extern "C" UINT __stdcall WixQueryOsDriverInfo(
464 __in MSIHANDLE hInstall
465 )
466{
467 HRESULT hr = S_OK;
468 DWORD er = ERROR_SUCCESS;
469
470 hr = WcaInitialize(hInstall, "WixQueryOsDriverInfo");
471 ExitOnFailure(hr, "WixQueryOsDriverInfo failed to initialize");
472
473 // Detect the WDDM driver status
474 hr = DetectWDDMDriver();
475 ExitOnFailure(hr, "Failed to detect WIX_WDDM_DRIVER_PRESENT");
476
477 // Detect whether composition is enabled
478 hr = DetectIsCompositionEnabled();
479 ExitOnFailure(hr, "Failed to detect WIX_DWM_COMPOSITION_ENABLED");
480
481LExit:
482 if (FAILED(hr))
483 {
484 er = ERROR_INSTALL_FAILURE;
485 }
486 return WcaFinalize(er);
487}
diff --git a/src/ca/RemoveFoldersEx.cpp b/src/ca/RemoveFoldersEx.cpp
new file mode 100644
index 00000000..194c6662
--- /dev/null
+++ b/src/ca/RemoveFoldersEx.cpp
@@ -0,0 +1,197 @@
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
5LPCWSTR vcsRemoveFolderExQuery = L"SELECT `WixRemoveFolderEx`, `Component_`, `Property`, `InstallMode` FROM `WixRemoveFolderEx`";
6enum eRemoveFolderExQuery { rfqId = 1, rfqComponent, rfqProperty, feqMode };
7
8static HRESULT RecursePath(
9 __in_z LPCWSTR wzPath,
10 __in_z LPCWSTR wzId,
11 __in_z LPCWSTR wzComponent,
12 __in_z LPCWSTR wzProperty,
13 __in int iMode,
14 __inout DWORD* pdwCounter,
15 __inout MSIHANDLE* phTable,
16 __inout MSIHANDLE* phColumns
17 )
18{
19 HRESULT hr = S_OK;
20 DWORD er;
21 LPWSTR sczSearch = NULL;
22 LPWSTR sczProperty = NULL;
23 HANDLE hFind = INVALID_HANDLE_VALUE;
24 WIN32_FIND_DATAW wfd;
25 LPWSTR sczNext = NULL;
26
27 // First recurse down to all the child directories.
28 hr = StrAllocFormatted(&sczSearch, L"%s*", wzPath);
29 ExitOnFailure(hr, "Failed to allocate file search string in path: %S", wzPath);
30
31 hFind = ::FindFirstFileW(sczSearch, &wfd);
32 if (INVALID_HANDLE_VALUE == hFind)
33 {
34 er = ::GetLastError();
35 if (ERROR_PATH_NOT_FOUND == er)
36 {
37 WcaLog(LOGMSG_STANDARD, "Search path not found: %ls", sczSearch);
38 ExitFunction1(hr = S_FALSE);
39 }
40 else
41 {
42 hr = HRESULT_FROM_WIN32(er);
43 }
44 ExitOnFailure(hr, "Failed to find all files in path: %S", wzPath);
45 }
46
47 do
48 {
49 // Skip files and the dot directories.
50 if (FILE_ATTRIBUTE_DIRECTORY != (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || L'.' == wfd.cFileName[0] && (L'\0' == wfd.cFileName[1] || (L'.' == wfd.cFileName[1] && L'\0' == wfd.cFileName[2])))
51 {
52 continue;
53 }
54
55 hr = StrAllocFormatted(&sczNext, L"%s%s\\", wzPath, wfd.cFileName);
56 ExitOnFailure(hr, "Failed to concat filename '%S' to string: %S", wfd.cFileName, wzPath);
57
58 hr = RecursePath(sczNext, wzId, wzComponent, wzProperty, iMode, pdwCounter, phTable, phColumns);
59 ExitOnFailure(hr, "Failed to recurse path: %S", sczNext);
60 } while (::FindNextFileW(hFind, &wfd));
61
62 er = ::GetLastError();
63 if (ERROR_NO_MORE_FILES == er)
64 {
65 hr = S_OK;
66 }
67 else
68 {
69 hr = HRESULT_FROM_WIN32(er);
70 ExitOnFailure(hr, "Failed while looping through files in directory: %S", wzPath);
71 }
72
73 // Finally, set a property that points at our path.
74 hr = StrAllocFormatted(&sczProperty, L"_%s_%u", wzProperty, *pdwCounter);
75 ExitOnFailure(hr, "Failed to allocate Property for RemoveFile table with property: %S.", wzProperty);
76
77 ++(*pdwCounter);
78
79 hr = WcaSetProperty(sczProperty, wzPath);
80 ExitOnFailure(hr, "Failed to set Property: %S with path: %S", sczProperty, wzPath);
81
82 // Add the row to remove any files and another row to remove the folder.
83 hr = WcaAddTempRecord(phTable, phColumns, L"RemoveFile", NULL, 1, 5, L"RfxFiles", wzComponent, L"*.*", sczProperty, iMode);
84 ExitOnFailure(hr, "Failed to add row to remove all files for WixRemoveFolderEx row: %S under path:", wzId, wzPath);
85
86 hr = WcaAddTempRecord(phTable, phColumns, L"RemoveFile", NULL, 1, 5, L"RfxFolder", wzComponent, NULL, sczProperty, iMode);
87 ExitOnFailure(hr, "Failed to add row to remove folder for WixRemoveFolderEx row: %S under path: %S", wzId, wzPath);
88
89LExit:
90 if (INVALID_HANDLE_VALUE != hFind)
91 {
92 ::FindClose(hFind);
93 }
94
95 ReleaseStr(sczNext);
96 ReleaseStr(sczProperty);
97 ReleaseStr(sczSearch);
98 return hr;
99}
100
101extern "C" UINT WINAPI WixRemoveFoldersEx(
102 __in MSIHANDLE hInstall
103 )
104{
105 //AssertSz(FALSE, "debug WixRemoveFoldersEx");
106
107 HRESULT hr = S_OK;
108 PMSIHANDLE hView;
109 PMSIHANDLE hRec;
110 LPWSTR sczId = NULL;
111 LPWSTR sczComponent = NULL;
112 LPWSTR sczProperty = NULL;
113 LPWSTR sczPath = NULL;
114 LPWSTR sczExpandedPath = NULL;
115 int iMode = 0;
116 DWORD dwCounter = 0;
117 DWORD_PTR cchLen = 0;
118 MSIHANDLE hTable = NULL;
119 MSIHANDLE hColumns = NULL;
120
121 hr = WcaInitialize(hInstall, "WixRemoveFoldersEx");
122 ExitOnFailure(hr, "Failed to initialize WixRemoveFoldersEx.");
123
124 // anything to do?
125 if (S_OK != WcaTableExists(L"WixRemoveFolderEx"))
126 {
127 WcaLog(LOGMSG_STANDARD, "WixRemoveFolderEx table doesn't exist, so there are no folders to remove.");
128 ExitFunction();
129 }
130
131 // query and loop through all the remove folders exceptions
132 hr = WcaOpenExecuteView(vcsRemoveFolderExQuery, &hView);
133 ExitOnFailure(hr, "Failed to open view on WixRemoveFolderEx table");
134
135 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
136 {
137 hr = WcaGetRecordString(hRec, rfqId, &sczId);
138 ExitOnFailure(hr, "Failed to get remove folder identity.");
139
140 hr = WcaGetRecordString(hRec, rfqComponent, &sczComponent);
141 ExitOnFailure(hr, "Failed to get remove folder component.");
142
143 hr = WcaGetRecordString(hRec, rfqProperty, &sczProperty);
144 ExitOnFailure(hr, "Failed to get remove folder property.");
145
146 hr = WcaGetRecordInteger(hRec, feqMode, &iMode);
147 ExitOnFailure(hr, "Failed to get remove folder mode");
148
149 hr = WcaGetProperty(sczProperty, &sczPath);
150 ExitOnFailure(hr, "Failed to resolve remove folder property: %S for row: %S", sczProperty, sczId);
151
152 // fail early if the property isn't set as you probably don't want your installers trying to delete SystemFolder
153 // StringCchLengthW succeeds only if the string is zero characters plus 1 for the terminating null
154 hr = ::StringCchLengthW(sczPath, 1, reinterpret_cast<UINT_PTR*>(&cchLen));
155 if (SUCCEEDED(hr))
156 {
157 ExitOnFailure(hr = E_INVALIDARG, "Missing folder property: %S for row: %S", sczProperty, sczId);
158 }
159
160 hr = PathExpand(&sczExpandedPath, sczPath, PATH_EXPAND_ENVIRONMENT);
161 ExitOnFailure(hr, "Failed to expand path: %S for row: %S", sczPath, sczId);
162
163 hr = PathBackslashTerminate(&sczExpandedPath);
164 ExitOnFailure(hr, "Failed to backslash-terminate path: %S", sczExpandedPath);
165
166 WcaLog(LOGMSG_STANDARD, "Recursing path: %S for row: %S.", sczExpandedPath, sczId);
167 hr = RecursePath(sczExpandedPath, sczId, sczComponent, sczProperty, iMode, &dwCounter, &hTable, &hColumns);
168 ExitOnFailure(hr, "Failed while navigating path: %S for row: %S", sczPath, sczId);
169 }
170
171 // reaching the end of the list is actually a good thing, not an error
172 if (E_NOMOREITEMS == hr)
173 {
174 hr = S_OK;
175 }
176 ExitOnFailure(hr, "Failure occured while processing WixRemoveFolderEx table");
177
178LExit:
179 if (hColumns)
180 {
181 ::MsiCloseHandle(hColumns);
182 }
183
184 if (hTable)
185 {
186 ::MsiCloseHandle(hTable);
187 }
188
189 ReleaseStr(sczExpandedPath);
190 ReleaseStr(sczPath);
191 ReleaseStr(sczProperty);
192 ReleaseStr(sczComponent);
193 ReleaseStr(sczId);
194
195 DWORD er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
196 return WcaFinalize(er);
197}
diff --git a/src/ca/RestartManager.cpp b/src/ca/RestartManager.cpp
new file mode 100644
index 00000000..3cfc07ee
--- /dev/null
+++ b/src/ca/RestartManager.cpp
@@ -0,0 +1,185 @@
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 <restartmanager.h>
5
6// Include space for the terminating null.
7#define CCH_SESSION_KEY CCH_RM_SESSION_KEY + 1
8
9enum eRmuResourceType
10{
11 etInvalid,
12 etFilename,
13 etApplication,
14 etServiceName,
15
16 // Mask types from Attributes.
17 etTypeMask = 0xf,
18};
19
20LPCWSTR vcsRestartResourceQuery =
21 L"SELECT `WixRestartResource`.`WixRestartResource`, `WixRestartResource`.`Component_`, `WixRestartResource`.`Resource`, `WixRestartResource`.`Attributes` "
22 L"FROM `WixRestartResource`";
23enum eRestartResourceQuery { rrqRestartResource = 1, rrqComponent, rrqResource, rrqAttributes };
24
25/********************************************************************
26WixRegisterRestartResources - Immediate CA to register resources with RM.
27
28Enumerates components before InstallValidate and registers resources
29to be restarted by Restart Manager if the component action
30is anything other than None.
31
32Do not disable file system redirection.
33
34********************************************************************/
35extern "C" UINT __stdcall WixRegisterRestartResources(
36 __in MSIHANDLE hInstall
37 )
38{
39 HRESULT hr = S_OK;
40 UINT er = ERROR_SUCCESS;
41
42 PMSIHANDLE hView = NULL;
43 PMSIHANDLE hRec = NULL;
44
45 LPWSTR wzSessionKey = NULL;
46 size_t cchSessionKey = 0;
47 PRMU_SESSION pSession = NULL;
48
49 LPWSTR wzRestartResource = NULL;
50 LPWSTR wzComponent = NULL;
51 LPWSTR wzResource = NULL;
52 int iAttributes = NULL;
53 BOOL fIsComponentNull = FALSE;
54 WCA_TODO todo = WCA_TODO_UNKNOWN;
55 int iType = etInvalid;
56
57 hr = WcaInitialize(hInstall, "WixRegisterRestartResources");
58 ExitOnFailure(hr, "Failed to initialize.");
59
60 // Skip if the table doesn't exist.
61 if (S_OK != WcaTableExists(L"WixRestartResource"))
62 {
63 WcaLog(LOGMSG_STANDARD, "The RestartResource table does not exist; there are no resources to register with Restart Manager.");
64 ExitFunction();
65 }
66
67 // Get the existing Restart Manager session if available.
68 hr = WcaGetProperty(L"MsiRestartManagerSessionKey", &wzSessionKey);
69 ExitOnFailure(hr, "Failed to get the MsiRestartManagerSessionKey property.");
70
71 hr = ::StringCchLengthW(wzSessionKey, CCH_SESSION_KEY, &cchSessionKey);
72 ExitOnFailure(hr, "Failed to get the MsiRestartManagerSessionKey string length.");
73
74 // Skip if the property doesn't exist.
75 if (0 == cchSessionKey)
76 {
77 WcaLog(LOGMSG_STANDARD, "The MsiRestartManagerSessionKey property is not available to join.");
78 ExitFunction();
79 }
80
81 // Join the existing Restart Manager session if supported.
82 hr = RmuJoinSession(&pSession, wzSessionKey);
83 if (E_MODNOTFOUND == hr)
84 {
85 WcaLog(LOGMSG_STANDARD, "The Restart Manager is not supported on this platform. Skipping.");
86 ExitFunction1(hr = S_OK);
87 }
88 else if (FAILED(hr))
89 {
90 WcaLog(LOGMSG_STANDARD, "Failed to join the existing Restart Manager session %ls.", wzSessionKey);
91 ExitFunction1(hr = S_OK);
92 }
93
94 // Loop through each record in the table.
95 hr = WcaOpenExecuteView(vcsRestartResourceQuery, &hView);
96 ExitOnFailure(hr, "Failed to open a view on the RestartResource table.");
97
98 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
99 {
100 hr = WcaGetRecordString(hRec, rrqRestartResource, &wzRestartResource);
101 ExitOnFailure(hr, "Failed to get the RestartResource field value.");
102
103 hr = WcaGetRecordString(hRec, rrqComponent, &wzComponent);
104 ExitOnFailure(hr, "Failed to get the Component_ field value.");
105
106 hr = WcaGetRecordFormattedString(hRec, rrqResource, &wzResource);
107 ExitOnFailure(hr, "Failed to get the Resource formatted field value.");
108
109 hr = WcaGetRecordInteger(hRec, rrqAttributes, &iAttributes);
110 ExitOnFailure(hr, "Failed to get the Attributes field value.");
111
112 fIsComponentNull = ::MsiRecordIsNull(hRec, rrqComponent);
113 todo = WcaGetComponentToDo(wzComponent);
114
115 // Only register resources for components that are null, or being installed, reinstalled, or uninstalled.
116 if (!fIsComponentNull && WCA_TODO_UNKNOWN == todo)
117 {
118 WcaLog(LOGMSG_VERBOSE, "Skipping resource %ls.", wzRestartResource);
119 continue;
120 }
121
122 // Get the type from Attributes and add to the Restart Manager.
123 iType = iAttributes & etTypeMask;
124 switch (iType)
125 {
126 case etFilename:
127 WcaLog(LOGMSG_VERBOSE, "Registering file name %ls with the Restart Manager.", wzResource);
128 hr = RmuAddFile(pSession, wzResource);
129 ExitOnFailure(hr, "Failed to register the file name with the Restart Manager session.");
130 break;
131
132 case etApplication:
133 WcaLog(LOGMSG_VERBOSE, "Registering process name %ls with the Restart Manager.", wzResource);
134 hr = RmuAddProcessesByName(pSession, wzResource);
135 if (E_NOTFOUND == hr)
136 {
137 // ERROR_ACCESS_DENIED was returned when trying to register this process.
138 // Since other instances may have been registered, log a message and continue the setup rather than failing.
139 WcaLog(LOGMSG_STANDARD, "The process, %ls, could not be registered with the Restart Manager (probably because the setup is not elevated and the process is in another user context). A reboot may be requested later.", wzResource);
140 hr = S_OK;
141 }
142 else
143 {
144 ExitOnFailure(hr, "Failed to register the process name with the Restart Manager session.");
145 }
146 break;
147
148 case etServiceName:
149 WcaLog(LOGMSG_VERBOSE, "Registering service name %ls with the Restart Manager.", wzResource);
150 hr = RmuAddService(pSession, wzResource);
151 ExitOnFailure(hr, "Failed to register the service name with the Restart Manager session.");
152 break;
153
154 default:
155 WcaLog(LOGMSG_VERBOSE, "The resource type %d for %ls is not supported and will not be registered.", iType, wzRestartResource);
156 break;
157 }
158 }
159
160 if (E_NOMOREITEMS == hr)
161 {
162 hr = S_OK;
163 }
164 ExitOnFailure(hr, "Failed while looping through all rows to register resources.");
165
166 // Register the resources and unjoin the session.
167 hr = RmuEndSession(pSession);
168 if (FAILED(hr))
169 {
170 WcaLog(LOGMSG_VERBOSE, "Failed to register the resources with the Restart Manager.");
171 ExitFunction1(hr = S_OK);
172 }
173
174LExit:
175 ReleaseStr(wzRestartResource);
176 ReleaseStr(wzComponent);
177 ReleaseStr(wzResource);
178
179 if (FAILED(hr))
180 {
181 er = ERROR_INSTALL_FAILURE;
182 }
183
184 return WcaFinalize(er);
185}
diff --git a/src/ca/TouchFile.cpp b/src/ca/TouchFile.cpp
new file mode 100644
index 00000000..1c40a3eb
--- /dev/null
+++ b/src/ca/TouchFile.cpp
@@ -0,0 +1,308 @@
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
5LPCWSTR vcsTouchFileQuery = L"SELECT `WixTouchFile`, `Component_`, `Path`, `Attributes` FROM `WixTouchFile`";
6enum TOUCH_FILE_QUERY { tfqId = 1, tfqComponent, tfqPath, tfqTouchFileAttributes };
7
8enum TOUCH_FILE_ATTRIBUTE
9{
10 TOUCH_FILE_ATTRIBUTE_ON_INSTALL = 0x01,
11 TOUCH_FILE_ATTRIBUTE_ON_REINSTALL = 0x02,
12 TOUCH_FILE_ATTRIBUTE_ON_UNINSTALL = 0x04,
13 TOUCH_FILE_ATTRIBUTE_64BIT = 0x10,
14 TOUCH_FILE_ATTRIBUTE_VITAL = 0x20
15};
16
17
18static BOOL SetExistingFileModifiedTime(
19 __in_z LPCWSTR wzId,
20 __in_z LPCWSTR wzPath,
21 __in BOOL f64Bit,
22 __in FILETIME* pftModified
23 )
24{
25 HRESULT hr = S_OK;
26 BOOL fReenableFileSystemRedirection = FALSE;
27
28 if (f64Bit)
29 {
30 hr = WcaDisableWow64FSRedirection();
31 ExitOnFailure(hr, "Failed to disable 64-bit file system redirection to path: '%ls' for: %ls", wzPath, wzId);
32
33 fReenableFileSystemRedirection = TRUE;
34 }
35
36 hr = FileSetTime(wzPath, NULL, NULL, pftModified);
37
38LExit:
39 if (fReenableFileSystemRedirection)
40 {
41 WcaRevertWow64FSRedirection();
42 }
43
44 return SUCCEEDED(hr);
45}
46
47
48static HRESULT AddDataToCustomActionData(
49 __deref_inout_z LPWSTR* psczCustomActionData,
50 __in_z LPCWSTR wzId,
51 __in_z LPCWSTR wzPath,
52 __in int iTouchFileAttributes,
53 __in FILETIME ftModified
54 )
55{
56 HRESULT hr = S_OK;
57
58 hr = WcaWriteStringToCaData(wzId, psczCustomActionData);
59 ExitOnFailure(hr, "Failed to add touch file identity to custom action data.");
60
61 hr = WcaWriteStringToCaData(wzPath, psczCustomActionData);
62 ExitOnFailure(hr, "Failed to add touch file path to custom action data.");
63
64 hr = WcaWriteIntegerToCaData(iTouchFileAttributes, psczCustomActionData);
65 ExitOnFailure(hr, "Failed to add touch file attributes to custom action data.");
66
67 hr = WcaWriteIntegerToCaData(ftModified.dwHighDateTime, psczCustomActionData);
68 ExitOnFailure(hr, "Failed to add touch file high date/time to custom action data.");
69
70 hr = WcaWriteIntegerToCaData(ftModified.dwLowDateTime, psczCustomActionData);
71 ExitOnFailure(hr, "Failed to add touch file low date/time to custom action data.");
72
73LExit:
74 return hr;
75}
76
77
78static BOOL TryGetExistingFileModifiedTime(
79 __in_z LPCWSTR wzId,
80 __in_z LPCWSTR wzPath,
81 __in BOOL f64Bit,
82 __inout FILETIME* pftModified
83 )
84{
85 HRESULT hr = S_OK;
86 BOOL fReenableFileSystemRedirection = FALSE;
87
88 if (f64Bit)
89 {
90 hr = WcaDisableWow64FSRedirection();
91 ExitOnFailure(hr, "Failed to disable 64-bit file system redirection to path: '%ls' for: %ls", wzPath, wzId);
92
93 fReenableFileSystemRedirection = TRUE;
94 }
95
96 hr = FileGetTime(wzPath, NULL, NULL, pftModified);
97 if (E_PATHNOTFOUND == hr || E_FILENOTFOUND == hr)
98 {
99 // If the file doesn't exist yet there is nothing to rollback (i.e. file will probably be removed during rollback), so
100 // keep the error code but don't log anything.
101 }
102 else if (FAILED(hr))
103 {
104 WcaLog(LOGMSG_STANDARD, "Cannot access modified timestamp for file: '%ls' due to error: 0x%x. Continuing with out rollback for: %ls", wzPath, hr, wzId);
105 }
106
107LExit:
108 if (fReenableFileSystemRedirection)
109 {
110 WcaRevertWow64FSRedirection();
111 }
112
113 return SUCCEEDED(hr);
114}
115
116
117static HRESULT ProcessTouchFileTable(
118 __in BOOL fInstalling
119 )
120{
121 HRESULT hr = S_OK;
122
123 FILETIME ftModified = {};
124
125 PMSIHANDLE hView;
126 PMSIHANDLE hRec;
127
128 LPWSTR sczId = NULL;
129 LPWSTR sczComponent = NULL;
130 int iTouchFileAttributes = 0;
131 LPWSTR sczPath = NULL;
132
133 FILETIME ftRollbackModified = {};
134 LPWSTR sczRollbackData = NULL;
135 LPWSTR sczExecuteData = NULL;
136
137 if (S_OK != WcaTableExists(L"WixTouchFile"))
138 {
139 ExitFunction();
140 }
141
142 ::GetSystemTimeAsFileTime(&ftModified);
143
144 hr = WcaOpenExecuteView(vcsTouchFileQuery, &hView);
145 ExitOnFailure(hr, "Failed to open view on WixTouchFile table");
146
147 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
148 {
149 hr = WcaGetRecordString(hRec, tfqId, &sczId);
150 ExitOnFailure(hr, "Failed to get touch file identity.");
151
152 hr = WcaGetRecordString(hRec, tfqComponent, &sczComponent);
153 ExitOnFailure(hr, "Failed to get touch file component for: %ls", sczId);
154
155 hr = WcaGetRecordInteger(hRec, tfqTouchFileAttributes, &iTouchFileAttributes);
156 ExitOnFailure(hr, "Failed to get touch file attributes for: %ls", sczId);
157
158 WCA_TODO todo = WcaGetComponentToDo(sczComponent);
159
160 BOOL fOnInstall = fInstalling && WCA_TODO_INSTALL == todo && (iTouchFileAttributes & TOUCH_FILE_ATTRIBUTE_ON_INSTALL);
161 BOOL fOnReinstall = fInstalling && WCA_TODO_REINSTALL == todo && (iTouchFileAttributes & TOUCH_FILE_ATTRIBUTE_ON_REINSTALL);
162 BOOL fOnUninstall = !fInstalling && WCA_TODO_UNINSTALL == todo && (iTouchFileAttributes & TOUCH_FILE_ATTRIBUTE_ON_UNINSTALL);
163
164 if (fOnInstall || fOnReinstall || fOnUninstall)
165 {
166 hr = WcaGetRecordFormattedString(hRec, tfqPath, &sczPath);
167 ExitOnFailure(hr, "Failed to get touch file path for: %ls", sczId);
168
169 if (TryGetExistingFileModifiedTime(sczId, sczPath, (iTouchFileAttributes & TOUCH_FILE_ATTRIBUTE_64BIT), &ftRollbackModified))
170 {
171 hr = AddDataToCustomActionData(&sczRollbackData, sczId, sczPath, iTouchFileAttributes, ftRollbackModified);
172 ExitOnFailure(hr, "Failed to add to rollback custom action data for: %ls", sczId);
173 }
174
175 hr = AddDataToCustomActionData(&sczExecuteData, sczId, sczPath, iTouchFileAttributes, ftModified);
176 ExitOnFailure(hr, "Failed to add to execute custom action data for: %ls", sczId);
177 }
178 }
179
180 if (E_NOMOREITEMS == hr)
181 {
182 hr = S_OK;
183 }
184 ExitOnFailure(hr, "Failure occured while processing WixTouchFile table");
185
186 if (sczRollbackData)
187 {
188 hr = WcaDoDeferredAction(L"WixRollbackTouchFile", sczRollbackData, 0);
189 ExitOnFailure(hr, "Failed to schedule WixRollbackTouchFile");
190 }
191
192 if (sczExecuteData)
193 {
194 hr = WcaDoDeferredAction(L"WixExecuteTouchFile", sczExecuteData, 0);
195 ExitOnFailure(hr, "Failed to schedule WixExecuteTouchFile");
196 }
197
198LExit:
199 ReleaseStr(sczExecuteData);
200 ReleaseStr(sczRollbackData);
201 ReleaseStr(sczPath);
202 ReleaseStr(sczComponent);
203 ReleaseStr(sczId);
204
205 return hr;
206}
207
208
209extern "C" UINT WINAPI WixTouchFileDuringInstall(
210 __in MSIHANDLE hInstall
211 )
212{
213 //AssertSz(FALSE, "debug WixTouchFileDuringInstall");
214
215 HRESULT hr = S_OK;
216
217 hr = WcaInitialize(hInstall, "WixTouchFileDuringInstall");
218 ExitOnFailure(hr, "Failed to initialize WixTouchFileDuringInstall.");
219
220 hr = ProcessTouchFileTable(TRUE);
221
222LExit:
223 DWORD er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
224 return WcaFinalize(er);
225}
226
227
228extern "C" UINT WINAPI WixTouchFileDuringUninstall(
229 __in MSIHANDLE hInstall
230 )
231{
232 //AssertSz(FALSE, "debug WixTouchFileDuringUninstall");
233
234 HRESULT hr = S_OK;
235
236 hr = WcaInitialize(hInstall, "WixTouchFileDuringUninstall");
237 ExitOnFailure(hr, "Failed to initialize WixTouchFileDuringUninstall.");
238
239 hr = ProcessTouchFileTable(FALSE);
240
241LExit:
242 DWORD er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
243 return WcaFinalize(er);
244}
245
246
247extern "C" UINT WINAPI WixExecuteTouchFile(
248 __in MSIHANDLE hInstall
249 )
250{
251 HRESULT hr = S_OK;
252
253 LPWSTR sczData = NULL;
254 LPWSTR pwz = NULL;
255
256 LPWSTR sczId = NULL;
257 LPWSTR sczPath = NULL;
258 int iTouchFileAttributes = 0;
259 FILETIME ftModified = {};
260
261 hr = WcaInitialize(hInstall, "WixExecuteTouchFile");
262 ExitOnFailure(hr, "Failed to initialize WixExecuteTouchFile.");
263
264 hr = WcaGetProperty(L"CustomActionData", &sczData);
265 ExitOnFailure(hr, "Failed to get custom action data for WixExecuteTouchFile.");
266
267 pwz = sczData;
268
269 while (pwz && *pwz)
270 {
271 hr = WcaReadStringFromCaData(&pwz, &sczId);
272 ExitOnFailure(hr, "Failed to get touch file identity from custom action data.");
273
274 hr = WcaReadStringFromCaData(&pwz, &sczPath);
275 ExitOnFailure(hr, "Failed to get touch file path from custom action data for: %ls", sczId);
276
277 hr = WcaReadIntegerFromCaData(&pwz, &iTouchFileAttributes);
278 ExitOnFailure(hr, "Failed to get touch file attributes from custom action data for: %ls", sczId);
279
280 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&ftModified.dwHighDateTime));
281 ExitOnFailure(hr, "Failed to get touch file high date/time from custom action data for: %ls", sczId);
282
283 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&ftModified.dwLowDateTime));
284 ExitOnFailure(hr, "Failed to get touch file low date/time from custom action data for: %ls", sczId);
285
286 hr = SetExistingFileModifiedTime(sczId, sczPath, (iTouchFileAttributes & TOUCH_FILE_ATTRIBUTE_64BIT), &ftModified);
287 if (FAILED(hr))
288 {
289 if (iTouchFileAttributes & TOUCH_FILE_ATTRIBUTE_VITAL)
290 {
291 ExitOnFailure(hr, "Failed to touch file: '%ls' for: %ls", &sczPath, sczId);
292 }
293 else
294 {
295 WcaLog(LOGMSG_STANDARD, "Could not touch non-vital file: '%ls' for: %ls with error: 0x%x. Continuing...", sczPath, sczId, hr);
296 hr = S_OK;
297 }
298 }
299 }
300
301LExit:
302 ReleaseStr(sczPath);
303 ReleaseStr(sczId);
304 ReleaseStr(sczData);
305
306 DWORD er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
307 return WcaFinalize(er);
308}
diff --git a/src/ca/XmlConfig.cpp b/src/ca/XmlConfig.cpp
new file mode 100644
index 00000000..c12b2bc2
--- /dev/null
+++ b/src/ca/XmlConfig.cpp
@@ -0,0 +1,1099 @@
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 XMLCONFIG_ELEMENT 0x00000001
6#define XMLCONFIG_VALUE 0x00000002
7#define XMLCONFIG_DOCUMENT 0x00000004
8#define XMLCONFIG_CREATE 0x00000010
9#define XMLCONFIG_DELETE 0x00000020
10#define XMLCONFIG_INSTALL 0x00000100
11#define XMLCONFIG_UNINSTALL 0x00000200
12#define XMLCONFIG_PRESERVE_MODIFIED 0x00001000
13
14enum eXmlAction
15{
16 xaUnknown = 0,
17 xaOpenFile,
18 xaOpenFilex64,
19 xaWriteValue,
20 xaWriteDocument,
21 xaDeleteValue,
22 xaCreateElement,
23 xaDeleteElement,
24};
25
26enum eXmlPreserveDate
27{
28 xdDontPreserve = 0,
29 xdPreserve
30};
31
32LPCWSTR vcsXmlConfigQuery =
33 L"SELECT `XmlConfig`.`XmlConfig`, `XmlConfig`.`File`, `XmlConfig`.`ElementPath`, `XmlConfig`.`VerifyPath`, `XmlConfig`.`Name`, "
34 L"`XmlConfig`.`Value`, `XmlConfig`.`Flags`, `XmlConfig`.`Component_`, `Component`.`Attributes` "
35 L"FROM `XmlConfig`,`Component` WHERE `XmlConfig`.`Component_`=`Component`.`Component` ORDER BY `File`, `Sequence`";
36enum eXmlConfigQuery { xfqXmlConfig = 1, xfqFile, xfqElementPath, xfqVerifyPath, xfqName, xfqValue, xfqXmlFlags, xfqComponent, xfqCompAttributes };
37
38struct XML_CONFIG_CHANGE
39{
40 WCHAR wzId[MAX_DARWIN_KEY + 1];
41
42 WCHAR wzComponent[MAX_DARWIN_KEY + 1];
43 INSTALLSTATE isInstalled;
44 INSTALLSTATE isAction;
45
46 WCHAR wzFile[MAX_PATH];
47 LPWSTR pwzElementPath;
48 LPWSTR pwzVerifyPath;
49 WCHAR wzName[MAX_DARWIN_COLUMN];
50 LPWSTR pwzValue;
51 BOOL fInstalledFile;
52
53 int iXmlFlags;
54 int iCompAttributes;
55
56 XML_CONFIG_CHANGE* pxfcAdditionalChanges;
57 int cAdditionalChanges;
58
59 XML_CONFIG_CHANGE* pxfcPrev;
60 XML_CONFIG_CHANGE* pxfcNext;
61};
62
63static HRESULT FreeXmlConfigChangeList(
64 __in_opt XML_CONFIG_CHANGE* pxfcList
65 )
66{
67 HRESULT hr = S_OK;
68
69 XML_CONFIG_CHANGE* pxfcDelete;
70 while(pxfcList)
71 {
72 pxfcDelete = pxfcList;
73 pxfcList = pxfcList->pxfcNext;
74
75 if (pxfcDelete->pwzElementPath)
76 {
77 hr = MemFree(pxfcDelete->pwzElementPath);
78 ExitOnFailure(hr, "failed to free xml file element path in change list item");
79 }
80
81 if (pxfcDelete->pwzVerifyPath)
82 {
83 hr = MemFree(pxfcDelete->pwzVerifyPath);
84 ExitOnFailure(hr, "failed to free xml file verify path in change list item");
85 }
86
87 if (pxfcDelete->pwzValue)
88 {
89 hr = MemFree(pxfcDelete->pwzValue);
90 ExitOnFailure(hr, "failed to free xml file value in change list item");
91 }
92
93 hr = MemFree(pxfcDelete);
94 ExitOnFailure(hr, "failed to free xml file change list item");
95 }
96
97LExit:
98 return hr;
99}
100
101static HRESULT AddXmlConfigChangeToList(
102 __inout XML_CONFIG_CHANGE** ppxfcHead,
103 __inout XML_CONFIG_CHANGE** ppxfcTail
104 )
105{
106 Assert(ppxfcHead && ppxfcTail);
107
108 HRESULT hr = S_OK;
109
110 XML_CONFIG_CHANGE* pxfc = static_cast<XML_CONFIG_CHANGE*>(MemAlloc(sizeof(XML_CONFIG_CHANGE), TRUE));
111 ExitOnNull(pxfc, hr, E_OUTOFMEMORY, "failed to allocate memory for new xml file change list element");
112
113 // Add it to the end of the list
114 if (NULL == *ppxfcHead)
115 {
116 *ppxfcHead = pxfc;
117 *ppxfcTail = pxfc;
118 }
119 else
120 {
121 Assert(*ppxfcTail && (*ppxfcTail)->pxfcNext == NULL);
122 (*ppxfcTail)->pxfcNext = pxfc;
123 pxfc->pxfcPrev = *ppxfcTail;
124 *ppxfcTail = pxfc;
125 }
126
127LExit:
128 return hr;
129}
130
131
132static HRESULT ReadXmlConfigTable(
133 __inout XML_CONFIG_CHANGE** ppxfcHead,
134 __inout XML_CONFIG_CHANGE** ppxfcTail
135 )
136{
137 Assert(ppxfcHead && ppxfcTail);
138
139 HRESULT hr = S_OK;
140 UINT er = ERROR_SUCCESS;
141
142 PMSIHANDLE hView = NULL;
143 PMSIHANDLE hRec = NULL;
144
145 LPWSTR pwzData = NULL;
146
147 // loop through all the xml configurations
148 hr = WcaOpenExecuteView(vcsXmlConfigQuery, &hView);
149 ExitOnFailure(hr, "failed to open view on XmlConfig table");
150
151 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
152 {
153 hr = AddXmlConfigChangeToList(ppxfcHead, ppxfcTail);
154 ExitOnFailure(hr, "failed to add xml file change to list");
155
156 // Get record Id
157 hr = WcaGetRecordString(hRec, xfqXmlConfig, &pwzData);
158 ExitOnFailure(hr, "failed to get XmlConfig record Id");
159 hr = StringCchCopyW((*ppxfcTail)->wzId, countof((*ppxfcTail)->wzId), pwzData);
160 ExitOnFailure(hr, "failed to copy XmlConfig record Id");
161
162 // Get component name
163 hr = WcaGetRecordString(hRec, xfqComponent, &pwzData);
164 ExitOnFailure(hr, "failed to get component name for XmlConfig: %ls", (*ppxfcTail)->wzId);
165
166 // Get the component's state
167 if (0 < lstrlenW(pwzData))
168 {
169 hr = StringCchCopyW((*ppxfcTail)->wzComponent, countof((*ppxfcTail)->wzComponent), pwzData);
170 ExitOnFailure(hr, "failed to copy component id");
171
172 er = ::MsiGetComponentStateW(WcaGetInstallHandle(), (*ppxfcTail)->wzComponent, &(*ppxfcTail)->isInstalled, &(*ppxfcTail)->isAction);
173 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to get install state for component id");
174 }
175
176 // Get the xml file
177 hr = WcaGetRecordFormattedString(hRec, xfqFile, &pwzData);
178 ExitOnFailure(hr, "failed to get xml file for XmlConfig: %ls", (*ppxfcTail)->wzId);
179 hr = StringCchCopyW((*ppxfcTail)->wzFile, countof((*ppxfcTail)->wzFile), pwzData);
180 ExitOnFailure(hr, "failed to copy xml file path");
181
182 // Figure out if the file is already on the machine or if it's being installed
183 hr = WcaGetRecordString(hRec, xfqFile, &pwzData);
184 ExitOnFailure(hr, "failed to get xml file for XmlConfig: %ls", (*ppxfcTail)->wzId);
185 if (NULL != wcsstr(pwzData, L"[!") || NULL != wcsstr(pwzData, L"[#"))
186 {
187 (*ppxfcTail)->fInstalledFile = TRUE;
188 }
189
190 // Get the XmlConfig table flags
191 hr = WcaGetRecordInteger(hRec, xfqXmlFlags, &(*ppxfcTail)->iXmlFlags);
192 ExitOnFailure(hr, "failed to get XmlConfig flags for XmlConfig: %ls", (*ppxfcTail)->wzId);
193
194 // Get the Element Path
195 hr = WcaGetRecordFormattedString(hRec, xfqElementPath, &(*ppxfcTail)->pwzElementPath);
196 ExitOnFailure(hr, "failed to get Element Path for XmlConfig: %ls", (*ppxfcTail)->wzId);
197
198 // Get the Verify Path
199 hr = WcaGetRecordFormattedString(hRec, xfqVerifyPath, &(*ppxfcTail)->pwzVerifyPath);
200 ExitOnFailure(hr, "failed to get Verify Path for XmlConfig: %ls", (*ppxfcTail)->wzId);
201
202 // Get the name
203 hr = WcaGetRecordFormattedString(hRec, xfqName, &pwzData);
204 ExitOnFailure(hr, "failed to get Name for XmlConfig: %ls", (*ppxfcTail)->wzId);
205 hr = StringCchCopyW((*ppxfcTail)->wzName, countof((*ppxfcTail)->wzName), pwzData);
206 ExitOnFailure(hr, "failed to copy name of element");
207
208 // Get the value
209 hr = WcaGetRecordFormattedString(hRec, xfqValue, &pwzData);
210 ExitOnFailure(hr, "failed to get Value for XmlConfig: %ls", (*ppxfcTail)->wzId);
211 hr = StrAllocString(&(*ppxfcTail)->pwzValue, pwzData, 0);
212 ExitOnFailure(hr, "failed to allocate buffer for value");
213
214 // Get the component attributes
215 hr = WcaGetRecordInteger(hRec, xfqCompAttributes, &(*ppxfcTail)->iCompAttributes);
216 ExitOnFailure(hr, "failed to get component attributes for XmlConfig: %ls", (*ppxfcTail)->wzId);
217 }
218
219 // if we looped through all records all is well
220 if (E_NOMOREITEMS == hr)
221 {
222 hr = S_OK;
223 }
224 ExitOnFailure(hr, "failed while looping through all objects to secure");
225
226LExit:
227 ReleaseStr(pwzData);
228
229 return hr;
230}
231
232static HRESULT ProcessChanges(
233 __inout XML_CONFIG_CHANGE** ppxfcHead
234 )
235{
236 Assert(ppxfcHead && *ppxfcHead);
237 HRESULT hr = S_OK;
238
239 XML_CONFIG_CHANGE* pxfc = NULL;
240 XML_CONFIG_CHANGE* pxfcNext = NULL;
241 XML_CONFIG_CHANGE* pxfcCheck = NULL;
242 int cAdditionalChanges = 0;
243 XML_CONFIG_CHANGE* pxfcLast = NULL;
244
245 // If there's only one item in the list, none of this matters
246 if (pxfc && !pxfc->pxfcNext)
247 {
248 ExitFunction();
249 }
250
251 // Loop through the list
252 pxfc = *ppxfcHead;
253 while (pxfc)
254 {
255 // Keep track of where our next spot will be since our current node may be moved
256 pxfcNext = pxfc->pxfcNext;
257
258 // With each node, check to see if it's element path matches the Id of some other node in the list
259 pxfcCheck = *ppxfcHead;
260 while (pxfcCheck)
261 {
262 if (0 == lstrcmpW(pxfc->pwzElementPath, pxfcCheck->wzId) && 0 == pxfc->iXmlFlags
263 && XMLCONFIG_CREATE & pxfcCheck->iXmlFlags && XMLCONFIG_ELEMENT & pxfcCheck->iXmlFlags)
264 {
265 // We found a match. First, take it out of the current list
266 if (pxfc->pxfcPrev)
267 {
268 pxfc->pxfcPrev->pxfcNext = pxfc->pxfcNext;
269 }
270 else // it was the head. Update the head
271 {
272 *ppxfcHead = pxfc->pxfcNext;
273 }
274
275 if (pxfc->pxfcNext)
276 {
277 pxfc->pxfcNext->pxfcPrev = pxfc->pxfcPrev;
278 }
279
280 pxfc->pxfcNext = NULL;
281 pxfc->pxfcPrev = NULL;
282
283 // Now, add this node to the end of the matched node's additional changes list
284 if (!pxfcCheck->pxfcAdditionalChanges)
285 {
286 pxfcCheck->pxfcAdditionalChanges = pxfc;
287 pxfcCheck->cAdditionalChanges = 1;
288 }
289 else
290 {
291 pxfcLast = pxfcCheck->pxfcAdditionalChanges;
292 cAdditionalChanges = 1;
293 while (pxfcLast->pxfcNext)
294 {
295 pxfcLast = pxfcLast->pxfcNext;
296 ++cAdditionalChanges;
297 }
298 pxfcLast->pxfcNext = pxfc;
299 pxfc->pxfcPrev = pxfcLast;
300 pxfcCheck->cAdditionalChanges = ++cAdditionalChanges;
301 }
302 }
303
304 pxfcCheck = pxfcCheck->pxfcNext;
305 }
306
307 pxfc = pxfcNext;
308 }
309
310LExit:
311
312 return hr;
313}
314
315
316static HRESULT BeginChangeFile(
317 __in LPCWSTR pwzFile,
318 __in int iCompAttributes,
319 __inout LPWSTR* ppwzCustomActionData
320 )
321{
322 Assert(pwzFile && *pwzFile && ppwzCustomActionData);
323
324 HRESULT hr = S_OK;
325 BOOL fIs64Bit = iCompAttributes & msidbComponentAttributes64bit;
326
327 LPBYTE pbData = NULL;
328 DWORD cbData = 0;
329
330 LPWSTR pwzRollbackCustomActionData = NULL;
331
332 if (fIs64Bit)
333 {
334 hr = WcaWriteIntegerToCaData((int)xaOpenFilex64, ppwzCustomActionData);
335 ExitOnFailure(hr, "failed to write 64-bit file indicator to custom action data");
336 }
337 else
338 {
339 hr = WcaWriteIntegerToCaData((int)xaOpenFile, ppwzCustomActionData);
340 ExitOnFailure(hr, "failed to write file indicator to custom action data");
341 }
342
343 hr = WcaWriteStringToCaData(pwzFile, ppwzCustomActionData);
344 ExitOnFailure(hr, "failed to write file to custom action data: %ls", pwzFile);
345
346 // If the file already exits, then we have to put it back the way it was on failure
347 if (FileExistsEx(pwzFile, NULL))
348 {
349 hr = FileRead(&pbData, &cbData, pwzFile);
350 ExitOnFailure(hr, "failed to read file: %ls", pwzFile);
351
352 // Set up the rollback for this file
353 hr = WcaWriteIntegerToCaData((int)fIs64Bit, &pwzRollbackCustomActionData);
354 ExitOnFailure(hr, "failed to write component bitness to rollback custom action data");
355
356 hr = WcaWriteStringToCaData(pwzFile, &pwzRollbackCustomActionData);
357 ExitOnFailure(hr, "failed to write file name to rollback custom action data: %ls", pwzFile);
358
359 hr = WcaWriteStreamToCaData(pbData, cbData, &pwzRollbackCustomActionData);
360 ExitOnFailure(hr, "failed to write file contents to rollback custom action data.");
361
362 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"ExecXmlConfigRollback"), pwzRollbackCustomActionData, COST_XMLFILE);
363 ExitOnFailure(hr, "failed to schedule ExecXmlConfigRollback for file: %ls", pwzFile);
364
365 ReleaseStr(pwzRollbackCustomActionData);
366 }
367LExit:
368 ReleaseMem(pbData);
369
370 return hr;
371}
372
373
374static HRESULT WriteChangeData(
375 __in XML_CONFIG_CHANGE* pxfc,
376 __in eXmlAction action,
377 __inout LPWSTR* ppwzCustomActionData
378 )
379{
380 Assert(pxfc && ppwzCustomActionData);
381
382 HRESULT hr = S_OK;
383 XML_CONFIG_CHANGE* pxfcAdditionalChanges = NULL;
384
385 hr = WcaWriteStringToCaData(pxfc->pwzElementPath, ppwzCustomActionData);
386 ExitOnFailure(hr, "failed to write ElementPath to custom action data: %ls", pxfc->pwzElementPath);
387
388 hr = WcaWriteStringToCaData(pxfc->pwzVerifyPath, ppwzCustomActionData);
389 ExitOnFailure(hr, "failed to write VerifyPath to custom action data: %ls", pxfc->pwzVerifyPath);
390
391 hr = WcaWriteStringToCaData(pxfc->wzName, ppwzCustomActionData);
392 ExitOnFailure(hr, "failed to write Name to custom action data: %ls", pxfc->wzName);
393
394 hr = WcaWriteStringToCaData(pxfc->pwzValue, ppwzCustomActionData);
395 ExitOnFailure(hr, "failed to write Value to custom action data: %ls", pxfc->pwzValue);
396
397 if (pxfc->iXmlFlags & XMLCONFIG_CREATE && pxfc->iXmlFlags & XMLCONFIG_ELEMENT && xaCreateElement == action && pxfc->pxfcAdditionalChanges)
398 {
399 hr = WcaWriteIntegerToCaData(pxfc->cAdditionalChanges, ppwzCustomActionData);
400 ExitOnFailure(hr, "failed to write additional changes value to custom action data");
401
402 pxfcAdditionalChanges = pxfc->pxfcAdditionalChanges;
403 while (pxfcAdditionalChanges)
404 {
405 Assert((0 == lstrcmpW(pxfcAdditionalChanges->wzComponent, pxfc->wzComponent)) && 0 == pxfcAdditionalChanges->iXmlFlags && (0 == lstrcmpW(pxfcAdditionalChanges->wzFile, pxfc->wzFile)));
406
407 hr = WcaWriteStringToCaData(pxfcAdditionalChanges->wzName, ppwzCustomActionData);
408 ExitOnFailure(hr, "failed to write Name to custom action data: %ls", pxfc->wzName);
409
410 hr = WcaWriteStringToCaData(pxfcAdditionalChanges->pwzValue, ppwzCustomActionData);
411 ExitOnFailure(hr, "failed to write Value to custom action data: %ls", pxfc->pwzValue);
412
413 pxfcAdditionalChanges = pxfcAdditionalChanges->pxfcNext;
414 }
415 }
416 else
417 {
418 hr = WcaWriteIntegerToCaData(0, ppwzCustomActionData);
419 ExitOnFailure(hr, "failed to write additional changes value to custom action data");
420 }
421
422LExit:
423 return hr;
424}
425
426
427/******************************************************************
428 SchedXmlConfig - entry point for XmlConfig Custom Action
429
430********************************************************************/
431extern "C" UINT __stdcall SchedXmlConfig(
432 __in MSIHANDLE hInstall
433 )
434{
435// AssertSz(FALSE, "debug SchedXmlConfig");
436
437 HRESULT hr = S_OK;
438 UINT er = ERROR_SUCCESS;
439
440 LPWSTR pwzCurrentFile = NULL;
441 BOOL fCurrentFileChanged = FALSE;
442
443 PMSIHANDLE hView = NULL;
444 PMSIHANDLE hRec = NULL;
445
446 XML_CONFIG_CHANGE* pxfcHead = NULL;
447 XML_CONFIG_CHANGE* pxfcTail = NULL; // TODO: do we need this any more?
448 XML_CONFIG_CHANGE* pxfc = NULL;
449
450 eXmlAction xa = xaUnknown;
451 eXmlPreserveDate xd;
452
453 LPWSTR pwzCustomActionData = NULL;
454
455 DWORD cFiles = 0;
456
457 // initialize
458 hr = WcaInitialize(hInstall, "SchedXmlConfig");
459 ExitOnFailure(hr, "failed to initialize");
460
461 hr = ReadXmlConfigTable(&pxfcHead, &pxfcTail);
462 MessageExitOnFailure(hr, msierrXmlConfigFailedRead, "failed to read XmlConfig table");
463
464 hr = ProcessChanges(&pxfcHead);
465 ExitOnFailure(hr, "failed to process XmlConfig changes");
466
467 // loop through all the xml configurations
468 for (pxfc = pxfcHead; pxfc; pxfc = pxfc->pxfcNext)
469 {
470 // If this is a different file, or the first file...
471 if (NULL == pwzCurrentFile || 0 != lstrcmpW(pwzCurrentFile, pxfc->wzFile))
472 {
473 // Remember the file we're currently working on
474 hr = StrAllocString(&pwzCurrentFile, pxfc->wzFile, 0);
475 ExitOnFailure(hr, "failed to copy file name");
476
477 fCurrentFileChanged = TRUE;
478 }
479
480 //
481 // Figure out what action to take
482 //
483 xa = xaUnknown;
484
485 // If it's being installed or reinstalled or uninstalled and that matches
486 // what we are doing then calculate the right action.
487 if ((XMLCONFIG_INSTALL & pxfc->iXmlFlags && (WcaIsInstalling(pxfc->isInstalled, pxfc->isAction) || WcaIsReInstalling(pxfc->isInstalled, pxfc->isAction))) ||
488 (XMLCONFIG_UNINSTALL & pxfc->iXmlFlags && WcaIsUninstalling(pxfc->isInstalled, pxfc->isAction)))
489 {
490 if (XMLCONFIG_CREATE & pxfc->iXmlFlags && XMLCONFIG_ELEMENT & pxfc->iXmlFlags)
491 {
492 xa = xaCreateElement;
493 }
494 else if (XMLCONFIG_DELETE & pxfc->iXmlFlags && XMLCONFIG_ELEMENT & pxfc->iXmlFlags)
495 {
496 xa = xaDeleteElement;
497 }
498 else if (XMLCONFIG_DELETE & pxfc->iXmlFlags && XMLCONFIG_VALUE & pxfc->iXmlFlags)
499 {
500 xa = xaDeleteValue;
501 }
502 else if (XMLCONFIG_CREATE & pxfc->iXmlFlags && XMLCONFIG_VALUE & pxfc->iXmlFlags)
503 {
504 xa = xaWriteValue;
505 }
506 else if (XMLCONFIG_CREATE & pxfc->iXmlFlags && XMLCONFIG_DOCUMENT & pxfc->iXmlFlags)
507 {
508 xa = xaWriteDocument;
509 }
510 else if (XMLCONFIG_DELETE & pxfc->iXmlFlags && XMLCONFIG_DOCUMENT & pxfc->iXmlFlags)
511 {
512 hr = E_INVALIDARG;
513 ExitOnFailure(hr, "Invalid flag configuration. Cannot delete a fragment node.");
514 }
515 }
516
517 if (XMLCONFIG_PRESERVE_MODIFIED & pxfc->iXmlFlags)
518 {
519 xd = xdPreserve;
520 }
521 else
522 {
523 xd= xdDontPreserve;
524 }
525
526 if (xaUnknown != xa)
527 {
528 if (fCurrentFileChanged)
529 {
530 hr = BeginChangeFile(pwzCurrentFile, pxfc->iCompAttributes, &pwzCustomActionData);
531 ExitOnFailure(hr, "failed to begin file change for file: %ls", pwzCurrentFile);
532
533 fCurrentFileChanged = FALSE;
534 ++cFiles;
535 }
536
537 hr = WcaWriteIntegerToCaData((int)xa, &pwzCustomActionData);
538 ExitOnFailure(hr, "failed to write action indicator custom action data");
539
540 hr = WcaWriteIntegerToCaData((int)xd, &pwzCustomActionData);
541 ExitOnFailure(hr, "failed to write Preserve Date indicator to custom action data");
542
543 hr = WriteChangeData(pxfc, xa, &pwzCustomActionData);
544 ExitOnFailure(hr, "failed to write change data");
545 }
546 }
547
548 // If we looped through all records all is well
549 if (E_NOMOREITEMS == hr)
550 {
551 hr = S_OK;
552 }
553 ExitOnFailure(hr, "failed while looping through all objects to secure");
554
555 // Schedule the custom action and add to progress bar
556 if (pwzCustomActionData && *pwzCustomActionData)
557 {
558 Assert(0 < cFiles);
559
560 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"ExecXmlConfig"), pwzCustomActionData, cFiles * COST_XMLFILE);
561 ExitOnFailure(hr, "failed to schedule ExecXmlConfig action");
562 }
563
564LExit:
565 ReleaseStr(pwzCurrentFile);
566 ReleaseStr(pwzCustomActionData);
567
568 FreeXmlConfigChangeList(pxfcHead);
569
570 if (FAILED(hr))
571 {
572 er = ERROR_INSTALL_FAILURE;
573 }
574 return WcaFinalize(er);
575}
576
577
578/******************************************************************
579 ExecXmlConfig - entry point for XmlConfig Custom Action
580
581*******************************************************************/
582extern "C" UINT __stdcall ExecXmlConfig(
583 __in MSIHANDLE hInstall
584 )
585{
586 //AssertSz(FALSE, "debug ExecXmlConfig");
587 HRESULT hr = S_OK;
588 HRESULT hrOpenFailure = S_OK;
589 UINT er = ERROR_SUCCESS;
590
591 BOOL fIsWow64Process = FALSE;
592 BOOL fIsFSRedirectDisabled = FALSE;
593 BOOL fPreserveDate = FALSE;
594
595 LPWSTR pwzCustomActionData = NULL;
596 LPWSTR pwzData = NULL;
597 LPWSTR pwzFile = NULL;
598 LPWSTR pwzElementPath = NULL;
599 LPWSTR pwzVerifyPath = NULL;
600 LPWSTR pwzName = NULL;
601 LPWSTR pwzValue = NULL;
602 LPWSTR pwz = NULL;
603 int cAdditionalChanges = 0;
604
605 IXMLDOMDocument* pixd = NULL;
606 IXMLDOMNode* pixn = NULL;
607 IXMLDOMNode* pixnVerify = NULL;
608 IXMLDOMNode* pixnNewNode = NULL;
609 IXMLDOMNode* pixnRemovedChild = NULL;
610
611 IXMLDOMDocument* pixdNew = NULL;
612 IXMLDOMElement* pixeNew = NULL;
613
614 FILETIME ft;
615
616 int id = IDRETRY;
617
618 eXmlAction xa;
619 eXmlPreserveDate xd;
620
621 // initialize
622 hr = WcaInitialize(hInstall, "ExecXmlConfig");
623 ExitOnFailure(hr, "failed to initialize");
624
625 hr = XmlInitialize();
626 ExitOnFailure(hr, "failed to initialize xml utilities");
627
628 hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
629 ExitOnFailure(hr, "failed to get CustomActionData");
630
631 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
632
633 pwz = pwzCustomActionData;
634
635 hr = WcaReadIntegerFromCaData(&pwz, (int*) &xa);
636 ExitOnFailure(hr, "failed to process CustomActionData");
637
638 // Initialize the Wow64 API - store the result in fWow64APIPresent
639 // If it fails, this doesn't warrant an error yet, because we only need the Wow64 API in some cases
640 WcaInitializeWow64();
641 fIsWow64Process = WcaIsWow64Process();
642
643 if (xaOpenFile != xa && xaOpenFilex64 != xa)
644 {
645 ExitOnFailure(hr = E_INVALIDARG, "invalid custom action data");
646 }
647
648 // loop through all the passed in data
649 while (pwz && *pwz)
650 {
651 hr = WcaReadStringFromCaData(&pwz, &pwzFile);
652 ExitOnFailure(hr, "failed to read file name from custom action data");
653
654 // Default to not preserve date, preserve it if any modifications require us to
655 fPreserveDate = FALSE;
656
657 // Open the file
658 ReleaseNullObject(pixd);
659
660 if (xaOpenFilex64 == xa)
661 {
662 if (!fIsWow64Process)
663 {
664 hr = E_NOTIMPL;
665 ExitOnFailure(hr, "Custom action was told to act on a 64-bit component, but the custom action process is not running in WOW.");
666 }
667
668 hr = WcaDisableWow64FSRedirection();
669 ExitOnFailure(hr, "Custom action was told to act on a 64-bit component, but was unable to disable filesystem redirection through the Wow64 API.");
670
671 fIsFSRedirectDisabled = TRUE;
672 }
673
674 hr = XmlLoadDocumentFromFileEx(pwzFile, XML_LOAD_PRESERVE_WHITESPACE, &pixd);
675 if (FAILED(hr))
676 {
677 // 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.
678 hrOpenFailure = hr;
679 hr = S_OK;
680 }
681 else
682 {
683 hrOpenFailure = S_OK;
684 }
685
686 WcaLog(LOGMSG_VERBOSE, "Configuring Xml File: %ls", pwzFile);
687
688 while (pwz && *pwz)
689 {
690 // If we skip past an element that has additional changes we need to strip them off the stream before
691 // moving on to the next element. Do that now and then restart the outer loop.
692 if (cAdditionalChanges > 0)
693 {
694 while (cAdditionalChanges > 0)
695 {
696 hr = WcaReadStringFromCaData(&pwz, &pwzName);
697 ExitOnFailure(hr, "failed to process CustomActionData");
698 hr = WcaReadStringFromCaData(&pwz, &pwzValue);
699 ExitOnFailure(hr, "failed to process CustomActionData");
700
701 cAdditionalChanges--;
702 }
703 continue;
704 }
705
706 hr = WcaReadIntegerFromCaData(&pwz, (int*) &xa);
707 ExitOnFailure(hr, "failed to process CustomActionData");
708
709 // Break if we need to move on to a different file
710 if (xaOpenFile == xa || xaOpenFilex64 == xa)
711 {
712 break;
713 }
714
715 hr = WcaReadIntegerFromCaData(&pwz, (int*) &xd);
716 ExitOnFailure(hr, "failed to process CustomActionData");
717
718 if (xdPreserve == xd)
719 {
720 fPreserveDate = TRUE;
721 }
722
723 // Get path, name, and value to be written
724 hr = WcaReadStringFromCaData(&pwz, &pwzElementPath);
725 ExitOnFailure(hr, "failed to process CustomActionData");
726 hr = WcaReadStringFromCaData(&pwz, &pwzVerifyPath);
727 ExitOnFailure(hr, "failed to process CustomActionData");
728 hr = WcaReadStringFromCaData(&pwz, &pwzName);
729 ExitOnFailure(hr, "failed to process CustomActionData");
730 hr = WcaReadStringFromCaData(&pwz, &pwzValue);
731 ExitOnFailure(hr, "failed to process CustomActionData");
732 hr = WcaReadIntegerFromCaData(&pwz, &cAdditionalChanges);
733 ExitOnFailure(hr, "failed to process CustomActionData");
734
735 // 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.
736 if (FAILED(hrOpenFailure))
737 {
738 if (xaCreateElement == xa || xaWriteValue == xa || xaWriteDocument == xa)
739 {
740 MessageExitOnFailure(hr = hrOpenFailure, msierrXmlConfigFailedOpen, "failed to load XML file: %ls", pwzFile);
741 }
742 else
743 {
744 continue;
745 }
746 }
747
748 // Select the node we're about to modify
749 ReleaseNullObject(pixn);
750
751 hr = XmlSelectSingleNode(pixd, pwzElementPath, &pixn);
752
753 // If we failed to find the node that we are going to add to, we've got a problem. Otherwise, just continue since the node's already gone.
754 if (S_FALSE == hr)
755 {
756 if (xaCreateElement == xa || xaWriteValue == xa || xaWriteDocument == xa)
757 {
758 hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND);
759 }
760 else
761 {
762 hr = S_OK;
763 continue;
764 }
765 }
766
767 MessageExitOnFailure(hr, msierrXmlConfigFailedSelect, "failed to find node: %ls in XML file: %ls", pwzElementPath, pwzFile);
768
769 // Make the modification
770 switch (xa)
771 {
772 case xaWriteValue:
773 if (pwzName && *pwzName)
774 {
775 // We're setting an attribute
776 hr = XmlSetAttribute(pixn, pwzName, pwzValue);
777 ExitOnFailure(hr, "failed to set attribute: %ls to value %ls", pwzName, pwzValue);
778 }
779 else
780 {
781 // We're setting the text of the node
782 hr = XmlSetText(pixn, pwzValue);
783 ExitOnFailure(hr, "failed to set text to: %ls for element %ls. Make sure that XPath points to an element.", pwzValue, pwzElementPath);
784 }
785 break;
786 case xaWriteDocument:
787 if (NULL != pwzVerifyPath && 0 != pwzVerifyPath[0])
788 {
789 hr = XmlSelectSingleNode(pixn, pwzVerifyPath, &pixnVerify);
790 if (S_OK == hr)
791 {
792 // We found the verify path which means we have no further work to do
793 continue;
794 }
795 ExitOnFailure(hr, "failed to query verify path: %ls", pwzVerifyPath);
796 }
797
798 hr = XmlLoadDocumentEx(pwzValue, XML_LOAD_PRESERVE_WHITESPACE, &pixdNew);
799 ExitOnFailure(hr, "Failed to load value as document.");
800
801 hr = pixdNew->get_documentElement(&pixeNew);
802 ExitOnFailure(hr, "Failed to get document element.");
803
804 hr = pixn->appendChild(pixeNew, NULL);
805 ExitOnFailure(hr, "Failed to append document element on to parent element.");
806
807 ReleaseNullObject(pixeNew);
808 ReleaseNullObject(pixdNew);
809 break;
810
811 case xaCreateElement:
812 if (NULL != pwzVerifyPath && 0 != pwzVerifyPath[0])
813 {
814 hr = XmlSelectSingleNode(pixn, pwzVerifyPath, &pixnVerify);
815 if (S_OK == hr)
816 {
817 // We found the verify path which means we have no further work to do
818 continue;
819 }
820 ExitOnFailure(hr, "failed to query verify path: %ls", pwzVerifyPath);
821 }
822
823 hr = XmlCreateChild(pixn, pwzName, &pixnNewNode);
824 ExitOnFailure(hr, "failed to create child element: %ls", pwzName);
825
826 if (pwzValue && *pwzValue)
827 {
828 hr = XmlSetText(pixnNewNode, pwzValue);
829 ExitOnFailure(hr, "failed to set text to: %ls for node: %ls", pwzValue, pwzName);
830 }
831
832 while (cAdditionalChanges > 0)
833 {
834 hr = WcaReadStringFromCaData(&pwz, &pwzName);
835 ExitOnFailure(hr, "failed to process CustomActionData");
836 hr = WcaReadStringFromCaData(&pwz, &pwzValue);
837 ExitOnFailure(hr, "failed to process CustomActionData");
838
839 // Set the additional attribute
840 hr = XmlSetAttribute(pixnNewNode, pwzName, pwzValue);
841 ExitOnFailure(hr, "failed to set attribute: %ls to value %ls", pwzName, pwzValue);
842
843 cAdditionalChanges--;
844 }
845
846 ReleaseNullObject(pixnNewNode);
847 break;
848 case xaDeleteValue:
849 if (pwzName && *pwzName)
850 {
851 // Delete the attribute
852 hr = XmlRemoveAttribute(pixn, pwzName);
853 ExitOnFailure(hr, "failed to remove attribute: %ls", pwzName);
854 }
855 else
856 {
857 // Clear the text value for the node
858 hr = XmlSetText(pixn, L"");
859 ExitOnFailure(hr, "failed to clear text value");
860 }
861 break;
862 case xaDeleteElement:
863 if (NULL != pwzVerifyPath && 0 != pwzVerifyPath[0])
864 {
865 hr = XmlSelectSingleNode(pixn, pwzVerifyPath, &pixnVerify);
866 if (S_OK == hr)
867 {
868 hr = pixn->removeChild(pixnVerify, &pixnRemovedChild);
869 ExitOnFailure(hr, "failed to remove created child element");
870
871 ReleaseNullObject(pixnRemovedChild);
872 }
873 else
874 {
875 WcaLog(LOGMSG_VERBOSE, "Failed to select path %ls for deleting. Skipping...", pwzVerifyPath);
876 hr = S_OK;
877 }
878 }
879 else
880 {
881 // TODO: This requires a VerifyPath to delete an element. Should we support not having one?
882 WcaLog(LOGMSG_VERBOSE, "No VerifyPath specified for delete element of ID: %ls", pwzElementPath);
883 }
884 break;
885 default:
886 ExitOnFailure(hr = E_UNEXPECTED, "Invalid modification specified in custom action data");
887 break;
888 }
889 }
890
891
892 // Now that we've made all of the changes to this file, save it and move on to the next
893 if (S_OK == hrOpenFailure)
894 {
895 if (fPreserveDate)
896 {
897 hr = FileGetTime(pwzFile, NULL, NULL, &ft);
898 ExitOnFailure(hr, "failed to get modified time of file : %ls", pwzFile);
899 }
900
901 int iSaveAttempt = 0;
902
903 do
904 {
905 hr = XmlSaveDocument(pixd, pwzFile);
906 if (FAILED(hr))
907 {
908 id = WcaErrorMessage(msierrXmlConfigFailedSave, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 1, pwzFile);
909 switch (id)
910 {
911 case IDABORT:
912 ExitOnFailure(hr, "Failed to save changes to XML file: %ls", pwzFile);
913 case IDRETRY:
914 hr = S_FALSE; // hit me, baby, one more time
915 break;
916 case IDIGNORE:
917 hr = S_OK; // pretend everything is okay and bail
918 break;
919 case 0: // No UI case, MsiProcessMessage returns 0
920 if (STIERR_SHARING_VIOLATION == hr)
921 {
922 // Only in case of sharing violation do we retry 30 times, once a second.
923 if (iSaveAttempt < 30)
924 {
925 hr = S_FALSE;
926 ++iSaveAttempt;
927 WcaLog(LOGMSG_VERBOSE, "Unable to save changes to XML file: %ls, retry attempt: %x", pwzFile, iSaveAttempt);
928 Sleep(1000);
929 }
930 else
931 {
932 ExitOnFailure(hr, "Failed to save changes to XML file: %ls", pwzFile);
933 }
934 }
935 break;
936 default: // Unknown error
937 ExitOnFailure(hr, "Failed to save changes to XML file: %ls", pwzFile);
938 }
939 }
940 } while (S_FALSE == hr);
941
942 if (fPreserveDate)
943 {
944 hr = FileSetTime(pwzFile, NULL, NULL, &ft);
945 ExitOnFailure(hr, "failed to set modified time of file : %ls", pwzFile);
946 }
947
948 if (fIsFSRedirectDisabled)
949 {
950 fIsFSRedirectDisabled = FALSE;
951 WcaRevertWow64FSRedirection();
952 }
953 }
954 }
955
956LExit:
957 // Make sure we revert FS Redirection if necessary before exiting
958 if (fIsFSRedirectDisabled)
959 {
960 fIsFSRedirectDisabled = FALSE;
961 WcaRevertWow64FSRedirection();
962 }
963 WcaFinalizeWow64();
964
965 ReleaseStr(pwzCustomActionData);
966 ReleaseStr(pwzData);
967 ReleaseStr(pwzFile);
968 ReleaseStr(pwzElementPath);
969 ReleaseStr(pwzVerifyPath);
970 ReleaseStr(pwzName);
971 ReleaseStr(pwzValue);
972
973 ReleaseObject(pixeNew);
974 ReleaseObject(pixdNew);
975
976 ReleaseObject(pixn);
977 ReleaseObject(pixd);
978 ReleaseObject(pixnNewNode);
979 ReleaseObject(pixnRemovedChild);
980
981 XmlUninitialize();
982
983 if (FAILED(hr))
984 {
985 er = ERROR_INSTALL_FAILURE;
986 }
987 return WcaFinalize(er);
988}
989
990
991/******************************************************************
992 ExecXmlConfigRollback - entry point for XmlConfig rollback Custom Action
993
994*******************************************************************/
995extern "C" UINT __stdcall ExecXmlConfigRollback(
996 __in MSIHANDLE hInstall
997 )
998{
999// AssertSz(FALSE, "debug ExecXmlConfigRollback");
1000 HRESULT hr = S_OK;
1001 UINT er = ERROR_SUCCESS;
1002
1003 int iIs64Bit;
1004 BOOL fIs64Bit = FALSE;
1005
1006 LPWSTR pwzCustomActionData = NULL;
1007 LPWSTR pwz = NULL;
1008 LPWSTR pwzFileName = NULL;
1009 LPBYTE pbData = NULL;
1010 DWORD_PTR cbData = 0;
1011 DWORD cbDataWritten = 0;
1012
1013 FILETIME ft;
1014
1015 HANDLE hFile = INVALID_HANDLE_VALUE;
1016
1017 // initialize
1018 hr = WcaInitialize(hInstall, "ExecXmlConfigRollback");
1019 ExitOnFailure(hr, "failed to initialize");
1020
1021
1022 hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
1023 ExitOnFailure(hr, "failed to get CustomActionData");
1024
1025 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
1026
1027 pwz = pwzCustomActionData;
1028
1029 hr = WcaReadIntegerFromCaData(&pwz, &iIs64Bit);
1030 ExitOnFailure(hr, "failed to read component bitness from custom action data");
1031
1032 hr = WcaReadStringFromCaData(&pwz, &pwzFileName);
1033 ExitOnFailure(hr, "failed to read file name from custom action data");
1034
1035 hr = WcaReadStreamFromCaData(&pwz, &pbData, &cbData);
1036 ExitOnFailure(hr, "failed to read file contents from custom action data");
1037
1038 fIs64Bit = (BOOL)iIs64Bit;
1039
1040 if (fIs64Bit)
1041 {
1042 hr = WcaInitializeWow64();
1043 if (S_FALSE == hr)
1044 {
1045 hr = TYPE_E_DLLFUNCTIONNOTFOUND;
1046 }
1047 ExitOnFailure(hr, "failed to initialize Wow64 API");
1048
1049 if (!WcaIsWow64Process())
1050 {
1051 hr = E_NOTIMPL;
1052 ExitOnFailure(hr, "Custom action was told to rollback a 64-bit component, but the Wow64 API is unavailable.");
1053 }
1054
1055 hr = WcaDisableWow64FSRedirection();
1056 ExitOnFailure(hr, "Custom action was told to rollback a 64-bit component, but was unable to Disable Filesystem Redirection through the Wow64 API.");
1057 }
1058
1059 hr = FileGetTime(pwzFileName, NULL, NULL, &ft);
1060 ExitOnFailure(hr, "Failed to get modified date of file %ls.", pwzFileName);
1061
1062 // Open the file
1063 hFile = ::CreateFileW(pwzFileName, GENERIC_WRITE, NULL, NULL, TRUNCATE_EXISTING, NULL, NULL);
1064 ExitOnInvalidHandleWithLastError(hFile, hr, "failed to open file: %ls", pwzFileName);
1065
1066 // Write out the old data
1067 if (!::WriteFile(hFile, pbData, (DWORD)cbData, &cbDataWritten, NULL))
1068 {
1069 ExitOnLastError(hr, "failed to write to file: %ls", pwzFileName);
1070 }
1071
1072 Assert(cbData == cbDataWritten);
1073
1074 ReleaseFile(hFile);
1075
1076 hr = FileSetTime(pwzFileName, NULL, NULL, &ft);
1077 ExitOnFailure(hr, "Failed to set modified date of file %ls.", pwzFileName);
1078
1079LExit:
1080 ReleaseStr(pwzCustomActionData);
1081 ReleaseStr(pwzFileName);
1082
1083 ReleaseFile(hFile);
1084
1085 if (fIs64Bit)
1086 {
1087 WcaRevertWow64FSRedirection();
1088 WcaFinalizeWow64();
1089 }
1090
1091 ReleaseMem(pbData);
1092
1093 if (FAILED(hr))
1094 {
1095 er = ERROR_INSTALL_FAILURE;
1096 }
1097 return WcaFinalize(er);
1098}
1099
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
diff --git a/src/ca/caSuffix.h b/src/ca/caSuffix.h
new file mode 100644
index 00000000..303a99e9
--- /dev/null
+++ b/src/ca/caSuffix.h
@@ -0,0 +1,11 @@
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#if defined _WIN64
6#define PLATFORM_DECORATION(f) f L"_64"
7#elif defined ARM
8#define PLATFORM_DECORATION(f) f L"_ARM"
9#else
10#define PLATFORM_DECORATION(f) f
11#endif
diff --git a/src/ca/cost.h b/src/ca/cost.h
new file mode 100644
index 00000000..6507e85d
--- /dev/null
+++ b/src/ca/cost.h
@@ -0,0 +1,9 @@
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
5const UINT COST_SECUREOBJECT = 1000;
6const UINT COST_SERVICECONFIG = 1000;
7const UINT COST_XMLFILE = 1000;
8const UINT COST_CLOSEAPP = 500;
9const UINT COST_INTERNETSHORTCUT = 2000;
diff --git a/src/ca/exitearlywithsuccess.cpp b/src/ca/exitearlywithsuccess.cpp
new file mode 100644
index 00000000..00828329
--- /dev/null
+++ b/src/ca/exitearlywithsuccess.cpp
@@ -0,0 +1,27 @@
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/******************************************************************
7WixExitEarlyWithSuccess - entry point for WixExitEarlyWithSuccess
8 custom action which does nothing except return exit code
9 ERROR_NO_MORE_ITEMS. The Windows Installer documentation at
10 http://msdn.microsoft.com/library/aa368072.aspx indicates that
11 this exit code is not treated as an error. This will cause a
12 calling application to receive a successful return code if
13 this custom action executes. This can be useful for backwards
14 compatibility when an application redistributes an MSI and
15 a future major upgrade is released for that MSI. It should be
16 conditioned on a property set by an entry in the Upgrade table
17 of the MSI that detects newer major upgrades of the same MSI
18 already installed on the system. It should be scheduled after
19 the FindRelatedProducts action so that the property will be
20 set if appropriate.
21********************************************************************/
22extern "C" UINT __stdcall WixExitEarlyWithSuccess(
23 __in MSIHANDLE /*hInstall*/
24 )
25{
26 return ERROR_NO_MORE_ITEMS;
27}
diff --git a/src/ca/netshortcuts.cpp b/src/ca/netshortcuts.cpp
new file mode 100644
index 00000000..59ef838b
--- /dev/null
+++ b/src/ca/netshortcuts.cpp
@@ -0,0 +1,434 @@
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
5LPCWSTR vcsShortcutsQuery =
6 L"SELECT `Component_`, `Directory_`, `Name`, `Target`, `Attributes`, `IconFile`, `IconIndex` "
7 L"FROM `WixInternetShortcut`";
8enum eShortcutsQuery { esqComponent = 1, esqDirectory, esqFilename, esqTarget, esqAttributes, esqIconFile, esqIconIndex };
9enum eShortcutsAttributes { esaLink = 0, esaURL = 1 };
10
11/******************************************************************
12 WixSchedInternetShortcuts - entry point
13
14********************************************************************/
15extern "C" UINT __stdcall WixSchedInternetShortcuts(
16 __in MSIHANDLE hInstall
17 )
18{
19 HRESULT hr = S_OK;
20 UINT er = ERROR_SUCCESS;
21
22 UINT uiCost = 0;
23
24 PMSIHANDLE hView = NULL;
25 PMSIHANDLE hRec = NULL;
26
27 MSIHANDLE hCreateFolderTable = NULL;
28 MSIHANDLE hCreateFolderColumns = NULL;
29
30 LPWSTR pwzCustomActionData = NULL;
31 LPWSTR pwzComponent = NULL;
32 LPWSTR pwzDirectory = NULL;
33 LPWSTR pwzFilename = NULL;
34 LPWSTR pwzTarget = NULL;
35 LPWSTR pwzShortcutPath = NULL;
36 int iAttr = 0;
37 LPWSTR pwzIconFile = NULL;
38 int iIconIndex = 0;
39 IUniformResourceLocatorW* piURL = NULL;
40 IShellLinkW* piShellLink = NULL;
41 BOOL fInitializedCom = FALSE;
42
43 hr = WcaInitialize(hInstall, "WixSchedInternetShortcuts");
44 ExitOnFailure(hr, "failed to initialize WixSchedInternetShortcuts.");
45
46 // anything to do?
47 if (S_OK != WcaTableExists(L"WixInternetShortcut"))
48 {
49 WcaLog(LOGMSG_STANDARD, "WixInternetShortcut table doesn't exist, so there are no Internet shortcuts to process");
50 goto LExit;
51 }
52
53 // check to see if we can create a shortcut - Server Core and others may not have a shell registered.
54 hr = ::CoInitialize(NULL);
55 ExitOnFailure(hr, "failed to initialize COM");
56 fInitializedCom = TRUE;
57
58 hr = ::CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_ALL, IID_IUniformResourceLocatorW, (void**)&piURL);
59 if (S_OK != hr)
60 {
61 WcaLog(LOGMSG_STANDARD, "failed to create an instance of IUniformResourceLocatorW, skipping shortcut creation");
62 ExitFunction1(hr = S_OK);
63 }
64
65 hr = ::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_ALL, IID_IShellLinkW, (void**)&piShellLink);
66 if (S_OK != hr)
67 {
68 WcaLog(LOGMSG_STANDARD, "failed to create an instance of IShellLinkW, skipping shortcut creation");
69 ExitFunction1(hr = S_OK);
70 }
71
72 // query and loop through all the shortcuts
73 hr = WcaOpenExecuteView(vcsShortcutsQuery, &hView);
74 ExitOnFailure(hr, "failed to open view on WixInternetShortcut table");
75
76 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
77 {
78 // read column values
79 hr = WcaGetRecordString(hRec, esqComponent, &pwzComponent);
80 ExitOnFailure(hr, "failed to get shortcut component");
81 hr = WcaGetRecordString(hRec, esqDirectory, &pwzDirectory);
82 ExitOnFailure(hr, "failed to get shortcut directory");
83 hr = WcaGetRecordString(hRec, esqFilename, &pwzFilename);
84 ExitOnFailure(hr, "failed to get shortcut filename");
85 hr = WcaGetRecordFormattedString(hRec, esqTarget, &pwzTarget);
86 ExitOnFailure(hr, "failed to get shortcut target");
87 hr = WcaGetRecordInteger(hRec, esqAttributes, &iAttr);
88 ExitOnFailure(hr, "failed to get shortcut attributes");
89 hr = WcaGetRecordFormattedString(hRec, esqIconFile, &pwzIconFile);
90 ExitOnFailure(hr, "failed to get shortcut icon file");
91 hr = WcaGetRecordInteger(hRec, esqIconIndex, &iIconIndex);
92 ExitOnFailure(hr, "failed to get shortcut icon index");
93
94 // skip processing this WixInternetShortcut row if the component isn't being configured
95 WCA_TODO todo = WcaGetComponentToDo(pwzComponent);
96 if (WCA_TODO_UNKNOWN == todo)
97 {
98 WcaLog(LOGMSG_VERBOSE, "Skipping shortcut for null-action component '%ls'", pwzComponent);
99 continue;
100 }
101
102 // we need to create the directory where the shortcut is supposed to live; rather
103 // than doing so in our deferred custom action, use the CreateFolder table to have MSI
104 // make (and remove) them on our behalf (including the correct cleanup of parent directories).
105 MSIDBERROR dbError = MSIDBERROR_NOERROR;
106 WcaLog(LOGMSG_STANDARD, "Adding folder '%ls', component '%ls' to the CreateFolder table", pwzDirectory, pwzComponent);
107 hr = WcaAddTempRecord(&hCreateFolderTable, &hCreateFolderColumns, L"CreateFolder", &dbError, 0, 2, pwzDirectory, pwzComponent);
108 if (MSIDBERROR_DUPLICATEKEY == dbError)
109 {
110 WcaLog(LOGMSG_STANDARD, "Folder '%ls' already exists in the CreateFolder table; the above error is harmless", pwzDirectory);
111 hr = S_OK;
112 }
113 ExitOnFailure(hr, "Couldn't add temporary CreateFolder row");
114
115 // only if we're installing/reinstalling do we need to schedule the deferred CA
116 // (uninstallation is handled via permanent RemoveFile rows and temporary CreateFolder rows)
117 if (WCA_TODO_INSTALL == todo || WCA_TODO_REINSTALL == todo)
118 {
119 // turn the Directory_ id into a path
120 hr = WcaGetTargetPath(pwzDirectory, &pwzShortcutPath);
121 ExitOnFailure(hr, "failed to allocate string for shortcut directory");
122
123 // append the shortcut filename
124 hr = StrAllocConcat(&pwzShortcutPath, pwzFilename, 0);
125 ExitOnFailure(hr, "failed to allocate string for shortcut filename");
126
127 // write the shortcut path and target to custom action data for deferred CAs
128 hr = WcaWriteStringToCaData(pwzShortcutPath, &pwzCustomActionData);
129 ExitOnFailure(hr, "failed to write shortcut path to custom action data");
130 hr = WcaWriteStringToCaData(pwzTarget, &pwzCustomActionData);
131 ExitOnFailure(hr, "failed to write shortcut target to custom action data");
132 hr = WcaWriteIntegerToCaData(iAttr, &pwzCustomActionData);
133 ExitOnFailure(hr, "failed to write shortcut attributes to custom action data");
134 hr = WcaWriteStringToCaData(pwzIconFile, &pwzCustomActionData);
135 ExitOnFailure(hr, "failed to write icon file to custom action data");
136 hr = WcaWriteIntegerToCaData(iIconIndex, &pwzCustomActionData);
137 ExitOnFailure(hr, "failed to write icon index to custom action data");
138
139 uiCost += COST_INTERNETSHORTCUT;
140 }
141 }
142
143 if (E_NOMOREITEMS == hr)
144 {
145 hr = S_OK;
146 }
147 ExitOnFailure(hr, "Failure occured while processing WixInternetShortcut table");
148
149 // if we have any shortcuts to install
150 if (pwzCustomActionData && *pwzCustomActionData)
151 {
152 // add cost to progress bar
153 hr = WcaProgressMessage(uiCost, TRUE);
154 ExitOnFailure(hr, "failed to extend progress bar for InternetShortcuts");
155
156 // provide custom action data to deferred and rollback CAs
157 hr = WcaSetProperty(PLATFORM_DECORATION(L"WixRollbackInternetShortcuts"), pwzCustomActionData);
158 ExitOnFailure(hr, "failed to set WixRollbackInternetShortcuts rollback custom action data");
159 hr = WcaSetProperty(PLATFORM_DECORATION(L"WixCreateInternetShortcuts"), pwzCustomActionData);
160 ExitOnFailure(hr, "failed to set WixCreateInternetShortcuts custom action data");
161 }
162
163LExit:
164 if (hCreateFolderTable)
165 {
166 ::MsiCloseHandle(hCreateFolderTable);
167 }
168
169 if (hCreateFolderColumns)
170 {
171 ::MsiCloseHandle(hCreateFolderColumns);
172 }
173
174 ReleaseStr(pwzCustomActionData);
175 ReleaseStr(pwzComponent);
176 ReleaseStr(pwzDirectory);
177 ReleaseStr(pwzFilename);
178 ReleaseStr(pwzTarget);
179 ReleaseStr(pwzShortcutPath);
180 ReleaseObject(piShellLink);
181 ReleaseObject(piURL);
182
183 if (fInitializedCom)
184 {
185 ::CoUninitialize();
186 }
187
188 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
189 return WcaFinalize(er);
190}
191
192
193
194/******************************************************************
195 CreateUrl - Creates a shortcut via IUniformResourceLocatorW
196
197*******************************************************************/
198static HRESULT CreateUrl(
199 __in_z LPCWSTR wzTarget,
200 __in_z LPCWSTR wzShortcutPath,
201 __in_z_opt LPCWSTR wzIconPath,
202 __in int iconIndex
203)
204{
205 HRESULT hr = S_OK;
206 IUniformResourceLocatorW* piURL = NULL;
207 IPersistFile* piPersistFile = NULL;
208 IPropertySetStorage* piProperties = NULL;
209 IPropertyStorage* piStorage = NULL;
210
211 // create an internet shortcut object
212 WcaLog(LOGMSG_STANDARD, "Creating IUniformResourceLocatorW shortcut '%ls' target '%ls'", wzShortcutPath, wzTarget);
213 hr = ::CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_ALL, IID_IUniformResourceLocatorW, (void**)&piURL);
214 ExitOnFailure(hr, "failed to create an instance of IUniformResourceLocatorW");
215
216 // set shortcut target
217 hr = piURL->SetURL(wzTarget, 0);
218 ExitOnFailure(hr, "failed to set shortcut '%ls' target '%ls'", wzShortcutPath, wzTarget);
219
220 if (wzIconPath)
221 {
222 hr = piURL->QueryInterface(IID_IPropertySetStorage, (void **)&piProperties);
223 ExitOnFailure(hr, "failed to get IPropertySetStorage for shortcut '%ls'", wzShortcutPath);
224
225 hr = piProperties->Open(FMTID_Intshcut, STGM_WRITE, &piStorage);
226 ExitOnFailure(hr, "failed to open storage for shortcut '%ls'", wzShortcutPath);
227
228 PROPSPEC ppids[2] = { {PRSPEC_PROPID, PID_IS_ICONINDEX}, {PRSPEC_PROPID, PID_IS_ICONFILE} };
229 PROPVARIANT ppvar[2];
230
231 PropVariantInit(ppvar);
232 PropVariantInit(ppvar + 1);
233
234 ppvar[0].vt = VT_I4;
235 ppvar[0].lVal = iconIndex;
236 ppvar[1].vt = VT_LPWSTR;
237 ppvar[1].pwszVal = (LPWSTR)wzIconPath;
238
239 hr = piStorage->WriteMultiple(2, ppids, ppvar, 0);
240 ExitOnFailure(hr, "failed to write icon storage for shortcut '%ls'", wzShortcutPath);
241
242 hr = piStorage->Commit(STGC_DEFAULT);
243 ExitOnFailure(hr, "failed to commit icon storage for shortcut '%ls'", wzShortcutPath);
244 }
245
246 // get an IPersistFile and save the shortcut
247 hr = piURL->QueryInterface(IID_IPersistFile, (void**)&piPersistFile);
248 ExitOnFailure(hr, "failed to get IPersistFile for shortcut '%ls'", wzShortcutPath);
249
250 hr = piPersistFile->Save(wzShortcutPath, TRUE);
251 ExitOnFailure(hr, "failed to save shortcut '%ls'", wzShortcutPath);
252
253LExit:
254 ReleaseObject(piPersistFile);
255 ReleaseObject(piURL);
256 ReleaseObject(piStorage);
257 ReleaseObject(piProperties);
258
259 return hr;
260}
261
262/******************************************************************
263 CreateLink - Creates a shortcut via IShellLinkW
264
265*******************************************************************/
266static HRESULT CreateLink(
267 __in_z LPCWSTR wzTarget,
268 __in_z LPCWSTR wzShortcutPath,
269 __in_z_opt LPCWSTR wzIconPath,
270 __in int iconIndex
271)
272{
273 HRESULT hr = S_OK;
274 IShellLinkW* piShellLink = NULL;
275 IPersistFile* piPersistFile = NULL;
276
277 // create an internet shortcut object
278 WcaLog(LOGMSG_STANDARD, "Creating IShellLinkW shortcut '%ls' target '%ls'", wzShortcutPath, wzTarget);
279 hr = ::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_ALL, IID_IShellLinkW, (void**)&piShellLink);
280 ExitOnFailure(hr, "failed to create an instance of IShellLinkW");
281
282 // set shortcut target
283 hr = piShellLink->SetPath(wzTarget);
284 ExitOnFailure(hr, "failed to set shortcut '%ls' target '%ls'", wzShortcutPath, wzTarget);
285
286 if (wzIconPath)
287 {
288 hr = piShellLink->SetIconLocation(wzIconPath, iconIndex);
289 ExitOnFailure(hr, "failed to set icon for shortcut '%ls'", wzShortcutPath);
290 }
291
292 // get an IPersistFile and save the shortcut
293 hr = piShellLink->QueryInterface(IID_IPersistFile, (void**)&piPersistFile);
294 ExitOnFailure(hr, "failed to get IPersistFile for shortcut '%ls'", wzShortcutPath);
295
296 hr = piPersistFile->Save(wzShortcutPath, TRUE);
297 ExitOnFailure(hr, "failed to save shortcut '%ls'", wzShortcutPath);
298
299LExit:
300 ReleaseObject(piPersistFile);
301 ReleaseObject(piShellLink);
302
303 return hr;
304}
305
306
307
308/******************************************************************
309 WixCreateInternetShortcuts - entry point for Internet shortcuts
310 custom action
311*******************************************************************/
312extern "C" UINT __stdcall WixCreateInternetShortcuts(
313 __in MSIHANDLE hInstall
314 )
315{
316 HRESULT hr = S_OK;
317 UINT er = ERROR_SUCCESS;
318
319 LPWSTR pwz = NULL;
320 LPWSTR pwzCustomActionData = NULL;
321 LPWSTR pwzTarget = NULL;
322 LPWSTR pwzShortcutPath = NULL;
323 LPWSTR pwzIconPath = NULL;
324 BOOL fInitializedCom = FALSE;
325 int iAttr = 0;
326 int iIconIndex = 0;
327
328 // initialize
329 hr = WcaInitialize(hInstall, "WixCreateInternetShortcuts");
330 ExitOnFailure(hr, "failed to initialize WixCreateInternetShortcuts");
331
332 hr = ::CoInitialize(NULL);
333 ExitOnFailure(hr, "failed to initialize COM");
334 fInitializedCom = TRUE;
335
336 // extract the custom action data
337 hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData);
338 ExitOnFailure(hr, "failed to get CustomActionData");
339
340 // loop through all the custom action data
341 pwz = pwzCustomActionData;
342 while (pwz && *pwz)
343 {
344 hr = WcaReadStringFromCaData(&pwz, &pwzShortcutPath);
345 ExitOnFailure(hr, "failed to read shortcut path from custom action data");
346 hr = WcaReadStringFromCaData(&pwz, &pwzTarget);
347 ExitOnFailure(hr, "failed to read shortcut target from custom action data");
348 hr = WcaReadIntegerFromCaData(&pwz, &iAttr);
349 ExitOnFailure(hr, "failed to read shortcut attributes from custom action data");
350 hr = WcaReadStringFromCaData(&pwz, &pwzIconPath);
351 ExitOnFailure(hr, "failed to read shortcut icon path from custom action data");
352 hr = WcaReadIntegerFromCaData(&pwz, &iIconIndex);
353 ExitOnFailure(hr, "failed to read shortcut icon index from custom action data");
354
355 if ((iAttr & esaURL) == esaURL)
356 {
357 hr = CreateUrl(pwzTarget, pwzShortcutPath, pwzIconPath, iIconIndex);
358 }
359 else
360 {
361 hr = CreateLink(pwzTarget, pwzShortcutPath, pwzIconPath, iIconIndex);
362 }
363 ExitOnFailure(hr, "failed to create Internet shortcut");
364
365 // tick the progress bar
366 hr = WcaProgressMessage(COST_INTERNETSHORTCUT, FALSE);
367 ExitOnFailure(hr, "failed to tick progress bar for shortcut: %ls", pwzShortcutPath);
368 }
369
370LExit:
371 ReleaseStr(pwzCustomActionData);
372 ReleaseStr(pwzTarget);
373 ReleaseStr(pwzShortcutPath);
374
375 if (fInitializedCom)
376 {
377 ::CoUninitialize();
378 }
379
380 er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er;
381 return WcaFinalize(er);
382}
383
384
385
386/******************************************************************
387 WixRollbackInternetShortcuts - entry point for Internet shortcuts
388 custom action (rollback)
389*******************************************************************/
390extern "C" UINT __stdcall WixRollbackInternetShortcuts(
391 __in MSIHANDLE hInstall
392 )
393{
394 HRESULT hr = S_OK;
395 UINT er = ERROR_SUCCESS;
396
397 LPWSTR pwz = NULL;
398 LPWSTR pwzCustomActionData = NULL;
399 LPWSTR pwzShortcutPath = NULL;
400 int iAttr = 0;
401
402 // initialize
403 hr = WcaInitialize(hInstall, "WixRemoveInternetShortcuts");
404 ExitOnFailure(hr, "failed to initialize WixRemoveInternetShortcuts");
405
406 hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData);
407 ExitOnFailure(hr, "failed to get CustomActionData");
408
409 // loop through all the custom action data
410 pwz = pwzCustomActionData;
411 while (pwz && *pwz)
412 {
413 // extract the custom action data we're interested in
414 hr = WcaReadStringFromCaData(&pwz, &pwzShortcutPath);
415 ExitOnFailure(hr, "failed to read shortcut path from custom action data for rollback");
416
417 // delete file
418 hr = FileEnsureDelete(pwzShortcutPath);
419 ExitOnFailure(hr, "failed to delete file '%ls'", pwzShortcutPath);
420
421 // skip over the shortcut target and attributes
422 hr = WcaReadStringFromCaData(&pwz, &pwzShortcutPath);
423 ExitOnFailure(hr, "failed to skip shortcut target from custom action data for rollback");
424 hr = WcaReadIntegerFromCaData(&pwz, &iAttr);
425 ExitOnFailure(hr, "failed to read shortcut attributes from custom action data");
426 }
427
428LExit:
429 ReleaseStr(pwzCustomActionData);
430 ReleaseStr(pwzShortcutPath);
431
432 er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er;
433 return WcaFinalize(er);
434}
diff --git a/src/ca/precomp.h b/src/ca/precomp.h
index 3edad7ed..45984156 100644
--- a/src/ca/precomp.h
+++ b/src/ca/precomp.h
@@ -2,12 +2,54 @@
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. 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 3
4 4
5#if _WIN32_MSI < 150
6#define _WIN32_MSI 150
7#endif
8
5#include <windows.h> 9#include <windows.h>
6#include <msiquery.h> 10#include <msiquery.h>
11#include <msidefs.h>
12#include <stierr.h>
13
14#include <strsafe.h>
15
16#include <msxml2.h>
17#include <Iads.h>
18#include <activeds.h>
19#include <lm.h> // NetApi32.lib
20#include <Ntsecapi.h>
21#include <Dsgetdc.h>
22#include <shlobj.h>
23#include <intshcut.h>
7 24
8#define MAXUINT USHRT_MAX 25#define MAXUINT USHRT_MAX
9#include <Setup.Configuration.h> 26#include <Setup.Configuration.h>
10 27
11#include "wcautil.h" 28#include "wcautil.h"
29#include "wcawow64.h"
30#include "wcawrapquery.h"
31#include "aclutil.h"
32#include "dirutil.h"
12#include "fileutil.h" 33#include "fileutil.h"
34#include "memutil.h"
35#include "osutil.h"
36#include "pathutil.h"
37#include "procutil.h"
38#include "shelutil.h"
13#include "strutil.h" 39#include "strutil.h"
40#include "sczutil.h"
41#include "rmutil.h"
42#include "userutil.h"
43#include "xmlutil.h"
44#include "wiutil.h"
45
46#include "CustomMsiErrors.h"
47
48#include "sca.h"
49#include "scacost.h"
50#include "cost.h"
51#include "scauser.h"
52#include "scasmb.h"
53#include "scasmbexec.h"
54
55#include "caSuffix.h"
diff --git a/src/ca/qtexecca.cpp b/src/ca/qtexecca.cpp
new file mode 100644
index 00000000..6acad0bb
--- /dev/null
+++ b/src/ca/qtexecca.cpp
@@ -0,0 +1,312 @@
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
7// These old "CA" prefix names are deprecated, and intended to go away in wix 4.0, only staying now for compatibility reasons
8const LPCWSTR CAQUIET_TIMEOUT_PROPERTY = L"QtExecCmdTimeout";
9const LPCWSTR CAQUIET_ARGUMENTS_PROPERTY = L"QtExecCmdLine";
10const LPCWSTR CAQUIET64_ARGUMENTS_PROPERTY = L"QtExec64CmdLine";
11// end deprecated section
12
13// WixCA name quiet commandline argument properties
14const LPCWSTR WIX_QUIET_ARGUMENTS_PROPERTY = L"WixQuietExecCmdLine";
15const LPCWSTR WIX_QUIET64_ARGUMENTS_PROPERTY = L"WixQuietExec64CmdLine";
16
17// WixCA quiet timeout properties
18const LPCWSTR WIX_QUIET_TIMEOUT_PROPERTY = L"WixQuietExecCmdTimeout";
19const LPCWSTR WIX_QUIET64_TIMEOUT_PROPERTY = L"WixQuietExec64CmdTimeout";
20
21// WixCA silent commandline argument properties
22const LPCWSTR WIX_SILENT_ARGUMENTS_PROPERTY = L"WixSilentExecCmdLine";
23const LPCWSTR WIX_SILENT64_ARGUMENTS_PROPERTY = L"WixSilentExec64CmdLine";
24
25// WixCA silent timeout properties
26const LPCWSTR WIX_SILENT_TIMEOUT_PROPERTY = L"WixSilentExecCmdTimeout";
27const LPCWSTR WIX_SILENT64_TIMEOUT_PROPERTY = L"WixSilentExec64CmdTimeout";
28
29HRESULT BuildCommandLine(
30 __in LPCWSTR wzProperty,
31 __out LPWSTR *ppwzCommand
32 )
33{
34 Assert(ppwzCommand);
35
36 HRESULT hr = S_OK;
37 BOOL fScheduled = ::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_SCHEDULED);
38 BOOL fRollback = ::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_ROLLBACK);
39 BOOL fCommit = ::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_COMMIT);
40
41 if (fScheduled || fRollback || fCommit)
42 {
43 if (WcaIsPropertySet("CustomActionData"))
44 {
45 hr = WcaGetProperty( L"CustomActionData", ppwzCommand);
46 ExitOnFailure(hr, "Failed to get CustomActionData");
47 }
48 }
49 else if (WcaIsUnicodePropertySet(wzProperty))
50 {
51 hr = WcaGetFormattedProperty(wzProperty, ppwzCommand);
52 ExitOnFailure(hr, "Failed to get %ls", wzProperty);
53 hr = WcaSetProperty(wzProperty, L""); // clear out the property now that we've read it
54 ExitOnFailure(hr, "Failed to set %ls", wzProperty);
55 }
56
57 if (!*ppwzCommand)
58 {
59 ExitOnFailure(hr = E_INVALIDARG, "Failed to get command line data");
60 }
61
62 if (L'"' != **ppwzCommand)
63 {
64 WcaLog(LOGMSG_STANDARD, "Command string must begin with quoted application name.");
65 ExitOnFailure(hr = E_INVALIDARG, "invalid command line property value");
66 }
67
68LExit:
69 return hr;
70}
71
72#define ONEMINUTE 60000
73
74DWORD GetTimeout(LPCWSTR wzPropertyName)
75{
76 DWORD dwTimeout = ONEMINUTE;
77 HRESULT hr = S_OK;
78
79 LPWSTR pwzData = NULL;
80
81 if (WcaIsUnicodePropertySet(wzPropertyName))
82 {
83 hr = WcaGetProperty(wzPropertyName, &pwzData);
84 ExitOnFailure(hr, "Failed to get %ls", wzPropertyName);
85
86 if ((dwTimeout = (DWORD)_wtoi(pwzData)) == 0)
87 {
88 dwTimeout = ONEMINUTE;
89 }
90 }
91
92LExit:
93 ReleaseStr(pwzData);
94
95 return dwTimeout;
96
97}
98
99HRESULT ExecCommon(
100 __in LPCWSTR wzArgumentsProperty,
101 __in LPCWSTR wzTimeoutProperty,
102 __in BOOL fLogCommand,
103 __in BOOL fLogOutput
104 )
105{
106 HRESULT hr = S_OK;
107 LPWSTR pwzCommand = NULL;
108 DWORD dwTimeout = 0;
109
110 hr = BuildCommandLine(wzArgumentsProperty, &pwzCommand);
111 ExitOnFailure(hr, "Failed to get Command Line");
112
113 dwTimeout = GetTimeout(wzTimeoutProperty);
114
115 hr = QuietExec(pwzCommand, dwTimeout, fLogCommand, fLogOutput);
116 ExitOnFailure(hr, "QuietExec Failed");
117
118LExit:
119 ReleaseStr(pwzCommand);
120
121 return hr;
122}
123
124HRESULT ExecCommon64(
125 __in LPCWSTR wzArgumentsProperty,
126 __in LPCWSTR wzTimeoutProperty,
127 __in BOOL fLogCommand,
128 __in BOOL fLogOutput
129 )
130{
131 HRESULT hr = S_OK;
132 LPWSTR pwzCommand = NULL;
133 DWORD dwTimeout = 0;
134 BOOL fIsWow64Initialized = FALSE;
135 BOOL fRedirected = FALSE;
136
137 hr = WcaInitializeWow64();
138 if (S_FALSE == hr)
139 {
140 hr = TYPE_E_DLLFUNCTIONNOTFOUND;
141 }
142 ExitOnFailure(hr, "Failed to intialize WOW64.");
143 fIsWow64Initialized = TRUE;
144
145 hr = WcaDisableWow64FSRedirection();
146 ExitOnFailure(hr, "Failed to enable filesystem redirection.");
147 fRedirected = TRUE;
148
149 hr = BuildCommandLine(wzArgumentsProperty, &pwzCommand);
150 ExitOnFailure(hr, "Failed to get Command Line");
151
152 dwTimeout = GetTimeout(wzTimeoutProperty);
153
154 hr = QuietExec(pwzCommand, dwTimeout, fLogCommand, fLogOutput);
155 ExitOnFailure(hr, "QuietExec64 Failed");
156
157LExit:
158 ReleaseStr(pwzCommand);
159
160 if (fRedirected)
161 {
162 WcaRevertWow64FSRedirection();
163 }
164
165 if (fIsWow64Initialized)
166 {
167 WcaFinalizeWow64();
168 }
169
170 return hr;
171}
172
173// These two custom actions are deprecated, and should go away in wix v4.0. WixQuietExec replaces this one,
174// and is not intended to have any difference in behavior apart from CA name and property names.
175extern "C" UINT __stdcall CAQuietExec(
176 __in MSIHANDLE hInstall
177 )
178{
179 Assert(hInstall);
180 HRESULT hr = S_OK;
181 UINT er = ERROR_SUCCESS;
182
183 hr = WcaInitialize(hInstall, "CAQuietExec");
184 ExitOnFailure(hr, "Failed to initialize");
185
186 hr = ExecCommon(CAQUIET_ARGUMENTS_PROPERTY, CAQUIET_TIMEOUT_PROPERTY, TRUE, TRUE);
187 ExitOnFailure(hr, "Failed in ExecCommon method");
188
189LExit:
190 if (FAILED(hr))
191 {
192 er = ERROR_INSTALL_FAILURE;
193 }
194
195 return WcaFinalize(er);
196}
197
198// 2nd deprecated custom action name, superseded by WixQuietExec64
199extern "C" UINT __stdcall CAQuietExec64(
200 __in MSIHANDLE hInstall
201 )
202{
203 Assert(hInstall);
204 HRESULT hr = S_OK;
205 UINT er = ERROR_SUCCESS;
206
207 hr = WcaInitialize(hInstall, "CAQuietExec64");
208 ExitOnFailure(hr, "Failed to initialize");
209
210 hr = ExecCommon64(CAQUIET64_ARGUMENTS_PROPERTY, CAQUIET_TIMEOUT_PROPERTY, TRUE, TRUE);
211 ExitOnFailure(hr, "Failed in ExecCommon64 method");
212
213LExit:
214 if (FAILED(hr))
215 {
216 er = ERROR_INSTALL_FAILURE;
217 }
218
219 return WcaFinalize(er);
220}
221
222extern "C" UINT __stdcall WixQuietExec(
223 __in MSIHANDLE hInstall
224 )
225{
226 Assert(hInstall);
227 HRESULT hr = S_OK;
228 UINT er = ERROR_SUCCESS;
229
230 hr = WcaInitialize(hInstall, "WixQuietExec");
231 ExitOnFailure(hr, "Failed to initialize");
232
233 hr = ExecCommon(WIX_QUIET_ARGUMENTS_PROPERTY, WIX_QUIET_TIMEOUT_PROPERTY, TRUE, TRUE);
234 ExitOnFailure(hr, "Failed in ExecCommon method");
235
236LExit:
237 if (FAILED(hr))
238 {
239 er = ERROR_INSTALL_FAILURE;
240 }
241
242 return WcaFinalize(er);
243}
244
245extern "C" UINT __stdcall WixQuietExec64(
246 __in MSIHANDLE hInstall
247 )
248{
249 Assert(hInstall);
250 HRESULT hr = S_OK;
251 UINT er = ERROR_SUCCESS;
252
253 hr = WcaInitialize(hInstall, "WixQuietExec64");
254 ExitOnFailure(hr, "Failed to initialize");
255
256 hr = ExecCommon64(WIX_QUIET64_ARGUMENTS_PROPERTY, WIX_QUIET64_TIMEOUT_PROPERTY, TRUE, TRUE);
257 ExitOnFailure(hr, "Failed in ExecCommon method");
258
259LExit:
260 if (FAILED(hr))
261 {
262 er = ERROR_INSTALL_FAILURE;
263 }
264
265 return WcaFinalize(er);
266}
267
268extern "C" UINT __stdcall WixSilentExec(
269 __in MSIHANDLE hInstall
270 )
271{
272 Assert(hInstall);
273 HRESULT hr = S_OK;
274 UINT er = ERROR_SUCCESS;
275
276 hr = WcaInitialize(hInstall, "WixSilentExec");
277 ExitOnFailure(hr, "Failed to initialize");
278
279 hr = ExecCommon(WIX_SILENT_ARGUMENTS_PROPERTY, WIX_SILENT_TIMEOUT_PROPERTY, FALSE, FALSE);
280 ExitOnFailure(hr, "Failed in ExecCommon method");
281
282LExit:
283 if (FAILED(hr))
284 {
285 er = ERROR_INSTALL_FAILURE;
286 }
287
288 return WcaFinalize(er);
289}
290
291extern "C" UINT __stdcall WixSilentExec64(
292 __in MSIHANDLE hInstall
293 )
294{
295 Assert(hInstall);
296 HRESULT hr = S_OK;
297 UINT er = ERROR_SUCCESS;
298
299 hr = WcaInitialize(hInstall, "WixSilentExec64");
300 ExitOnFailure(hr, "Failed to initialize");
301
302 hr = ExecCommon64(WIX_SILENT64_ARGUMENTS_PROPERTY, WIX_SILENT64_TIMEOUT_PROPERTY, FALSE, FALSE);
303 ExitOnFailure(hr, "Failed in ExecCommon method");
304
305LExit:
306 if (FAILED(hr))
307 {
308 er = ERROR_INSTALL_FAILURE;
309 }
310
311 return WcaFinalize(er);
312}
diff --git a/src/ca/sca.h b/src/ca/sca.h
new file mode 100644
index 00000000..599122ff
--- /dev/null
+++ b/src/ca/sca.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// user creation attributes definitions
5enum SCAU_ATTRIBUTES
6{
7 SCAU_DONT_EXPIRE_PASSWRD = 0x00000001,
8 SCAU_PASSWD_CANT_CHANGE = 0x00000002,
9 SCAU_PASSWD_CHANGE_REQD_ON_LOGIN = 0x00000004,
10 SCAU_DISABLE_ACCOUNT = 0x00000008,
11 SCAU_FAIL_IF_EXISTS = 0x00000010,
12 SCAU_UPDATE_IF_EXISTS = 0x00000020,
13 SCAU_ALLOW_LOGON_AS_SERVICE = 0x00000040,
14 SCAU_ALLOW_LOGON_AS_BATCH = 0x00000080,
15
16 SCAU_DONT_REMOVE_ON_UNINSTALL = 0x00000100,
17 SCAU_DONT_CREATE_USER = 0x00000200,
18 SCAU_NON_VITAL = 0x00000400,
19}; \ No newline at end of file
diff --git a/src/ca/scacost.h b/src/ca/scacost.h
new file mode 100644
index 00000000..5b215035
--- /dev/null
+++ b/src/ca/scacost.h
@@ -0,0 +1,18 @@
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
5const UINT COST_PERFMON_REGISTER = 1000;
6const UINT COST_PERFMON_UNREGISTER = 1000;
7
8const UINT COST_SMB_CREATESMB = 10000;
9const UINT COST_SMB_DROPSMB = 5000;
10const UINT COST_USER_ADD = 10000;
11const UINT COST_USER_DELETE = 10000;
12
13const UINT COST_PERFMONMANIFEST_REGISTER = 1000;
14const UINT COST_PERFMONMANIFEST_UNREGISTER = 1000;
15
16const UINT COST_EVENTMANIFEST_REGISTER = 1000;
17const UINT COST_EVENTMANIFEST_UNREGISTER = 1000;
18
diff --git a/src/ca/scaexec.cpp b/src/ca/scaexec.cpp
new file mode 100644
index 00000000..ab9e6599
--- /dev/null
+++ b/src/ca/scaexec.cpp
@@ -0,0 +1,807 @@
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/********************************************************************
7 * CreateSmb - CUSTOM ACTION ENTRY POINT for creating fileshares
8 *
9 * Input: deferred CustomActionData -
10 * wzFsKey\twzShareDesc\twzFullPath\tfIntegratedAuth\twzUserName\tnPermissions\twzUserName\tnPermissions...
11 *
12 * ****************************************************************/
13extern "C" UINT __stdcall CreateSmb(MSIHANDLE hInstall)
14{
15//AssertSz(0, "debug CreateSmb");
16 UINT er = ERROR_SUCCESS;
17 HRESULT hr = S_OK;
18
19 LPWSTR pwzData = NULL;
20 LPWSTR pwz = NULL;
21 LPWSTR pwzFsKey = NULL;
22 LPWSTR pwzShareDesc = NULL;
23 LPWSTR pwzDirectory = NULL;
24 int iAccessMode = 0;
25 DWORD nExPermissions = 0;
26 BOOL fIntegratedAuth;
27 LPWSTR pwzExUser = NULL;
28 SCA_SMBP ssp = {0};
29 DWORD dwExUserPerms = 0;
30 DWORD dwCounter = 0;
31 SCA_SMBP_USER_PERMS* pUserPermsList = NULL;
32
33 hr = WcaInitialize(hInstall, "CreateSmb");
34 ExitOnFailure(hr, "failed to initialize");
35
36 hr = WcaGetProperty( L"CustomActionData", &pwzData);
37 ExitOnFailure(hr, "failed to get CustomActionData");
38
39 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
40
41 pwz = pwzData;
42 hr = WcaReadStringFromCaData(&pwz, &pwzFsKey); // share name
43 ExitOnFailure(hr, "failed to read share name");
44 hr = WcaReadStringFromCaData(&pwz, &pwzShareDesc); // share description
45 ExitOnFailure(hr, "failed to read share name");
46 hr = WcaReadStringFromCaData(&pwz, &pwzDirectory); // full path to share
47 ExitOnFailure(hr, "failed to read share name");
48 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int *>(&fIntegratedAuth));
49 ExitOnFailure(hr, "failed to read integrated authentication");
50
51 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int *>(&dwExUserPerms));
52 ExitOnFailure(hr, "failed to read count of permissions to set");
53 if(dwExUserPerms > 0)
54 {
55 pUserPermsList = static_cast<SCA_SMBP_USER_PERMS*>(MemAlloc(sizeof(SCA_SMBP_USER_PERMS)*dwExUserPerms, TRUE));
56 ExitOnNull(pUserPermsList, hr, E_OUTOFMEMORY, "failed to allocate memory for permissions structure");
57
58 //Pull out all of the ExUserPerm strings
59 for (dwCounter = 0; dwCounter < dwExUserPerms; ++dwCounter)
60 {
61 hr = WcaReadStringFromCaData(&pwz, &pwzExUser); // user account
62 ExitOnFailure(hr, "failed to read user account");
63 pUserPermsList[dwCounter].wzUser = pwzExUser;
64 pwzExUser = NULL;
65
66 hr = WcaReadIntegerFromCaData(&pwz, &iAccessMode);
67 ExitOnFailure(hr, "failed to read access mode");
68 pUserPermsList[dwCounter].accessMode = (ACCESS_MODE)iAccessMode;
69 iAccessMode = 0;
70
71 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int *>(&nExPermissions));
72 ExitOnFailure(hr, "failed to read count of permissions");
73 pUserPermsList[dwCounter].nPermissions = nExPermissions;
74 nExPermissions = 0;
75 }
76 }
77
78 ssp.wzKey = pwzFsKey;
79 ssp.wzDescription = pwzShareDesc;
80 ssp.wzDirectory = pwzDirectory;
81 ssp.fUseIntegratedAuth = fIntegratedAuth;
82 ssp.dwUserPermissionCount = dwExUserPerms;
83 ssp.pUserPerms = pUserPermsList;
84
85 hr = ScaEnsureSmbExists(&ssp);
86 MessageExitOnFailure(hr, msierrSMBFailedCreate, "failed to create share: '%ls'", pwzFsKey);
87
88 hr = WcaProgressMessage(COST_SMB_CREATESMB, FALSE);
89
90LExit:
91 ReleaseStr(pwzFsKey);
92 ReleaseStr(pwzShareDesc);
93 ReleaseStr(pwzDirectory);
94 ReleaseStr(pwzData);
95
96 if (pUserPermsList)
97 {
98 MemFree(pUserPermsList);
99 }
100
101 if (FAILED(hr))
102 {
103 er = ERROR_INSTALL_FAILURE;
104 }
105 return WcaFinalize(er);
106}
107
108
109
110/********************************************************************
111 DropSmb - CUSTOM ACTION ENTRY POINT for creating fileshares
112
113 Input: deferred CustomActionData - wzFsKey\twzShareDesc\twzFullPath\tnPermissions\tfIntegratedAuth\twzUserName\twzPassword
114
115 * ****************************************************************/
116extern "C" UINT __stdcall DropSmb(MSIHANDLE hInstall)
117{
118 //AssertSz(0, "debug DropSmb");
119 UINT er = ERROR_SUCCESS;
120 HRESULT hr = S_OK;
121
122 LPWSTR pwzData = NULL;
123 LPWSTR pwz = NULL;
124 LPWSTR pwzFsKey = NULL;
125 SCA_SMBP ssp = {0};
126
127 hr = WcaInitialize(hInstall, "DropSmb");
128 ExitOnFailure(hr, "failed to initialize");
129
130 hr = WcaGetProperty( L"CustomActionData", &pwzData);
131 ExitOnFailure(hr, "failed to get CustomActionData");
132
133 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
134
135 pwz = pwzData;
136 hr = WcaReadStringFromCaData(&pwz, &pwzFsKey); // share name
137 ExitOnFailure(hr, "failed to read share name");
138
139 ssp.wzKey = pwzFsKey;
140
141 hr = ScaDropSmb(&ssp);
142 MessageExitOnFailure(hr, msierrSMBFailedDrop, "failed to delete share: '%ls'", pwzFsKey);
143
144 hr = WcaProgressMessage(COST_SMB_DROPSMB, FALSE);
145
146LExit:
147 ReleaseStr(pwzFsKey);
148 ReleaseStr(pwzData);
149
150 if (FAILED(hr))
151 {
152 er = ERROR_INSTALL_FAILURE;
153 }
154 return WcaFinalize(er);
155}
156
157
158static HRESULT AddUserToGroup(
159 __in LPWSTR wzUser,
160 __in LPCWSTR wzUserDomain,
161 __in LPCWSTR wzGroup,
162 __in LPCWSTR wzGroupDomain
163 )
164{
165 Assert(wzUser && *wzUser && wzUserDomain && wzGroup && *wzGroup && wzGroupDomain);
166
167 HRESULT hr = S_OK;
168 IADsGroup *pGroup = NULL;
169 BSTR bstrUser = NULL;
170 BSTR bstrGroup = NULL;
171 LPCWSTR wz = NULL;
172 LPWSTR pwzUser = NULL;
173 LOCALGROUP_MEMBERS_INFO_3 lgmi;
174
175 if (*wzGroupDomain)
176 {
177 wz = wzGroupDomain;
178 }
179
180 // Try adding it to the global group first
181 UINT ui = ::NetGroupAddUser(wz, wzGroup, wzUser);
182 if (NERR_GroupNotFound == ui)
183 {
184 // Try adding it to the local group
185 if (wzUserDomain)
186 {
187 hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzUserDomain, wzUser);
188 ExitOnFailure(hr, "failed to allocate user domain string");
189 }
190
191 lgmi.lgrmi3_domainandname = (NULL == pwzUser ? wzUser : pwzUser);
192 ui = ::NetLocalGroupAddMembers(wz, wzGroup, 3 , reinterpret_cast<LPBYTE>(&lgmi), 1);
193 }
194 hr = HRESULT_FROM_WIN32(ui);
195 if (HRESULT_FROM_WIN32(ERROR_MEMBER_IN_ALIAS) == hr) // if they're already a member of the group don't report an error
196 hr = S_OK;
197
198 //
199 // If we failed, try active directory
200 //
201 if (FAILED(hr))
202 {
203 WcaLog(LOGMSG_VERBOSE, "Failed to add user: %ls, domain %ls to group: %ls, domain: %ls with error 0x%x. Attempting to use Active Directory", wzUser, wzUserDomain, wzGroup, wzGroupDomain, hr);
204
205 hr = UserCreateADsPath(wzUserDomain, wzUser, &bstrUser);
206 ExitOnFailure(hr, "failed to create user ADsPath for user: %ls domain: %ls", wzUser, wzUserDomain);
207
208 hr = UserCreateADsPath(wzGroupDomain, wzGroup, &bstrGroup);
209 ExitOnFailure(hr, "failed to create group ADsPath for group: %ls domain: %ls", wzGroup, wzGroupDomain);
210
211 hr = ::ADsGetObject(bstrGroup,IID_IADsGroup, reinterpret_cast<void**>(&pGroup));
212 ExitOnFailure(hr, "Failed to get group '%ls'.", reinterpret_cast<WCHAR*>(bstrGroup) );
213
214 hr = pGroup->Add(bstrUser);
215 if ((HRESULT_FROM_WIN32(ERROR_OBJECT_ALREADY_EXISTS) == hr) || (HRESULT_FROM_WIN32(ERROR_MEMBER_IN_ALIAS) == hr))
216 hr = S_OK;
217
218 ExitOnFailure(hr, "Failed to add user %ls to group '%ls'.", reinterpret_cast<WCHAR*>(bstrUser), reinterpret_cast<WCHAR*>(bstrGroup) );
219 }
220
221LExit:
222 ReleaseObject(pGroup);
223 ReleaseBSTR(bstrUser);
224 ReleaseBSTR(bstrGroup);
225
226 return hr;
227}
228
229static HRESULT RemoveUserFromGroup(
230 __in LPWSTR wzUser,
231 __in LPCWSTR wzUserDomain,
232 __in LPCWSTR wzGroup,
233 __in LPCWSTR wzGroupDomain
234 )
235{
236 Assert(wzUser && *wzUser && wzUserDomain && wzGroup && *wzGroup && wzGroupDomain);
237
238 HRESULT hr = S_OK;
239 IADsGroup *pGroup = NULL;
240 BSTR bstrUser = NULL;
241 BSTR bstrGroup = NULL;
242 LPCWSTR wz = NULL;
243 LPWSTR pwzUser = NULL;
244 LOCALGROUP_MEMBERS_INFO_3 lgmi;
245
246 if (*wzGroupDomain)
247 {
248 wz = wzGroupDomain;
249 }
250
251 // Try removing it from the global group first
252 UINT ui = ::NetGroupDelUser(wz, wzGroup, wzUser);
253 if (NERR_GroupNotFound == ui)
254 {
255 // Try removing it from the local group
256 if (wzUserDomain)
257 {
258 hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzUserDomain, wzUser);
259 ExitOnFailure(hr, "failed to allocate user domain string");
260 }
261
262 lgmi.lgrmi3_domainandname = (NULL == pwzUser ? wzUser : pwzUser);
263 ui = ::NetLocalGroupDelMembers(wz, wzGroup, 3 , reinterpret_cast<LPBYTE>(&lgmi), 1);
264 }
265 hr = HRESULT_FROM_WIN32(ui);
266
267 //
268 // If we failed, try active directory
269 //
270 if (FAILED(hr))
271 {
272 WcaLog(LOGMSG_VERBOSE, "Failed to remove user: %ls, domain %ls from group: %ls, domain: %ls with error 0x%x. Attempting to use Active Directory", wzUser, wzUserDomain, wzGroup, wzGroupDomain, hr);
273
274 hr = UserCreateADsPath(wzUserDomain, wzUser, &bstrUser);
275 ExitOnFailure(hr, "failed to create user ADsPath in order to remove user: %ls domain: %ls from a group", wzUser, wzUserDomain);
276
277 hr = UserCreateADsPath(wzGroupDomain, wzGroup, &bstrGroup);
278 ExitOnFailure(hr, "failed to create group ADsPath in order to remove user from group: %ls domain: %ls", wzGroup, wzGroupDomain);
279
280 hr = ::ADsGetObject(bstrGroup,IID_IADsGroup, reinterpret_cast<void**>(&pGroup));
281 ExitOnFailure(hr, "Failed to get group '%ls'.", reinterpret_cast<WCHAR*>(bstrGroup) );
282
283 hr = pGroup->Remove(bstrUser);
284 ExitOnFailure(hr, "Failed to remove user %ls from group '%ls'.", reinterpret_cast<WCHAR*>(bstrUser), reinterpret_cast<WCHAR*>(bstrGroup) );
285 }
286
287LExit:
288 ReleaseObject(pGroup);
289 ReleaseBSTR(bstrUser);
290 ReleaseBSTR(bstrGroup);
291
292 return hr;
293}
294
295
296static HRESULT ModifyUserLocalServiceRight(
297 __in_opt LPCWSTR wzDomain,
298 __in LPCWSTR wzName,
299 __in BOOL fAdd
300 )
301{
302 HRESULT hr = S_OK;
303 NTSTATUS nt = 0;
304
305 LPWSTR pwzUser = NULL;
306 PSID psid = NULL;
307 LSA_HANDLE hPolicy = NULL;
308 LSA_OBJECT_ATTRIBUTES ObjectAttributes = { 0 };
309 LSA_UNICODE_STRING lucPrivilege = { 0 };
310
311 if (wzDomain && *wzDomain)
312 {
313 hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzDomain, wzName);
314 ExitOnFailure(hr, "Failed to allocate user with domain string");
315 }
316 else
317 {
318 hr = StrAllocString(&pwzUser, wzName, 0);
319 ExitOnFailure(hr, "Failed to allocate string from user name.");
320 }
321
322 hr = AclGetAccountSid(NULL, pwzUser, &psid);
323 ExitOnFailure(hr, "Failed to get SID for user: %ls", pwzUser);
324
325 nt = ::LsaOpenPolicy(NULL, &ObjectAttributes, POLICY_ALL_ACCESS, &hPolicy);
326 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
327 ExitOnFailure(hr, "Failed to open LSA policy store.");
328
329 lucPrivilege.Buffer = L"SeServiceLogonRight";
330 lucPrivilege.Length = static_cast<USHORT>(lstrlenW(lucPrivilege.Buffer) * sizeof(WCHAR));
331 lucPrivilege.MaximumLength = (lucPrivilege.Length + 1) * sizeof(WCHAR);
332
333 if (fAdd)
334 {
335 nt = ::LsaAddAccountRights(hPolicy, psid, &lucPrivilege, 1);
336 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
337 ExitOnFailure(hr, "Failed to add 'logon as service' bit to user: %ls", pwzUser);
338 }
339 else
340 {
341 nt = ::LsaRemoveAccountRights(hPolicy, psid, FALSE, &lucPrivilege, 1);
342 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
343 ExitOnFailure(hr, "Failed to remove 'logon as service' bit from user: %ls", pwzUser);
344 }
345
346LExit:
347 if (hPolicy)
348 {
349 ::LsaClose(hPolicy);
350 }
351
352 ReleaseSid(psid);
353 ReleaseStr(pwzUser);
354 return hr;
355}
356
357
358static HRESULT ModifyUserLocalBatchRight(
359 __in_opt LPCWSTR wzDomain,
360 __in LPCWSTR wzName,
361 __in BOOL fAdd
362 )
363{
364 HRESULT hr = S_OK;
365 NTSTATUS nt = 0;
366
367 LPWSTR pwzUser = NULL;
368 PSID psid = NULL;
369 LSA_HANDLE hPolicy = NULL;
370 LSA_OBJECT_ATTRIBUTES ObjectAttributes = { 0 };
371 LSA_UNICODE_STRING lucPrivilege = { 0 };
372
373 if (wzDomain && *wzDomain)
374 {
375 hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzDomain, wzName);
376 ExitOnFailure(hr, "Failed to allocate user with domain string");
377 }
378 else
379 {
380 hr = StrAllocString(&pwzUser, wzName, 0);
381 ExitOnFailure(hr, "Failed to allocate string from user name.");
382 }
383
384 hr = AclGetAccountSid(NULL, pwzUser, &psid);
385 ExitOnFailure(hr, "Failed to get SID for user: %ls", pwzUser);
386
387 nt = ::LsaOpenPolicy(NULL, &ObjectAttributes, POLICY_ALL_ACCESS, &hPolicy);
388 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
389 ExitOnFailure(hr, "Failed to open LSA policy store.");
390
391 lucPrivilege.Buffer = L"SeBatchLogonRight";
392 lucPrivilege.Length = static_cast<USHORT>(lstrlenW(lucPrivilege.Buffer) * sizeof(WCHAR));
393 lucPrivilege.MaximumLength = (lucPrivilege.Length + 1) * sizeof(WCHAR);
394
395 if (fAdd)
396 {
397 nt = ::LsaAddAccountRights(hPolicy, psid, &lucPrivilege, 1);
398 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
399 ExitOnFailure(hr, "Failed to add 'logon as batch job' bit to user: %ls", pwzUser);
400 }
401 else
402 {
403 nt = ::LsaRemoveAccountRights(hPolicy, psid, FALSE, &lucPrivilege, 1);
404 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
405 ExitOnFailure(hr, "Failed to remove 'logon as batch job' bit from user: %ls", pwzUser);
406 }
407
408 LExit:
409 if (hPolicy)
410 {
411 ::LsaClose(hPolicy);
412 }
413
414 ReleaseSid(psid);
415 ReleaseStr(pwzUser);
416 return hr;
417}
418
419static void SetUserPasswordAndAttributes(
420 __in USER_INFO_1* puserInfo,
421 __in LPWSTR wzPassword,
422 __in int iAttributes
423 )
424{
425 Assert(puserInfo);
426
427 // Set the User's password
428 puserInfo->usri1_password = wzPassword;
429
430 // Apply the Attributes
431 if (SCAU_DONT_EXPIRE_PASSWRD & iAttributes)
432 {
433 puserInfo->usri1_flags |= UF_DONT_EXPIRE_PASSWD;
434 }
435 else
436 {
437 puserInfo->usri1_flags &= ~UF_DONT_EXPIRE_PASSWD;
438 }
439
440 if (SCAU_PASSWD_CANT_CHANGE & iAttributes)
441 {
442 puserInfo->usri1_flags |= UF_PASSWD_CANT_CHANGE;
443 }
444 else
445 {
446 puserInfo->usri1_flags &= ~UF_PASSWD_CANT_CHANGE;
447 }
448
449 if (SCAU_DISABLE_ACCOUNT & iAttributes)
450 {
451 puserInfo->usri1_flags |= UF_ACCOUNTDISABLE;
452 }
453 else
454 {
455 puserInfo->usri1_flags &= ~UF_ACCOUNTDISABLE;
456 }
457
458 if (SCAU_PASSWD_CHANGE_REQD_ON_LOGIN & iAttributes) // TODO: for some reason this doesn't work
459 {
460 puserInfo->usri1_flags |= UF_PASSWORD_EXPIRED;
461 }
462 else
463 {
464 puserInfo->usri1_flags &= ~UF_PASSWORD_EXPIRED;
465 }
466}
467
468
469/********************************************************************
470 CreateUser - CUSTOM ACTION ENTRY POINT for creating users
471
472 Input: deferred CustomActionData - UserName\tDomain\tPassword\tAttributes\tGroupName\tDomain\tGroupName\tDomain...
473 * *****************************************************************/
474extern "C" UINT __stdcall CreateUser(
475 __in MSIHANDLE hInstall
476 )
477{
478 //AssertSz(0, "Debug CreateUser");
479
480 HRESULT hr = S_OK;
481 UINT er = ERROR_SUCCESS;
482
483 LPWSTR pwzData = NULL;
484 LPWSTR pwz = NULL;
485 LPWSTR pwzName = NULL;
486 LPWSTR pwzDomain = NULL;
487 LPWSTR pwzPassword = NULL;
488 LPWSTR pwzGroup = NULL;
489 LPWSTR pwzGroupDomain = NULL;
490 PDOMAIN_CONTROLLER_INFOW pDomainControllerInfo = NULL;
491 int iAttributes = 0;
492 BOOL fInitializedCom = FALSE;
493
494 USER_INFO_1 userInfo;
495 USER_INFO_1* puserInfo = NULL;
496 DWORD dw;
497 LPCWSTR wz = NULL;
498
499 hr = WcaInitialize(hInstall, "CreateUser");
500 ExitOnFailure(hr, "failed to initialize");
501
502 hr = ::CoInitialize(NULL);
503 ExitOnFailure(hr, "failed to initialize COM");
504 fInitializedCom = TRUE;
505
506 hr = WcaGetProperty( L"CustomActionData", &pwzData);
507 ExitOnFailure(hr, "failed to get CustomActionData");
508
509 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
510
511 //
512 // Read in the CustomActionData
513 //
514 pwz = pwzData;
515 hr = WcaReadStringFromCaData(&pwz, &pwzName);
516 ExitOnFailure(hr, "failed to read user name from custom action data");
517
518 hr = WcaReadStringFromCaData(&pwz, &pwzDomain);
519 ExitOnFailure(hr, "failed to read domain from custom action data");
520
521 hr = WcaReadIntegerFromCaData(&pwz, &iAttributes);
522 ExitOnFailure(hr, "failed to read attributes from custom action data");
523
524 hr = WcaReadStringFromCaData(&pwz, &pwzPassword);
525 ExitOnFailure(hr, "failed to read password from custom action data");
526
527 if (!(SCAU_DONT_CREATE_USER & iAttributes))
528 {
529 ::ZeroMemory(&userInfo, sizeof(USER_INFO_1));
530 userInfo.usri1_name = pwzName;
531 userInfo.usri1_priv = USER_PRIV_USER;
532 userInfo.usri1_flags = UF_SCRIPT;
533 userInfo.usri1_home_dir = NULL;
534 userInfo.usri1_comment = NULL;
535 userInfo.usri1_script_path = NULL;
536
537 SetUserPasswordAndAttributes(&userInfo, pwzPassword, iAttributes);
538
539 //
540 // Create the User
541 //
542 if (pwzDomain && *pwzDomain)
543 {
544 er = ::DsGetDcNameW( NULL, (LPCWSTR)pwzDomain, NULL, NULL, NULL, &pDomainControllerInfo );
545 if (RPC_S_SERVER_UNAVAILABLE == er)
546 {
547 // MSDN says, if we get the above error code, try again with the "DS_FORCE_REDISCOVERY" flag
548 er = ::DsGetDcNameW( NULL, (LPCWSTR)pwzDomain, NULL, NULL, DS_FORCE_REDISCOVERY, &pDomainControllerInfo );
549 }
550 if (ERROR_SUCCESS == er)
551 {
552 wz = pDomainControllerInfo->DomainControllerName + 2; //Add 2 so that we don't get the \\ prefix
553 }
554 else
555 {
556 wz = pwzDomain;
557 }
558 }
559
560 er = ::NetUserAdd(wz, 1, reinterpret_cast<LPBYTE>(&userInfo), &dw);
561 if (NERR_UserExists == er)
562 {
563 if (SCAU_UPDATE_IF_EXISTS & iAttributes)
564 {
565 er = ::NetUserGetInfo(wz, pwzName, 1, reinterpret_cast<LPBYTE*>(&puserInfo));
566 if (NERR_Success == er)
567 {
568 // Change the existing user's password and attributes again then try
569 // to update user with this new data
570 SetUserPasswordAndAttributes(puserInfo, pwzPassword, iAttributes);
571
572 er = ::NetUserSetInfo(wz, pwzName, 1, reinterpret_cast<LPBYTE>(puserInfo), &dw);
573 }
574 }
575 else if (!(SCAU_FAIL_IF_EXISTS & iAttributes))
576 {
577 er = NERR_Success;
578 }
579 }
580 else if (NERR_PasswordTooShort == er || NERR_PasswordTooLong == er)
581 {
582 MessageExitOnFailure(hr = HRESULT_FROM_WIN32(er), msierrUSRFailedUserCreatePswd, "failed to create user: %ls due to invalid password.", pwzName);
583 }
584 MessageExitOnFailure(hr = HRESULT_FROM_WIN32(er), msierrUSRFailedUserCreate, "failed to create user: %ls", pwzName);
585 }
586
587 if (SCAU_ALLOW_LOGON_AS_SERVICE & iAttributes)
588 {
589 hr = ModifyUserLocalServiceRight(pwzDomain, pwzName, TRUE);
590 MessageExitOnFailure(hr, msierrUSRFailedGrantLogonAsService, "Failed to grant logon as service rights to user: %ls", pwzName);
591 }
592
593 if (SCAU_ALLOW_LOGON_AS_BATCH & iAttributes)
594 {
595 hr = ModifyUserLocalBatchRight(pwzDomain, pwzName, TRUE);
596 MessageExitOnFailure(hr, msierrUSRFailedGrantLogonAsService, "Failed to grant logon as batch job rights to user: %ls", pwzName);
597 }
598
599 //
600 // Add the users to groups
601 //
602 while (S_OK == (hr = WcaReadStringFromCaData(&pwz, &pwzGroup)))
603 {
604 hr = WcaReadStringFromCaData(&pwz, &pwzGroupDomain);
605 ExitOnFailure(hr, "failed to get domain for group: %ls", pwzGroup);
606
607 hr = AddUserToGroup(pwzName, pwzDomain, pwzGroup, pwzGroupDomain);
608 MessageExitOnFailure(hr, msierrUSRFailedUserGroupAdd, "failed to add user: %ls to group %ls", pwzName, pwzGroup);
609 }
610 if (E_NOMOREITEMS == hr) // if there are no more items, all is well
611 {
612 hr = S_OK;
613 }
614 ExitOnFailure(hr, "failed to get next group in which to include user:%ls", pwzName);
615
616LExit:
617 if (puserInfo)
618 {
619 ::NetApiBufferFree((LPVOID)puserInfo);
620 }
621
622 if (pDomainControllerInfo)
623 {
624 ::NetApiBufferFree((LPVOID)pDomainControllerInfo);
625 }
626
627 ReleaseStr(pwzData);
628 ReleaseStr(pwzName);
629 ReleaseStr(pwzDomain);
630 ReleaseStr(pwzPassword);
631 ReleaseStr(pwzGroup);
632 ReleaseStr(pwzGroupDomain);
633
634 if (fInitializedCom)
635 {
636 ::CoUninitialize();
637 }
638
639 if (SCAU_NON_VITAL & iAttributes)
640 {
641 er = ERROR_SUCCESS;
642 }
643 else if (FAILED(hr))
644 {
645 er = ERROR_INSTALL_FAILURE;
646 }
647
648 return WcaFinalize(er);
649}
650
651
652/********************************************************************
653 RemoveUser - CUSTOM ACTION ENTRY POINT for removing users
654
655 Input: deferred CustomActionData - Name\tDomain
656 * *****************************************************************/
657extern "C" UINT __stdcall RemoveUser(
658 MSIHANDLE hInstall
659 )
660{
661 //AssertSz(0, "Debug RemoveAccount");
662
663 HRESULT hr = S_OK;
664 UINT er = ERROR_SUCCESS;
665
666 LPWSTR pwzData = NULL;
667 LPWSTR pwz = NULL;
668 LPWSTR pwzName = NULL;
669 LPWSTR pwzDomain= NULL;
670 LPWSTR pwzGroup = NULL;
671 LPWSTR pwzGroupDomain = NULL;
672 int iAttributes = 0;
673 LPCWSTR wz = NULL;
674 PDOMAIN_CONTROLLER_INFOW pDomainControllerInfo = NULL;
675 BOOL fInitializedCom = FALSE;
676
677 hr = WcaInitialize(hInstall, "RemoveUser");
678 ExitOnFailure(hr, "failed to initialize");
679
680 hr = ::CoInitialize(NULL);
681 ExitOnFailure(hr, "failed to initialize COM");
682 fInitializedCom = TRUE;
683
684 hr = WcaGetProperty(L"CustomActionData", &pwzData);
685 ExitOnFailure(hr, "failed to get CustomActionData");
686
687 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
688
689 //
690 // Read in the CustomActionData
691 //
692 pwz = pwzData;
693 hr = WcaReadStringFromCaData(&pwz, &pwzName);
694 ExitOnFailure(hr, "failed to read name from custom action data");
695
696 hr = WcaReadStringFromCaData(&pwz, &pwzDomain);
697 ExitOnFailure(hr, "failed to read domain from custom action data");
698
699 hr = WcaReadIntegerFromCaData(&pwz, &iAttributes);
700 ExitOnFailure(hr, "failed to read attributes from custom action data");
701
702 //
703 // Remove the logon as service privilege.
704 //
705 if (SCAU_ALLOW_LOGON_AS_SERVICE & iAttributes)
706 {
707 hr = ModifyUserLocalServiceRight(pwzDomain, pwzName, FALSE);
708 if (FAILED(hr))
709 {
710 WcaLogError(hr, "Failed to remove logon as service right from user, continuing...");
711 hr = S_OK;
712 }
713 }
714
715 if (SCAU_ALLOW_LOGON_AS_BATCH & iAttributes)
716 {
717 hr = ModifyUserLocalBatchRight(pwzDomain, pwzName, FALSE);
718 if (FAILED(hr))
719 {
720 WcaLogError(hr, "Failed to remove logon as batch job right from user, continuing...");
721 hr = S_OK;
722 }
723 }
724
725 //
726 // Remove the User Account if the user was created by us.
727 //
728 if (!(SCAU_DONT_CREATE_USER & iAttributes))
729 {
730 if (pwzDomain && *pwzDomain)
731 {
732 er = ::DsGetDcNameW( NULL, (LPCWSTR)pwzDomain, NULL, NULL, NULL, &pDomainControllerInfo );
733 if (RPC_S_SERVER_UNAVAILABLE == er)
734 {
735 // MSDN says, if we get the above error code, try again with the "DS_FORCE_REDISCOVERY" flag
736 er = ::DsGetDcNameW( NULL, (LPCWSTR)pwzDomain, NULL, NULL, DS_FORCE_REDISCOVERY, &pDomainControllerInfo );
737 }
738 if (ERROR_SUCCESS == er)
739 {
740 wz = pDomainControllerInfo->DomainControllerName + 2; //Add 2 so that we don't get the \\ prefix
741 }
742 else
743 {
744 wz = pwzDomain;
745 }
746 }
747
748 er = ::NetUserDel(wz, pwzName);
749 if (NERR_UserNotFound == er)
750 {
751 er = NERR_Success;
752 }
753 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to delete user account: %ls", pwzName);
754 }
755 else
756 {
757 //
758 // Remove the user from the groups
759 //
760 while (S_OK == (hr = WcaReadStringFromCaData(&pwz, &pwzGroup)))
761 {
762 hr = WcaReadStringFromCaData(&pwz, &pwzGroupDomain);
763
764 if (FAILED(hr))
765 {
766 WcaLogError(hr, "failed to get domain for group: %ls, continuing anyway.", pwzGroup);
767 }
768 else
769 {
770 hr = RemoveUserFromGroup(pwzName, pwzDomain, pwzGroup, pwzGroupDomain);
771 if (FAILED(hr))
772 {
773 WcaLogError(hr, "failed to remove user: %ls from group %ls, continuing anyway.", pwzName, pwzGroup);
774 }
775 }
776 }
777
778 if (E_NOMOREITEMS == hr) // if there are no more items, all is well
779 {
780 hr = S_OK;
781 }
782
783 ExitOnFailure(hr, "failed to get next group from which to remove user:%ls", pwzName);
784 }
785
786LExit:
787 if (pDomainControllerInfo)
788 {
789 ::NetApiBufferFree(static_cast<LPVOID>(pDomainControllerInfo));
790 }
791
792 ReleaseStr(pwzData);
793 ReleaseStr(pwzName);
794 ReleaseStr(pwzDomain);
795
796 if (fInitializedCom)
797 {
798 ::CoUninitialize();
799 }
800
801 if (FAILED(hr))
802 {
803 er = ERROR_INSTALL_FAILURE;
804 }
805
806 return WcaFinalize(er);
807}
diff --git a/src/ca/scamanifest.cpp b/src/ca/scamanifest.cpp
new file mode 100644
index 00000000..58b4054d
--- /dev/null
+++ b/src/ca/scamanifest.cpp
@@ -0,0 +1,377 @@
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
5LPCWSTR vcsPerfmonManifestQuery = L"SELECT `Component_`, `File`, `ResourceFileDirectory` FROM `PerfmonManifest`";
6LPCWSTR vcsEventManifestQuery = L"SELECT `Component_`, `File` FROM `EventManifest`";
7enum ePerfMonManifestQuery { pfmComponent = 1, pfmFile, pfmResourceFileDir };
8enum eEventManifestQuery { emComponent = 1, emFile};
9
10BOOL IsVistaOrAbove()
11{
12 OSVERSIONINFO osvi;
13 ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
14 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
15 #pragma warning(suppress: 4996) //TODO: use non-deprecated function to check OS version
16 if (!::GetVersionEx(&osvi))
17 {
18 return false;
19 }
20 return osvi.dwMajorVersion >= 6;
21}
22
23
24/********************************************************************
25 ConfigurePerfmonManifestRegister - CUSTOM ACTION ENTRY POINT for scheduling
26 Perfmon counter manifest registering
27
28********************************************************************/
29extern "C" UINT __stdcall ConfigurePerfmonManifestRegister(
30 __in MSIHANDLE hInstall
31 )
32{
33 HRESULT hr;
34 UINT er = ERROR_SUCCESS;
35
36 PMSIHANDLE hView, hRec;
37 LPWSTR pwzData = NULL, pwzResourceFilePath = NULL, pwzFile = NULL, pwzCommand = NULL;
38 INSTALLSTATE isInstalled, isAction;
39
40 hr = WcaInitialize(hInstall, "ConfigurePerfmonManifestReg");
41 ExitOnFailure(hr, "Failed to initialize");
42
43 if (!IsVistaOrAbove())
44 {
45 WcaLog(LOGMSG_VERBOSE, "Skipping ConfigurePerfmonManifestRegister() because the target system does not support perfmon manifest");
46 ExitFunction1(hr = S_FALSE);
47 }
48 // check to see if necessary tables are specified
49 if (S_OK != WcaTableExists(L"PerfmonManifest"))
50 {
51 WcaLog(LOGMSG_VERBOSE, "Skipping ConfigurePerfmonManifestRegister() because PerfmonManifest table not present");
52 ExitFunction1(hr = S_FALSE);
53 }
54
55 hr = WcaOpenExecuteView(vcsPerfmonManifestQuery, &hView);
56 ExitOnFailure(hr, "failed to open view on PerfMonManifest table");
57 while ((hr = WcaFetchRecord(hView, &hRec)) == S_OK)
58 {
59 // get component install state
60 hr = WcaGetRecordString(hRec, pfmComponent, &pwzData);
61 ExitOnFailure(hr, "failed to get Component for PerfMonManifest");
62 er = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction);
63 hr = HRESULT_FROM_WIN32(er);
64 ExitOnFailure(hr, "failed to get Component state for PerfMonManifest");
65 if (!WcaIsInstalling(isInstalled, isAction))
66 {
67 continue;
68 }
69
70 hr = WcaGetRecordFormattedString(hRec, pfmFile, &pwzFile);
71 ExitOnFailure(hr, "failed to get File for PerfMonManifest");
72
73 hr = WcaGetRecordFormattedString(hRec, pfmResourceFileDir, &pwzResourceFilePath);
74 ExitOnFailure(hr, "failed to get ApplicationIdentity for PerfMonManifest");
75 size_t iResourcePath = lstrlenW(pwzResourceFilePath);
76 if ( iResourcePath > 0 && *(pwzResourceFilePath + iResourcePath -1) == L'\\')
77 *(pwzResourceFilePath + iResourcePath -1) = 0; //remove the trailing '\'
78
79 hr = StrAllocFormatted(&pwzCommand, L"\"unlodctr.exe\" /m:\"%s\"", pwzFile);
80 ExitOnFailure(hr, "failed to copy string in PerfMonManifest");
81
82 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"RollbackRegisterPerfmonManifest"), pwzCommand, COST_PERFMONMANIFEST_UNREGISTER);
83 ExitOnFailure(hr, "failed to schedule RollbackRegisterPerfmonManifest action");
84
85 if ( *pwzResourceFilePath )
86 {
87 hr = StrAllocFormatted(&pwzCommand, L"\"lodctr.exe\" /m:\"%s\" \"%s\"", pwzFile, pwzResourceFilePath);
88 ExitOnFailure(hr, "failed to copy string in PerfMonManifest");
89 }
90 else
91 {
92 hr = StrAllocFormatted(&pwzCommand, L"\"lodctr.exe\" /m:\"%s\"", pwzFile);
93 ExitOnFailure(hr, "failed to copy string in PerfMonManifest");
94 }
95
96 WcaLog(LOGMSG_VERBOSE, "RegisterPerfmonManifest's CustomActionData: '%ls'", pwzCommand);
97
98 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"RegisterPerfmonManifest"), pwzCommand, COST_PERFMONMANIFEST_REGISTER);
99 ExitOnFailure(hr, "failed to schedule RegisterPerfmonManifest action");
100 }
101
102 if (hr == E_NOMOREITEMS)
103 {
104 hr = S_OK;
105 }
106 ExitOnFailure(hr, "Failure while processing PerfMonManifest");
107
108 hr = S_OK;
109
110LExit:
111 ReleaseStr(pwzData);
112 ReleaseStr(pwzResourceFilePath);
113 ReleaseStr(pwzFile);
114 ReleaseStr(pwzCommand);
115
116 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
117 return WcaFinalize(er);
118}
119
120
121/********************************************************************
122 ConfigurePerfmonUninstall - CUSTOM ACTION ENTRY POINT for uninstalling
123 Perfmon counters
124
125********************************************************************/
126extern "C" UINT __stdcall ConfigurePerfmonManifestUnregister(
127 __in MSIHANDLE hInstall
128 )
129{
130 HRESULT hr;
131 UINT er = ERROR_SUCCESS;
132
133 PMSIHANDLE hView, hRec;
134 LPWSTR pwzData = NULL, pwzResourceFilePath = NULL, pwzFile = NULL, pwzCommand = NULL;
135 INSTALLSTATE isInstalled, isAction;
136
137 hr = WcaInitialize(hInstall, "ConfigurePerfmonManifestUnreg");
138 ExitOnFailure(hr, "Failed to initialize");
139
140 if (!IsVistaOrAbove())
141 {
142 WcaLog(LOGMSG_VERBOSE, "Skipping ConfigurePerfmonManifestUnregister() because the target system does not support perfmon manifest");
143 ExitFunction1(hr = S_FALSE);
144 }
145 // check to see if necessary tables are specified
146 if (WcaTableExists(L"PerfmonManifest") != S_OK)
147 {
148 WcaLog(LOGMSG_VERBOSE, "Skipping ConfigurePerfmonManifestUnregister() because PerfmonManifest table not present");
149 ExitFunction1(hr = S_FALSE);
150 }
151
152 hr = WcaOpenExecuteView(vcsPerfmonManifestQuery, &hView);
153 ExitOnFailure(hr, "failed to open view on PerfMonManifest table");
154 while ((hr = WcaFetchRecord(hView, &hRec)) == S_OK)
155 {
156 // get component install state
157 hr = WcaGetRecordString(hRec, pfmComponent, &pwzData);
158 ExitOnFailure(hr, "failed to get Component for PerfMonManifest");
159 er = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction);
160 hr = HRESULT_FROM_WIN32(er);
161 ExitOnFailure(hr, "failed to get Component state for PerfMonManifest");
162 if (!WcaIsUninstalling(isInstalled, isAction))
163 {
164 continue;
165 }
166
167 hr = WcaGetRecordFormattedString(hRec, pfmFile, &pwzFile);
168 ExitOnFailure(hr, "failed to get File for PerfMonManifest");
169
170 hr = WcaGetRecordFormattedString(hRec, pfmResourceFileDir, &pwzResourceFilePath);
171 ExitOnFailure(hr, "failed to get ApplicationIdentity for PerfMonManifest");
172 size_t iResourcePath = lstrlenW(pwzResourceFilePath);
173 if ( iResourcePath > 0 && *(pwzResourceFilePath + iResourcePath -1) == L'\\')
174 *(pwzResourceFilePath + iResourcePath -1) = 0; //remove the trailing '\'
175
176 hr = StrAllocFormatted(&pwzCommand, L"\"lodctr.exe\" /m:\"%s\" \"%s\"", pwzFile, pwzResourceFilePath);
177 ExitOnFailure(hr, "failed to copy string in PerfMonManifest");
178
179 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"RollbackUnregisterPerfmonManifest"), pwzCommand, COST_PERFMONMANIFEST_REGISTER);
180 ExitOnFailure(hr, "failed to schedule RollbackUnregisterPerfmonManifest action");
181
182 hr = StrAllocFormatted(&pwzCommand, L"\"unlodctr.exe\" /m:\"%s\"", pwzFile);
183 ExitOnFailure(hr, "failed to copy string in PerfMonManifest");
184
185 WcaLog(LOGMSG_VERBOSE, "UnRegisterPerfmonManifest's CustomActionData: '%ls'", pwzCommand);
186
187 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"UnregisterPerfmonManifest"), pwzCommand, COST_PERFMONMANIFEST_UNREGISTER);
188 ExitOnFailure(hr, "failed to schedule UnregisterPerfmonManifest action");
189 }
190
191 if (hr == E_NOMOREITEMS)
192 {
193 hr = S_OK;
194 }
195 ExitOnFailure(hr, "Failure while processing PerfMonManifest");
196
197 hr = S_OK;
198
199LExit:
200 ReleaseStr(pwzData);
201 ReleaseStr(pwzResourceFilePath);
202 ReleaseStr(pwzFile);
203 ReleaseStr(pwzCommand);
204
205 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
206 return WcaFinalize(er);
207}
208
209/********************************************************************
210 ConfigureEventManifestRegister - CUSTOM ACTION ENTRY POINT for scheduling
211 Event manifest registering
212
213********************************************************************/
214extern "C" UINT __stdcall ConfigureEventManifestRegister(
215 __in MSIHANDLE hInstall
216 )
217{
218 HRESULT hr;
219 UINT er = ERROR_SUCCESS;
220
221 PMSIHANDLE hView, hRec;
222 LPWSTR pwzData = NULL, pwzFile = NULL, pwzCommand = NULL;
223 INSTALLSTATE isInstalled, isAction;
224
225 hr = WcaInitialize(hInstall, "ConfigureEventManifestReg");
226 ExitOnFailure(hr, "Failed to initialize");
227
228 if (!IsVistaOrAbove())
229 {
230 WcaLog(LOGMSG_VERBOSE, "Skipping ConfigureEventManifestRegister() because the target system does not support event manifest");
231 ExitFunction1(hr = S_FALSE);
232 }
233 // check to see if necessary tables are specified
234 if (S_OK != WcaTableExists(L"EventManifest"))
235 {
236 WcaLog(LOGMSG_VERBOSE, "Skipping ConfigureEventManifestRegister() because EventManifest table not present");
237 ExitFunction1(hr = S_FALSE);
238 }
239
240 hr = WcaOpenExecuteView(vcsEventManifestQuery, &hView);
241 ExitOnFailure(hr, "failed to open view on EventManifest table");
242 while ((hr = WcaFetchRecord(hView, &hRec)) == S_OK)
243 {
244 // get component install state
245 hr = WcaGetRecordString(hRec, emComponent, &pwzData);
246 ExitOnFailure(hr, "failed to get Component for EventManifest");
247 er = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction);
248 hr = HRESULT_FROM_WIN32(er);
249 ExitOnFailure(hr, "failed to get Component state for EventManifest");
250 if (!WcaIsInstalling(isInstalled, isAction))
251 {
252 continue;
253 }
254
255 hr = WcaGetRecordFormattedString(hRec, emFile, &pwzFile);
256 ExitOnFailure(hr, "failed to get File for EventManifest");
257
258 hr = StrAllocFormatted(&pwzCommand, L"\"wevtutil.exe\" um \"%s\"", pwzFile);
259 ExitOnFailure(hr, "failed to copy string in EventManifest");
260
261 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"RollbackRegisterEventManifest"), pwzCommand, COST_PERFMONMANIFEST_UNREGISTER);
262 ExitOnFailure(hr, "failed to schedule RollbackRegisterEventManifest action");
263
264 hr = StrAllocFormatted(&pwzCommand, L"\"wevtutil.exe\" im \"%s\"", pwzFile);
265 ExitOnFailure(hr, "failed to copy string in EventManifest");
266 WcaLog(LOGMSG_VERBOSE, "RegisterEventManifest's CustomActionData: '%ls'", pwzCommand);
267
268 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"RegisterEventManifest"), pwzCommand, COST_EVENTMANIFEST_REGISTER);
269 ExitOnFailure(hr, "failed to schedule RegisterEventManifest action");
270 }
271
272 if (hr == E_NOMOREITEMS)
273 {
274 hr = S_OK;
275 }
276 ExitOnFailure(hr, "Failure while processing EventManifest");
277
278 hr = S_OK;
279
280LExit:
281 ReleaseStr(pwzData);
282 ReleaseStr(pwzFile);
283 ReleaseStr(pwzCommand);
284
285 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
286 return WcaFinalize(er);
287}
288
289
290
291/********************************************************************
292 ConfigureEventManifestRegister - CUSTOM ACTION ENTRY POINT for scheduling
293 Event manifest registering
294
295********************************************************************/
296extern "C" UINT __stdcall ConfigureEventManifestUnregister(
297 __in MSIHANDLE hInstall
298 )
299{
300 HRESULT hr;
301 UINT er = ERROR_SUCCESS;
302
303 PMSIHANDLE hView, hRec;
304 LPWSTR pwzData = NULL, pwzFile = NULL, pwzCommand = NULL;
305 INSTALLSTATE isInstalled, isAction;
306
307 hr = WcaInitialize(hInstall, "ConfigureEventManifestUnreg");
308 ExitOnFailure(hr, "Failed to initialize");
309
310 if (!IsVistaOrAbove())
311 {
312 WcaLog(LOGMSG_VERBOSE, "Skipping ConfigureEventManifestUnregister() because the target system does not support event manifest");
313 ExitFunction1(hr = S_FALSE);
314 }
315 // check to see if necessary tables are specified
316 if (S_OK != WcaTableExists(L"EventManifest"))
317 {
318 WcaLog(LOGMSG_VERBOSE, "Skipping ConfigureEventManifestUnregister() because EventManifest table not present");
319 ExitFunction1(hr = S_FALSE);
320 }
321
322 hr = WcaOpenExecuteView(vcsEventManifestQuery, &hView);
323 ExitOnFailure(hr, "failed to open view on EventManifest table");
324 while ((hr = WcaFetchRecord(hView, &hRec)) == S_OK)
325 {
326 // get component install state
327 hr = WcaGetRecordString(hRec, emComponent, &pwzData);
328 ExitOnFailure(hr, "failed to get Component for EventManifest");
329 er = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction);
330 hr = HRESULT_FROM_WIN32(er);
331 ExitOnFailure(hr, "failed to get Component state for EventManifest");
332
333 // nothing to do on an install
334 // schedule the rollback action when reinstalling to re-register pre-patch manifest
335 if (!WcaIsUninstalling(isInstalled, isAction) && !WcaIsReInstalling(isInstalled, isAction))
336 {
337 continue;
338 }
339
340 hr = WcaGetRecordFormattedString(hRec, emFile, &pwzFile);
341 ExitOnFailure(hr, "failed to get File for EventManifest");
342
343 hr = StrAllocFormatted(&pwzCommand, L"\"wevtutil.exe\" im \"%s\"", pwzFile);
344 ExitOnFailure(hr, "failed to copy string in EventManifest");
345
346 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"RollbackUnregisterEventManifest"), pwzCommand, COST_PERFMONMANIFEST_REGISTER);
347 ExitOnFailure(hr, "failed to schedule RollbackUnregisterEventManifest action");
348
349 // no need to uninstall on a repair/patch. Register action will re-register and update the manifest.
350 if (!WcaIsReInstalling(isInstalled, isAction))
351 {
352 hr = StrAllocFormatted(&pwzCommand, L"\"wevtutil.exe\" um \"%s\"", pwzFile);
353 ExitOnFailure(hr, "failed to copy string in EventManifest");
354 WcaLog(LOGMSG_VERBOSE, "UnregisterEventManifest's CustomActionData: '%ls'", pwzCommand);
355
356 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"UnregisterEventManifest"), pwzCommand, COST_PERFMONMANIFEST_UNREGISTER);
357 ExitOnFailure(hr, "failed to schedule UnregisterEventManifest action");
358 }
359 }
360
361 if (hr == E_NOMOREITEMS)
362 {
363 hr = S_OK;
364 }
365 ExitOnFailure(hr, "Failure while processing EventManifest");
366
367 hr = S_OK;
368
369LExit:
370 ReleaseStr(pwzData);
371 ReleaseStr(pwzFile);
372 ReleaseStr(pwzCommand);
373
374 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
375 return WcaFinalize(er);
376}
377
diff --git a/src/ca/scaperf.cpp b/src/ca/scaperf.cpp
new file mode 100644
index 00000000..82f458af
--- /dev/null
+++ b/src/ca/scaperf.cpp
@@ -0,0 +1,310 @@
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
5LPCWSTR vcsPerfCounterDataQuery = L"SELECT `PerformanceCategory`, `Component_`, `Name`, `IniData`, `ConstantData` FROM `PerformanceCategory`";
6enum ePerfCounterDataQuery { pcdqId = 1, pcdqComponent, pcdqName, pcdqIniData, pcdqConstantData };
7
8LPCWSTR vcsPerfMonQuery = L"SELECT `Component_`, `File`, `Name` FROM `Perfmon`";
9enum ePerfMonQuery { pmqComponent = 1, pmqFile, pmqName };
10
11
12static HRESULT ProcessPerformanceCategory(
13 __in MSIHANDLE hInstall,
14 __in BOOL fInstall
15 );
16
17
18/********************************************************************
19 InstallPerfCounterData - CUSTOM ACTION ENTRY POINT for installing
20 Performance Counters.
21
22********************************************************************/
23extern "C" UINT __stdcall InstallPerfCounterData(
24 __in MSIHANDLE hInstall
25 )
26{
27 // AssertSz(FALSE, "debug InstallPerfCounterData{}");
28 HRESULT hr;
29 UINT er = ERROR_SUCCESS;
30
31 hr = WcaInitialize(hInstall, "InstallPerfCounterData");
32 ExitOnFailure(hr, "Failed to initialize InstallPerfCounterData.");
33
34 hr = ProcessPerformanceCategory(hInstall, TRUE);
35 MessageExitOnFailure(hr, msierrInstallPerfCounterData, "Failed to process PerformanceCategory table.");
36
37LExit:
38 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
39 return WcaFinalize(er);
40}
41
42
43/********************************************************************
44 UninstallPerfCounterData - CUSTOM ACTION ENTRY POINT for installing
45 Performance Counters.
46
47********************************************************************/
48extern "C" UINT __stdcall UninstallPerfCounterData(
49 __in MSIHANDLE hInstall
50 )
51{
52 // AssertSz(FALSE, "debug UninstallPerfCounterData{}");
53 HRESULT hr;
54 UINT er = ERROR_SUCCESS;
55
56 hr = WcaInitialize(hInstall, "UninstallPerfCounterData");
57 ExitOnFailure(hr, "Failed to initialize UninstallPerfCounterData.");
58
59 hr = ProcessPerformanceCategory(hInstall, FALSE);
60 MessageExitOnFailure(hr, msierrUninstallPerfCounterData, "Failed to process PerformanceCategory table.");
61
62LExit:
63 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
64 return WcaFinalize(er);
65}
66
67
68/********************************************************************
69 RegisterPerfmon - CUSTOM ACTION ENTRY POINT for installing Perfmon counters
70
71********************************************************************/
72extern "C" UINT __stdcall ConfigurePerfmonInstall(
73 __in MSIHANDLE hInstall
74 )
75{
76// Assert(FALSE);
77 HRESULT hr;
78 UINT er = ERROR_SUCCESS;
79
80 PMSIHANDLE hView, hRec;
81 LPWSTR pwzData = NULL, pwzName = NULL, pwzFile = NULL;
82 INSTALLSTATE isInstalled, isAction;
83
84 hr = WcaInitialize(hInstall, "ConfigurePerfmonInstall");
85 ExitOnFailure(hr, "Failed to initialize");
86
87 // check to see if necessary tables are specified
88 if (S_OK != WcaTableExists(L"Perfmon"))
89 {
90 WcaLog(LOGMSG_VERBOSE, "Skipping RegisterPerfmon() because Perfmon table not present");
91 ExitFunction1(hr = S_FALSE);
92 }
93
94 hr = WcaOpenExecuteView(vcsPerfMonQuery, &hView);
95 ExitOnFailure(hr, "failed to open view on PerfMon table");
96 while ((hr = WcaFetchRecord(hView, &hRec)) == S_OK)
97 {
98 // get component install state
99 hr = WcaGetRecordString(hRec, pmqComponent, &pwzData);
100 ExitOnFailure(hr, "failed to get Component for PerfMon");
101 er = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction);
102 hr = HRESULT_FROM_WIN32(er);
103 ExitOnFailure(hr, "failed to get Component state for PerfMon");
104 if (!WcaIsInstalling(isInstalled, isAction))
105 {
106 continue;
107 }
108
109 hr = WcaGetRecordString(hRec, pmqName, &pwzName);
110 ExitOnFailure(hr, "failed to get Name for PerfMon");
111
112 hr = WcaGetRecordFormattedString(hRec, pmqFile, &pwzFile);
113 ExitOnFailure(hr, "failed to get File for PerfMon");
114
115 WcaLog(LOGMSG_VERBOSE, "ConfigurePerfmonInstall's CustomActionData: '%ls', '%ls'", pwzName, pwzFile);
116 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"RegisterPerfmon"), pwzFile, COST_PERFMON_REGISTER);
117 ExitOnFailure(hr, "failed to schedule RegisterPerfmon action");
118 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"RollbackRegisterPerfmon"), pwzName, COST_PERFMON_UNREGISTER);
119 ExitOnFailure(hr, "failed to schedule RollbackRegisterPerfmon action");
120 }
121
122 if (hr == E_NOMOREITEMS)
123 {
124 hr = S_OK;
125 }
126 ExitOnFailure(hr, "Failure while processing PerfMon");
127
128 hr = S_OK;
129
130LExit:
131 ReleaseStr(pwzData);
132 ReleaseStr(pwzName);
133 ReleaseStr(pwzFile);
134
135 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
136 return WcaFinalize(er);
137}
138
139
140/********************************************************************
141 ConfigurePerfmonUninstall - CUSTOM ACTION ENTRY POINT for uninstalling
142 Perfmon counters
143
144********************************************************************/
145extern "C" UINT __stdcall ConfigurePerfmonUninstall(
146 __in MSIHANDLE hInstall
147 )
148{
149// Assert(FALSE);
150 HRESULT hr;
151 UINT er = ERROR_SUCCESS;
152
153 PMSIHANDLE hView, hRec;
154 LPWSTR pwzData = NULL, pwzName = NULL, pwzFile = NULL;
155 INSTALLSTATE isInstalled, isAction;
156
157 hr = WcaInitialize(hInstall, "ConfigurePerfmonUninstall");
158 ExitOnFailure(hr, "Failed to initialize");
159
160 // check to see if necessary tables are specified
161 if (WcaTableExists(L"Perfmon") != S_OK)
162 {
163 WcaLog(LOGMSG_VERBOSE, "Skipping UnregisterPerfmon() because Perfmon table not present");
164 ExitFunction1(hr = S_FALSE);
165 }
166
167 hr = WcaOpenExecuteView(vcsPerfMonQuery, &hView);
168 ExitOnFailure(hr, "failed to open view on PerfMon table");
169 while ((hr = WcaFetchRecord(hView, &hRec)) == S_OK)
170 {
171 // get component install state
172 hr = WcaGetRecordString(hRec, pmqComponent, &pwzData);
173 ExitOnFailure(hr, "failed to get Component for PerfMon");
174 er = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction);
175 hr = HRESULT_FROM_WIN32(er);
176 ExitOnFailure(hr, "failed to get Component state for PerfMon");
177 if (!WcaIsUninstalling(isInstalled, isAction))
178 {
179 continue;
180 }
181
182 hr = WcaGetRecordString(hRec, pmqName, &pwzName);
183 ExitOnFailure(hr, "failed to get Name for PerfMon");
184
185 hr = WcaGetRecordFormattedString(hRec, pmqFile, &pwzFile);
186 ExitOnFailure(hr, "failed to get File for PerfMon");
187
188 WcaLog(LOGMSG_VERBOSE, "ConfigurePerfmonUninstall's CustomActionData: '%ls', '%ls'", pwzName, pwzFile);
189 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"UnregisterPerfmon"), pwzName, COST_PERFMON_UNREGISTER);
190 ExitOnFailure(hr, "failed to schedule UnregisterPerfmon action");
191 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"RollbackUnregisterPerfmon"), pwzFile, COST_PERFMON_REGISTER);
192 ExitOnFailure(hr, "failed to schedule RollbackUnregisterPerfmon action");
193 }
194
195 if (hr == E_NOMOREITEMS)
196 {
197 hr = S_OK;
198 }
199 ExitOnFailure(hr, "Failure while processing PerfMon");
200
201 hr = S_OK;
202
203LExit:
204 ReleaseStr(pwzData);
205 ReleaseStr(pwzName);
206 ReleaseStr(pwzFile);
207
208 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
209 return WcaFinalize(er);
210}
211
212
213
214static HRESULT ProcessPerformanceCategory(
215 __in MSIHANDLE hInstall,
216 __in BOOL fInstall
217 )
218{
219 HRESULT hr = S_OK;
220 DWORD er = ERROR_SUCCESS;
221
222 PMSIHANDLE hView, hRec;
223 LPWSTR pwzId = NULL;
224 LPWSTR pwzComponent = NULL;
225 LPWSTR pwzName = NULL;
226 LPWSTR pwzData = NULL;
227 INSTALLSTATE isInstalled, isAction;
228
229 LPWSTR pwzCustomActionData = NULL;
230
231 // check to see if necessary tables are specified
232 if (S_OK != WcaTableExists(L"PerformanceCategory"))
233 {
234 ExitFunction1(hr = S_FALSE);
235 }
236
237 hr = WcaOpenExecuteView(vcsPerfCounterDataQuery, &hView);
238 ExitOnFailure(hr, "failed to open view on PerformanceCategory table");
239 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
240 {
241 hr = WcaGetRecordString(hRec, pcdqId, &pwzId);
242 ExitOnFailure(hr, "Failed to get id for PerformanceCategory.");
243
244 // Check to see if the Component is being installed or uninstalled
245 // when we are processing the same.
246 hr = WcaGetRecordString(hRec, pcdqComponent, &pwzComponent);
247 ExitOnFailure(hr, "Failed to get Component for PerformanceCategory: %ls", pwzId);
248
249 er = ::MsiGetComponentStateW(hInstall, pwzComponent, &isInstalled, &isAction);
250 hr = HRESULT_FROM_WIN32(er);
251 ExitOnFailure(hr, "Failed to get Component state for PerformanceCategory: %ls", pwzId);
252
253 if ((fInstall && !WcaIsInstalling(isInstalled, isAction)) ||
254 (!fInstall && !WcaIsUninstalling(isInstalled, isAction)))
255 {
256 continue;
257 }
258
259 hr = WcaGetRecordString(hRec, pcdqName, &pwzName);
260 ExitOnFailure(hr, "Failed to get Name for PerformanceCategory: %ls", pwzId);
261 hr = WcaWriteStringToCaData(pwzName, &pwzCustomActionData);
262 ExitOnFailure(hr, "Failed to add Name to CustomActionData for PerformanceCategory: %ls", pwzId);
263
264 hr = WcaGetRecordString(hRec, pcdqIniData, &pwzData);
265 ExitOnFailure(hr, "Failed to get IniData for PerformanceCategory: %ls", pwzId);
266 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
267 ExitOnFailure(hr, "Failed to add IniData to CustomActionData for PerformanceCategory: %ls", pwzId);
268
269 hr = WcaGetRecordString(hRec, pcdqConstantData, &pwzData);
270 ExitOnFailure(hr, "Failed to get ConstantData for PerformanceCategory: %ls", pwzId);
271 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
272 ExitOnFailure(hr, "Failed to add ConstantData to CustomActionData for PerformanceCategory: %ls", pwzId);
273 }
274
275 if (hr == E_NOMOREITEMS)
276 {
277 hr = S_OK;
278 }
279 ExitOnFailure(hr, "Failure while processing PerformanceCategory table.");
280
281 // If there was any data built up, schedule it for execution.
282 if (pwzCustomActionData)
283 {
284 if (fInstall)
285 {
286 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"RollbackRegisterPerfCounterData"), pwzCustomActionData, COST_PERFMON_UNREGISTER);
287 ExitOnFailure(hr, "Failed to schedule RollbackRegisterPerfCounterData action for PerformanceCategory: %ls", pwzId);
288
289 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"RegisterPerfCounterData"), pwzCustomActionData, COST_PERFMON_REGISTER);
290 ExitOnFailure(hr, "Failed to schedule RegisterPerfCounterData action for PerformanceCategory: %ls", pwzId);
291 }
292 else
293 {
294 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"RollbackUnregisterPerfCounterData"), pwzCustomActionData, COST_PERFMON_REGISTER);
295 ExitOnFailure(hr, "Failed to schedule RollbackUnregisterPerfCounterData action for PerformanceCategory: %ls", pwzId);
296
297 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"UnregisterPerfCounterData"), pwzCustomActionData, COST_PERFMON_UNREGISTER);
298 ExitOnFailure(hr, "Failed to schedule UnregisterPerfCounterData action for PerformanceCategory: %ls", pwzId);
299 }
300 }
301
302LExit:
303 ReleaseStr(pwzCustomActionData);
304 ReleaseStr(pwzData);
305 ReleaseStr(pwzName);
306 ReleaseStr(pwzComponent);
307 ReleaseStr(pwzId);
308
309 return hr;
310}
diff --git a/src/ca/scaperfexec.cpp b/src/ca/scaperfexec.cpp
new file mode 100644
index 00000000..bf58c8d0
--- /dev/null
+++ b/src/ca/scaperfexec.cpp
@@ -0,0 +1,423 @@
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
5typedef DWORD (STDAPICALLTYPE *PFNPERFCOUNTERTEXTSTRINGS)(LPWSTR lpCommandLine, BOOL bQuietModeArg);
6
7static HRESULT ExecutePerfCounterData(
8 __in MSIHANDLE hInstall,
9 __in BOOL fInstall
10 );
11static HRESULT CreateDataFile(
12 __in LPCWSTR wzTempFolder,
13 __in LPCWSTR wzData,
14 __in BOOL fIniData,
15 __out HANDLE *phFile,
16 __out_opt LPWSTR *ppwzFile
17 );
18
19
20/********************************************************************
21 RegisterPerfCounterData - CUSTOM ACTION ENTRY POINT for registering
22 performance counters
23
24 Input: deferred CustomActionData: wzName\twzIniData\twzConstantData\twzName\twzIniData\twzConstantData\t...
25*******************************************************************/
26extern "C" UINT __stdcall RegisterPerfCounterData(
27 __in MSIHANDLE hInstall
28 )
29{
30 // AssertSz(FALSE, "debug RegisterPerfCounterData()");
31 HRESULT hr = S_OK;
32 DWORD er = ERROR_SUCCESS;
33
34 hr = WcaInitialize(hInstall, "RegisterPerfCounterData");
35 ExitOnFailure(hr, "Failed to initialize RegisterPerfCounterData.");
36
37 hr = ExecutePerfCounterData(hInstall, TRUE);
38 MessageExitOnFailure(hr, msierrInstallPerfCounterData, "Failed to execute PerformanceCategory table.");
39
40LExit:
41 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
42 return WcaFinalize(er);
43}
44
45
46/********************************************************************
47 UnregisterPerfCounterData - CUSTOM ACTION ENTRY POINT for registering
48 performance counters
49
50 Input: deferred CustomActionData: wzName\twzIniData\twzConstantData\twzName\twzIniData\twzConstantData\t...
51*******************************************************************/
52extern "C" UINT __stdcall UnregisterPerfCounterData(
53 __in MSIHANDLE hInstall
54 )
55{
56 // AssertSz(FALSE, "debug UnregisterPerfCounterData()");
57 HRESULT hr = S_OK;
58 DWORD er = ERROR_SUCCESS;
59
60 hr = WcaInitialize(hInstall, "UnregisterPerfCounterData");
61 ExitOnFailure(hr, "Failed to initialize UnregisterPerfCounterData.");
62
63 hr = ExecutePerfCounterData(hInstall, FALSE);
64 MessageExitOnFailure(hr, msierrUninstallPerfCounterData, "Failed to execute PerformanceCategory table.");
65
66LExit:
67 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
68 return WcaFinalize(er);
69}
70
71
72/********************************************************************
73 RegisterPerfmon - CUSTOM ACTION ENTRY POINT for registering
74 counters
75
76 Input: deferred CustomActionData -
77 wzFile or wzName
78*******************************************************************/
79extern "C" UINT __stdcall RegisterPerfmon(
80 __in MSIHANDLE hInstall
81 )
82{
83// Assert(FALSE);
84 UINT er = ERROR_SUCCESS;
85 HRESULT hr = S_OK;
86 LPWSTR pwzData = NULL;
87
88 HMODULE hMod = NULL;
89 PFNPERFCOUNTERTEXTSTRINGS pfnPerfCounterTextString;
90 DWORD_PTR dwRet;
91 LPWSTR pwzShortPath = NULL;
92 DWORD_PTR cchShortPath = MAX_PATH;
93 DWORD_PTR cchShortPathLength = 0;
94
95 LPWSTR pwzCommand = NULL;
96
97 hr = WcaInitialize(hInstall, "RegisterPerfmon");
98 ExitOnFailure(hr, "failed to initialize");
99
100 hr = WcaGetProperty(L"CustomActionData", &pwzData);
101 ExitOnFailure(hr, "failed to get CustomActionData");
102
103 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
104
105 // do the perfmon registration
106 if (NULL == hMod)
107 {
108 hr = LoadSystemLibrary(L"loadperf.dll", &hMod);
109 }
110 ExitOnFailure(hr, "failed to load DLL for PerfMon");
111
112 pfnPerfCounterTextString = (PFNPERFCOUNTERTEXTSTRINGS)::GetProcAddress(hMod, "LoadPerfCounterTextStringsW");
113 ExitOnNullWithLastError(pfnPerfCounterTextString, hr, "failed to get DLL function for PerfMon");
114
115 hr = StrAlloc(&pwzShortPath, cchShortPath);
116 ExitOnFailure(hr, "failed to allocate string");
117
118 WcaLog(LOGMSG_VERBOSE, "Converting DLL path to short format: %ls", pwzData);
119 cchShortPathLength = ::GetShortPathNameW(pwzData, pwzShortPath, cchShortPath);
120 if (cchShortPathLength > cchShortPath)
121 {
122 cchShortPath = cchShortPathLength + 1;
123 hr = StrAlloc(&pwzShortPath, cchShortPath);
124 ExitOnFailure(hr, "failed to allocate string");
125
126 cchShortPathLength = ::GetShortPathNameW(pwzData, pwzShortPath, cchShortPath);
127 }
128
129 if (0 == cchShortPathLength)
130 {
131 ExitOnLastError(hr, "failed to get short path format of path: %ls", pwzData);
132 }
133
134 hr = StrAllocFormatted(&pwzCommand, L"lodctr \"%s\"", pwzShortPath);
135 ExitOnFailure(hr, "failed to format lodctr string");
136
137 WcaLog(LOGMSG_VERBOSE, "RegisterPerfmon running command: '%ls'", pwzCommand);
138 dwRet = (*pfnPerfCounterTextString)(pwzCommand, TRUE);
139 if (dwRet != ERROR_SUCCESS && dwRet != ERROR_ALREADY_EXISTS)
140 {
141 hr = HRESULT_FROM_WIN32(dwRet);
142 MessageExitOnFailure(hr, msierrPERFMONFailedRegisterDLL, "failed to register with PerfMon, DLL: %ls", pwzData);
143 }
144
145 hr = S_OK;
146LExit:
147 ReleaseStr(pwzData);
148
149 if (FAILED(hr))
150 er = ERROR_INSTALL_FAILURE;
151 return WcaFinalize(er);
152}
153
154
155extern "C" UINT __stdcall UnregisterPerfmon(
156 __in MSIHANDLE hInstall
157 )
158{
159// Assert(FALSE);
160 UINT er = ERROR_SUCCESS;
161 HRESULT hr = S_OK;
162 LPWSTR pwzData = NULL;
163
164 HMODULE hMod = NULL;
165 PFNPERFCOUNTERTEXTSTRINGS pfnPerfCounterTextString;
166 DWORD dwRet;
167 WCHAR wz[255];
168
169 hr = WcaInitialize(hInstall, "UnregisterPerfmon");
170 ExitOnFailure(hr, "failed to initialize");
171
172 hr = WcaGetProperty(L"CustomActionData", &pwzData);
173 ExitOnFailure(hr, "failed to get CustomActionData");
174
175 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
176
177 // do the perfmon unregistration
178 hr = E_FAIL;
179 if (hMod == NULL)
180 {
181 hr = LoadSystemLibrary(L"loadperf.dll", &hMod);
182 }
183 ExitOnFailure(hr, "failed to load DLL for PerfMon");
184
185 pfnPerfCounterTextString = (PFNPERFCOUNTERTEXTSTRINGS)::GetProcAddress(hMod, "UnloadPerfCounterTextStringsW");
186 ExitOnNullWithLastError(pfnPerfCounterTextString, hr, "failed to get DLL function for PerfMon");
187
188 hr = ::StringCchPrintfW(wz, countof(wz), L"unlodctr \"%s\"", pwzData);
189 ExitOnFailure(hr, "Failed to format unlodctr string with: %ls", pwzData);
190 WcaLog(LOGMSG_VERBOSE, "UnregisterPerfmon running command: '%ls'", wz);
191 dwRet = (*pfnPerfCounterTextString)(wz, TRUE);
192 // if the counters aren't registered, then OK to continue
193 if (dwRet != ERROR_SUCCESS && dwRet != ERROR_FILE_NOT_FOUND && dwRet != ERROR_BADKEY)
194 {
195 hr = HRESULT_FROM_WIN32(dwRet);
196 MessageExitOnFailure(hr, msierrPERFMONFailedUnregisterDLL, "failed to unregsister with PerfMon, DLL: %ls", pwzData);
197 }
198
199 hr = S_OK;
200LExit:
201 ReleaseStr(pwzData);
202
203 if (FAILED(hr))
204 er = ERROR_INSTALL_FAILURE;
205 return WcaFinalize(er);
206}
207
208
209static HRESULT ExecutePerfCounterData(
210 __in MSIHANDLE /*hInstall*/,
211 __in BOOL fInstall
212 )
213{
214 HRESULT hr = S_OK;
215 DWORD er = ERROR_SUCCESS;
216
217 HMODULE hModule = NULL;
218 PFNPERFCOUNTERTEXTSTRINGS pfnPerfCounterTextString = NULL;
219 LPCWSTR wzPrefix = NULL;
220
221 LPWSTR pwzCustomActionData = NULL;
222 LPWSTR pwz = NULL;
223
224 LPWSTR pwzName = NULL;
225 LPWSTR pwzIniData = NULL;
226 LPWSTR pwzConstantData = NULL;
227 LPWSTR pwzTempFolder = NULL;
228 LPWSTR pwzIniFile = NULL;
229 LPWSTR pwzExecute = NULL;
230
231 HANDLE hIniData = INVALID_HANDLE_VALUE;
232 HANDLE hConstantData = INVALID_HANDLE_VALUE;
233
234 // Load the system performance counter helper DLL then get the appropriate
235 // entrypoint out of it. Fortunately, they have the same signature so we
236 // can use one function pointer to point to both.
237 hr = LoadSystemLibrary(L"loadperf.dll", &hModule);
238 ExitOnFailure(hr, "failed to load DLL for PerfMon");
239
240 if (fInstall)
241 {
242 wzPrefix = L"lodctr";
243 pfnPerfCounterTextString = (PFNPERFCOUNTERTEXTSTRINGS)::GetProcAddress(hModule, "LoadPerfCounterTextStringsW");
244 }
245 else
246 {
247 wzPrefix = L"unlodctr";
248 pfnPerfCounterTextString = (PFNPERFCOUNTERTEXTSTRINGS)::GetProcAddress(hModule, "UnloadPerfCounterTextStringsW");
249 }
250 ExitOnNullWithLastError(pfnPerfCounterTextString, hr, "Failed to get DLL function for PerfMon");
251
252 // Now get the CustomActionData and execute it.
253 hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData);
254 ExitOnFailure(hr, "Failed to get CustomActionData.");
255
256 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
257
258 pwz = pwzCustomActionData;
259
260 while (S_OK == (hr = WcaReadStringFromCaData(&pwz, &pwzName)))
261 {
262 hr = WcaReadStringFromCaData(&pwz, &pwzIniData);
263 ExitOnFailure(hr, "Failed to read IniData from custom action data.");
264
265 hr = WcaReadStringFromCaData(&pwz, &pwzConstantData);
266 ExitOnFailure(hr, "Failed to read ConstantData from custom action data.");
267
268 if (fInstall)
269 {
270 hr = PathCreateTempDirectory(NULL, L"WIXPF%03x", 999, &pwzTempFolder);
271 ExitOnFailure(hr, "Failed to create temp directory.");
272
273 hr = CreateDataFile(pwzTempFolder, pwzIniData, TRUE, &hIniData, &pwzIniFile);
274 ExitOnFailure(hr, "Failed to create .ini file for performance counter category: %ls", pwzName);
275
276 hr = CreateDataFile(pwzTempFolder, pwzConstantData, FALSE, &hConstantData, NULL);
277 ExitOnFailure(hr, "Failed to create .h file for performance counter category: %ls", pwzName);
278
279 hr = StrAllocFormatted(&pwzExecute, L"%s \"%s\"", wzPrefix, pwzIniFile);
280 ExitOnFailure(hr, "Failed to allocate string to execute.");
281
282 // Execute the install.
283 er = (*pfnPerfCounterTextString)(pwzExecute, TRUE);
284 hr = HRESULT_FROM_WIN32(er);
285 ExitOnFailure(hr, "Failed to execute install of performance counter category: %ls", pwzName);
286
287 if (INVALID_HANDLE_VALUE != hIniData)
288 {
289 ::CloseHandle(hIniData);
290 hIniData = INVALID_HANDLE_VALUE;
291 }
292
293 if (INVALID_HANDLE_VALUE != hConstantData)
294 {
295 ::CloseHandle(hConstantData);
296 hConstantData = INVALID_HANDLE_VALUE;
297 }
298
299 DirEnsureDelete(pwzTempFolder, TRUE, TRUE);
300 }
301 else
302 {
303 hr = StrAllocFormatted(&pwzExecute, L"%s \"%s\"", wzPrefix, pwzName);
304 ExitOnFailure(hr, "Failed to allocate string to execute.");
305
306 // Execute the uninstall and if the counter isn't registered then ignore
307 // the error since it won't hurt anything.
308 er = (*pfnPerfCounterTextString)(pwzExecute, TRUE);
309 if (ERROR_FILE_NOT_FOUND == er || ERROR_BADKEY == er)
310 {
311 er = ERROR_SUCCESS;
312 }
313 hr = HRESULT_FROM_WIN32(er);
314 ExitOnFailure(hr, "Failed to execute uninstall of performance counter category: %ls", pwzName);
315 }
316 }
317
318 if (E_NOMOREITEMS == hr) // If there are no more items, all is well
319 {
320 hr = S_OK;
321 }
322 ExitOnFailure(hr, "Failed to execute all perf counter data.");
323
324 hr = S_OK;
325
326LExit:
327 if (INVALID_HANDLE_VALUE != hIniData)
328 {
329 ::CloseHandle(hIniData);
330 }
331
332 if (INVALID_HANDLE_VALUE != hConstantData)
333 {
334 ::CloseHandle(hConstantData);
335 }
336
337 ReleaseStr(pwzExecute);
338 ReleaseStr(pwzIniFile);
339 ReleaseStr(pwzTempFolder);
340 ReleaseStr(pwzConstantData);
341 ReleaseStr(pwzIniData);
342 ReleaseStr(pwzName);
343 ReleaseStr(pwzCustomActionData);
344
345 if (hModule)
346 {
347 ::FreeLibrary(hModule);
348 }
349
350 return hr;
351}
352
353
354static HRESULT CreateDataFile(
355 __in LPCWSTR wzTempFolder,
356 __in LPCWSTR wzData,
357 __in BOOL fIniData,
358 __out HANDLE *phFile,
359 __out_opt LPWSTR *ppwzFile
360 )
361{
362 HRESULT hr = S_OK;
363 HANDLE hFile = INVALID_HANDLE_VALUE;
364 LPWSTR pwzFile = NULL;
365 LPSTR pszData = NULL;
366 DWORD cbData = 0;
367 DWORD cbWritten = 0;
368
369 // Convert the data to UTF-8 because lodctr/unloctr
370 // doesn't like unicode.
371 hr = StrAnsiAllocString(&pszData, wzData, 0, CP_UTF8);
372 ExitOnFailure(hr, "Failed to covert data to ANSI.");
373
374 cbData = lstrlenA(pszData);
375
376 // Concatenate the paths together, open the file data file
377 // and dump the data in there.
378 hr = StrAllocString(&pwzFile, wzTempFolder, 0);
379 ExitOnFailure(hr, "Failed to copy temp directory name.");
380
381 hr = StrAllocConcat(&pwzFile, L"wixperf", 0);
382 ExitOnFailure(hr, "Failed to add name of file.");
383
384 hr = StrAllocConcat(&pwzFile, fIniData ? L".ini" : L".h", 0);
385 ExitOnFailure(hr, "Failed to add extension of file.");
386
387 hFile = ::CreateFileW(pwzFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
388 if (INVALID_HANDLE_VALUE == hFile)
389 {
390 ExitWithLastError(hr, "Failed to open new temp file: %ls", pwzFile);
391 }
392
393 if (!::WriteFile(hFile, pszData, cbData, &cbWritten, NULL))
394 {
395 ExitWithLastError(hr, "Failed to write data to new temp file: %ls", pwzFile);
396 }
397
398 if (INVALID_HANDLE_VALUE != hFile)
399 {
400 ::CloseHandle(hFile);
401 hFile = INVALID_HANDLE_VALUE;
402 }
403
404 // Return the requested values.
405 *phFile = hFile;
406 hFile = INVALID_HANDLE_VALUE;
407
408 if (ppwzFile)
409 {
410 *ppwzFile = pwzFile;
411 pwzFile = NULL;
412 }
413
414LExit:
415 if (INVALID_HANDLE_VALUE != hFile)
416 {
417 ::CloseHandle(hFile);
418 }
419 ReleaseStr(pszData);
420 ReleaseStr(pwzFile);
421
422 return hr;
423}
diff --git a/src/ca/scasched.cpp b/src/ca/scasched.cpp
new file mode 100644
index 00000000..ba230a9e
--- /dev/null
+++ b/src/ca/scasched.cpp
@@ -0,0 +1,127 @@
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/********************************************************************
7ConfigureSmb - CUSTOM ACTION ENTRY POINT for installing fileshare settings
8
9********************************************************************/
10extern "C" UINT __stdcall ConfigureSmbInstall(
11 __in MSIHANDLE hInstall
12 )
13{
14 HRESULT hr = S_OK;
15 UINT er = ERROR_SUCCESS;
16
17 SCA_SMB* pssList = NULL;
18
19 // initialize
20 hr = WcaInitialize(hInstall, "ConfigureSmbInstall");
21 ExitOnFailure(hr, "Failed to initialize");
22
23 // check to see if necessary tables are specified
24 if (WcaTableExists(L"FileShare") != S_OK)
25 {
26 WcaLog(LOGMSG_VERBOSE, "Skipping SMB CustomAction, no FileShare table");
27 ExitFunction1(hr = S_FALSE);
28 }
29
30 hr = ScaSmbRead(&pssList);
31 ExitOnFailure(hr, "failed to read FileShare table");
32
33 hr = ScaSmbInstall(pssList);
34 ExitOnFailure(hr, "failed to install FileShares");
35
36LExit:
37 if (pssList)
38 ScaSmbFreeList(pssList);
39
40 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
41 return WcaFinalize(er);
42}
43
44
45/********************************************************************
46ConfigureSmb - CUSTOM ACTION ENTRY POINT for installing fileshare settings
47
48********************************************************************/
49extern "C" UINT __stdcall ConfigureSmbUninstall(
50 __in MSIHANDLE hInstall
51 )
52{
53 HRESULT hr = S_OK;
54 UINT er = ERROR_SUCCESS;
55
56 SCA_SMB* pssList = NULL;
57
58 // initialize
59 hr = WcaInitialize(hInstall, "ConfigureSmbUninstall");
60 ExitOnFailure(hr, "Failed to initialize");
61
62 // check to see if necessary tables are specified
63 if (WcaTableExists(L"FileShare") != S_OK)
64 {
65 WcaLog(LOGMSG_VERBOSE, "Skipping SMB CustomAction, no FileShare table");
66 ExitFunction1(hr = S_FALSE);
67 }
68
69 hr = ScaSmbRead(&pssList);
70 ExitOnFailure(hr, "failed to read FileShare table");
71
72 hr = ScaSmbUninstall(pssList);
73 ExitOnFailure(hr, "failed to uninstall FileShares");
74
75LExit:
76 if (pssList)
77 ScaSmbFreeList(pssList);
78
79 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
80 return WcaFinalize(er);
81}
82
83
84/********************************************************************
85ConfigureUsers - CUSTOM ACTION ENTRY POINT for installing users
86
87********************************************************************/
88extern "C" UINT __stdcall ConfigureUsers(
89 __in MSIHANDLE hInstall
90 )
91{
92 //AssertSz(0, "Debug ConfigureUsers");
93
94 HRESULT hr = S_OK;
95 UINT er = ERROR_SUCCESS;
96
97 BOOL fInitializedCom = FALSE;
98 SCA_USER* psuList = NULL;
99
100 // initialize
101 hr = WcaInitialize(hInstall, "ConfigureUsers");
102 ExitOnFailure(hr, "Failed to initialize");
103
104 hr = ::CoInitialize(NULL);
105 ExitOnFailure(hr, "failed to initialize COM");
106 fInitializedCom = TRUE;
107
108 hr = ScaUserRead(&psuList);
109 ExitOnFailure(hr, "failed to read User table");
110
111 hr = ScaUserExecute(psuList);
112 ExitOnFailure(hr, "failed to add/remove User actions");
113
114LExit:
115 if (psuList)
116 {
117 ScaUserFreeList(psuList);
118 }
119
120 if (fInitializedCom)
121 {
122 ::CoUninitialize();
123 }
124
125 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
126 return WcaFinalize(er);
127} \ No newline at end of file
diff --git a/src/ca/scasmb.h b/src/ca/scasmb.h
new file mode 100644
index 00000000..7dbeb14d
--- /dev/null
+++ b/src/ca/scasmb.h
@@ -0,0 +1,46 @@
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 "scauser.h"
6
7// structs
8// Structure used to hold and extra user/permission pairs from the FileSharePermissions Table
9struct SCA_SMB_EX_USER_PERMS
10{
11 int nPermissions;
12 ACCESS_MODE accessMode;
13 SCA_USER scau;
14 SCA_SMB_EX_USER_PERMS* pExUserPermsNext;
15};
16
17struct SCA_SMB // hungarian ss
18{
19 WCHAR wzId[MAX_DARWIN_KEY + 1];
20 WCHAR wzShareName[MAX_DARWIN_KEY + 1];
21 WCHAR wzDescription[MAX_DARWIN_COLUMN + 1];
22 WCHAR wzComponent[MAX_DARWIN_KEY + 1];
23 WCHAR wzDirectory[MAX_PATH + 1];
24
25 int nUserPermissionCount;
26 int nPermissions;
27 SCA_SMB_EX_USER_PERMS* pExUserPerms;
28
29 INSTALLSTATE isInstalled, isAction;
30
31 BOOL fUseIntegratedAuth;
32 BOOL fLegacyUserProvided;
33 struct SCA_USER scau;
34
35 struct SCA_SMB* pssNext;
36};
37
38
39#define RESERVED 0
40
41// schedule prototypes
42HRESULT ScaSmbRead(SCA_SMB** ppssList);
43HRESULT ScaSmbExPermsRead(SCA_SMB* pss);
44HRESULT ScaSmbUninstall(SCA_SMB* pssList);
45HRESULT ScaSmbInstall(SCA_SMB* pssList);
46void ScaSmbFreeList(SCA_SMB* pssList);
diff --git a/src/ca/scasmbexec.cpp b/src/ca/scasmbexec.cpp
new file mode 100644
index 00000000..ced3aa78
--- /dev/null
+++ b/src/ca/scasmbexec.cpp
@@ -0,0 +1,316 @@
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/********************************************************************
7 AllocateAcl - allocate an acl and populate it with this user and
8 permission information user could be user or domain\user
9
10********************************************************************/
11HRESULT AllocateAcl(SCA_SMBP* pssp, PACL* ppACL)
12{
13 HRESULT hr = S_OK;
14 EXPLICIT_ACCESSW* pEA = NULL;
15 DWORD cEA = 0;
16 DWORD dwCounter = 0;
17
18 PSID psid = NULL;
19 LPCWSTR wzUser = NULL;
20 DWORD nPermissions = 0;
21 DWORD nErrorReturn = 0;
22 ACCESS_MODE accessMode = NOT_USED_ACCESS;
23
24 cEA = pssp->dwUserPermissionCount + 1;
25 if (cEA >= MAXSIZE_T / sizeof(EXPLICIT_ACCESSW))
26 {
27 ExitOnFailure(hr = E_OUTOFMEMORY, "Too many user permissions to allocate: %u", cEA);
28 }
29
30 pEA = static_cast<EXPLICIT_ACCESSW*>(MemAlloc(cEA * sizeof(EXPLICIT_ACCESSW), TRUE));
31 ExitOnNull(pEA, hr, E_OUTOFMEMORY, "failed to allocate memory for explicit access structure");
32
33 // figure out how big the psid is
34 for (dwCounter = 0; dwCounter < pssp->dwUserPermissionCount; ++dwCounter)
35 {
36 wzUser = pssp->pUserPerms[dwCounter].wzUser;
37 nPermissions = pssp->pUserPerms[dwCounter].nPermissions;
38 accessMode = pssp->pUserPerms[dwCounter].accessMode;
39 //
40 // create the appropriate SID
41 //
42
43 // figure out the right user to put into the access block
44 if (0 == lstrcmpW(wzUser, L"Everyone"))
45 {
46 hr = AclGetWellKnownSid(WinWorldSid, &psid);
47 }
48 else if (0 == lstrcmpW(wzUser, L"Administrators"))
49 {
50 hr = AclGetWellKnownSid(WinBuiltinAdministratorsSid, &psid);
51 }
52 else if (0 == lstrcmpW(wzUser, L"LocalSystem"))
53 {
54 hr = AclGetWellKnownSid(WinLocalSystemSid, &psid);
55 }
56 else if (0 == lstrcmpW(wzUser, L"LocalService"))
57 {
58 hr = AclGetWellKnownSid(WinLocalServiceSid, &psid);
59 }
60 else if (0 == lstrcmpW(wzUser, L"NetworkService"))
61 {
62 hr = AclGetWellKnownSid(WinNetworkServiceSid, &psid);
63 }
64 else if (0 == lstrcmpW(wzUser, L"AuthenticatedUser"))
65 {
66 hr = AclGetWellKnownSid(WinAuthenticatedUserSid, &psid);
67 }
68 else if (0 == lstrcmpW(wzUser, L"Guests"))
69 {
70 hr = AclGetWellKnownSid(WinBuiltinGuestsSid, &psid);
71 }
72 else if(0 == lstrcmpW(wzUser, L"CREATOR OWNER"))
73 {
74 hr = AclGetWellKnownSid(WinCreatorOwnerSid, &psid);
75 }
76 else
77 {
78 hr = AclGetAccountSid(NULL, wzUser, &psid);
79 }
80 ExitOnFailure(hr, "failed to get sid for account: %ls", wzUser);
81
82 // we now have a valid pSid, fill in the EXPLICIT_ACCESS
83
84 /* Permissions options: (see sca.sdh for defined sdl options)
85 #define GENERIC_READ (0x80000000L) 2147483648
86 #define GENERIC_WRITE (0x40000000L) 1073741824
87 #define GENERIC_EXECUTE (0x20000000L) 536870912
88 #define GENERIC_ALL (0x10000000L) 268435456
89 */
90 pEA[dwCounter].grfAccessPermissions = nPermissions;
91 pEA[dwCounter].grfAccessMode = accessMode;
92 pEA[dwCounter].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
93#pragma prefast(push)
94#pragma prefast(disable:25029)
95 ::BuildTrusteeWithSidW(&(pEA[dwCounter].Trustee), psid);
96#pragma prefast(pop)
97 }
98
99 // create a new ACL that contains the ACE
100 *ppACL = NULL;
101#pragma prefast(push)
102#pragma prefast(disable:25029)
103 nErrorReturn = ::SetEntriesInAclW(dwCounter, pEA, NULL, ppACL);
104#pragma prefast(pop)
105 ExitOnFailure(hr = HRESULT_FROM_WIN32(nErrorReturn), "failed to allocate ACL");
106
107LExit:
108 if (psid)
109 {
110 AclFreeSid(psid);
111 }
112
113 ReleaseMem(pEA);
114
115 return hr;
116}
117
118
119
120/********************************************************************
121 FillShareInfo - fill the NetShareAdd data structure
122
123********************************************************************/
124void FillShareInfo(SHARE_INFO_502* psi, SCA_SMBP* pssp, PSECURITY_DESCRIPTOR pSD)
125{
126 psi->shi502_netname = pssp->wzKey;
127 psi->shi502_type = STYPE_DISKTREE;
128 psi->shi502_remark = pssp->wzDescription;
129 psi->shi502_permissions = 0; // not used
130 psi->shi502_max_uses = 0xFFFFFFFF;
131 psi->shi502_current_uses = 0;
132 psi->shi502_path = pssp->wzDirectory;
133 psi->shi502_passwd = NULL; // not file share perms
134 psi->shi502_reserved = 0;
135 psi->shi502_security_descriptor = pSD;
136}
137
138
139
140/* NET_API_STATUS return codes
141NERR_Success = 0
142NERR_DuplicateShare = 2118
143NERR_BufTooSmall = 2123
144NERR_NetNameNotFound = 2310
145NERR_RedirectedPath = 2117
146NERR_UnknownDevDir = 2116
147*/
148
149/********************************************************************
150 DoesShareExists - Does a share of this name exist on this computer?
151
152********************************************************************/
153HRESULT DoesShareExist(__in LPWSTR wzShareName)
154{
155 HRESULT hr = S_OK;
156 NET_API_STATUS s;
157 SHARE_INFO_502* psi = NULL;
158 s = ::NetShareGetInfo(NULL, wzShareName, 502, (BYTE**) &psi);
159
160 switch (s)
161 {
162 case NERR_Success:
163 hr = S_OK;
164 break;
165 case NERR_NetNameNotFound:
166 hr = E_FILENOTFOUND;
167 break;
168 default:
169 WcaLogError(s, "NetShareGetInfo returned an unexpected value.", NULL);
170 hr = HRESULT_FROM_WIN32(s);
171 break;
172 }
173
174 ::NetApiBufferFree(psi);
175
176 return hr;
177}
178
179
180
181/********************************************************************
182 CreateShare - create the file share on this computer
183
184********************************************************************/
185HRESULT CreateShare(SCA_SMBP* pssp)
186{
187 if (!pssp || !(pssp->wzKey))
188 return E_INVALIDARG;
189
190 HRESULT hr = S_OK;
191 PACL pACL = NULL;
192 SHARE_INFO_502 si;
193 NET_API_STATUS s;
194 DWORD dwParamErr = 0;
195
196 BOOL fShareExists = SUCCEEDED(DoesShareExist(pssp->wzKey));
197
198 PSECURITY_DESCRIPTOR pSD = static_cast<PSECURITY_DESCRIPTOR>(MemAlloc(SECURITY_DESCRIPTOR_MIN_LENGTH, TRUE));
199 ExitOnNull(pSD, hr, E_OUTOFMEMORY, "Failed to allocate memory for security descriptor");
200
201#pragma prefast(push)
202#pragma prefast(disable:25029)
203 if (!::InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
204#pragma prefast(pop)
205 {
206 ExitOnLastError(hr, "failed to initialize security descriptor");
207 }
208
209 hr = AllocateAcl(pssp, &pACL);
210 ExitOnFailure(hr, "Failed to allocate ACL for fileshare");
211
212 if (NULL == pACL)
213 {
214 WcaLog(LOGMSG_VERBOSE, "Ignoring NULL DACL.");
215 }
216#pragma prefast(push)
217#pragma prefast(disable:25028) // We only call this when pACL isn't NULL, so this call is safe according to the docs
218 // add the ACL to the security descriptor.
219 else if (!::SetSecurityDescriptorDacl(pSD, TRUE, pACL, FALSE))
220 {
221 ExitOnLastError(hr, "Failed to set security descriptor");
222 }
223#pragma prefast(pop)
224
225 // all that is left is to create the share
226 FillShareInfo(&si, pssp, pSD);
227
228 // Fail if the directory doesn't exist
229 if (!DirExists(pssp->wzDirectory, NULL))
230 ExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND), "Can't create a file share on directory that doesn't exist: %ls.", pssp->wzDirectory);
231
232 WcaLog(LOGMSG_VERBOSE, "Creating file share on directory \'%ls\' named \'%ls\'.", pssp->wzDirectory, pssp->wzKey);
233
234 if (!fShareExists)
235 {
236 s = ::NetShareAdd(NULL, 502, (BYTE*) &si, &dwParamErr);
237 WcaLog(LOGMSG_VERBOSE, "Adding a new file share.");
238 }
239 else
240 {
241 // The share exists. Write our new permissions over the top.
242 s = ::NetShareSetInfo(NULL, pssp->wzKey, 502, (BYTE*) &si, &dwParamErr);
243 WcaLog(LOGMSG_VERBOSE, "Setting permissions on existing share.");
244 }
245
246 if (NERR_Success != s)
247 {
248 hr = E_FAIL;
249 if (!fShareExists && NERR_DuplicateShare == s)
250 WcaLog(LOGMSG_VERBOSE, "Duplicate error when existence check failed.");
251
252 // error codes listed above.
253 ExitOnFailure(hr, "Failed to create/modify file share: Err: %d", s);
254 }
255
256LExit:
257 if (pACL)
258 {
259 ::LocalFree(pACL);
260 }
261
262 ReleaseMem(pSD);
263
264 return hr;
265}
266
267
268/********************************************************************
269 ScaEnsureSmbExists
270
271********************************************************************/
272HRESULT ScaEnsureSmbExists(SCA_SMBP* pssp)
273{
274 HRESULT hr = S_OK;
275
276 // create the share
277 hr = CreateShare(pssp);
278
279 return hr;
280}
281
282
283//
284// Delete File Shares - real work
285//
286
287/********************************************************************
288 ScaDropSmb - delete this file share from this computer
289
290********************************************************************/
291HRESULT ScaDropSmb(SCA_SMBP* pssp)
292{
293 HRESULT hr = S_OK;
294 NET_API_STATUS s;
295
296 hr = DoesShareExist(pssp->wzKey);
297
298 if (E_FILENOTFOUND == hr)
299 {
300 WcaLog(LOGMSG_VERBOSE, "Share doesn't exist, share removal skipped. (%ls)", pssp->wzKey);
301 ExitFunction1(hr = S_OK);
302
303 }
304
305 ExitOnFailure(hr, "Unable to detect share. (%ls)", pssp->wzKey);
306
307 s = ::NetShareDel(NULL, pssp->wzKey, 0);
308 if (NERR_Success != s)
309 {
310 hr = E_FAIL;
311 ExitOnFailure(hr, "Failed to remove file share: Err: %d", s);
312 }
313
314LExit:
315 return hr;
316}
diff --git a/src/ca/scasmbexec.h b/src/ca/scasmbexec.h
new file mode 100644
index 00000000..e3c8f8bb
--- /dev/null
+++ b/src/ca/scasmbexec.h
@@ -0,0 +1,27 @@
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
5struct SCA_SMBP_USER_PERMS
6{
7 DWORD nPermissions;
8 ACCESS_MODE accessMode;
9 WCHAR* wzUser;
10 //Not adding Password because I can't find anywhere that it is used
11};
12
13struct SCA_SMBP // hungarian ssp
14{
15 WCHAR* wzKey;
16 WCHAR* wzDescription;
17 WCHAR* wzComponent;
18 WCHAR* wzDirectory; // full path of the dir to share to
19
20 DWORD dwUserPermissionCount; //Count of SCA_SMBP_EX_USER_PERMS structures
21 SCA_SMBP_USER_PERMS* pUserPerms;
22 BOOL fUseIntegratedAuth;
23};
24
25
26HRESULT ScaEnsureSmbExists(SCA_SMBP* pssp);
27HRESULT ScaDropSmb(SCA_SMBP* pssp);
diff --git a/src/ca/scasmbsched.cpp b/src/ca/scasmbsched.cpp
new file mode 100644
index 00000000..72536d6d
--- /dev/null
+++ b/src/ca/scasmbsched.cpp
@@ -0,0 +1,639 @@
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/********************************************************************
7 Helper functions to maintain a list of file shares to create / remove
8
9********************************************************************/
10SCA_SMB* NewSmb()
11{
12 SCA_SMB* pss = (SCA_SMB*)MemAlloc(sizeof(SCA_SMB), TRUE);
13 Assert(pss);
14 return pss;
15}
16
17
18SCA_SMB_EX_USER_PERMS* NewExUserPermsSmb()
19{
20 SCA_SMB_EX_USER_PERMS* pExUserPerms = (SCA_SMB_EX_USER_PERMS*)MemAlloc(sizeof(SCA_SMB_EX_USER_PERMS), TRUE);
21 Assert(pExUserPerms);
22 return pExUserPerms;
23}
24
25
26SCA_SMB* AddSmbToList(SCA_SMB* pssList, SCA_SMB* pss)
27{
28 if (pssList)
29 {
30 SCA_SMB* pssT = pssList;
31 while (pssT->pssNext)
32 {
33 pssT = pssT->pssNext;
34 }
35
36 pssT->pssNext = pss;
37 }
38 else
39 {
40 pssList = pss;
41 }
42
43 return pssList;
44}
45
46
47SCA_SMB_EX_USER_PERMS* AddExUserPermsSmbToList(
48 SCA_SMB_EX_USER_PERMS* pExUserPermsList,
49 SCA_SMB_EX_USER_PERMS* pExUserPerms
50 )
51{
52 SCA_SMB_EX_USER_PERMS* pExUserPermsTemp = pExUserPermsList;
53 if (pExUserPermsList)
54 {
55 while (pExUserPermsTemp->pExUserPermsNext)
56 {
57 pExUserPermsTemp = pExUserPermsTemp->pExUserPermsNext;
58 }
59
60 pExUserPermsTemp->pExUserPermsNext = pExUserPerms;
61 }
62 else
63 {
64 pExUserPermsList = pExUserPerms;
65 }
66
67 return pExUserPermsList;
68}
69
70void ScaSmbFreeList(SCA_SMB* pssList)
71{
72 SCA_SMB* pssDelete = pssList;
73 while (pssList)
74 {
75 pssDelete = pssList;
76 pssList = pssList->pssNext;
77
78 MemFree(pssDelete);
79 }
80}
81
82void ScaExUserPermsSmbFreeList(SCA_SMB_EX_USER_PERMS* pExUserPermsList)
83{
84 SCA_SMB_EX_USER_PERMS* pExUserPermsDelete = pExUserPermsList;
85 while (pExUserPermsList)
86 {
87 pExUserPermsDelete = pExUserPermsList;
88 pExUserPermsList = pExUserPermsList->pExUserPermsNext;
89
90 MemFree(pExUserPermsDelete);
91 }
92}
93
94// sql query constants
95LPCWSTR vcsSmbQuery = L"SELECT `FileShare`, `ShareName`, `Description`, `Directory_`, "
96 L"`Component_`, `User_`, `Permissions` FROM `FileShare`";
97
98enum eSmbQuery {
99 ssqFileShare = 1,
100 ssqShareName,
101 ssqDescription,
102 ssqDirectory,
103 ssqComponent,
104 ssqUser,
105 ssqPermissions
106 };
107
108
109/********************************************************************
110 ScaSmbRead - read all of the information from the msi tables and
111 return a list of file share jobs to be done.
112
113********************************************************************/
114HRESULT ScaSmbRead(SCA_SMB** ppssList)
115{
116 HRESULT hr = S_OK;
117 UINT er = ERROR_SUCCESS;
118 PMSIHANDLE hView, hRec;
119
120 LPWSTR pwzData = NULL;
121
122 SCA_SMB* pss = NULL;
123 BOOL bUserPermissionsTableExists = FALSE;
124
125 if (S_OK != WcaTableExists(L"FileShare"))
126 {
127 WcaLog(LOGMSG_VERBOSE, "Skipping ScaSmbCreateShare() - FileShare table not present");
128 ExitFunction1(hr = S_FALSE);
129 }
130
131 if (S_OK == WcaTableExists(L"FileSharePermissions"))
132 {
133 bUserPermissionsTableExists = TRUE;
134 }
135 else
136 {
137 WcaLog(LOGMSG_VERBOSE, "No Additional Permissions - FileSharePermissions table not present");
138 }
139
140 WcaLog(LOGMSG_VERBOSE, "Reading File Share Tables");
141
142 // loop through all the fileshares
143 hr = WcaOpenExecuteView(vcsSmbQuery, &hView);
144 ExitOnFailure(hr, "Failed to open view on FileShare table");
145 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
146 {
147 pss = NewSmb();
148 if (!pss)
149 {
150 hr = E_OUTOFMEMORY;
151 break;
152 }
153 Assert(pss);
154 ::ZeroMemory(pss, sizeof(*pss));
155
156 hr = WcaGetRecordString(hRec, ssqFileShare, &pwzData);
157 ExitOnFailure(hr, "Failed to get FileShare.FileShare");
158 hr = ::StringCchCopyW(pss->wzId, countof(pss->wzId), pwzData);
159 ExitOnFailure(hr, "Failed to copy ID string to smb object");
160
161 hr = WcaGetRecordFormattedString(hRec, ssqShareName, &pwzData);
162 ExitOnFailure(hr, "Failed to get FileShare.ShareName");
163 hr = ::StringCchCopyW(pss->wzShareName, countof(pss->wzShareName), pwzData);
164 ExitOnFailure(hr, "Failed to copy share name string to smb object");
165
166 hr = WcaGetRecordString(hRec, ssqComponent, &pwzData);
167 ExitOnFailure(hr, "Failed to get Component for FileShare: '%ls'", pss->wzShareName);
168 hr = ::StringCchCopyW(pss->wzComponent, countof(pss->wzComponent), pwzData);
169 ExitOnFailure(hr, "Failed to copy component string to smb object");
170
171 hr = WcaGetRecordFormattedString(hRec, ssqDescription, &pwzData);
172 ExitOnFailure(hr, "Failed to get Share Description for FileShare: '%ls'", pss->wzShareName);
173 hr = ::StringCchCopyW(pss->wzDescription, countof(pss->wzDescription), pwzData);
174 ExitOnFailure(hr, "Failed to copy description string to smb object");
175
176 // get user info from the user table
177 hr = WcaGetRecordFormattedString(hRec, ssqUser, &pwzData);
178 ExitOnFailure(hr, "Failed to get User record for FileShare: '%ls'", pss->wzShareName);
179
180 // get component install state
181 er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pss->wzComponent, &pss->isInstalled, &pss->isAction);
182 hr = HRESULT_FROM_WIN32(er);
183 ExitOnFailure(hr, "Failed to get Component state for FileShare");
184
185 // if a user was specified
186 if (*pwzData)
187 {
188 pss->fUseIntegratedAuth = FALSE;
189 pss->fLegacyUserProvided = TRUE;
190 hr = ScaGetUser(pwzData, &pss->scau);
191 ExitOnFailure(hr, "Failed to get user information for fileshare: '%ls'", pss->wzShareName);
192 }
193 else
194 {
195 pss->fLegacyUserProvided = FALSE;
196 // TODO: figure out whether this is useful still
197 //pss->fUseIntegratedAuth = TRUE;
198 // integrated authorization doesn't have a User record
199 }
200
201 // get the share's directory
202 hr = WcaGetRecordString(hRec, ssqDirectory, &pwzData);
203 ExitOnFailure(hr, "Failed to get directory for FileShare: '%ls'", pss->wzShareName);
204
205 WCHAR wzPath[MAX_PATH];
206 DWORD dwLen;
207 dwLen = countof(wzPath);
208 // review: relevant for file shares?
209 if (INSTALLSTATE_SOURCE == pss->isAction)
210 {
211 er = ::MsiGetSourcePathW(WcaGetInstallHandle(), pwzData, wzPath, &dwLen);
212 }
213 else
214 {
215 er = ::MsiGetTargetPathW(WcaGetInstallHandle(), pwzData, wzPath, &dwLen);
216 }
217 hr = HRESULT_FROM_WIN32(er);
218 ExitOnFailure(hr, "Failed to get Source/TargetPath for Directory");
219
220 // If the path is to the root of a drive, then it needs a trailing backslash.
221 // Otherwise, it can't have a trailing backslash.
222 if (3 < dwLen)
223 {
224 if (wzPath[dwLen - 1] == L'\\')
225 {
226 wzPath[dwLen - 1] = 0;
227 }
228 }
229 else if (2 == dwLen && wzPath[1] == L':')
230 {
231 wzPath[2] = L'\\';
232 wzPath[3] = 0;
233 }
234
235 hr = ::StringCchCopyW(pss->wzDirectory, countof(pss->wzDirectory), wzPath);
236 ExitOnFailure(hr, "Failed to copy directory string to smb object");
237
238 hr = WcaGetRecordInteger(hRec, ssqPermissions, &pss->nPermissions);
239 ExitOnFailure(hr, "Failed to get FileShare.Permissions");
240
241 // Check to see if additional user & permissions are specified for this share
242 if (bUserPermissionsTableExists)
243 {
244 hr = ScaSmbExPermsRead(pss);
245 ExitOnFailure(hr, "Failed to get Additional File Share Permissions");
246 }
247
248 *ppssList = AddSmbToList(*ppssList, pss);
249 pss = NULL; // set the smb NULL so it doesn't accidentally get freed below
250 }
251
252 if (E_NOMOREITEMS == hr)
253 {
254 hr = S_OK;
255 }
256 ExitOnFailure(hr, "Failure occured while processing FileShare table");
257
258LExit:
259 // if anything was left over after an error clean it all up
260 if (pss)
261 {
262 ScaSmbFreeList(pss);
263 }
264
265 ReleaseStr(pwzData);
266
267 return hr;
268}
269
270
271/********************************************************************
272 RetrieveSMBShareUserPermList - retrieve SMB Share's user permission list
273
274********************************************************************/
275HRESULT RetrieveFileShareUserPerm(SCA_SMB* pss, SCA_SMB_EX_USER_PERMS** ppExUserPermsList, DWORD *pUserPermsCount)
276{
277 HRESULT hr = S_OK;
278 SHARE_INFO_502* psi = NULL;
279 NET_API_STATUS s;
280 BOOL bValid, bDaclDefaulted;
281 PACL acl = NULL;
282 PEXPLICIT_ACCESSW pEA = NULL;
283 ULONG nCount = 0;
284 DWORD er = ERROR_SUCCESS;
285 PSID pSID = NULL;
286 DWORD nUserNameSize = MAX_DARWIN_COLUMN;
287 DWORD nDomainNameSize = MAX_DARWIN_COLUMN;
288 SID_NAME_USE peUse;
289 DWORD dwCounter = 0;
290 SCA_SMB_EX_USER_PERMS* pExUserPermsList = NULL;
291 DWORD dwUserPermsCount = 0;
292
293 *pUserPermsCount = 0;
294 s = ::NetShareGetInfo(NULL, pss->wzShareName, 502, (LPBYTE*)&psi);
295 WcaLog(LOGMSG_VERBOSE, "retrieving permissions on existing file share.");
296 if (NERR_NetNameNotFound == s)
297 {
298 WcaLog(LOGMSG_VERBOSE, "File share has already been removed.");
299 ExitFunction1(hr = S_OK);
300 }
301 else if (NERR_Success != s || psi == NULL)
302 {
303 hr = E_FAIL;
304 ExitOnFailure(hr, "Failed to get share information with return code: %d", s);
305 }
306 if (!::GetSecurityDescriptorDacl(psi->shi502_security_descriptor, &bValid, &acl, &bDaclDefaulted) || !bValid)
307 {
308 ExitOnLastError(hr, "Failed to get acl from security descriptor");
309 }
310
311 er = ::GetExplicitEntriesFromAclW(acl, &nCount, &pEA);
312 hr = HRESULT_FROM_WIN32(er);
313 ExitOnFailure(hr, "Failed to get access entries from acl for file share %ls", pss->wzShareName);
314 for (dwCounter = 0; dwCounter < nCount; ++dwCounter)
315 {
316 if (TRUSTEE_IS_SID == pEA[dwCounter].Trustee.TrusteeForm)
317 {
318 SCA_SMB_EX_USER_PERMS* pExUserPerms = NewExUserPermsSmb();
319 ::ZeroMemory(pExUserPerms, sizeof(*pExUserPerms));
320 pExUserPermsList = AddExUserPermsSmbToList(pExUserPermsList, pExUserPerms);
321 pSID = (PSID)(pEA[dwCounter].Trustee.ptstrName);
322 if (!::LookupAccountSidW(NULL, pSID, pExUserPerms->scau.wzName, &nUserNameSize, pExUserPerms->scau.wzDomain, &nDomainNameSize, &peUse))
323 {
324 hr = E_FAIL;
325 ExitOnFailure(hr, "Failed to get account name from SID");
326 }
327 pExUserPerms->nPermissions = pEA[dwCounter].grfAccessPermissions;
328 pExUserPerms->accessMode = pEA[dwCounter].grfAccessMode;
329 ++dwUserPermsCount;
330 nUserNameSize = MAX_DARWIN_COLUMN;
331 nDomainNameSize = MAX_DARWIN_COLUMN;
332 }
333 }
334 *ppExUserPermsList = pExUserPermsList;
335 *pUserPermsCount = dwUserPermsCount;
336
337LExit:
338 if (psi)
339 {
340 ::NetApiBufferFree(psi);
341 }
342
343 if (pEA)
344 {
345 ::LocalFree(pEA);
346 }
347
348 return hr;
349}
350
351
352/********************************************************************
353 SchedCreateSmb - schedule one instance of a file share creation
354
355********************************************************************/
356HRESULT SchedCreateSmb(SCA_SMB* pss)
357{
358 HRESULT hr = S_OK;
359
360 WCHAR wzDomainUser[255]; // "domain\user"
361 SCA_SMB_EX_USER_PERMS* pExUserPermsList = NULL;
362 int nCounter = 0;
363 WCHAR* pwzRollbackCustomActionData = NULL;
364 WCHAR* pwzCustomActionData = NULL;
365
366 hr = WcaWriteStringToCaData(pss->wzShareName, &pwzRollbackCustomActionData);
367 ExitOnFailure(hr, "failed to add ShareName to CustomActionData");
368
369 hr = WcaWriteStringToCaData(pss->wzShareName, &pwzCustomActionData);
370 ExitOnFailure(hr, "failed to add ShareName to CustomActionData");
371
372 hr = WcaWriteStringToCaData(pss->wzDescription, &pwzCustomActionData);
373 ExitOnFailure(hr, "Failed to add server name to CustomActionData");
374
375 hr = WcaWriteStringToCaData(pss->wzDirectory, &pwzCustomActionData);
376 ExitOnFailure(hr, "Failed to add full path instance to CustomActionData");
377
378 hr = WcaWriteStringToCaData(pss->fUseIntegratedAuth ? L"1" : L"0", &pwzCustomActionData);
379 ExitOnFailure(hr, "Failed to add server name to CustomActionData");
380
381 if (pss->fLegacyUserProvided)
382 {
383 hr = WcaWriteIntegerToCaData(pss->nUserPermissionCount + 1, &pwzCustomActionData);
384 ExitOnFailure(hr, "Failed to add additional user permission count to CustomActionData");
385
386 hr = UserBuildDomainUserName(wzDomainUser, countof(wzDomainUser), pss->scau.wzName, pss->scau.wzDomain);
387 ExitOnFailure(hr, "Failed to build user and domain name for CustomActionData");
388 hr = WcaWriteStringToCaData(wzDomainUser, &pwzCustomActionData);
389 ExitOnFailure(hr, "Failed to add server Domain\\UserName to CustomActionData");
390
391 hr = WcaWriteIntegerToCaData(pss->nPermissions, &pwzCustomActionData);
392 ExitOnFailure(hr, "Failed to add permissions to CustomActionData");
393 }
394 else
395 {
396 hr = WcaWriteIntegerToCaData(pss->nUserPermissionCount, &pwzCustomActionData);
397 ExitOnFailure(hr, "Failed to add additional user permission count to CustomActionData");
398 }
399
400 if (pss->nUserPermissionCount > 0)
401 {
402 nCounter = 0;
403 for (pExUserPermsList = pss->pExUserPerms; pExUserPermsList; pExUserPermsList = pExUserPermsList->pExUserPermsNext)
404 {
405 Assert(nCounter < pss->nUserPermissionCount);
406
407 hr = UserBuildDomainUserName(wzDomainUser, countof(wzDomainUser), pExUserPermsList->scau.wzName, pExUserPermsList->scau.wzDomain);
408 ExitOnFailure(hr, "Failed to build user and domain name for CustomActionData");
409 hr = WcaWriteStringToCaData(wzDomainUser, &pwzCustomActionData);
410 ExitOnFailure(hr, "Failed to add server Domain\\UserName to CustomActionData");
411
412 hr = WcaWriteIntegerToCaData((int)pExUserPermsList->accessMode, &pwzCustomActionData);
413 ExitOnFailure(hr, "Failed to add access mode to CustomActionData");
414
415 hr = WcaWriteIntegerToCaData(pExUserPermsList->nPermissions, &pwzCustomActionData);
416 ExitOnFailure(hr, "Failed to add permissions to CustomActionData");
417 ++nCounter;
418 }
419 Assert(nCounter == pss->nUserPermissionCount);
420 }
421
422 // Schedule the rollback first
423 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"CreateSmbRollback"), pwzRollbackCustomActionData, COST_SMB_DROPSMB);
424 ExitOnFailure(hr, "Failed to schedule DropSmb action");
425
426 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"CreateSmb"), pwzCustomActionData, COST_SMB_CREATESMB);
427 ExitOnFailure(hr, "Failed to schedule CreateSmb action");
428
429LExit:
430 ReleaseStr(pwzRollbackCustomActionData);
431 ReleaseStr(pwzCustomActionData);
432
433 if (pExUserPermsList)
434 {
435 ScaExUserPermsSmbFreeList(pExUserPermsList);
436 }
437
438 return hr;
439}
440
441
442/********************************************************************
443 ScaSmbInstall - for every file share, schedule the create custom action
444
445********************************************************************/
446HRESULT ScaSmbInstall(SCA_SMB* pssList)
447{
448 HRESULT hr = S_FALSE; // assume nothing will be done
449 SCA_SMB* pss = NULL;
450
451 for (pss = pssList; pss; pss = pss->pssNext)
452 {
453 // if installing this component
454 if (WcaIsInstalling(pss->isInstalled, pss->isAction) )
455 {
456 hr = SchedCreateSmb(pss);
457 ExitOnFailure(hr, "Failed to schedule the creation of the fileshare: %ls", pss->wzShareName);
458 }
459 }
460
461LExit:
462 return hr;
463}
464
465
466/********************************************************************
467 SchedDropSmb - schedule one instance of a file share removal
468
469********************************************************************/
470HRESULT SchedDropSmb(SCA_SMB* pss)
471{
472 HRESULT hr = S_OK;
473
474 WCHAR* pwzCustomActionData = NULL;
475 WCHAR* pwzRollbackCustomActionData = NULL;
476 SCA_SMB_EX_USER_PERMS *pExUserPermsList = NULL;
477 SCA_SMB_EX_USER_PERMS *pExUserPerm = NULL;
478 WCHAR wzDomainUser[255]; // "domain\user"
479 DWORD dwUserPermsCount = 0;
480
481 // roll back DropSmb
482 hr = WcaWriteStringToCaData(pss->wzShareName, &pwzRollbackCustomActionData);
483 ExitOnFailure(hr, "failed to add ShareName to CustomActionData");
484
485 hr = WcaWriteStringToCaData(pss->wzDescription, &pwzRollbackCustomActionData);
486 ExitOnFailure(hr, "Failed to add server name to CustomActionData");
487
488 hr = WcaWriteStringToCaData(pss->wzDirectory, &pwzRollbackCustomActionData);
489 ExitOnFailure(hr, "Failed to add full path instance to CustomActionData");
490
491 hr = WcaWriteStringToCaData(L"1", &pwzRollbackCustomActionData);
492 ExitOnFailure(hr, "Failed to add useintegrated flag to CustomActionData");
493
494 hr = RetrieveFileShareUserPerm(pss, &pExUserPermsList, &dwUserPermsCount);
495 ExitOnFailure(hr, "Failed to retrieve SMBShare's user permissions");
496
497 hr = WcaWriteIntegerToCaData((int)dwUserPermsCount, &pwzRollbackCustomActionData);
498 ExitOnFailure(hr, "Failed to add additional user permission count to CustomActionData");
499
500 for (pExUserPerm = pExUserPermsList; pExUserPerm; pExUserPerm = pExUserPerm->pExUserPermsNext)
501 {
502 hr = UserBuildDomainUserName(wzDomainUser, countof(wzDomainUser), pExUserPerm->scau.wzName, pExUserPerm->scau.wzDomain);
503 ExitOnFailure(hr, "Failed to build user and domain name for CustomActionData");
504 hr = WcaWriteStringToCaData(wzDomainUser, &pwzRollbackCustomActionData);
505 ExitOnFailure(hr, "Failed to add server Domain\\UserName to CustomActionData");
506
507 hr = WcaWriteIntegerToCaData((int)pExUserPerm->accessMode, &pwzRollbackCustomActionData);
508 ExitOnFailure(hr, "Failed to add access mode to CustomActionData");
509
510 hr = WcaWriteIntegerToCaData(pExUserPerm->nPermissions, &pwzRollbackCustomActionData);
511 ExitOnFailure(hr, "Failed to add permissions to CustomActionData");
512 }
513
514 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"DropSmbRollback"), pwzRollbackCustomActionData, COST_SMB_CREATESMB);
515 ExitOnFailure(hr, "Failed to schedule DropSmbRollback action");
516
517 // DropSMB
518 hr = WcaWriteStringToCaData(pss->wzShareName, &pwzCustomActionData);
519 ExitOnFailure(hr, "failed to add ShareName to CustomActionData");
520
521 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"DropSmb"), pwzCustomActionData, COST_SMB_DROPSMB);
522 ExitOnFailure(hr, "Failed to schedule DropSmb action");
523
524LExit:
525 ReleaseStr(pwzCustomActionData);
526
527 if (pExUserPermsList)
528 {
529 ScaExUserPermsSmbFreeList(pExUserPermsList);
530 }
531
532 return hr;
533
534}
535
536
537/********************************************************************
538 ScaSmbUninstall - for every file share, schedule the drop custom action
539
540********************************************************************/
541HRESULT ScaSmbUninstall(SCA_SMB* pssList)
542{
543 HRESULT hr = S_FALSE; // assume nothing will be done
544 SCA_SMB* pss = NULL;
545
546 for (pss = pssList; pss; pss = pss->pssNext)
547 {
548 // if uninstalling this component
549 if (WcaIsUninstalling(pss->isInstalled, pss->isAction) )
550 {
551 hr = SchedDropSmb(pss);
552 ExitOnFailure(hr, "Failed to remove file share %ls", pss->wzShareName);
553 }
554 }
555
556LExit:
557 return hr;
558}
559
560LPCWSTR vcsSmbExUserPermsQuery = L"SELECT `FileShare_`,`User_`,`Permissions` "
561 L"FROM `FileSharePermissions` WHERE `FileShare_`=?";
562
563enum eSmbUserPermsQuery {
564 ssupqFileShare = 1,
565 ssupqUser,
566 ssupqPermissions
567
568};
569
570
571/********************************************************************
572 ScaSmbExPermsRead - for Every entry in File Permissions table add a
573 User Name & Permissions structure to the List
574
575********************************************************************/
576HRESULT ScaSmbExPermsRead(SCA_SMB* pss)
577{
578 HRESULT hr = S_OK;
579 PMSIHANDLE hView, hRec;
580
581 LPWSTR pwzData = NULL;
582 SCA_SMB_EX_USER_PERMS* pExUserPermsList = pss->pExUserPerms;
583 SCA_SMB_EX_USER_PERMS* pExUserPerms = NULL;
584 int nCounter = 0;
585
586 hRec = ::MsiCreateRecord(1);
587 hr = WcaSetRecordString(hRec, 1, pss->wzId);
588 ExitOnFailure(hr, "Failed to look up FileShare");
589
590 hr = WcaOpenView(vcsSmbExUserPermsQuery, &hView);
591 ExitOnFailure(hr, "Failed to open view on FileSharePermissions table");
592 hr = WcaExecuteView(hView, hRec);
593 ExitOnFailure(hr, "Failed to execute view on FileSharePermissions table");
594
595 // loop through all User/Permissions paris returned
596 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
597 {
598 pExUserPerms = NewExUserPermsSmb();
599 if (!pExUserPerms)
600 {
601 hr = E_OUTOFMEMORY;
602 break;
603 }
604 Assert(pExUserPerms);
605 ::ZeroMemory(pExUserPerms, sizeof(*pExUserPerms));
606
607 hr = WcaGetRecordString(hRec, ssupqUser, &pwzData);
608 ExitOnFailure(hr, "Failed to get FileSharePermissions.User");
609 hr = ScaGetUser(pwzData, &pExUserPerms->scau);
610 ExitOnFailure(hr, "Failed to get user information for fileshare: '%ls'", pss->wzShareName);
611
612 hr = WcaGetRecordInteger(hRec, ssupqPermissions, &pExUserPerms->nPermissions);
613 ExitOnFailure(hr, "Failed to get FileSharePermissions.Permissions");
614 pExUserPerms->accessMode = SET_ACCESS; // we only support SET_ACCESS here
615
616 pExUserPermsList = AddExUserPermsSmbToList(pExUserPermsList, pExUserPerms);
617 ++nCounter;
618 pExUserPerms = NULL; // set the smb NULL so it doesn't accidentally get freed below
619 }
620
621 if (E_NOMOREITEMS == hr)
622 {
623 hr = S_OK;
624 pss->pExUserPerms = pExUserPermsList;
625 pss->nUserPermissionCount = nCounter;
626 }
627 ExitOnFailure(hr, "Failure occured while processing FileShare table");
628
629LExit:
630 // if anything was left over after an error clean it all up
631 if (pExUserPerms)
632 {
633 ScaExUserPermsSmbFreeList(pExUserPerms);
634 }
635
636 ReleaseStr(pwzData);
637
638 return hr;
639}
diff --git a/src/ca/scauser.cpp b/src/ca/scauser.cpp
new file mode 100644
index 00000000..43317bdc
--- /dev/null
+++ b/src/ca/scauser.cpp
@@ -0,0 +1,676 @@
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
5LPCWSTR vcsUserQuery = L"SELECT `User`, `Component_`, `Name`, `Domain`, `Password` FROM `User` WHERE `User`=?";
6enum eUserQuery { vuqUser = 1, vuqComponent, vuqName, vuqDomain, vuqPassword };
7
8LPCWSTR vcsGroupQuery = L"SELECT `Group`, `Component_`, `Name`, `Domain` FROM `Group` WHERE `Group`=?";
9enum eGroupQuery { vgqGroup = 1, vgqComponent, vgqName, vgqDomain };
10
11LPCWSTR vcsUserGroupQuery = L"SELECT `User_`, `Group_` FROM `UserGroup` WHERE `User_`=?";
12enum eUserGroupQuery { vugqUser = 1, vugqGroup };
13
14LPCWSTR vActionableQuery = L"SELECT `User`,`Component_`,`Name`,`Domain`,`Password`,`Attributes` FROM `User` WHERE `Component_` IS NOT NULL";
15enum eActionableQuery { vaqUser = 1, vaqComponent, vaqName, vaqDomain, vaqPassword, vaqAttributes };
16
17
18static HRESULT AddUserToList(
19 __inout SCA_USER** ppsuList
20 );
21
22static HRESULT AddGroupToList(
23 __inout SCA_GROUP** ppsgList
24 );
25
26
27HRESULT __stdcall ScaGetUser(
28 __in LPCWSTR wzUser,
29 __out SCA_USER* pscau
30 )
31{
32 if (!wzUser || !pscau)
33 {
34 return E_INVALIDARG;
35 }
36
37 HRESULT hr = S_OK;
38 PMSIHANDLE hView, hRec;
39
40 LPWSTR pwzData = NULL;
41
42 // clear struct and bail right away if no user key was passed to search for
43 ::ZeroMemory(pscau, sizeof(*pscau));
44 if (!*wzUser)
45 {
46 ExitFunction1(hr = S_OK);
47 }
48
49 hRec = ::MsiCreateRecord(1);
50 hr = WcaSetRecordString(hRec, 1, wzUser);
51 ExitOnFailure(hr, "Failed to look up User");
52
53 hr = WcaOpenView(vcsUserQuery, &hView);
54 ExitOnFailure(hr, "Failed to open view on User table");
55 hr = WcaExecuteView(hView, hRec);
56 ExitOnFailure(hr, "Failed to execute view on User table");
57
58 hr = WcaFetchSingleRecord(hView, &hRec);
59 if (S_OK == hr)
60 {
61 hr = WcaGetRecordString(hRec, vuqUser, &pwzData);
62 ExitOnFailure(hr, "Failed to get User.User");
63 hr = ::StringCchCopyW(pscau->wzKey, countof(pscau->wzKey), pwzData);
64 ExitOnFailure(hr, "Failed to copy key string to user object");
65
66 hr = WcaGetRecordString(hRec, vuqComponent, &pwzData);
67 ExitOnFailure(hr, "Failed to get User.Component_");
68 hr = ::StringCchCopyW(pscau->wzComponent, countof(pscau->wzComponent), pwzData);
69 ExitOnFailure(hr, "Failed to copy component string to user object");
70
71 hr = WcaGetRecordFormattedString(hRec, vuqName, &pwzData);
72 ExitOnFailure(hr, "Failed to get User.Name");
73 hr = ::StringCchCopyW(pscau->wzName, countof(pscau->wzName), pwzData);
74 ExitOnFailure(hr, "Failed to copy name string to user object");
75
76 hr = WcaGetRecordFormattedString(hRec, vuqDomain, &pwzData);
77 ExitOnFailure(hr, "Failed to get User.Domain");
78 hr = ::StringCchCopyW(pscau->wzDomain, countof(pscau->wzDomain), pwzData);
79 ExitOnFailure(hr, "Failed to copy domain string to user object");
80
81 hr = WcaGetRecordFormattedString(hRec, vuqPassword, &pwzData);
82 ExitOnFailure(hr, "Failed to get User.Password");
83 hr = ::StringCchCopyW(pscau->wzPassword, countof(pscau->wzPassword), pwzData);
84 ExitOnFailure(hr, "Failed to copy password string to user object");
85 }
86 else if (E_NOMOREITEMS == hr)
87 {
88 WcaLog(LOGMSG_STANDARD, "Error: Cannot locate User.User='%ls'", wzUser);
89 hr = E_FAIL;
90 }
91 else
92 {
93 ExitOnFailure(hr, "Error or found multiple matching User rows");
94 }
95
96LExit:
97 ReleaseStr(pwzData);
98
99 return hr;
100}
101
102HRESULT __stdcall ScaGetUserDeferred(
103 __in LPCWSTR wzUser,
104 __in WCA_WRAPQUERY_HANDLE hUserQuery,
105 __out SCA_USER* pscau
106 )
107{
108 if (!wzUser || !pscau)
109 {
110 return E_INVALIDARG;
111 }
112
113 HRESULT hr = S_OK;
114 MSIHANDLE hRec, hRecTest;
115
116 LPWSTR pwzData = NULL;
117
118 // clear struct and bail right away if no user key was passed to search for
119 ::ZeroMemory(pscau, sizeof(*pscau));
120 if (!*wzUser)
121 {
122 ExitFunction1(hr = S_OK);
123 }
124
125 // Reset back to the first record
126 WcaFetchWrappedReset(hUserQuery);
127
128 hr = WcaFetchWrappedRecordWhereString(hUserQuery, vuqUser, wzUser, &hRec);
129 if (S_OK == hr)
130 {
131 hr = WcaFetchWrappedRecordWhereString(hUserQuery, vuqUser, wzUser, &hRecTest);
132 if (S_OK == hr)
133 {
134 AssertSz(FALSE, "Found multiple matching User rows");
135 }
136
137 hr = WcaGetRecordString(hRec, vuqUser, &pwzData);
138 ExitOnFailure(hr, "Failed to get User.User");
139 hr = ::StringCchCopyW(pscau->wzKey, countof(pscau->wzKey), pwzData);
140 ExitOnFailure(hr, "Failed to copy key string to user object (in deferred CA)");
141
142 hr = WcaGetRecordString(hRec, vuqComponent, &pwzData);
143 ExitOnFailure(hr, "Failed to get User.Component_");
144 hr = ::StringCchCopyW(pscau->wzComponent, countof(pscau->wzComponent), pwzData);
145 ExitOnFailure(hr, "Failed to copy component string to user object (in deferred CA)");
146
147 hr = WcaGetRecordString(hRec, vuqName, &pwzData);
148 ExitOnFailure(hr, "Failed to get User.Name");
149 hr = ::StringCchCopyW(pscau->wzName, countof(pscau->wzName), pwzData);
150 ExitOnFailure(hr, "Failed to copy name string to user object (in deferred CA)");
151
152 hr = WcaGetRecordString(hRec, vuqDomain, &pwzData);
153 ExitOnFailure(hr, "Failed to get User.Domain");
154 hr = ::StringCchCopyW(pscau->wzDomain, countof(pscau->wzDomain), pwzData);
155 ExitOnFailure(hr, "Failed to copy domain string to user object (in deferred CA)");
156
157 hr = WcaGetRecordString(hRec, vuqPassword, &pwzData);
158 ExitOnFailure(hr, "Failed to get User.Password");
159 hr = ::StringCchCopyW(pscau->wzPassword, countof(pscau->wzPassword), pwzData);
160 ExitOnFailure(hr, "Failed to copy password string to user object (in deferred CA)");
161 }
162 else if (E_NOMOREITEMS == hr)
163 {
164 WcaLog(LOGMSG_STANDARD, "Error: Cannot locate User.User='%ls'", wzUser);
165 hr = E_FAIL;
166 }
167 else
168 {
169 ExitOnFailure(hr, "Error fetching single User row");
170 }
171
172LExit:
173 ReleaseStr(pwzData);
174
175 return hr;
176}
177
178
179HRESULT __stdcall ScaGetGroup(
180 __in LPCWSTR wzGroup,
181 __out SCA_GROUP* pscag
182 )
183{
184 if (!wzGroup || !pscag)
185 {
186 return E_INVALIDARG;
187 }
188
189 HRESULT hr = S_OK;
190 PMSIHANDLE hView, hRec;
191
192 LPWSTR pwzData = NULL;
193
194 hRec = ::MsiCreateRecord(1);
195 hr = WcaSetRecordString(hRec, 1, wzGroup);
196 ExitOnFailure(hr, "Failed to look up Group");
197
198 hr = WcaOpenView(vcsGroupQuery, &hView);
199 ExitOnFailure(hr, "Failed to open view on Group table");
200 hr = WcaExecuteView(hView, hRec);
201 ExitOnFailure(hr, "Failed to execute view on Group table");
202
203 hr = WcaFetchSingleRecord(hView, &hRec);
204 if (S_OK == hr)
205 {
206 hr = WcaGetRecordString(hRec, vgqGroup, &pwzData);
207 ExitOnFailure(hr, "Failed to get Group.Group");
208 hr = ::StringCchCopyW(pscag->wzKey, countof(pscag->wzKey), pwzData);
209 ExitOnFailure(hr, "Failed to copy Group.Group.");
210
211 hr = WcaGetRecordString(hRec, vgqComponent, &pwzData);
212 ExitOnFailure(hr, "Failed to get Group.Component_");
213 hr = ::StringCchCopyW(pscag->wzComponent, countof(pscag->wzComponent), pwzData);
214 ExitOnFailure(hr, "Failed to copy Group.Component_.");
215
216 hr = WcaGetRecordFormattedString(hRec, vgqName, &pwzData);
217 ExitOnFailure(hr, "Failed to get Group.Name");
218 hr = ::StringCchCopyW(pscag->wzName, countof(pscag->wzName), pwzData);
219 ExitOnFailure(hr, "Failed to copy Group.Name.");
220
221 hr = WcaGetRecordFormattedString(hRec, vgqDomain, &pwzData);
222 ExitOnFailure(hr, "Failed to get Group.Domain");
223 hr = ::StringCchCopyW(pscag->wzDomain, countof(pscag->wzDomain), pwzData);
224 ExitOnFailure(hr, "Failed to copy Group.Domain.");
225 }
226 else if (E_NOMOREITEMS == hr)
227 {
228 WcaLog(LOGMSG_STANDARD, "Error: Cannot locate Group.Group='%ls'", wzGroup);
229 hr = E_FAIL;
230 }
231 else
232 {
233 ExitOnFailure(hr, "Error or found multiple matching Group rows");
234 }
235
236LExit:
237 ReleaseStr(pwzData);
238
239 return hr;
240}
241
242
243void ScaUserFreeList(
244 __in SCA_USER* psuList
245 )
246{
247 SCA_USER* psuDelete = psuList;
248 while (psuList)
249 {
250 psuDelete = psuList;
251 psuList = psuList->psuNext;
252
253 ScaGroupFreeList(psuDelete->psgGroups);
254 MemFree(psuDelete);
255 }
256}
257
258
259void ScaGroupFreeList(
260 __in SCA_GROUP* psgList
261 )
262{
263 SCA_GROUP* psgDelete = psgList;
264 while (psgList)
265 {
266 psgDelete = psgList;
267 psgList = psgList->psgNext;
268
269 MemFree(psgDelete);
270 }
271}
272
273
274HRESULT ScaUserRead(
275 __out SCA_USER** ppsuList
276 )
277{
278 //Assert(FALSE);
279 Assert(ppsuList);
280
281 HRESULT hr = S_OK;
282 UINT er = ERROR_SUCCESS;
283 PMSIHANDLE hView, hRec, hUserRec, hUserGroupView;
284
285 LPWSTR pwzData = NULL;
286
287 BOOL fUserGroupExists = FALSE;
288
289 SCA_USER *psu = NULL;
290
291 INSTALLSTATE isInstalled, isAction;
292
293 if (S_OK != WcaTableExists(L"User"))
294 {
295 WcaLog(LOGMSG_VERBOSE, "User Table does not exist, exiting");
296 ExitFunction1(hr = S_FALSE);
297 }
298
299 if (S_OK == WcaTableExists(L"UserGroup"))
300 {
301 fUserGroupExists = TRUE;
302 }
303
304 //
305 // loop through all the users
306 //
307 hr = WcaOpenExecuteView(vActionableQuery, &hView);
308 ExitOnFailure(hr, "failed to open view on User table");
309 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
310 {
311 hr = WcaGetRecordString(hRec, vaqComponent, &pwzData);
312 ExitOnFailure(hr, "failed to get User.Component");
313
314 er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &isInstalled, &isAction);
315 hr = HRESULT_FROM_WIN32(er);
316 ExitOnFailure(hr, "failed to get Component state for User");
317
318 // don't bother if we aren't installing or uninstalling this component
319 if (WcaIsInstalling(isInstalled, isAction) || WcaIsUninstalling(isInstalled, isAction))
320 {
321 //
322 // Add the user to the list and populate it's values
323 //
324 hr = AddUserToList(ppsuList);
325 ExitOnFailure(hr, "failed to add user to list");
326
327 psu = *ppsuList;
328
329 psu->isInstalled = isInstalled;
330 psu->isAction = isAction;
331 hr = ::StringCchCopyW(psu->wzComponent, countof(psu->wzComponent), pwzData);
332 ExitOnFailure(hr, "failed to copy component name: %ls", pwzData);
333
334 hr = WcaGetRecordString(hRec, vaqUser, &pwzData);
335 ExitOnFailure(hr, "failed to get User.User");
336 hr = ::StringCchCopyW(psu->wzKey, countof(psu->wzKey), pwzData);
337 ExitOnFailure(hr, "failed to copy user key: %ls", pwzData);
338
339 hr = WcaGetRecordFormattedString(hRec, vaqName, &pwzData);
340 ExitOnFailure(hr, "failed to get User.Name");
341 hr = ::StringCchCopyW(psu->wzName, countof(psu->wzName), pwzData);
342 ExitOnFailure(hr, "failed to copy user name: %ls", pwzData);
343
344 hr = WcaGetRecordFormattedString(hRec, vaqDomain, &pwzData);
345 ExitOnFailure(hr, "failed to get User.Domain");
346 hr = ::StringCchCopyW(psu->wzDomain, countof(psu->wzDomain), pwzData);
347 ExitOnFailure(hr, "failed to copy user domain: %ls", pwzData);
348
349 hr = WcaGetRecordFormattedString(hRec, vaqPassword, &pwzData);
350 ExitOnFailure(hr, "failed to get User.Password");
351 hr = ::StringCchCopyW(psu->wzPassword, countof(psu->wzPassword), pwzData);
352 ExitOnFailure(hr, "failed to copy user password");
353
354 hr = WcaGetRecordInteger(hRec, vaqAttributes, &psu->iAttributes);
355 ExitOnFailure(hr, "failed to get User.Attributes");
356
357 // Check if this user is to be added to any groups
358 if (fUserGroupExists)
359 {
360 hUserRec = ::MsiCreateRecord(1);
361 hr = WcaSetRecordString(hUserRec, 1, psu->wzKey);
362 ExitOnFailure(hr, "Failed to create user record for querying UserGroup table");
363
364 hr = WcaOpenView(vcsUserGroupQuery, &hUserGroupView);
365 ExitOnFailure(hr, "Failed to open view on UserGroup table for user %ls", psu->wzKey);
366 hr = WcaExecuteView(hUserGroupView, hUserRec);
367 ExitOnFailure(hr, "Failed to execute view on UserGroup table for user: %ls", psu->wzKey);
368
369 while (S_OK == (hr = WcaFetchRecord(hUserGroupView, &hRec)))
370 {
371 hr = WcaGetRecordString(hRec, vugqGroup, &pwzData);
372 ExitOnFailure(hr, "failed to get UserGroup.Group");
373
374 hr = AddGroupToList(&(psu->psgGroups));
375 ExitOnFailure(hr, "failed to add group to list");
376
377 hr = ScaGetGroup(pwzData, psu->psgGroups);
378 ExitOnFailure(hr, "failed to get information for group: %ls", pwzData);
379 }
380
381 if (E_NOMOREITEMS == hr)
382 {
383 hr = S_OK;
384 }
385 ExitOnFailure(hr, "failed to enumerate selected rows from UserGroup table");
386 }
387 }
388 }
389
390 if (E_NOMOREITEMS == hr)
391 {
392 hr = S_OK;
393 }
394 ExitOnFailure(hr, "failed to enumerate selected rows from User table");
395
396LExit:
397 ReleaseStr(pwzData);
398
399 return hr;
400}
401
402
403static HRESULT WriteGroupInfo(
404 __in SCA_GROUP* psgList,
405 __in LPWSTR *ppwzActionData
406 )
407{
408 HRESULT hr = S_OK;
409
410 for (SCA_GROUP* psg = psgList; psg; psg = psg->psgNext)
411 {
412 hr = WcaWriteStringToCaData(psg->wzName, ppwzActionData);
413 ExitOnFailure(hr, "failed to add group name to custom action data: %ls", psg->wzName);
414
415 hr = WcaWriteStringToCaData(psg->wzDomain, ppwzActionData);
416 ExitOnFailure(hr, "failed to add group domain to custom action data: %ls", psg->wzDomain);
417 }
418
419LExit:
420 return hr;
421}
422
423
424// Behaves like WriteGroupInfo, but it filters out groups the user is currently a member of,
425// because we don't want to rollback those
426static HRESULT WriteGroupRollbackInfo(
427 __in LPCWSTR pwzName,
428 __in LPCWSTR pwzDomain,
429 __in SCA_GROUP* psgList,
430 __in LPWSTR *ppwzActionData
431 )
432{
433 HRESULT hr = S_OK;
434 BOOL fIsMember = FALSE;
435
436 for (SCA_GROUP* psg = psgList; psg; psg = psg->psgNext)
437 {
438 hr = UserCheckIsMember(pwzName, pwzDomain, psg->wzName, psg->wzDomain, &fIsMember);
439 if (FAILED(hr))
440 {
441 WcaLog(LOGMSG_VERBOSE, "Failed to check if user: %ls (domain: %ls) is member of a group while collecting rollback information (error code 0x%x) - continuing", pwzName, pwzDomain, hr);
442 hr = S_OK;
443 continue;
444 }
445
446 // If the user is currently a member, we don't want to undo that on rollback, so skip adding
447 // this group record to the list of groups to rollback
448 if (fIsMember)
449 {
450 continue;
451 }
452
453 hr = WcaWriteStringToCaData(psg->wzName, ppwzActionData);
454 ExitOnFailure(hr, "failed to add group name to custom action data: %ls", psg->wzName);
455
456 hr = WcaWriteStringToCaData(psg->wzDomain, ppwzActionData);
457 ExitOnFailure(hr, "failed to add group domain to custom action data: %ls", psg->wzDomain);
458 }
459
460LExit:
461 return hr;
462}
463
464
465/* ****************************************************************
466ScaUserExecute - Schedules user account creation or removal based on
467component state.
468
469******************************************************************/
470HRESULT ScaUserExecute(
471 __in SCA_USER *psuList
472 )
473{
474 HRESULT hr = S_OK;
475 DWORD er = 0;
476 PDOMAIN_CONTROLLER_INFOW pDomainControllerInfo = NULL;
477
478 USER_INFO_0 *pUserInfo = NULL;
479 LPWSTR pwzActionData = NULL;
480 LPWSTR pwzRollbackData = NULL;
481
482 for (SCA_USER *psu = psuList; psu; psu = psu->psuNext)
483 {
484 USER_EXISTS ueUserExists = USER_EXISTS_INDETERMINATE;
485
486 // Always put the User Name and Domain plus Attributes on the front of the CustomAction
487 // data. Sometimes we'll add more data.
488 Assert(psu->wzName);
489 hr = WcaWriteStringToCaData(psu->wzName, &pwzActionData);
490 ExitOnFailure(hr, "Failed to add user name to custom action data: %ls", psu->wzName);
491 hr = WcaWriteStringToCaData(psu->wzDomain, &pwzActionData);
492 ExitOnFailure(hr, "Failed to add user domain to custom action data: %ls", psu->wzDomain);
493 hr = WcaWriteIntegerToCaData(psu->iAttributes, &pwzActionData);
494 ExitOnFailure(hr, "failed to add user attributes to custom action data for user: %ls", psu->wzKey);
495
496 // Check to see if the user already exists since we have to be very careful when adding
497 // and removing users. Note: MSDN says that it is safe to call these APIs from any
498 // user, so we should be safe calling it during immediate mode.
499 er = ::NetApiBufferAllocate(sizeof(USER_INFO_0), reinterpret_cast<LPVOID*>(&pUserInfo));
500 hr = HRESULT_FROM_WIN32(er);
501 ExitOnFailure(hr, "Failed to allocate memory to check existence of user: %ls", psu->wzName);
502
503 LPCWSTR wzDomain = psu->wzDomain;
504 if (wzDomain && *wzDomain)
505 {
506 er = ::DsGetDcNameW(NULL, wzDomain, NULL, NULL, NULL, &pDomainControllerInfo);
507 if (RPC_S_SERVER_UNAVAILABLE == er)
508 {
509 // MSDN says, if we get the above error code, try again with the "DS_FORCE_REDISCOVERY" flag
510 er = ::DsGetDcNameW(NULL, wzDomain, NULL, NULL, DS_FORCE_REDISCOVERY, &pDomainControllerInfo);
511 }
512 if (ERROR_SUCCESS == er)
513 {
514 wzDomain = pDomainControllerInfo->DomainControllerName + 2; //Add 2 so that we don't get the \\ prefix
515 }
516 }
517
518 er = ::NetUserGetInfo(wzDomain, psu->wzName, 0, reinterpret_cast<LPBYTE*>(pUserInfo));
519 if (NERR_Success == er)
520 {
521 ueUserExists = USER_EXISTS_YES;
522 }
523 else if (NERR_UserNotFound == er)
524 {
525 ueUserExists = USER_EXISTS_NO;
526 }
527 else
528 {
529 ueUserExists = USER_EXISTS_INDETERMINATE;
530 hr = HRESULT_FROM_WIN32(er);
531 WcaLog(LOGMSG_VERBOSE, "Failed to check existence of domain: %ls, user: %ls (error code 0x%x) - continuing", wzDomain, psu->wzName, hr);
532 hr = S_OK;
533 er = ERROR_SUCCESS;
534 }
535
536 if (WcaIsInstalling(psu->isInstalled, psu->isAction))
537 {
538 // If the user exists, check to see if we are supposed to fail if user the exists before
539 // the install.
540 if (USER_EXISTS_YES == ueUserExists)
541 {
542 // Reinstalls will always fail if we don't remove the check for "fail if exists".
543 if (WcaIsReInstalling(psu->isInstalled, psu->isAction))
544 {
545 psu->iAttributes &= ~SCAU_FAIL_IF_EXISTS;
546 }
547
548 if ((SCAU_FAIL_IF_EXISTS & (psu->iAttributes)) && !(SCAU_UPDATE_IF_EXISTS & (psu->iAttributes)))
549 {
550 hr = HRESULT_FROM_WIN32(NERR_UserExists);
551 MessageExitOnFailure(hr, msierrUSRFailedUserCreateExists, "Failed to create user: %ls because user already exists.", psu->wzName);
552 }
553 }
554
555 // Rollback only if the user already exists, we couldn't determine if the user exists, or we are going to create the user
556 if ((USER_EXISTS_YES == ueUserExists) || (USER_EXISTS_INDETERMINATE == ueUserExists) || !(psu->iAttributes & SCAU_DONT_CREATE_USER))
557 {
558 INT iRollbackUserAttributes = psu->iAttributes;
559
560 // If the user already exists, ensure this is accounted for in rollback
561 if (USER_EXISTS_YES == ueUserExists)
562 {
563 iRollbackUserAttributes |= SCAU_DONT_CREATE_USER;
564 }
565 else
566 {
567 iRollbackUserAttributes &= ~SCAU_DONT_CREATE_USER;
568 }
569
570 hr = WcaWriteStringToCaData(psu->wzName, &pwzRollbackData);
571 ExitOnFailure(hr, "Failed to add user name to rollback custom action data: %ls", psu->wzName);
572 hr = WcaWriteStringToCaData(psu->wzDomain, &pwzRollbackData);
573 ExitOnFailure(hr, "Failed to add user domain to rollback custom action data: %ls", psu->wzDomain);
574 hr = WcaWriteIntegerToCaData(iRollbackUserAttributes, &pwzRollbackData);
575 ExitOnFailure(hr, "failed to add user attributes to rollback custom action data for user: %ls", psu->wzKey);
576
577 // If the user already exists, add relevant group information to rollback data
578 if (USER_EXISTS_YES == ueUserExists || USER_EXISTS_INDETERMINATE == ueUserExists)
579 {
580 hr = WriteGroupRollbackInfo(psu->wzName, psu->wzDomain, psu->psgGroups, &pwzRollbackData);
581 ExitOnFailure(hr, "failed to add group information to rollback custom action data");
582 }
583
584 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"CreateUserRollback"), pwzRollbackData, COST_USER_DELETE);
585 ExitOnFailure(hr, "failed to schedule CreateUserRollback");
586 }
587
588 //
589 // Schedule the creation now.
590 //
591 hr = WcaWriteStringToCaData(psu->wzPassword, &pwzActionData);
592 ExitOnFailure(hr, "failed to add user password to custom action data for user: %ls", psu->wzKey);
593
594 // Add user's group information to custom action data
595 hr = WriteGroupInfo(psu->psgGroups, &pwzActionData);
596 ExitOnFailure(hr, "failed to add group information to custom action data");
597
598 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"CreateUser"), pwzActionData, COST_USER_ADD);
599 ExitOnFailure(hr, "failed to schedule CreateUser");
600 }
601 else if (((USER_EXISTS_YES == ueUserExists) || (USER_EXISTS_INDETERMINATE == ueUserExists)) && WcaIsUninstalling(psu->isInstalled, psu->isAction) && !(psu->iAttributes & SCAU_DONT_REMOVE_ON_UNINSTALL))
602 {
603 // Add user's group information - this will ensure the user can be removed from any groups they were added to, if the user isn't be deleted
604 hr = WriteGroupInfo(psu->psgGroups, &pwzActionData);
605 ExitOnFailure(hr, "failed to add group information to custom action data");
606
607 //
608 // Schedule the removal because the user exists and we don't have any flags set
609 // that say, don't remove the user on uninstall.
610 //
611 // Note: We can't rollback the removal of a user which is why RemoveUser is a commit
612 // CustomAction.
613 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"RemoveUser"), pwzActionData, COST_USER_DELETE);
614 ExitOnFailure(hr, "failed to schedule RemoveUser");
615 }
616
617 ReleaseNullStr(pwzActionData);
618 ReleaseNullStr(pwzRollbackData);
619 if (pUserInfo)
620 {
621 ::NetApiBufferFree(static_cast<LPVOID>(pUserInfo));
622 pUserInfo = NULL;
623 }
624 if (pDomainControllerInfo)
625 {
626 ::NetApiBufferFree(static_cast<LPVOID>(pDomainControllerInfo));
627 pDomainControllerInfo = NULL;
628 }
629 }
630
631LExit:
632 ReleaseStr(pwzActionData);
633 ReleaseStr(pwzRollbackData);
634 if (pUserInfo)
635 {
636 ::NetApiBufferFree(static_cast<LPVOID>(pUserInfo));
637 }
638 if (pDomainControllerInfo)
639 {
640 ::NetApiBufferFree(static_cast<LPVOID>(pDomainControllerInfo));
641 }
642
643 return hr;
644}
645
646
647static HRESULT AddUserToList(
648 __inout SCA_USER** ppsuList
649 )
650{
651 HRESULT hr = S_OK;
652 SCA_USER* psu = static_cast<SCA_USER*>(MemAlloc(sizeof(SCA_USER), TRUE));
653 ExitOnNull(psu, hr, E_OUTOFMEMORY, "failed to allocate memory for new user list element");
654
655 psu->psuNext = *ppsuList;
656 *ppsuList = psu;
657
658LExit:
659 return hr;
660}
661
662
663static HRESULT AddGroupToList(
664 __inout SCA_GROUP** ppsgList
665 )
666{
667 HRESULT hr = S_OK;
668 SCA_GROUP* psg = static_cast<SCA_GROUP*>(MemAlloc(sizeof(SCA_GROUP), TRUE));
669 ExitOnNull(psg, hr, E_OUTOFMEMORY, "failed to allocate memory for new group list element");
670
671 psg->psgNext = *ppsgList;
672 *ppsgList = psg;
673
674LExit:
675 return hr;
676}
diff --git a/src/ca/scauser.h b/src/ca/scauser.h
new file mode 100644
index 00000000..a5fd5ea8
--- /dev/null
+++ b/src/ca/scauser.h
@@ -0,0 +1,67 @@
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
5enum USER_EXISTS
6{
7 USER_EXISTS_YES,
8 USER_EXISTS_NO,
9 USER_EXISTS_INDETERMINATE
10};
11
12// structs
13struct SCA_GROUP
14{
15 WCHAR wzKey[MAX_DARWIN_KEY + 1];
16 WCHAR wzComponent[MAX_DARWIN_KEY + 1];
17
18 WCHAR wzDomain[MAX_DARWIN_COLUMN + 1];
19 WCHAR wzName[MAX_DARWIN_COLUMN + 1];
20
21 SCA_GROUP *psgNext;
22};
23
24struct SCA_USER
25{
26 WCHAR wzKey[MAX_DARWIN_KEY + 1];
27 WCHAR wzComponent[MAX_DARWIN_KEY + 1];
28 INSTALLSTATE isInstalled;
29 INSTALLSTATE isAction;
30
31 WCHAR wzDomain[MAX_DARWIN_COLUMN + 1];
32 WCHAR wzName[MAX_DARWIN_COLUMN + 1];
33 WCHAR wzPassword[MAX_DARWIN_COLUMN + 1];
34 INT iAttributes;
35
36 SCA_GROUP *psgGroups;
37
38 SCA_USER *psuNext;
39};
40
41
42// prototypes
43HRESULT __stdcall ScaGetUser(
44 __in LPCWSTR wzUser,
45 __out SCA_USER* pscau
46 );
47HRESULT __stdcall ScaGetUserDeferred(
48 __in LPCWSTR wzUser,
49 __in WCA_WRAPQUERY_HANDLE hUserQuery,
50 __out SCA_USER* pscau
51 );
52HRESULT __stdcall ScaGetGroup(
53 __in LPCWSTR wzGroup,
54 __out SCA_GROUP* pscag
55 );
56void ScaUserFreeList(
57 __in SCA_USER* psuList
58 );
59void ScaGroupFreeList(
60 __in SCA_GROUP* psgList
61 );
62HRESULT ScaUserRead(
63 __inout SCA_USER** ppsuList
64 );
65HRESULT ScaUserExecute(
66 __in SCA_USER *psuList
67 );
diff --git a/src/ca/secureobj.cpp b/src/ca/secureobj.cpp
new file mode 100644
index 00000000..f6d1406a
--- /dev/null
+++ b/src/ca/secureobj.cpp
@@ -0,0 +1,902 @@
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// structs
6LPCWSTR wzQUERY_SECUREOBJECTS = L"SELECT `SecureObjects`.`SecureObject`, `SecureObjects`.`Table`, `SecureObjects`.`Domain`, `SecureObjects`.`User`, "
7 L"`SecureObjects`.`Permission`, `SecureObjects`.`Component_`, `Component`.`Attributes` FROM `SecureObjects`,`Component` WHERE "
8 L"`SecureObjects`.`Component_`=`Component`.`Component`";
9enum eQUERY_SECUREOBJECTS { QSO_SECUREOBJECT = 1, QSO_TABLE, QSO_DOMAIN, QSO_USER, QSO_PERMISSION, QSO_COMPONENT, QSO_COMPATTRIBUTES };
10
11LPCWSTR wzQUERY_REGISTRY = L"SELECT `Registry`.`Registry`, `Registry`.`Root`, `Registry`.`Key` FROM `Registry` WHERE `Registry`.`Registry`=?";
12enum eQUERY_OBJECTCOMPONENT { QSOC_REGISTRY = 1, QSOC_REGROOT, QSOC_REGKEY };
13
14LPCWSTR wzQUERY_SERVICEINSTALL = L"SELECT `ServiceInstall`.`Name` FROM `ServiceInstall` WHERE `ServiceInstall`.`ServiceInstall`=?";
15enum eQUERY_SECURESERVICEINSTALL { QSSI_NAME = 1 };
16
17enum eOBJECTTYPE { OT_UNKNOWN, OT_SERVICE, OT_FOLDER, OT_FILE, OT_REGISTRY };
18
19static eOBJECTTYPE EObjectTypeFromString(
20 __in LPCWSTR pwzTable
21 )
22{
23 if (NULL == pwzTable)
24 {
25 return OT_UNKNOWN;
26 }
27
28 eOBJECTTYPE eType = OT_UNKNOWN;
29
30 // ensure we're looking at a known table
31 if (0 == lstrcmpW(L"ServiceInstall", pwzTable))
32 {
33 eType = OT_SERVICE;
34 }
35 else if (0 == lstrcmpW(L"CreateFolder", pwzTable))
36 {
37 eType = OT_FOLDER;
38 }
39 else if (0 == lstrcmpW(L"File", pwzTable))
40 {
41 eType = OT_FILE;
42 }
43 else if (0 == lstrcmpW(L"Registry", pwzTable))
44 {
45 eType = OT_REGISTRY;
46 }
47
48 return eType;
49}
50
51static SE_OBJECT_TYPE SEObjectTypeFromString(
52 __in LPCWSTR pwzTable
53 )
54{
55 if (NULL == pwzTable)
56 {
57 return SE_UNKNOWN_OBJECT_TYPE;
58 }
59
60 SE_OBJECT_TYPE objectType = SE_UNKNOWN_OBJECT_TYPE;
61
62 if (0 == lstrcmpW(L"ServiceInstall", pwzTable))
63 {
64 objectType = SE_SERVICE;
65 }
66 else if (0 == lstrcmpW(L"CreateFolder", pwzTable) || 0 == lstrcmpW(L"File", pwzTable))
67 {
68 objectType = SE_FILE_OBJECT;
69 }
70 else if (0 == lstrcmpW(L"Registry", pwzTable))
71 {
72 objectType = SE_REGISTRY_KEY;
73 }
74 else
75 {
76 // Do nothing; we'll return SE_UNKNOWN_OBJECT_TYPE, and the caller should handle the situation
77 }
78
79 return objectType;
80}
81
82static HRESULT StoreACLRollbackInfo(
83 __in LPWSTR pwzObject,
84 __in LPCWSTR pwzTable
85 )
86{
87 HRESULT hr = S_OK;
88 DWORD er = ERROR_SUCCESS;
89 PSECURITY_DESCRIPTOR psd = NULL;
90 SECURITY_DESCRIPTOR_CONTROL sdc = {0};
91 DWORD dwRevision = 0;
92 LPWSTR pwzCustomActionData = NULL;
93 LPWSTR pwzSecurityInfo = NULL;
94
95 Assert(pwzObject && pwzTable);
96
97 SE_OBJECT_TYPE objectType = SEObjectTypeFromString(const_cast<LPCWSTR> (pwzTable));
98
99 if (SE_UNKNOWN_OBJECT_TYPE != objectType)
100 {
101 er = ::GetNamedSecurityInfoW(pwzObject, objectType, DACL_SECURITY_INFORMATION, NULL, NULL, NULL, NULL, &psd);
102 if (ERROR_FILE_NOT_FOUND == er || ERROR_PATH_NOT_FOUND == er || ERROR_SERVICE_DOES_NOT_EXIST == HRESULT_CODE(er))
103 {
104 // If the file, path or service doesn't exist yet, skip rollback without a message
105 hr = HRESULT_FROM_WIN32(er);
106 ExitFunction();
107 }
108
109 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Unable to schedule rollback for object: %ls", pwzObject);
110
111 //Need to see if DACL is protected so getting Descriptor information
112 if (!::GetSecurityDescriptorControl(psd, &sdc, &dwRevision))
113 {
114 ExitOnLastError(hr, "Unable to schedule rollback for object (failed to get security descriptor control): %ls", pwzObject);
115 }
116
117 // Convert the security information to a string, and write this to the custom action data
118 if (!::ConvertSecurityDescriptorToStringSecurityDescriptorW(psd,SDDL_REVISION_1,DACL_SECURITY_INFORMATION,&pwzSecurityInfo,NULL))
119 {
120 hr = E_UNEXPECTED;
121 ExitOnFailure(hr, "Unable to schedule rollback for object (failed to convert security descriptor to a valid security descriptor string): %ls", pwzObject);
122 }
123
124 hr = WcaWriteStringToCaData(pwzObject, &pwzCustomActionData);
125 ExitOnFailure(hr, "failed to add object data to rollback CustomActionData");
126
127 hr = WcaWriteStringToCaData(pwzTable, &pwzCustomActionData);
128 ExitOnFailure(hr, "failed to add table name to rollback CustomActionData");
129
130 hr = WcaWriteStringToCaData(pwzSecurityInfo, &pwzCustomActionData);
131 ExitOnFailure(hr, "failed to add security info data to rollback CustomActionData");
132
133 // Write a 1 if DACL is protected, 0 otherwise
134 if (sdc & SE_DACL_PROTECTED)
135 {
136 hr = WcaWriteIntegerToCaData(1,&pwzCustomActionData);
137 ExitOnFailure(hr, "failed to add data to rollbackCustomActionData");
138 }
139 else
140 {
141 hr = WcaWriteIntegerToCaData(0,&pwzCustomActionData);
142 ExitOnFailure(hr, "failed to add data to rollback CustomActionData");
143 }
144
145 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"ExecSecureObjectsRollback"), pwzCustomActionData, COST_SECUREOBJECT);
146 ExitOnFailure(hr, "failed to schedule ExecSecureObjectsRollback for item: %ls of type: %ls", pwzObject, pwzTable);
147
148 ReleaseStr(pwzCustomActionData);
149 pwzCustomActionData = NULL;
150
151 }
152 else
153 {
154 MessageExitOnFailure(hr = E_UNEXPECTED, msierrSecureObjectsUnknownType, "unknown object type: %ls", pwzTable);
155 }
156LExit:
157 ReleaseStr(pwzCustomActionData);
158
159 if (psd)
160 {
161 ::LocalFree(psd);
162 }
163
164 return hr;
165}
166
167static HRESULT GetTargetPath(
168 __in eOBJECTTYPE eType,
169 __in LPCWSTR pwzSecureObject,
170 __out LPWSTR* ppwzTargetPath
171 )
172{
173 HRESULT hr = S_OK;
174
175 PMSIHANDLE hView = NULL;
176 PMSIHANDLE hRecObject = NULL;
177 PMSIHANDLE hRec = NULL;
178
179 int iRoot = 0;
180 int iAllUsers = 0;
181 LPWSTR pwzKey = NULL;
182 LPWSTR pwzFormattedString = NULL;
183
184 if (OT_SERVICE == eType)
185 {
186 hr = WcaTableExists(L"ServiceInstall");
187 if (S_FALSE == hr)
188 {
189 hr = E_UNEXPECTED;
190 }
191 ExitOnFailure(hr, "failed to open ServiceInstall table to secure object");
192
193 hr = WcaOpenView(wzQUERY_SERVICEINSTALL, &hView);
194 ExitOnFailure(hr, "failed to open view on ServiceInstall table");
195
196 // create a record that stores the object to secure
197 hRec = MsiCreateRecord(1);
198 MsiRecordSetStringW(hRec, 1, pwzSecureObject);
199
200 // execute a view looking for the object's ServiceInstall.ServiceInstall row.
201 hr = WcaExecuteView(hView, hRec);
202 ExitOnFailure(hr, "failed to execute view on ServiceInstall table");
203 hr = WcaFetchSingleRecord(hView, &hRecObject);
204 ExitOnFailure(hr, "failed to fetch ServiceInstall row for secure object");
205
206 hr = WcaGetRecordFormattedString(hRecObject, QSSI_NAME, ppwzTargetPath);
207 ExitOnFailure(hr, "failed to get service name for secure object: %ls", pwzSecureObject);
208 }
209 else if (OT_FOLDER == eType)
210 {
211 hr = WcaGetTargetPath(pwzSecureObject, ppwzTargetPath);
212 ExitOnFailure(hr, "failed to get target path for directory id: %ls", pwzSecureObject);
213 }
214 else if (OT_FILE == eType)
215 {
216 hr = StrAllocFormatted(&pwzFormattedString, L"[#%s]", pwzSecureObject);
217 ExitOnFailure(hr, "failed to create formatted string for securing file object: %ls", pwzSecureObject);
218
219 hr = WcaGetFormattedString(pwzFormattedString, ppwzTargetPath);
220 ExitOnFailure(hr, "failed to get file path from formatted string: %ls for secure object: %ls", pwzFormattedString, pwzSecureObject);
221 }
222 else if (OT_REGISTRY == eType)
223 {
224 hr = WcaTableExists(L"Registry");
225 if (S_FALSE == hr)
226 {
227 hr = E_UNEXPECTED;
228 }
229 ExitOnFailure(hr, "failed to open Registry table to secure object");
230
231 hr = WcaOpenView(wzQUERY_REGISTRY, &hView);
232 ExitOnFailure(hr, "failed to open view on Registry table");
233
234 // create a record that stores the object to secure
235 hRec = MsiCreateRecord(1);
236 MsiRecordSetStringW(hRec, 1, pwzSecureObject);
237
238 // execute a view looking for the object's Registry row
239 hr = WcaExecuteView(hView, hRec);
240 ExitOnFailure(hr, "failed to execute view on Registry table");
241 hr = WcaFetchSingleRecord(hView, &hRecObject);
242 ExitOnFailure(hr, "failed to fetch Registry row for secure object");
243
244 hr = WcaGetRecordInteger(hRecObject, QSOC_REGROOT, &iRoot);
245 ExitOnFailure(hr, "Failed to get reg key root for secure object: %ls", pwzSecureObject);
246
247 hr = WcaGetRecordFormattedString(hRecObject, QSOC_REGKEY, &pwzKey);
248 ExitOnFailure(hr, "Failed to get reg key for secure object: %ls", pwzSecureObject);
249
250 // Decode the root value
251 if (-1 == iRoot)
252 {
253 // They didn't specify a root so that means it's either HKCU or HKLM depending on ALLUSERS property
254 hr = WcaGetIntProperty(L"ALLUSERS", &iAllUsers);
255 ExitOnFailure(hr, "failed to get value of ALLUSERS property");
256
257 if (1 == iAllUsers)
258 {
259 hr = StrAllocString(ppwzTargetPath, L"MACHINE\\", 0);
260 ExitOnFailure(hr, "failed to allocate target registry string with HKLM root");
261 }
262 else
263 {
264 hr = StrAllocString(ppwzTargetPath, L"CURRENT_USER\\", 0);
265 ExitOnFailure(hr, "failed to allocate target registry string with HKCU root");
266 }
267 }
268 else if (msidbRegistryRootClassesRoot == iRoot)
269 {
270 hr = StrAllocString(ppwzTargetPath, L"CLASSES_ROOT\\", 0);
271 ExitOnFailure(hr, "failed to allocate target registry string with HKCR root");
272 }
273 else if (msidbRegistryRootCurrentUser == iRoot)
274 {
275 hr = StrAllocString(ppwzTargetPath, L"CURRENT_USER\\", 0);
276 ExitOnFailure(hr, "failed to allocate target registry string with HKCU root");
277 }
278 else if (msidbRegistryRootLocalMachine == iRoot)
279 {
280 hr = StrAllocString(ppwzTargetPath, L"MACHINE\\", 0);
281 ExitOnFailure(hr, "failed to allocate target registry string with HKLM root");
282 }
283 else if (msidbRegistryRootUsers == iRoot)
284 {
285 hr = StrAllocString(ppwzTargetPath, L"USERS\\", 0);
286 ExitOnFailure(hr, "failed to allocate target registry string with HKU root");
287 }
288 else
289 {
290 ExitOnFailure(hr = E_UNEXPECTED, "Unknown registry key root specified for secure object: '%ls' root: %d", pwzSecureObject, iRoot);
291 }
292
293 hr = StrAllocConcat(ppwzTargetPath, pwzKey, 0);
294 ExitOnFailure(hr, "Failed to concat key: %ls for secure object: %ls", pwzKey, pwzSecureObject);
295 }
296 else
297 {
298 AssertSz(FALSE, "How did you get here?");
299 ExitOnFailure(hr = E_UNEXPECTED, "Unknown secure object type: %d", eType);
300 }
301
302LExit:
303 ReleaseStr(pwzFormattedString);
304 ReleaseStr(pwzKey);
305
306 return hr;
307}
308
309/******************************************************************
310 SchedSecureObjects - entry point for SchedSecureObjects Custom Action
311
312 called as Type 1 CustomAction (binary DLL) from Windows Installer
313 in InstallExecuteSequence, to schedule ExecSecureObjects
314******************************************************************/
315extern "C" UINT __stdcall SchedSecureObjects(
316 __in MSIHANDLE hInstall
317 )
318{
319// AssertSz(FALSE, "debug SchedSecureObjects");
320 HRESULT hr = S_OK;
321 UINT er = ERROR_SUCCESS;
322
323 LPWSTR pwzSecureObject = NULL;
324 LPWSTR pwzData = NULL;
325 LPWSTR pwzTable = NULL;
326 LPWSTR pwzTargetPath = NULL;
327
328 PMSIHANDLE hView = NULL;
329 PMSIHANDLE hRec = NULL;
330
331 INSTALLSTATE isInstalled;
332 INSTALLSTATE isAction;
333
334 LPWSTR pwzCustomActionData = NULL;
335
336 DWORD cObjects = 0;
337 eOBJECTTYPE eType = OT_UNKNOWN;
338
339 //
340 // initialize
341 //
342 hr = WcaInitialize(hInstall, "SchedSecureObjects");
343 ExitOnFailure(hr, "failed to initialize");
344
345 // anything to do?
346 if (S_OK != WcaTableExists(L"SecureObjects"))
347 {
348 WcaLog(LOGMSG_STANDARD, "SecureObjects table doesn't exist, so there are no objects to secure.");
349 ExitFunction();
350 }
351
352 //
353 // loop through all the objects to be secured
354 //
355 hr = WcaOpenExecuteView(wzQUERY_SECUREOBJECTS, &hView);
356 ExitOnFailure(hr, "failed to open view on SecureObjects table");
357 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
358 {
359 hr = WcaGetRecordString(hRec, QSO_TABLE, &pwzTable);
360 ExitOnFailure(hr, "failed to get object table");
361
362 eType = EObjectTypeFromString(pwzTable);
363
364 if (OT_UNKNOWN == eType)
365 {
366 ExitOnFailure(hr = E_INVALIDARG, "unknown SecureObject.Table: %ls", pwzTable);
367 }
368
369 int iCompAttributes = 0;
370 hr = WcaGetRecordInteger(hRec, QSO_COMPATTRIBUTES, &iCompAttributes);
371 ExitOnFailure(hr, "failed to get Component attributes for secure object");
372
373 BOOL fIs64Bit = iCompAttributes & msidbComponentAttributes64bit;
374
375 // Only process entries in the SecureObjects table whose components match the bitness of this CA
376#ifdef _WIN64
377 if (!fIs64Bit)
378 {
379 continue;
380 }
381#else
382 if (fIs64Bit)
383 {
384 continue;
385 }
386#endif
387
388 // Get the object to secure
389 hr = WcaGetRecordString(hRec, QSO_SECUREOBJECT, &pwzSecureObject);
390 ExitOnFailure(hr, "failed to get name of object");
391
392 hr = GetTargetPath(eType, pwzSecureObject, &pwzTargetPath);
393 ExitOnFailure(hr, "failed to get target path of object '%ls'", pwzSecureObject);
394
395 hr = WcaGetRecordString(hRec, QSO_COMPONENT, &pwzData);
396 ExitOnFailure(hr, "failed to get Component name for secure object");
397
398 //
399 // if we are installing this Component
400 //
401 er = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction);
402 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to get install state for Component: %ls", pwzData);
403
404 if (WcaIsInstalling(isInstalled, isAction))
405 {
406 hr = WcaWriteStringToCaData(pwzTargetPath, &pwzCustomActionData);
407 ExitOnFailure(hr, "failed to add data to CustomActionData");
408
409 // add the data to the CustomActionData
410 hr = WcaGetRecordString(hRec, QSO_SECUREOBJECT, &pwzData);
411 ExitOnFailure(hr, "failed to get name of object");
412
413 hr = WcaWriteStringToCaData(pwzTable, &pwzCustomActionData);
414 ExitOnFailure(hr, "failed to add data to CustomActionData");
415
416 hr = WcaGetRecordFormattedString(hRec, QSO_DOMAIN, &pwzData);
417 ExitOnFailure(hr, "failed to get domain for user to configure object");
418 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
419 ExitOnFailure(hr, "failed to add data to CustomActionData");
420
421 hr = WcaGetRecordFormattedString(hRec, QSO_USER, &pwzData);
422 ExitOnFailure(hr, "failed to get user to configure object");
423 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
424 ExitOnFailure(hr, "failed to add data to CustomActionData");
425
426 hr = WcaGetRecordString(hRec, QSO_PERMISSION, &pwzData);
427 ExitOnFailure(hr, "failed to get permission to configure object");
428 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
429 ExitOnFailure(hr, "failed to add data to CustomActionData");
430
431 ++cObjects;
432 }
433 }
434
435 // if we looped through all records all is well
436 if (E_NOMOREITEMS == hr)
437 hr = S_OK;
438 ExitOnFailure(hr, "failed while looping through all objects to secure");
439
440 //
441 // schedule the custom action and add to progress bar
442 //
443 if (pwzCustomActionData && *pwzCustomActionData)
444 {
445 Assert(0 < cObjects);
446
447 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"ExecSecureObjects"), pwzCustomActionData, cObjects * COST_SECUREOBJECT);
448 ExitOnFailure(hr, "failed to schedule ExecSecureObjects action");
449 }
450
451LExit:
452 ReleaseStr(pwzSecureObject);
453 ReleaseStr(pwzCustomActionData);
454 ReleaseStr(pwzData);
455 ReleaseStr(pwzTable);
456 ReleaseStr(pwzTargetPath);
457
458 if (FAILED(hr))
459 {
460 er = ERROR_INSTALL_FAILURE;
461 }
462 return WcaFinalize(er);
463}
464
465/******************************************************************
466 SchedSecureObjectsRollback - entry point for SchedSecureObjectsRollback Custom Action
467
468 called as Type 1 CustomAction (binary DLL) from Windows Installer
469 in InstallExecuteSequence before SchedSecureObjects
470******************************************************************/
471extern "C" UINT __stdcall SchedSecureObjectsRollback(
472 __in MSIHANDLE hInstall
473 )
474{
475// AssertSz(FALSE, "debug SchedSecureObjectsRollback");
476 HRESULT hr = S_OK;
477 UINT er = ERROR_SUCCESS;
478
479 LPWSTR pwzSecureObject = NULL;
480 LPWSTR pwzTable = NULL;
481 LPWSTR pwzTargetPath = NULL;
482
483 PMSIHANDLE hView = NULL;
484 PMSIHANDLE hRec = NULL;
485
486 LPWSTR pwzCustomActionData = NULL;
487
488 eOBJECTTYPE eType = OT_UNKNOWN;
489
490 //
491 // initialize
492 //
493 hr = WcaInitialize(hInstall, "SchedSecureObjectsRollback");
494 ExitOnFailure(hr, "failed to initialize");
495
496 //
497 // loop through all the objects to be secured
498 //
499 hr = WcaOpenExecuteView(wzQUERY_SECUREOBJECTS, &hView);
500 ExitOnFailure(hr, "failed to open view on SecureObjects table");
501 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
502 {
503 hr = WcaGetRecordString(hRec, QSO_TABLE, &pwzTable);
504 ExitOnFailure(hr, "failed to get object table");
505
506 eType = EObjectTypeFromString(pwzTable);
507
508 if (OT_UNKNOWN == eType)
509 {
510 ExitOnFailure(hr = E_INVALIDARG, "unknown SecureObject.Table: %ls", pwzTable);
511 }
512
513 int iCompAttributes = 0;
514 hr = WcaGetRecordInteger(hRec, QSO_COMPATTRIBUTES, &iCompAttributes);
515 ExitOnFailure(hr, "failed to get Component attributes for secure object");
516
517 BOOL fIs64Bit = iCompAttributes & msidbComponentAttributes64bit;
518
519 // Only process entries in the SecureObjects table whose components match the bitness of this CA
520#ifdef _WIN64
521 if (!fIs64Bit)
522 {
523 continue;
524 }
525#else
526 if (fIs64Bit)
527 {
528 continue;
529 }
530#endif
531
532 // get the object being secured that we are planning to schedule rollback for
533 hr = WcaGetRecordString(hRec, QSO_SECUREOBJECT, &pwzSecureObject);
534 ExitOnFailure(hr, "failed to get name of object");
535
536 hr = GetTargetPath(eType, pwzSecureObject, &pwzTargetPath);
537 ExitOnFailure(hr, "failed to get target path of object '%ls' in order to schedule rollback", pwzSecureObject);
538
539 hr = StoreACLRollbackInfo(pwzTargetPath, pwzTable);
540 if (FAILED(hr))
541 {
542 WcaLog(LOGMSG_STANDARD, "Failed to store ACL rollback information with error 0x%x - continuing", hr);
543 }
544 }
545
546 // if we looped through all records all is well
547 if (E_NOMOREITEMS == hr)
548 {
549 hr = S_OK;
550 }
551 ExitOnFailure(hr, "failed while looping through all objects to schedule rollback for");
552
553LExit:
554 ReleaseStr(pwzCustomActionData);
555 ReleaseStr(pwzSecureObject);
556 ReleaseStr(pwzTable);
557 ReleaseStr(pwzTargetPath);
558
559 if (FAILED(hr))
560 {
561 er = ERROR_INSTALL_FAILURE;
562 }
563 return WcaFinalize(er);
564}
565
566/******************************************************************
567 CaExecSecureObjects - entry point for SecureObjects Custom Action
568 called as Type 1025 CustomAction (deferred binary DLL)
569
570 NOTE: deferred CustomAction since it modifies the machine
571 NOTE: CustomActionData == wzObject\twzTable\twzDomain\twzUser\tdwPermissions\twzObject\t...
572******************************************************************/
573extern "C" UINT __stdcall ExecSecureObjects(
574 __in MSIHANDLE hInstall
575 )
576{
577// AssertSz(FALSE, "debug ExecSecureObjects");
578 HRESULT hr = S_OK;
579 DWORD er = ERROR_SUCCESS;
580
581 LPWSTR pwz = NULL;
582 LPWSTR pwzData = NULL;
583 LPWSTR pwzObject = NULL;
584 LPWSTR pwzTable = NULL;
585 LPWSTR pwzDomain = NULL;
586 DWORD dwRevision = 0;
587 LPWSTR pwzUser = NULL;
588 DWORD dwPermissions = 0;
589 LPWSTR pwzAccount = NULL;
590 PSID psid = NULL;
591
592 EXPLICIT_ACCESSW ea = {0};
593 SE_OBJECT_TYPE objectType = SE_UNKNOWN_OBJECT_TYPE;
594 PSECURITY_DESCRIPTOR psd = NULL;
595 SECURITY_DESCRIPTOR_CONTROL sdc = {0};
596 SECURITY_INFORMATION si = {0};
597 PACL pAclExisting = NULL; // doesn't get freed
598 PACL pAclNew = NULL;
599
600 PMSIHANDLE hActionRec = ::MsiCreateRecord(1);
601
602 //
603 // initialize
604 //
605 hr = WcaInitialize(hInstall, "ExecSecureObjects");
606 ExitOnFailure(hr, "failed to initialize");
607
608 hr = WcaGetProperty(L"CustomActionData", &pwzData);
609 ExitOnFailure(hr, "failed to get CustomActionData");
610
611 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
612
613 pwz = pwzData;
614
615 //
616 // loop through all the passed in data
617 //
618 while (pwz && *pwz)
619 {
620 hr = WcaReadStringFromCaData(&pwz, &pwzObject);
621 ExitOnFailure(hr, "failed to process CustomActionData");
622
623 hr = WcaReadStringFromCaData(&pwz, &pwzTable);
624 ExitOnFailure(hr, "failed to process CustomActionData");
625 hr = WcaReadStringFromCaData(&pwz, &pwzDomain);
626 ExitOnFailure(hr, "failed to process CustomActionData");
627 hr = WcaReadStringFromCaData(&pwz, &pwzUser);
628 ExitOnFailure(hr, "failed to process CustomActionData");
629 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwPermissions));
630 ExitOnFailure(hr, "failed to processCustomActionData");
631
632 WcaLog(LOGMSG_VERBOSE, "Securing Object: %ls Type: %ls User: %ls", pwzObject, pwzTable, pwzUser);
633
634 //
635 // create the appropriate SID
636 //
637
638 // figure out the right user to put into the access block
639 if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Everyone"))
640 {
641 hr = AclGetWellKnownSid(WinWorldSid, &psid);
642 }
643 else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Administrators"))
644 {
645 hr = AclGetWellKnownSid(WinBuiltinAdministratorsSid, &psid);
646 }
647 else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"LocalSystem"))
648 {
649 hr = AclGetWellKnownSid(WinLocalSystemSid, &psid);
650 }
651 else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"LocalService"))
652 {
653 hr = AclGetWellKnownSid(WinLocalServiceSid, &psid);
654 }
655 else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"NetworkService"))
656 {
657 hr = AclGetWellKnownSid(WinNetworkServiceSid, &psid);
658 }
659 else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"AuthenticatedUser"))
660 {
661 hr = AclGetWellKnownSid(WinAuthenticatedUserSid, &psid);
662 }
663 else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Guests"))
664 {
665 hr = AclGetWellKnownSid(WinBuiltinGuestsSid, &psid);
666 }
667 else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"CREATOR OWNER"))
668 {
669 hr = AclGetWellKnownSid(WinCreatorOwnerSid, &psid);
670 }
671 else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"INTERACTIVE"))
672 {
673 hr = AclGetWellKnownSid(WinInteractiveSid, &psid);
674 }
675 else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Users"))
676 {
677 hr = AclGetWellKnownSid(WinBuiltinUsersSid, &psid);
678 }
679 else
680 {
681 hr = StrAllocFormatted(&pwzAccount, L"%s%s%s", pwzDomain, *pwzDomain ? L"\\" : L"", pwzUser);
682 ExitOnFailure(hr, "failed to build domain user name");
683
684 hr = AclGetAccountSid(NULL, pwzAccount, &psid);
685 }
686 ExitOnFailure(hr, "failed to get sid for account: %ls%ls%ls", pwzDomain, *pwzDomain ? L"\\" : L"", pwzUser);
687
688 //
689 // build up the explicit access
690 //
691 ea.grfAccessMode = SET_ACCESS;
692
693 if (0 == lstrcmpW(L"CreateFolder", pwzTable))
694 {
695 ea.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
696 }
697 else
698 {
699 ea.grfInheritance = NO_INHERITANCE;
700 }
701
702#pragma prefast(push)
703#pragma prefast(disable:25029)
704 ::BuildTrusteeWithSidW(&ea.Trustee, psid);
705#pragma prefast(pop)
706
707 objectType = SEObjectTypeFromString(const_cast<LPCWSTR> (pwzTable));
708
709 // always add these permissions for services
710 // these are basic permissions that are often forgotten
711 if (0 == lstrcmpW(L"ServiceInstall", pwzTable))
712 {
713 dwPermissions |= SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_INTERROGATE;
714 }
715
716 ea.grfAccessPermissions = dwPermissions;
717
718 if (SE_UNKNOWN_OBJECT_TYPE != objectType)
719 {
720 er = ::GetNamedSecurityInfoW(pwzObject, objectType, DACL_SECURITY_INFORMATION, NULL, NULL, &pAclExisting, NULL, &psd);
721 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to get security info for object: %ls", pwzObject);
722
723 //Need to see if DACL is protected so getting Descriptor information
724 if (!::GetSecurityDescriptorControl(psd, &sdc, &dwRevision))
725 {
726 ExitOnLastError(hr, "failed to get security descriptor control for object: %ls", pwzObject);
727 }
728
729#pragma prefast(push)
730#pragma prefast(disable:25029)
731 er = ::SetEntriesInAclW(1, &ea, pAclExisting, &pAclNew);
732#pragma prefast(pop)
733 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to add ACLs for object: %ls", pwzObject);
734
735 if (sdc & SE_DACL_PROTECTED)
736 {
737 si = DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION;
738 }
739 else
740 {
741 si = DACL_SECURITY_INFORMATION;
742 }
743 er = ::SetNamedSecurityInfoW(pwzObject, objectType, si, NULL, NULL, pAclNew, NULL);
744 MessageExitOnFailure(hr = HRESULT_FROM_WIN32(er), msierrSecureObjectsFailedSet, "failed to set security info for object: %ls", pwzObject);
745 }
746 else
747 {
748 MessageExitOnFailure(hr = E_UNEXPECTED, msierrSecureObjectsUnknownType, "unknown object type: %ls", pwzTable);
749 }
750
751 hr = WcaProgressMessage(COST_SECUREOBJECT, FALSE);
752 ExitOnFailure(hr, "failed to send progress message");
753
754 objectType = SE_UNKNOWN_OBJECT_TYPE;
755 }
756
757LExit:
758 ReleaseStr(pwzUser);
759 ReleaseStr(pwzDomain);
760 ReleaseStr(pwzTable);
761 ReleaseStr(pwzObject);
762 ReleaseStr(pwzData);
763 ReleaseStr(pwzAccount);
764
765 if (pAclNew)
766 {
767 ::LocalFree(pAclNew);
768 }
769 if (psd)
770 {
771 ::LocalFree(psd);
772 }
773 if (psid)
774 {
775 AclFreeSid(psid);
776 }
777
778 if (FAILED(hr))
779 {
780 er = ERROR_INSTALL_FAILURE;
781 }
782 return WcaFinalize(er);
783}
784
785extern "C" UINT __stdcall ExecSecureObjectsRollback(
786 __in MSIHANDLE hInstall
787 )
788{
789// AssertSz(FALSE, "debug ExecSecureObjectsRollback");
790 HRESULT hr = S_OK;
791 DWORD er = ERROR_SUCCESS;
792
793 LPWSTR pwz = NULL;
794 LPWSTR pwzData = NULL;
795 LPWSTR pwzObject = NULL;
796 LPWSTR pwzTable = NULL;
797 LPWSTR pwzSecurityInfo = NULL;
798
799 SE_OBJECT_TYPE objectType = SE_UNKNOWN_OBJECT_TYPE;
800 PSECURITY_DESCRIPTOR psd = NULL;
801 ULONG psdSize;
802 SECURITY_DESCRIPTOR_CONTROL sdc = {0};
803 SECURITY_INFORMATION si = DACL_SECURITY_INFORMATION;
804 PACL pDacl = NULL;
805 BOOL bDaclPresent = false;
806 BOOL bDaclDefaulted = false;
807 DWORD dwRevision = 0;
808 int iProtected;
809
810 // initialize
811 hr = WcaInitialize(hInstall, "ExecSecureObjectsRollback");
812 ExitOnFailure(hr, "failed to initialize");
813
814 hr = WcaGetProperty(L"CustomActionData", &pwzData);
815 ExitOnFailure(hr, "failed to get CustomActionData");
816
817 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
818
819 pwz = pwzData;
820
821 hr = WcaReadStringFromCaData(&pwz, &pwzObject);
822 ExitOnFailure(hr, "failed to process CustomActionData");
823
824 hr = WcaReadStringFromCaData(&pwz, &pwzTable);
825 ExitOnFailure(hr, "failed to process CustomActionData");
826
827 objectType = SEObjectTypeFromString(const_cast<LPCWSTR> (pwzTable));
828
829 if (SE_UNKNOWN_OBJECT_TYPE != objectType)
830 {
831 hr = WcaReadStringFromCaData(&pwz, &pwzSecurityInfo);
832 ExitOnFailure(hr, "failed to process CustomActionData");
833
834 hr = WcaReadIntegerFromCaData(&pwz, &iProtected);
835 ExitOnFailure(hr, "failed to process CustomActionData");
836
837 if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(pwzSecurityInfo,SDDL_REVISION_1,&psd,&psdSize))
838 {
839 ExitOnLastError(hr, "failed to convert security descriptor string to a valid security descriptor");
840 }
841
842 if (!::GetSecurityDescriptorDacl(psd,&bDaclPresent,&pDacl,&bDaclDefaulted))
843 {
844 hr = E_UNEXPECTED;
845 ExitOnFailure(hr, "failed to get security descriptor's DACL - error code: %d",pwzSecurityInfo,GetLastError());
846 }
847
848 // The below situation may always be caught by the above if block - the documentation isn't very clear. To be safe, we're going to test for it.
849 if (!bDaclPresent)
850 {
851 hr = E_UNEXPECTED;
852 ExitOnFailure(hr, "security descriptor does not contain a DACL");
853 }
854
855 //Need to see if DACL is protected so getting Descriptor information
856 if (!::GetSecurityDescriptorControl(psd, &sdc, &dwRevision))
857 {
858 ExitOnLastError(hr, "failed to get security descriptor control for object: %ls", pwzObject);
859 }
860
861 // Write a 1 if DACL is protected, 0 otherwise
862 switch (iProtected)
863 {
864 case 0:
865 // Unnecessary to do anything - leave si to the default flags
866 break;
867
868 case 1:
869 si = si | PROTECTED_DACL_SECURITY_INFORMATION;
870 break;
871
872 default:
873 hr = E_UNEXPECTED;
874 ExitOnFailure(hr, "unrecognized value in CustomActionData");
875 break;
876 }
877
878 er = ::SetNamedSecurityInfoW(pwzObject, objectType, si, NULL, NULL, pDacl, NULL);
879 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to set security info for object: %ls error code: %d", pwzObject, GetLastError());
880 }
881 else
882 {
883 MessageExitOnFailure(hr = E_UNEXPECTED, msierrSecureObjectsUnknownType, "unknown object type: %ls", pwzTable);
884 }
885
886LExit:
887 ReleaseStr(pwzData);
888 ReleaseStr(pwzObject);
889 ReleaseStr(pwzTable);
890 ReleaseStr(pwzSecurityInfo);
891
892 if (psd)
893 {
894 ::LocalFree(psd);
895 }
896
897 if (FAILED(hr))
898 {
899 er = ERROR_INSTALL_FAILURE;
900 }
901 return WcaFinalize(er);
902}
diff --git a/src/ca/serviceconfig.cpp b/src/ca/serviceconfig.cpp
new file mode 100644
index 00000000..c2b77035
--- /dev/null
+++ b/src/ca/serviceconfig.cpp
@@ -0,0 +1,821 @@
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// structs
6LPCWSTR wzQUERY_SERVICECONFIG = L"SELECT `ServiceName`, `Component_`, `NewService`, `FirstFailureActionType`, `SecondFailureActionType`, `ThirdFailureActionType`, `ResetPeriodInDays`, `RestartServiceDelayInSeconds`, `ProgramCommandLine`, `RebootMessage` FROM `ServiceConfig`";
7enum eQUERY_SERVICECONFIG { QSC_SERVICENAME = 1, QSC_COMPONENT, QSC_NEWSERVICE, QSC_FIRSTFAILUREACTIONTYPE, QSC_SECONDFAILUREACTIONTYPE, QSC_THIRDFAILUREACTIONTYPE, QSC_RESETPERIODINDAYS, QSC_RESTARTSERVICEDELAYINSECONDS, QSC_PROGRAMCOMMANDLINE, QSC_REBOOTMESSAGE };
8
9// consts
10LPCWSTR c_wzActionTypeNone = L"none";
11LPCWSTR c_wzActionTypeReboot = L"reboot";
12LPCWSTR c_wzActionTypeRestart = L"restart";
13LPCWSTR c_wzActionTypeRunCommand = L"runCommand";
14
15// prototypes
16static SC_ACTION_TYPE GetSCActionType(
17 __in LPCWSTR pwzActionTypeName
18 );
19
20static HRESULT GetSCActionTypeString(
21 __in SC_ACTION_TYPE type,
22 __out_ecount(cchActionTypeString) LPWSTR wzActionTypeString,
23 __in DWORD cchActionTypeString
24 );
25
26static HRESULT GetService(
27 __in SC_HANDLE hSCM,
28 __in LPCWSTR wzService,
29 __in DWORD dwOpenServiceAccess,
30 __out SC_HANDLE* phService
31 );
32
33static HRESULT ConfigureService(
34 __in SC_HANDLE hSCM,
35 __in SC_HANDLE hService,
36 __in LPCWSTR wzServiceName,
37 __in DWORD dwRestartServiceDelayInSeconds,
38 __in LPCWSTR wzFirstFailureActionType,
39 __in LPCWSTR wzSecondFailureActionType,
40 __in LPCWSTR wzThirdFailureActionType,
41 __in DWORD dwResetPeriodInDays,
42 __in LPWSTR wzRebootMessage,
43 __in LPWSTR wzProgramCommandLine
44 );
45
46
47/******************************************************************
48SchedServiceConfig - entry point for SchedServiceConfig Custom Action
49
50called as Type 1 CustomAction (binary DLL) from Windows Installer
51in InstallExecuteSequence before CaExecServiceConfig
52********************************************************************/
53extern "C" UINT __stdcall SchedServiceConfig(
54 __in MSIHANDLE hInstall
55 )
56{
57 //AssertSz(FALSE, "debug SchedServiceConfig");
58 HRESULT hr = S_OK;
59 UINT er = ERROR_SUCCESS;
60
61 LPWSTR pwzScriptKey = NULL;
62 LPWSTR pwzCustomActionData = NULL;
63
64 PMSIHANDLE hView = NULL;
65 PMSIHANDLE hRec = NULL;
66 LPWSTR pwzData = NULL;
67 int iData = 0;
68 DWORD cServices = 0;
69
70 // initialize
71 hr = WcaInitialize(hInstall, "SchedServiceConfig");
72 ExitOnFailure(hr, "Failed to initialize.");
73
74 // Get the script key for this CustomAction and put it on the front of the
75 // CustomActionData of the install action.
76 hr = WcaCaScriptCreateKey(&pwzScriptKey);
77 ExitOnFailure(hr, "Failed to get encoding key.");
78
79 hr = WcaWriteStringToCaData(pwzScriptKey, &pwzCustomActionData);
80 ExitOnFailure(hr, "Failed to add encoding key to CustomActionData.");
81
82 // Loop through all the services to be configured.
83 hr = WcaOpenExecuteView(wzQUERY_SERVICECONFIG, &hView);
84 ExitOnFailure(hr, "Failed to open view on ServiceConfig table.");
85
86 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
87 {
88 INSTALLSTATE isInstalled = INSTALLSTATE_UNKNOWN;
89 INSTALLSTATE isAction = INSTALLSTATE_UNKNOWN;
90
91 // Get component name to check if we are installing it. If so
92 // then add the table data to the CustomActionData, otherwise
93 // skip it.
94 hr = WcaGetRecordString(hRec, QSC_COMPONENT, &pwzData);
95 ExitOnFailure(hr, "Failed to get component name");
96
97 hr = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction);
98 ExitOnFailure(hr = HRESULT_FROM_WIN32(hr), "Failed to get install state for Component: %ls", pwzData);
99
100 if (WcaIsInstalling(isInstalled, isAction))
101 {
102 // Add the data to the CustomActionData (for install).
103 hr = WcaGetRecordFormattedString(hRec, QSC_SERVICENAME, &pwzData);
104 ExitOnFailure(hr, "Failed to get name of service.");
105 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
106 ExitOnFailure(hr, "Failed to add name to CustomActionData.");
107
108 hr = WcaGetRecordInteger(hRec, QSC_NEWSERVICE, &iData);
109 ExitOnFailure(hr, "Failed to get ServiceConfig.NewService.");
110 hr = WcaWriteIntegerToCaData(0 != iData, &pwzCustomActionData);
111 ExitOnFailure(hr, "Failed to add NewService data to CustomActionData");
112
113 hr = WcaGetRecordString(hRec, QSC_FIRSTFAILUREACTIONTYPE, &pwzData);
114 ExitOnFailure(hr, "failed to get first failure action type");
115 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
116 ExitOnFailure(hr, "failed to add data to CustomActionData");
117
118 hr = WcaGetRecordString(hRec, QSC_SECONDFAILUREACTIONTYPE, &pwzData);
119 ExitOnFailure(hr, "failed to get second failure action type");
120 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
121 ExitOnFailure(hr, "failed to add data to CustomActionData");
122
123 hr = WcaGetRecordString(hRec, QSC_THIRDFAILUREACTIONTYPE, &pwzData);
124 ExitOnFailure(hr, "failed to get third failure action type");
125 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
126 ExitOnFailure(hr, "failed to add data to CustomActionData");
127
128 hr = WcaGetRecordInteger(hRec, QSC_RESETPERIODINDAYS, &iData);
129 if (S_FALSE == hr) // deal w/ possible null value
130 {
131 iData = 0;
132 }
133 ExitOnFailure(hr, "failed to get reset period in days between service restart attempts.");
134 hr = WcaWriteIntegerToCaData(iData, &pwzCustomActionData);
135 ExitOnFailure(hr, "failed to add data to CustomActionData");
136
137 hr = WcaGetRecordInteger(hRec, QSC_RESTARTSERVICEDELAYINSECONDS, &iData);
138 if (S_FALSE == hr) // deal w/ possible null value
139 {
140 iData = 0;
141 }
142 ExitOnFailure(hr, "failed to get server restart delay value.");
143 hr = WcaWriteIntegerToCaData(iData, &pwzCustomActionData);
144 ExitOnFailure(hr, "failed to add data to CustomActionData");
145
146 hr = WcaGetRecordFormattedString(hRec, QSC_PROGRAMCOMMANDLINE, &pwzData); // null value already dealt w/ properly
147 ExitOnFailure(hr, "failed to get command line to run on service failure.");
148 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
149 ExitOnFailure(hr, "failed to add data to CustomActionData");
150
151 hr = WcaGetRecordString(hRec, QSC_REBOOTMESSAGE, &pwzData); // null value already dealt w/ properly
152 ExitOnFailure(hr, "failed to get message to send to users when server reboots due to service failure.");
153 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
154 ExitOnFailure(hr, "failed to add data to CustomActionData");
155
156 ++cServices;
157 }
158 }
159
160 // if we looped through all records all is well
161 if (E_NOMOREITEMS == hr)
162 {
163 hr = S_OK;
164 }
165 ExitOnFailure(hr, "failed while looping through all objects to secure");
166
167 // setup CustomActionData and add to progress bar for download
168 if (0 < cServices)
169 {
170 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"RollbackServiceConfig"), pwzScriptKey, cServices * COST_SERVICECONFIG);
171 ExitOnFailure(hr, "failed to schedule RollbackServiceConfig action");
172
173 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"ExecServiceConfig"), pwzCustomActionData, cServices * COST_SERVICECONFIG);
174 ExitOnFailure(hr, "failed to schedule ExecServiceConfig action");
175 }
176
177LExit:
178 ReleaseStr(pwzData);
179 ReleaseStr(pwzCustomActionData);
180 ReleaseStr(pwzScriptKey);
181
182 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
183 return WcaFinalize(er);
184}
185
186
187/******************************************************************
188CaExecServiceConfig - entry point for ServiceConfig Custom Action.
189
190NOTE: deferred CustomAction since it modifies the machine
191NOTE: CustomActionData == wzServiceName\tfNewService\twzFirstFailureActionType\twzSecondFailureActionType\twzThirdFailureActionType\tdwResetPeriodInDays\tdwRestartServiceDelayInSeconds\twzProgramCommandLine\twzRebootMessage\twzServiceName\tfNewService\t...
192*******************************************************************/
193extern "C" UINT __stdcall ExecServiceConfig(
194 __in MSIHANDLE hInstall
195 )
196{
197 //AssertSz(FALSE, "debug ExecServiceConfig");
198 HRESULT hr = S_OK;
199 DWORD er = 0;
200
201 LPWSTR pwzCustomActionData = NULL;
202 LPWSTR pwz = NULL;
203
204 LPWSTR pwzScriptKey = NULL;
205 WCA_CASCRIPT_HANDLE hRollbackScript = NULL;
206
207 LPWSTR pwzServiceName = NULL;
208 BOOL fNewService = FALSE;
209 LPWSTR pwzFirstFailureActionType = NULL;
210 LPWSTR pwzSecondFailureActionType = NULL;
211 LPWSTR pwzThirdFailureActionType = NULL;
212 LPWSTR pwzProgramCommandLine = NULL;
213 LPWSTR pwzRebootMessage = NULL;
214 DWORD dwResetPeriodInDays = 0;
215 DWORD dwRestartServiceDelayInSeconds = 0;
216
217 LPVOID lpMsgBuf = NULL;
218 SC_HANDLE hSCM = NULL;
219 SC_HANDLE hService = NULL;
220
221 DWORD dwRestartDelay = 0;
222 WCHAR wzActionName[32] = { 0 };
223
224 DWORD cbExistingServiceConfig = 0;
225
226 SERVICE_FAILURE_ACTIONSW* psfa = NULL;
227
228 // initialize
229 hr = WcaInitialize(hInstall, "ExecServiceConfig");
230 ExitOnFailure(hr, "failed to initialize");
231
232 // Open the Services Control Manager up front.
233 hSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
234 if (NULL == hSCM)
235 {
236 er = ::GetLastError();
237 hr = HRESULT_FROM_WIN32(er);
238
239#pragma prefast(push)
240#pragma prefast(disable:25028)
241 ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, er, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&lpMsgBuf, 0, NULL);
242#pragma prefast(pop)
243
244 ExitOnFailure(hr, "Failed to get handle to SCM. Error: %ls", (LPWSTR)lpMsgBuf);
245 }
246
247 // First, get the script key out of the CustomActionData and
248 // use that to create the rollback script for this action.
249 hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
250 ExitOnFailure(hr, "failed to get CustomActionData");
251
252 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
253
254 pwz = pwzCustomActionData;
255
256 hr = WcaReadStringFromCaData(&pwz, &pwzScriptKey);
257 if (!pwzScriptKey)
258 {
259 hr = E_UNEXPECTED;
260 ExitOnFailure(hr, "Failed due to unexpected CustomActionData passed.");
261 }
262 ExitOnFailure(hr, "Failed to read encoding key from CustomActionData.");
263
264 hr = WcaCaScriptCreate(WCA_ACTION_INSTALL, WCA_CASCRIPT_ROLLBACK, FALSE, pwzScriptKey, FALSE, &hRollbackScript);
265 ExitOnFailure(hr, "Failed to open rollback CustomAction script.");
266
267 // Next, loop through the rest of the CustomActionData, processing
268 // each service config row in turn.
269 while (pwz && *pwz)
270 {
271 hr = WcaReadStringFromCaData(&pwz, &pwzServiceName);
272 ExitOnFailure(hr, "failed to process CustomActionData");
273 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&fNewService));
274 ExitOnFailure(hr, "failed to process CustomActionData");
275 hr = WcaReadStringFromCaData(&pwz, &pwzFirstFailureActionType);
276 ExitOnFailure(hr, "failed to process CustomActionData");
277 hr = WcaReadStringFromCaData(&pwz, &pwzSecondFailureActionType);
278 ExitOnFailure(hr, "failed to process CustomActionData");
279 hr = WcaReadStringFromCaData(&pwz, &pwzThirdFailureActionType);
280 ExitOnFailure(hr, "failed to process CustomActionData");
281 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwResetPeriodInDays));
282 ExitOnFailure(hr, "failed to process CustomActionData");
283 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwRestartServiceDelayInSeconds));
284 ExitOnFailure(hr, "failed to process CustomActionData");
285 hr = WcaReadStringFromCaData(&pwz, &pwzProgramCommandLine);
286 ExitOnFailure(hr, "failed to process CustomActionData");
287 hr = WcaReadStringFromCaData(&pwz, &pwzRebootMessage);
288 ExitOnFailure(hr, "failed to process CustomActionData");
289
290 WcaLog(LOGMSG_VERBOSE, "Configuring Service: %ls", pwzServiceName);
291
292 // Open the handle with all the permissions we might need:
293 // SERVICE_QUERY_CONFIG is needed for QueryServiceConfig2().
294 // SERVICE_CHANGE_CONFIG is needed for ChangeServiceConfig2().
295 // SERVICE_START is required in order to handle SC_ACTION_RESTART action.
296 hr = GetService(hSCM, pwzServiceName, SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_START, &hService);
297 ExitOnFailure(hr, "Failed to get service: %ls", pwzServiceName);
298
299 // If we are configuring a service that existed on the machine, we need to
300 // read the existing service configuration and write it out to the rollback
301 // log so rollback can put it back if anything goes wrong.
302 if (!fNewService)
303 {
304 // First, read the existing service config.
305 if (!::QueryServiceConfig2W(hService, SERVICE_CONFIG_FAILURE_ACTIONS, NULL, 0, &cbExistingServiceConfig) && ERROR_INSUFFICIENT_BUFFER != ::GetLastError())
306 {
307 ExitWithLastError(hr, "Failed to get current service config info.");
308 }
309
310 psfa = static_cast<LPSERVICE_FAILURE_ACTIONSW>(MemAlloc(cbExistingServiceConfig, TRUE));
311 ExitOnNull(psfa, hr, E_OUTOFMEMORY, "failed to allocate memory for service failure actions.");
312
313 if (!::QueryServiceConfig2W(hService, SERVICE_CONFIG_FAILURE_ACTIONS, (LPBYTE)psfa, cbExistingServiceConfig, &cbExistingServiceConfig))
314 {
315 ExitOnLastError(hr, "failed to Query Service.");
316 }
317
318 // Build up rollback log so we can restore service state if necessary
319 hr = WcaCaScriptWriteString(hRollbackScript, pwzServiceName);
320 ExitOnFailure(hr, "Failed to add service name to Rollback Log");
321
322 // If this service struct is empty, fill in default values
323 if (3 > psfa->cActions)
324 {
325 hr = WcaCaScriptWriteString(hRollbackScript, c_wzActionTypeNone);
326 ExitOnFailure(hr, "failed to add data to Rollback CustomActionData");
327
328 hr = WcaCaScriptWriteString(hRollbackScript, c_wzActionTypeNone);
329 ExitOnFailure(hr, "failed to add data to Rollback CustomActionData");
330
331 hr = WcaCaScriptWriteString(hRollbackScript, c_wzActionTypeNone);
332 ExitOnFailure(hr, "failed to add data to Rollback CustomActionData");
333 }
334 else
335 {
336 // psfa actually had actions defined, so use the first three.
337 for (int i = 0; i < 3; ++i)
338 {
339 hr = GetSCActionTypeString(psfa->lpsaActions[i].Type, wzActionName, countof(wzActionName));
340 ExitOnFailure(hr, "failed to query SFA object");
341
342 if (SC_ACTION_RESTART == psfa->lpsaActions[i].Type)
343 {
344 dwRestartDelay = psfa->lpsaActions[i].Delay / 1000;
345 }
346
347 hr = WcaCaScriptWriteString(hRollbackScript, wzActionName);
348 ExitOnFailure(hr, "failed to add data to Rollback CustomActionData");
349 }
350 }
351
352 hr = WcaCaScriptWriteNumber(hRollbackScript, psfa->dwResetPeriod / (24 * 60 * 60));
353 ExitOnFailure(hr, "failed to add data to CustomActionData");
354
355 hr = WcaCaScriptWriteNumber(hRollbackScript, dwRestartDelay);
356 ExitOnFailure(hr, "failed to add data to CustomActionData");
357
358 // Handle the null cases.
359 if (!psfa->lpCommand)
360 {
361 psfa->lpCommand = L"";
362 }
363 hr = WcaCaScriptWriteString(hRollbackScript, psfa->lpCommand);
364 ExitOnFailure(hr, "failed to add data to Rollback CustomActionData");
365
366 // Handle the null cases.
367 if (!psfa->lpRebootMsg)
368 {
369 psfa->lpRebootMsg = L"";
370 }
371 hr = WcaCaScriptWriteString(hRollbackScript, psfa->lpRebootMsg);
372 ExitOnFailure(hr, "failed to add data to Rollback CustomActionData");
373
374 // Nudge the system to get all our rollback data written to disk.
375 WcaCaScriptFlush(hRollbackScript);
376
377 ReleaseNullMem(psfa);
378 }
379
380 hr = ConfigureService(hSCM, hService, pwzServiceName, dwRestartServiceDelayInSeconds, pwzFirstFailureActionType,
381 pwzSecondFailureActionType, pwzThirdFailureActionType, dwResetPeriodInDays, pwzRebootMessage, pwzProgramCommandLine);
382 ExitOnFailure(hr, "Failed to configure service: %ls", pwzServiceName);
383
384 hr = WcaProgressMessage(COST_SERVICECONFIG, FALSE);
385 ExitOnFailure(hr, "failed to send progress message");
386
387 // Per-service cleanup
388 ::CloseServiceHandle(hService);
389 hService = NULL;
390 dwResetPeriodInDays = 0;
391 dwRestartServiceDelayInSeconds = 0;
392 }
393
394LExit:
395 WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_PRESERVE);
396
397 if (lpMsgBuf)
398 {
399 ::LocalFree(lpMsgBuf);
400 }
401
402 if (hService)
403 {
404 ::CloseServiceHandle(hService);
405 }
406
407 if (hSCM)
408 {
409 ::CloseServiceHandle(hSCM);
410 }
411
412 ReleaseMem(psfa);
413
414 ReleaseStr(pwzRebootMessage);
415 ReleaseStr(pwzProgramCommandLine);
416 ReleaseStr(pwzThirdFailureActionType);
417 ReleaseStr(pwzSecondFailureActionType);
418 ReleaseStr(pwzFirstFailureActionType);
419 ReleaseStr(pwzServiceName);
420 ReleaseStr(pwzScriptKey);
421 ReleaseStr(pwzCustomActionData);
422
423 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
424 return WcaFinalize(er);
425}
426
427
428/******************************************************************
429RollbackServiceConfig - entry point for ServiceConfig rollback
430 Custom Action.
431
432NOTE: CustomActionScript Data == wzServiceName\twzFirstFailureActionType\twzSecondFailureActionType\twzThirdFailureActionType\tdwResetPeriodInDays\tdwRestartServiceDelayInSeconds\twzProgramCommandLine\twzRebootMessage\twzServiceName\t...
433*******************************************************************/
434extern "C" UINT __stdcall RollbackServiceConfig(
435 __in MSIHANDLE hInstall
436 )
437{
438 //AssertSz(FALSE, "debug RollbackServiceConfig");
439 HRESULT hr = S_OK;
440 DWORD er = 0;
441
442 LPWSTR pwzCustomActionData = NULL;
443 LPWSTR pwz = NULL;
444
445 LPWSTR pwzScriptKey = NULL;
446 WCA_CASCRIPT_HANDLE hRollbackScript = NULL;
447
448 LPWSTR pwzServiceName = NULL;
449 LPWSTR pwzFirstFailureActionType = NULL;
450 LPWSTR pwzSecondFailureActionType = NULL;
451 LPWSTR pwzThirdFailureActionType = NULL;
452 LPWSTR pwzProgramCommandLine = NULL;
453 LPWSTR pwzRebootMessage = NULL;
454 DWORD dwResetPeriodInDays = 0;
455 DWORD dwRestartServiceDelayInSeconds = 0;
456
457 LPVOID lpMsgBuf = NULL;
458 SC_HANDLE hSCM = NULL;
459 SC_HANDLE hService = NULL;
460
461 // initialize
462 hr = WcaInitialize(hInstall, "RollbackServiceConfig");
463 ExitOnFailure(hr, "Failed to initialize 'RollbackServiceConfig'.");
464
465 // Open the Services Control Manager up front.
466 hSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
467 if (NULL == hSCM)
468 {
469 er = ::GetLastError();
470 hr = HRESULT_FROM_WIN32(er);
471
472#pragma prefast(push)
473#pragma prefast(disable:25028)
474 ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, er, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&lpMsgBuf, 0, NULL);
475#pragma prefast(pop)
476
477 ExitOnFailure(hr, "Failed to get handle to SCM. Error: %ls", (LPWSTR)lpMsgBuf);
478
479 // Make sure we still abort, in case hSCM was NULL but no error was returned from GetLastError
480 ExitOnNull(hSCM, hr, E_POINTER, "Getting handle to SCM reported success, but no handle was returned.");
481 }
482
483 // Get the script key from the CustomAction data and use it to open
484 // the rollback log and read the data over the CustomActionData
485 // because all of the information is in the script data not the
486 // CustomActionData.
487 hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
488 ExitOnFailure(hr, "failed to get CustomActionData");
489
490 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
491
492 pwz = pwzCustomActionData;
493
494 hr = WcaReadStringFromCaData(&pwz, &pwzScriptKey);
495 if (!pwzScriptKey)
496 {
497 hr = E_UNEXPECTED;
498 ExitOnFailure(hr, "Failed due to unexpected CustomActionData passed.");
499 }
500 ExitOnFailure(hr, "Failed to read encoding key from CustomActionData.");
501
502 hr = WcaCaScriptOpen(WCA_ACTION_INSTALL, WCA_CASCRIPT_ROLLBACK, FALSE, pwzScriptKey, &hRollbackScript);
503 ExitOnFailure(hr, "Failed to open rollback CustomAction script.");
504
505 hr = WcaCaScriptReadAsCustomActionData(hRollbackScript, &pwzCustomActionData);
506 ExitOnFailure(hr, "Failed to read rollback script into CustomAction data.");
507
508 // Loop through the script's CustomActionData, processing each
509 // service config in turn.
510 pwz = pwzCustomActionData;
511 while (pwz && *pwz)
512 {
513 hr = WcaReadStringFromCaData(&pwz, &pwzServiceName);
514 ExitOnFailure(hr, "failed to process CustomActionData");
515 hr = WcaReadStringFromCaData(&pwz, &pwzFirstFailureActionType);
516 ExitOnFailure(hr, "failed to process CustomActionData");
517 hr = WcaReadStringFromCaData(&pwz, &pwzSecondFailureActionType);
518 ExitOnFailure(hr, "failed to process CustomActionData");
519 hr = WcaReadStringFromCaData(&pwz, &pwzThirdFailureActionType);
520 ExitOnFailure(hr, "failed to process CustomActionData");
521 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwResetPeriodInDays));
522 ExitOnFailure(hr, "failed to process CustomActionData");
523 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwRestartServiceDelayInSeconds));
524 ExitOnFailure(hr, "failed to process CustomActionData");
525 hr = WcaReadStringFromCaData(&pwz, &pwzProgramCommandLine);
526 ExitOnFailure(hr, "failed to process CustomActionData");
527 hr = WcaReadStringFromCaData(&pwz, &pwzRebootMessage);
528 ExitOnFailure(hr, "failed to process CustomActionData");
529
530 WcaLog(LOGMSG_VERBOSE, "Reconfiguring Service: %ls", pwzServiceName);
531
532 // Open the handle with all the permissions we might need.
533 // SERVICE_CHANGE_CONFIG is needed for ChangeServiceConfig2().
534 // SERVICE_START is required in order to handle SC_ACTION_RESTART action.
535 hr = GetService(hSCM, pwzServiceName, SERVICE_CHANGE_CONFIG | SERVICE_START, &hService);
536 ExitOnFailure(hr, "Failed to get service: %ls", pwzServiceName);
537
538 hr = ConfigureService(hSCM, hService, pwzServiceName, dwRestartServiceDelayInSeconds, pwzFirstFailureActionType,
539 pwzSecondFailureActionType, pwzThirdFailureActionType, dwResetPeriodInDays, pwzRebootMessage, pwzProgramCommandLine);
540 ExitOnFailure(hr, "Failed to configure service: %ls", pwzServiceName);
541
542 hr = WcaProgressMessage(COST_SERVICECONFIG, FALSE);
543 ExitOnFailure(hr, "failed to send progress message");
544
545 // Per-service cleanup
546 ::CloseServiceHandle(hService);
547 hService = NULL;
548 dwResetPeriodInDays = 0;
549 dwRestartServiceDelayInSeconds = 0;
550 }
551
552LExit:
553 if (lpMsgBuf) // Allocated with FormatString.
554 {
555 ::LocalFree(lpMsgBuf);
556 }
557
558 if (hService)
559 {
560 ::CloseServiceHandle(hService);
561 }
562
563 if (hSCM)
564 {
565 ::CloseServiceHandle(hSCM);
566 }
567
568 WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_DELETE);
569
570 ReleaseStr(pwzRebootMessage);
571 ReleaseStr(pwzProgramCommandLine);
572 ReleaseStr(pwzThirdFailureActionType);
573 ReleaseStr(pwzSecondFailureActionType);
574 ReleaseStr(pwzFirstFailureActionType);
575 ReleaseStr(pwzServiceName);
576 ReleaseStr(pwzScriptKey);
577 ReleaseStr(pwzCustomActionData);
578
579 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
580 return WcaFinalize(er);
581}
582
583
584/**********************************************************
585GetSCActionType - helper function to return the SC_ACTION_TYPE
586for a given string matching the allowed set.
587REBOOT, RESTART, RUN_COMMAND and NONE
588**********************************************************/
589static SC_ACTION_TYPE GetSCActionType(
590 __in LPCWSTR pwzActionTypeName
591 )
592{
593 SC_ACTION_TYPE actionType;
594
595 // verify that action types are valid. if not, just default to NONE
596 if (0 == lstrcmpiW(c_wzActionTypeReboot, pwzActionTypeName))
597 {
598 actionType = SC_ACTION_REBOOT;
599 }
600 else if (0 == lstrcmpiW(c_wzActionTypeRestart, pwzActionTypeName))
601 {
602 actionType = SC_ACTION_RESTART;
603 }
604 else if (0 == lstrcmpiW(c_wzActionTypeRunCommand, pwzActionTypeName))
605 {
606 actionType = SC_ACTION_RUN_COMMAND;
607 }
608 else
609 {
610 // default to none
611 actionType = SC_ACTION_NONE;
612 }
613
614 return actionType;
615}
616
617
618static HRESULT GetSCActionTypeString(
619 __in SC_ACTION_TYPE type,
620 __out_ecount(cchActionTypeString) LPWSTR wzActionTypeString,
621 __in DWORD cchActionTypeString
622 )
623{
624 HRESULT hr = S_OK;
625
626 switch (type)
627 {
628 case SC_ACTION_REBOOT:
629 hr = StringCchCopyW(wzActionTypeString, cchActionTypeString, c_wzActionTypeReboot);
630 ExitOnFailure(hr, "Failed to copy 'reboot' into action type.");
631 break;
632 case SC_ACTION_RESTART:
633 hr = StringCchCopyW(wzActionTypeString, cchActionTypeString, c_wzActionTypeRestart);
634 ExitOnFailure(hr, "Failed to copy 'restart' into action type.");
635 break;
636 case SC_ACTION_RUN_COMMAND:
637 hr = StringCchCopyW(wzActionTypeString, cchActionTypeString, c_wzActionTypeRunCommand);
638 ExitOnFailure(hr, "Failed to copy 'runCommand' into action type.");
639 break;
640 case SC_ACTION_NONE:
641 hr = StringCchCopyW(wzActionTypeString, cchActionTypeString, c_wzActionTypeNone);
642 ExitOnFailure(hr, "Failed to copy 'none' into action type.");
643 break;
644 default:
645 break;
646 }
647
648LExit:
649 return hr;
650}
651
652
653static HRESULT GetService(
654 __in SC_HANDLE hSCM,
655 __in LPCWSTR wzService,
656 __in DWORD dwOpenServiceAccess,
657 __out SC_HANDLE* phService
658 )
659{
660 HRESULT hr = S_OK;
661 DWORD er = ERROR_SUCCESS;
662 LPVOID lpMsgBuf = NULL;
663
664 *phService = ::OpenServiceW(hSCM, wzService, dwOpenServiceAccess);
665 if (NULL == *phService)
666 {
667 er = ::GetLastError();
668 hr = HRESULT_FROM_WIN32(er);
669 if (ERROR_SERVICE_DOES_NOT_EXIST == er)
670 {
671 ExitOnFailure(hr, "Service '%ls' does not exist on this system.", wzService);
672 }
673 else
674 {
675#pragma prefast(push)
676#pragma prefast(disable:25028)
677 ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, er, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&lpMsgBuf, 0, NULL);
678#pragma prefast(pop)
679
680 ExitOnFailure(hr, "Failed to get handle to the service '%ls'. Error: %ls", wzService, (LPWSTR)lpMsgBuf);
681 }
682 }
683
684LExit:
685 if (lpMsgBuf) // Allocated with FormatString.
686 {
687 ::LocalFree(lpMsgBuf);
688 }
689
690 return hr;
691}
692
693
694static HRESULT ConfigureService(
695 __in SC_HANDLE /*hSCM*/,
696 __in SC_HANDLE hService,
697 __in LPCWSTR wzServiceName,
698 __in DWORD dwRestartServiceDelayInSeconds,
699 __in LPCWSTR wzFirstFailureActionType,
700 __in LPCWSTR wzSecondFailureActionType,
701 __in LPCWSTR wzThirdFailureActionType,
702 __in DWORD dwResetPeriodInDays,
703 __in LPWSTR wzRebootMessage,
704 __in LPWSTR wzProgramCommandLine
705 )
706{
707 HRESULT hr = S_OK;
708 DWORD er = ERROR_SUCCESS;
709
710 HANDLE hToken = NULL;
711 TOKEN_PRIVILEGES priv = { 0 };
712 TOKEN_PRIVILEGES* pPrevPriv = NULL;
713 DWORD cbPrevPriv = 0;
714 BOOL fAdjustedPrivileges = FALSE;
715
716 SC_ACTION actions[3]; // the UI always shows 3 actions, so we'll always do 3
717 SERVICE_FAILURE_ACTIONSW sfa;
718 LPVOID lpMsgBuf = NULL;
719
720 // Always get the shutdown privilege in case we need to configure service to reboot.
721 if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken))
722 {
723 ExitWithLastError(hr, "Failed to get process token.");
724 }
725
726 priv.PrivilegeCount = 1;
727 priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
728 if (!::LookupPrivilegeValueW(NULL, L"SeShutdownPrivilege", &priv.Privileges[0].Luid))
729 {
730 ExitWithLastError(hr, "Failed to get shutdown privilege LUID.");
731 }
732
733 cbPrevPriv = sizeof(TOKEN_PRIVILEGES);
734 pPrevPriv = static_cast<TOKEN_PRIVILEGES*>(MemAlloc(cbPrevPriv, TRUE));
735 ExitOnNull(pPrevPriv, hr, E_OUTOFMEMORY, "Failed to allocate memory for empty previous privileges.");
736
737 if (!::AdjustTokenPrivileges(hToken, FALSE, &priv, cbPrevPriv, pPrevPriv, &cbPrevPriv))
738 {
739 LPVOID pv = MemReAlloc(pPrevPriv, cbPrevPriv, TRUE);
740 ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for previous privileges.");
741 pPrevPriv = static_cast<TOKEN_PRIVILEGES*>(pv);
742
743 if (!::AdjustTokenPrivileges(hToken, FALSE, &priv, cbPrevPriv, pPrevPriv, &cbPrevPriv))
744 {
745 ExitWithLastError(hr, "Failed to get shutdown privilege LUID.");
746 }
747 }
748
749 fAdjustedPrivileges = TRUE;
750
751 // build up SC_ACTION array
752 // TODO: why is delay only respected when SC_ACTION_RESTART is requested?
753 actions[0].Type = GetSCActionType(wzFirstFailureActionType);
754 actions[0].Delay = 0;
755 if (SC_ACTION_RESTART == actions[0].Type)
756 {
757 actions[0].Delay = dwRestartServiceDelayInSeconds * 1000; // seconds to milliseconds
758 }
759
760 actions[1].Type = GetSCActionType(wzSecondFailureActionType);
761 actions[1].Delay = 0;
762 if (SC_ACTION_RESTART == actions[1].Type)
763 {
764 actions[1].Delay = dwRestartServiceDelayInSeconds * 1000; // seconds to milliseconds
765 }
766
767 actions[2].Type = GetSCActionType(wzThirdFailureActionType);
768 actions[2].Delay = 0;
769 if (SC_ACTION_RESTART == actions[2].Type)
770 {
771 actions[2].Delay = dwRestartServiceDelayInSeconds * 1000; // seconds to milliseconds
772 }
773
774 // build up the SERVICE_FAILURE_ACTIONSW struct
775 sfa.dwResetPeriod = dwResetPeriodInDays * (24 * 60 * 60); // days to seconds
776 sfa.lpRebootMsg = wzRebootMessage;
777 sfa.lpCommand = wzProgramCommandLine;
778 sfa.cActions = countof(actions);
779 sfa.lpsaActions = actions;
780
781 // Call ChangeServiceConfig2 to actually set up the failure actions
782 if (!::ChangeServiceConfig2W(hService, SERVICE_CONFIG_FAILURE_ACTIONS, (LPVOID)&sfa))
783 {
784 er = ::GetLastError();
785 hr = HRESULT_FROM_WIN32(er);
786
787#pragma prefast(push)
788#pragma prefast(disable:25028)
789 ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, er, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&lpMsgBuf, 0, NULL);
790#pragma prefast(pop)
791
792 // Check if this is a service that can't be modified.
793 if (ERROR_CANNOT_DETECT_PROCESS_ABORT == er)
794 {
795 WcaLog(LOGMSG_STANDARD, "WARNING: Service \"%ls\" is not configurable on this server and will not be set.", wzServiceName);
796 }
797 ExitOnFailure(hr, "Cannot change service configuration. Error: %ls", (LPWSTR)lpMsgBuf);
798
799 if (lpMsgBuf)
800 {
801 ::LocalFree(lpMsgBuf);
802 lpMsgBuf = NULL;
803 }
804 }
805
806LExit:
807 if (lpMsgBuf)
808 {
809 ::LocalFree(lpMsgBuf);
810 }
811
812 if (fAdjustedPrivileges)
813 {
814 ::AdjustTokenPrivileges(hToken, FALSE, pPrevPriv, 0, NULL, NULL);
815 }
816
817 ReleaseMem(pPrevPriv);
818 ReleaseHandle(hToken);
819
820 return hr;
821}
diff --git a/src/ca/shellexecca.cpp b/src/ca/shellexecca.cpp
new file mode 100644
index 00000000..ea21d3bd
--- /dev/null
+++ b/src/ca/shellexecca.cpp
@@ -0,0 +1,271 @@
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
5HRESULT ShellExec(
6 __in LPCWSTR wzTarget,
7 __in BOOL fUnelevated
8 )
9{
10 HRESULT hr = S_OK;
11 LPWSTR sczWorkingDirectory = NULL;
12
13 // a reasonable working directory (not the system32 default from MSI) is the directory where the target lives
14 hr = PathGetDirectory(wzTarget, &sczWorkingDirectory);
15 ExitOnFailure(hr, "failed to get directory for target: %ls", wzTarget);
16
17 if (!DirExists(sczWorkingDirectory, NULL))
18 {
19 ReleaseNullStr(sczWorkingDirectory);
20 }
21
22 if (fUnelevated)
23 {
24 hr = ShelExecUnelevated(wzTarget, NULL, NULL, sczWorkingDirectory, SW_SHOWDEFAULT);
25 ExitOnFailure(hr, "ShelExecUnelevated failed with target %ls", wzTarget);
26 }
27 else
28 {
29 HINSTANCE hinst = ::ShellExecuteW(NULL, NULL, wzTarget, NULL, sczWorkingDirectory, SW_SHOWDEFAULT);
30 if (hinst <= HINSTANCE(32))
31 {
32 LONG64 code = reinterpret_cast<LONG64>(hinst);
33 switch (code)
34 {
35 case ERROR_FILE_NOT_FOUND:
36 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
37 break;
38 case ERROR_PATH_NOT_FOUND:
39 hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
40 break;
41 case ERROR_BAD_FORMAT:
42 hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
43 break;
44 case SE_ERR_ASSOCINCOMPLETE:
45 case SE_ERR_NOASSOC:
46 hr = HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION);
47 break;
48 case SE_ERR_DDEBUSY:
49 case SE_ERR_DDEFAIL:
50 case SE_ERR_DDETIMEOUT:
51 hr = HRESULT_FROM_WIN32(ERROR_DDE_FAIL);
52 break;
53 case SE_ERR_DLLNOTFOUND:
54 hr = HRESULT_FROM_WIN32(ERROR_DLL_NOT_FOUND);
55 break;
56 case SE_ERR_OOM:
57 hr = E_OUTOFMEMORY;
58 break;
59 case SE_ERR_ACCESSDENIED:
60 hr = E_ACCESSDENIED;
61 break;
62 default:
63 hr = E_FAIL;
64 }
65
66 ExitOnFailure(hr, "ShellExec failed with return code %llu.", code);
67 }
68 }
69
70
71LExit:
72 ReleaseStr(sczWorkingDirectory);
73 return hr;
74}
75
76extern "C" UINT __stdcall WixShellExec(
77 __in MSIHANDLE hInstall
78 )
79{
80 Assert(hInstall);
81 HRESULT hr = S_OK;
82 UINT er = ERROR_SUCCESS;
83 LPWSTR pwzTarget = NULL;
84
85 hr = WcaInitialize(hInstall, "WixShellExec");
86 ExitOnFailure(hr, "failed to initialize");
87
88 hr = WcaGetFormattedProperty(L"WixShellExecTarget", &pwzTarget);
89 ExitOnFailure(hr, "failed to get WixShellExecTarget");
90
91 WcaLog(LOGMSG_VERBOSE, "WixShellExecTarget is %ls", pwzTarget);
92
93 if (!pwzTarget || !*pwzTarget)
94 {
95 hr = E_INVALIDARG;
96 ExitOnFailure(hr, "failed to get WixShellExecTarget");
97 }
98
99 hr = ShellExec(pwzTarget, FALSE);
100 ExitOnFailure(hr, "failed to launch target");
101
102LExit:
103 ReleaseStr(pwzTarget);
104
105 if (FAILED(hr))
106 {
107 er = ERROR_INSTALL_FAILURE;
108 }
109 return WcaFinalize(er);
110}
111
112extern "C" UINT __stdcall WixUnelevatedShellExec(
113 __in MSIHANDLE hInstall
114 )
115{
116 Assert(hInstall);
117 HRESULT hr = S_OK;
118 UINT er = ERROR_SUCCESS;
119 LPWSTR pwzTarget = NULL;
120
121 hr = WcaInitialize(hInstall, "WixUnelevatedShellExec");
122 ExitOnFailure(hr, "failed to initialize");
123
124 hr = WcaGetFormattedProperty(L"WixUnelevatedShellExecTarget", &pwzTarget);
125 ExitOnFailure(hr, "failed to get WixUnelevatedShellExecTarget");
126
127 WcaLog(LOGMSG_VERBOSE, "WixUnelevatedShellExecTarget is %ls", pwzTarget);
128
129 if (!pwzTarget || !*pwzTarget)
130 {
131 hr = E_INVALIDARG;
132 ExitOnFailure(hr, "failed to get WixShellExecTarget");
133 }
134
135 hr = ShellExec(pwzTarget, TRUE);
136 ExitOnFailure(hr, "failed to launch target");
137
138LExit:
139 ReleaseStr(pwzTarget);
140
141 if (FAILED(hr))
142 {
143 er = ERROR_INSTALL_FAILURE;
144 }
145 return WcaFinalize(er);
146}
147
148//
149// ExtractBinary extracts the data from the Binary table row with the given ID into a file.
150//
151HRESULT ExtractBinary(
152 __in LPCWSTR wzBinaryId,
153 __out BYTE** pbData,
154 __out DWORD* pcbData
155 )
156{
157 HRESULT hr = S_OK;
158 LPWSTR pwzSql = NULL;
159 PMSIHANDLE hView;
160 PMSIHANDLE hRec;
161
162 // make sure we're not horked from the get-go
163 hr = WcaTableExists(L"Binary");
164 if (S_OK != hr)
165 {
166 if (SUCCEEDED(hr))
167 {
168 hr = E_UNEXPECTED;
169 }
170 ExitOnFailure(hr, "There is no Binary table.");
171 }
172
173 ExitOnNull(wzBinaryId, hr, E_INVALIDARG, "Binary ID cannot be null");
174 ExitOnNull(*wzBinaryId, hr, E_INVALIDARG, "Binary ID cannot be empty string");
175
176 hr = StrAllocFormatted(&pwzSql, L"SELECT `Data` FROM `Binary` WHERE `Name`=\'%s\'", wzBinaryId);
177 ExitOnFailure(hr, "Failed to allocate Binary table query.");
178
179 hr = WcaOpenExecuteView(pwzSql, &hView);
180 ExitOnFailure(hr, "Failed to open view on Binary table");
181
182 hr = WcaFetchSingleRecord(hView, &hRec);
183 ExitOnFailure(hr, "Failed to retrieve request from Binary table");
184
185 hr = WcaGetRecordStream(hRec, 1, pbData, pcbData);
186 ExitOnFailure(hr, "Failed to read Binary.Data.");
187
188LExit:
189 ReleaseStr(pwzSql);
190
191 return hr;
192}
193
194extern "C" UINT __stdcall WixShellExecBinary(
195 __in MSIHANDLE hInstall
196 )
197{
198 Assert(hInstall);
199 HRESULT hr = S_OK;
200 UINT er = ERROR_SUCCESS;
201 LPWSTR pwzBinary = NULL;
202 LPWSTR pwzFilename = NULL;
203 BYTE* pbData = NULL;
204 DWORD cbData = 0;
205 HANDLE hFile = INVALID_HANDLE_VALUE;
206
207#if 0
208 ::MessageBoxA(0, "WixShellExecBinary", "-->> ATTACH HERE", MB_OK);
209#endif
210
211 hr = WcaInitialize(hInstall, "WixShellExecBinary");
212 ExitOnFailure(hr, "failed to initialize");
213
214 hr = WcaGetFormattedProperty(L"WixShellExecBinaryId", &pwzBinary);
215 ExitOnFailure(hr, "failed to get WixShellExecBinaryId");
216
217 WcaLog(LOGMSG_VERBOSE, "WixShellExecBinaryId is %ls", pwzBinary);
218
219 if (!pwzBinary || !*pwzBinary)
220 {
221 hr = E_INVALIDARG;
222 ExitOnFailure(hr, "failed to get WixShellExecBinaryId");
223 }
224
225 // get temporary path for extracted file
226 StrAlloc(&pwzFilename, MAX_PATH);
227 ExitOnFailure(hr, "Failed to allocate temporary path");
228 ::GetTempPathW(MAX_PATH, pwzFilename);
229 hr = ::StringCchCatW(pwzFilename, MAX_PATH, pwzBinary);
230 ExitOnFailure(hr, "Failed to append filename.");
231
232 // grab the bits
233 hr = ExtractBinary(pwzBinary, &pbData, &cbData);
234 ExitOnFailure(hr, "failed to extract binary data");
235
236 // write 'em to the temp file
237 hFile = ::CreateFileW(pwzFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
238 if (INVALID_HANDLE_VALUE == hFile)
239 {
240 ExitWithLastError(hr, "Failed to open new temp file: %ls", pwzFilename);
241 }
242
243 DWORD cbWritten = 0;
244 if (!::WriteFile(hFile, pbData, cbData, &cbWritten, NULL))
245 {
246 ExitWithLastError(hr, "Failed to write data to new temp file: %ls", pwzFilename);
247 }
248
249 // close it
250 ::CloseHandle(hFile);
251 hFile = INVALID_HANDLE_VALUE;
252
253 // and run it
254 hr = ShellExec(pwzFilename, FALSE);
255 ExitOnFailure(hr, "failed to launch target: %ls", pwzFilename);
256
257LExit:
258 ReleaseStr(pwzBinary);
259 ReleaseStr(pwzFilename);
260 ReleaseMem(pbData);
261 if (INVALID_HANDLE_VALUE != hFile)
262 {
263 ::CloseHandle(hFile);
264 }
265
266 if (FAILED(hr))
267 {
268 er = ERROR_INSTALL_FAILURE;
269 }
270 return WcaFinalize(er);
271}
diff --git a/src/ca/test.cpp b/src/ca/test.cpp
new file mode 100644
index 00000000..c4d215f0
--- /dev/null
+++ b/src/ca/test.cpp
@@ -0,0 +1,269 @@
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 WIXCA_UITHREAD_CLASS_WINDOW L"WixCaMessageWindow"
6
7extern HMODULE g_hInstCADLL;
8
9
10// structs
11
12struct UITHREAD_CONTEXT
13{
14 HANDLE hInitializedEvent;
15 HINSTANCE hInstance;
16 HWND hWnd;
17};
18
19
20// internal function declarations
21
22static HRESULT CreateMessageWindow(
23 __out HWND* phWnd
24 );
25
26static void CloseMessageWindow(
27 __in HWND hWnd
28 );
29
30static DWORD WINAPI ThreadProc(
31 __in LPVOID pvContext
32 );
33
34static LRESULT CALLBACK WndProc(
35 __in HWND hWnd,
36 __in UINT uMsg,
37 __in WPARAM wParam,
38 __in LPARAM lParam
39 );
40
41
42/******************************************************************
43WixFailWhenDeferred - entry point for WixFailWhenDeferred
44 custom action which always fails when running as a deferred
45 custom action (otherwise it blindly succeeds). It's useful when
46 testing the rollback of deferred custom actions: Schedule it
47 immediately after the rollback/deferred CA pair you're testing
48 and it will fail, causing your rollback CA to get invoked.
49********************************************************************/
50extern "C" UINT __stdcall WixFailWhenDeferred(
51 __in MSIHANDLE hInstall
52 )
53{
54 return ::MsiGetMode(hInstall, MSIRUNMODE_SCHEDULED) ? ERROR_INSTALL_FAILURE : ERROR_SUCCESS;
55}
56
57/******************************************************************
58WixWaitForEvent - entry point for WixWaitForEvent custom action
59 which waits for either the WixWaitForEventFail or
60 WixWaitForEventSucceed named auto reset events. Signaling the
61 WixWaitForEventFail event will return ERROR_INSTALL_FAILURE or
62 signaling the WixWaitForEventSucceed event will return
63 ERROR_SUCCESS. Both events are declared in the Global\ namespace.
64********************************************************************/
65extern "C" UINT __stdcall WixWaitForEvent(
66 __in MSIHANDLE hInstall
67 )
68{
69 HRESULT hr = S_OK;
70 UINT er = ERROR_SUCCESS;
71 HWND hMessageWindow = NULL;
72 LPCWSTR wzSDDL = L"D:(A;;GA;;;WD)";
73 OS_VERSION version = OS_VERSION_UNKNOWN;
74 DWORD dwServicePack = 0;
75 PSECURITY_DESCRIPTOR pSD = NULL;
76 SECURITY_ATTRIBUTES sa = { };
77 HANDLE rghEvents[2];
78
79 hr = WcaInitialize(hInstall, "WixWaitForEvent");
80 ExitOnFailure(hr, "Failed to initialize.");
81
82 // Create a window to prevent shutdown requests.
83 hr = CreateMessageWindow(&hMessageWindow);
84 ExitOnFailure(hr, "Failed to create message window.");
85
86 // If running on Vista/2008 or newer use integrity enhancements.
87 OsGetVersion(&version, &dwServicePack);
88 if (OS_VERSION_VISTA <= version)
89 {
90 // Add SACL to allow Everyone to signal from a medium integrity level.
91 wzSDDL = L"D:(A;;GA;;;WD)S:(ML;;NW;;;ME)";
92 }
93
94 // Create the security descriptor and attributes for the events.
95 if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(wzSDDL, SDDL_REVISION_1, &pSD, NULL))
96 {
97 ExitWithLastError(hr, "Failed to create the security descriptor for the events.");
98 }
99
100 sa.nLength = sizeof(sa);
101 sa.lpSecurityDescriptor = pSD;
102 sa.bInheritHandle = FALSE;
103
104 rghEvents[0] = ::CreateEventW(&sa, FALSE, FALSE, L"Global\\WixWaitForEventFail");
105 ExitOnNullWithLastError(rghEvents[0], hr, "Failed to create the Global\\WixWaitForEventFail event.");
106
107 rghEvents[1] = ::CreateEventW(&sa, FALSE, FALSE, L"Global\\WixWaitForEventSucceed");
108 ExitOnNullWithLastError(rghEvents[1], hr, "Failed to create the Global\\WixWaitForEventSucceed event.");
109
110 // Wait for either of the events to be signaled and handle accordingly.
111 er = ::WaitForMultipleObjects(countof(rghEvents), rghEvents, FALSE, INFINITE);
112 switch (er)
113 {
114 case WAIT_OBJECT_0 + 0:
115 er = ERROR_INSTALL_FAILURE;
116 break;
117 case WAIT_OBJECT_0 + 1:
118 er = ERROR_SUCCESS;
119 break;
120 default:
121 ExitOnWin32Error(er, hr, "Unexpected failure.");
122 }
123
124LExit:
125 ReleaseHandle(rghEvents[1]);
126 ReleaseHandle(rghEvents[0]);
127
128 if (pSD)
129 {
130 ::LocalFree(pSD);
131 }
132
133 if (hMessageWindow)
134 {
135 CloseMessageWindow(hMessageWindow);
136 }
137
138 if (FAILED(hr))
139 {
140 er = ERROR_INSTALL_FAILURE;
141 }
142
143 return WcaFinalize(er);
144}
145
146
147// internal function definitions
148
149static HRESULT CreateMessageWindow(
150 __out HWND* phWnd
151 )
152{
153 HRESULT hr = S_OK;
154 HANDLE rgWaitHandles[2] = { };
155 UITHREAD_CONTEXT context = { };
156
157 // Create event to signal after the UI thread / window is initialized.
158 rgWaitHandles[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL);
159 ExitOnNullWithLastError(rgWaitHandles[0], hr, "Failed to create initialization event.");
160
161 // Pass necessary information to create the window.
162 context.hInitializedEvent = rgWaitHandles[0];
163 context.hInstance = (HINSTANCE)g_hInstCADLL;
164
165 // Create our separate UI thread.
166 rgWaitHandles[1] = ::CreateThread(NULL, 0, ThreadProc, &context, 0, NULL);
167 ExitOnNullWithLastError(rgWaitHandles[1], hr, "Failed to create the UI thread.");
168
169 // Wait for either the thread to be initialized or the window to exit / fail prematurely.
170 ::WaitForMultipleObjects(countof(rgWaitHandles), rgWaitHandles, FALSE, INFINITE);
171
172 // Pass the window back to the caller.
173 *phWnd = context.hWnd;
174
175LExit:
176 ReleaseHandle(rgWaitHandles[1]);
177 ReleaseHandle(rgWaitHandles[0]);
178
179 return hr;
180}
181
182static void CloseMessageWindow(
183 __in HWND hWnd
184 )
185{
186 if (::IsWindow(hWnd))
187 {
188 ::PostMessageW(hWnd, WM_CLOSE, 0, 0);
189 }
190}
191
192static DWORD WINAPI ThreadProc(
193 __in LPVOID pvContext
194 )
195{
196 HRESULT hr = S_OK;
197 UITHREAD_CONTEXT* pContext = static_cast<UITHREAD_CONTEXT*>(pvContext);
198
199 WNDCLASSW wc = { };
200 BOOL fRegistered = TRUE;
201 HWND hWnd = NULL;
202
203 BOOL fRet = FALSE;
204 MSG msg = { };
205
206 wc.lpfnWndProc = WndProc;
207 wc.hInstance = pContext->hInstance;
208 wc.lpszClassName = WIXCA_UITHREAD_CLASS_WINDOW;
209
210 if (!::RegisterClassW(&wc))
211 {
212 ExitWithLastError(hr, "Failed to register window.");
213 }
214
215 fRegistered = TRUE;
216
217 // Create the window to handle reboots without activating it.
218 hWnd = ::CreateWindowExW(WS_EX_TOOLWINDOW, wc.lpszClassName, NULL, WS_POPUP | WS_VISIBLE, CW_USEDEFAULT, SW_SHOWNA, 0, 0, HWND_DESKTOP, NULL, pContext->hInstance, NULL);
219 ExitOnNullWithLastError(hWnd, hr, "Failed to create window.");
220
221 // Persist the window handle and let the caller know we've initialized.
222 pContext->hWnd = hWnd;
223 ::SetEvent(pContext->hInitializedEvent);
224
225 // Pump messages until the window is closed.
226 while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
227 {
228 if (-1 == fRet)
229 {
230 hr = E_UNEXPECTED;
231 ExitOnFailure(hr, "Unexpected return value from message pump.");
232 }
233 else if (!::IsDialogMessageW(msg.hwnd, &msg))
234 {
235 ::TranslateMessage(&msg);
236 ::DispatchMessageW(&msg);
237 }
238 }
239
240LExit:
241 if (fRegistered)
242 {
243 ::UnregisterClassW(WIXCA_UITHREAD_CLASS_WINDOW, pContext->hInstance);
244 }
245
246 return hr;
247}
248
249static LRESULT CALLBACK WndProc(
250 __in HWND hWnd,
251 __in UINT uMsg,
252 __in WPARAM wParam,
253 __in LPARAM lParam
254 )
255{
256 switch (uMsg)
257 {
258 case WM_QUERYENDSESSION:
259 // Prevent the process from being shut down.
260 WcaLog(LOGMSG_VERBOSE, "Disallowed system request to shut down the custom action server.");
261 return FALSE;
262
263 case WM_DESTROY:
264 ::PostQuitMessage(0);
265 return 0;
266 }
267
268 return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
269}
diff --git a/src/ca/utilca.def b/src/ca/utilca.def
index 4b34b3a4..97d5776f 100644
--- a/src/ca/utilca.def
+++ b/src/ca/utilca.def
@@ -4,4 +4,85 @@
4LIBRARY "utilca" 4LIBRARY "utilca"
5 5
6EXPORTS 6EXPORTS
7 7; BroadcastSettingChange.cpp
8 WixBroadcastSettingChange
9 WixBroadcastEnvironmentChange
10; checkreboot.cpp
11 WixCheckRebootRequired
12; closeapps.cpp
13 WixCloseApplications
14 WixCloseApplicationsDeferred
15; exitearlywithsuccess.cpp
16 WixExitEarlyWithSuccess
17; FormatFiles.cpp
18 WixSchedFormatFiles
19 WixExecFormatFiles
20; osinfo.cpp
21 WixQueryOsInfo
22 WixQueryOsDirs
23 WixQueryOsWellKnownSID
24 WixQueryOsDriverInfo
25; netshortcuts.cpp
26 WixSchedInternetShortcuts
27 WixCreateInternetShortcuts
28 WixRollbackInternetShortcuts
29; qtexecca.cpp
30 CAQuietExec
31 CAQuietExec64
32 WixQuietExec
33 WixQuietExec64
34 WixSilentExec
35 WixSilentExec64
36; RemoveFoldersEx.cpp
37 WixRemoveFoldersEx
38;scaexec.cpp
39 RegisterPerfCounterData
40 UnregisterPerfCounterData
41 RegisterPerfmon
42 UnregisterPerfmon
43 CreateSmb
44 DropSmb
45 CreateUser
46 RemoveUser
47;scasched.cpp
48 ConfigurePerfmonInstall
49 ConfigurePerfmonUninstall
50 ConfigureSmbInstall
51 ConfigureSmbUninstall
52 ConfigureUsers
53 InstallPerfCounterData
54 UninstallPerfCounterData
55 ConfigurePerfmonManifestRegister
56 ConfigurePerfmonManifestUnregister
57 ConfigureEventManifestRegister
58 ConfigureEventManifestUnregister
59; RestartManager.cpp
60 WixRegisterRestartResources
61; secureobj.cpp
62 SchedSecureObjects
63 SchedSecureObjectsRollback
64 ExecSecureObjects
65 ExecSecureObjectsRollback
66; serviceconfig.cpp
67 SchedServiceConfig
68 ExecServiceConfig
69 RollbackServiceConfig
70; shellexecca.cpp
71 WixShellExec
72 WixShellExecBinary
73 WixUnelevatedShellExec
74; test.cpp
75 WixFailWhenDeferred
76 WixWaitForEvent
77; TouchFile.cpp
78 WixTouchFileDuringInstall
79 WixTouchFileDuringUninstall
80 WixExecuteTouchFile
81; xmlfile.cpp
82 SchedXmlFile
83 ExecXmlFile
84 ExecXmlFileRollback
85; xmlconfig.cpp
86 SchedXmlConfig
87 ExecXmlConfig
88 ExecXmlConfigRollback
diff --git a/src/ca/utilca.vcxproj b/src/ca/utilca.vcxproj
index ef04c3ac..e9d74a66 100644
--- a/src/ca/utilca.vcxproj
+++ b/src/ca/utilca.vcxproj
@@ -38,18 +38,51 @@
38 </ImportGroup> 38 </ImportGroup>
39 39
40 <PropertyGroup> 40 <PropertyGroup>
41 <ProjectAdditionalLinkLibraries>msi.lib</ProjectAdditionalLinkLibraries> 41 <ProjectAdditionalLinkLibraries>activeds.lib;adsiid.lib;msi.lib;netapi32.lib;shlwapi.lib</ProjectAdditionalLinkLibraries>
42 </PropertyGroup> 42 </PropertyGroup>
43 43
44 <ItemGroup> 44 <ItemGroup>
45 <ClCompile Include="BroadcastSettingChange.cpp" />
46 <ClCompile Include="CheckReboot.cpp" />
47 <ClCompile Include="CloseApps.cpp" />
45 <ClCompile Include="dllmain.cpp"> 48 <ClCompile Include="dllmain.cpp">
46 <PrecompiledHeader>Create</PrecompiledHeader> 49 <PrecompiledHeader>Create</PrecompiledHeader>
47 </ClCompile> 50 </ClCompile>
51 <ClCompile Include="exitearlywithsuccess.cpp" />
52 <ClCompile Include="FormatFiles.cpp" />
53 <ClCompile Include="netshortcuts.cpp" />
54 <ClCompile Include="OsInfo.cpp" />
55 <ClCompile Include="qtexecca.cpp" />
56 <ClCompile Include="RemoveFoldersEx.cpp" />
57 <ClCompile Include="RestartManager.cpp" />
58 <ClCompile Include="scaexec.cpp" />
59 <ClCompile Include="scamanifest.cpp" />
60 <ClCompile Include="scaperf.cpp" />
61 <ClCompile Include="scaperfexec.cpp" />
62 <ClCompile Include="scasched.cpp" />
63 <ClCompile Include="scasmbexec.cpp" />
64 <ClCompile Include="scasmbsched.cpp" />
65 <ClCompile Include="scauser.cpp" />
66 <ClCompile Include="secureobj.cpp" />
67 <ClCompile Include="serviceconfig.cpp" />
68 <ClCompile Include="shellexecca.cpp" />
69 <ClCompile Include="test.cpp" />
70 <ClCompile Include="TouchFile.cpp" />
48 <ClCompile Include="utilca.cpp" /> 71 <ClCompile Include="utilca.cpp" />
72 <ClCompile Include="XmlConfig.cpp" />
73 <ClCompile Include="XmlFile.cpp" />
49 </ItemGroup> 74 </ItemGroup>
50 75
51 <ItemGroup> 76 <ItemGroup>
77 <ClInclude Include="caSuffix.h" />
78 <ClInclude Include="cost.h" />
79 <ClInclude Include="CustomMsiErrors.h" />
52 <ClInclude Include="precomp.h" /> 80 <ClInclude Include="precomp.h" />
81 <ClInclude Include="sca.h" />
82 <ClInclude Include="scacost.h" />
83 <ClInclude Include="scasmb.h" />
84 <ClInclude Include="scasmbexec.h" />
85 <ClInclude Include="scauser.h" />
53 </ItemGroup> 86 </ItemGroup>
54 87
55 <ItemGroup> 88 <ItemGroup>
diff --git a/src/wixlib/UtilExtension.wxs b/src/wixlib/UtilExtension.wxs
index ac11c788..e77b529b 100644
--- a/src/wixlib/UtilExtension.wxs
+++ b/src/wixlib/UtilExtension.wxs
@@ -63,7 +63,7 @@
63 </Fragment> 63 </Fragment>
64 64
65 <Fragment> 65 <Fragment>
66 <CustomAction Id="WixFailWhenDeferred" BinaryKey="WixCA" DllEntry="WixFailWhenDeferred" Execute="deferred" Return="check" SuppressModularization="yes" /> 66 <CustomAction Id="WixFailWhenDeferred" BinaryKey="UtilCA" DllEntry="WixFailWhenDeferred" Execute="deferred" Return="check" SuppressModularization="yes" />
67 67
68 <InstallExecuteSequence> 68 <InstallExecuteSequence>
69 <Custom Action="WixFailWhenDeferred" Before="InstallFinalize" Overridable="yes">WIXFAILWHENDEFERRED=1 AND VersionNT &gt; 400</Custom> 69 <Custom Action="WixFailWhenDeferred" Before="InstallFinalize" Overridable="yes">WIXFAILWHENDEFERRED=1 AND VersionNT &gt; 400</Custom>
@@ -71,7 +71,7 @@
71 </Fragment> 71 </Fragment>
72 72
73 <Fragment> 73 <Fragment>
74 <CustomAction Id="WixWaitForEvent" BinaryKey="WixCA" DllEntry="WixWaitForEvent" Execute="immediate" Return="check" SuppressModularization="yes" /> 74 <CustomAction Id="WixWaitForEvent" BinaryKey="UtilCA" DllEntry="WixWaitForEvent" Execute="immediate" Return="check" SuppressModularization="yes" />
75 75
76 <InstallExecuteSequence> 76 <InstallExecuteSequence>
77 <Custom Action="WixWaitForEvent" Before="InstallFinalize" Overridable="yes" /> 77 <Custom Action="WixWaitForEvent" Before="InstallFinalize" Overridable="yes" />
@@ -79,7 +79,7 @@
79 </Fragment> 79 </Fragment>
80 80
81 <Fragment> 81 <Fragment>
82 <CustomAction Id="WixWaitForEventDeferred" BinaryKey="WixCA" DllEntry="WixWaitForEvent" Execute="deferred" Return="check" SuppressModularization="yes" /> 82 <CustomAction Id="WixWaitForEventDeferred" BinaryKey="UtilCA" DllEntry="WixWaitForEvent" Execute="deferred" Return="check" SuppressModularization="yes" />
83 83
84 <InstallExecuteSequence> 84 <InstallExecuteSequence>
85 <Custom Action="WixWaitForEventDeferred" After="InstallInitialize" Overridable="yes" /> 85 <Custom Action="WixWaitForEventDeferred" After="InstallInitialize" Overridable="yes" />
@@ -87,7 +87,7 @@
87 </Fragment> 87 </Fragment>
88 88
89 <Fragment> 89 <Fragment>
90 <CustomAction Id="WixExitEarlyWithSuccess" BinaryKey="WixCA" DllEntry="WixExitEarlyWithSuccess" Execute="immediate" Return="check" SuppressModularization="yes" /> 90 <CustomAction Id="WixExitEarlyWithSuccess" BinaryKey="UtilCA" DllEntry="WixExitEarlyWithSuccess" Execute="immediate" Return="check" SuppressModularization="yes" />
91 91
92 <InstallExecuteSequence> 92 <InstallExecuteSequence>
93 <Custom Action="WixExitEarlyWithSuccess" After="FindRelatedProducts" Overridable="yes">NEWERVERSIONDETECTED AND VersionNT &gt; 400</Custom> 93 <Custom Action="WixExitEarlyWithSuccess" After="FindRelatedProducts" Overridable="yes">NEWERVERSIONDETECTED AND VersionNT &gt; 400</Custom>
@@ -95,7 +95,7 @@
95 </Fragment> 95 </Fragment>
96 96
97 <Fragment> 97 <Fragment>
98 <CustomAction Id="WixRemoveFoldersEx" BinaryKey="WixCA" DllEntry="WixRemoveFoldersEx" Execute="immediate" Return="ignore" /> 98 <CustomAction Id="WixRemoveFoldersEx" BinaryKey="UtilCA" DllEntry="WixRemoveFoldersEx" Execute="immediate" Return="ignore" />
99 99
100 <InstallExecuteSequence> 100 <InstallExecuteSequence>
101 <Custom Action="WixRemoveFoldersEx" Before="CostInitialize" /> 101 <Custom Action="WixRemoveFoldersEx" Before="CostInitialize" />
@@ -103,7 +103,7 @@
103 </Fragment> 103 </Fragment>
104 104
105 <Fragment> 105 <Fragment>
106 <CustomAction Id="WixBroadcastSettingChange" BinaryKey="WixCA" DllEntry="WixBroadcastSettingChange" Execute="immediate" Return="ignore" SuppressModularization="yes" /> 106 <CustomAction Id="WixBroadcastSettingChange" BinaryKey="UtilCA" DllEntry="WixBroadcastSettingChange" Execute="immediate" Return="ignore" SuppressModularization="yes" />
107 107
108 <InstallExecuteSequence> 108 <InstallExecuteSequence>
109 <Custom Action="WixBroadcastSettingChange" After="InstallFinalize" Overridable="yes" /> 109 <Custom Action="WixBroadcastSettingChange" After="InstallFinalize" Overridable="yes" />
@@ -111,7 +111,7 @@
111 </Fragment> 111 </Fragment>
112 112
113 <Fragment> 113 <Fragment>
114 <CustomAction Id="WixBroadcastEnvironmentChange" BinaryKey="WixCA" DllEntry="WixBroadcastEnvironmentChange" Execute="immediate" Return="ignore" SuppressModularization="yes" /> 114 <CustomAction Id="WixBroadcastEnvironmentChange" BinaryKey="UtilCA" DllEntry="WixBroadcastEnvironmentChange" Execute="immediate" Return="ignore" SuppressModularization="yes" />
115 115
116 <InstallExecuteSequence> 116 <InstallExecuteSequence>
117 <Custom Action="WixBroadcastEnvironmentChange" After="InstallFinalize" Overridable="yes" /> 117 <Custom Action="WixBroadcastEnvironmentChange" After="InstallFinalize" Overridable="yes" />
@@ -120,7 +120,7 @@
120 120
121 <!-- WiX OS-detection properties and custom action --> 121 <!-- WiX OS-detection properties and custom action -->
122 <Fragment> 122 <Fragment>
123 <CustomAction Id="WixQueryOsInfo" BinaryKey="WixCA" DllEntry="WixQueryOsInfo" Execute="firstSequence" Return="check" SuppressModularization="yes" /> 123 <CustomAction Id="WixQueryOsInfo" BinaryKey="UtilCA" DllEntry="WixQueryOsInfo" Execute="firstSequence" Return="check" SuppressModularization="yes" />
124 124
125 <InstallExecuteSequence> 125 <InstallExecuteSequence>
126 <Custom Action="WixQueryOsInfo" After="AppSearch" Overridable="yes">VersionNT &gt; 400 OR (VersionNT = 400 AND ServicePackLevel &gt; 3)</Custom> 126 <Custom Action="WixQueryOsInfo" After="AppSearch" Overridable="yes">VersionNT &gt; 400 OR (VersionNT = 400 AND ServicePackLevel &gt; 3)</Custom>
@@ -213,7 +213,7 @@
213 </Fragment> 213 </Fragment>
214 214
215 <Fragment> 215 <Fragment>
216 <CustomAction Id="WixQueryOsDirs" BinaryKey="WixCA" DllEntry="WixQueryOsDirs" Execute="firstSequence" Return="check" SuppressModularization="yes" /> 216 <CustomAction Id="WixQueryOsDirs" BinaryKey="UtilCA" DllEntry="WixQueryOsDirs" Execute="firstSequence" Return="check" SuppressModularization="yes" />
217 217
218 <InstallExecuteSequence> 218 <InstallExecuteSequence>
219 <Custom Action="WixQueryOsDirs" After="AppSearch" Overridable="yes">VersionNT &gt; 400 OR (VersionNT = 400 AND ServicePackLevel &gt; 3)</Custom> 219 <Custom Action="WixQueryOsDirs" After="AppSearch" Overridable="yes">VersionNT &gt; 400 OR (VersionNT = 400 AND ServicePackLevel &gt; 3)</Custom>
@@ -318,7 +318,7 @@
318 </Fragment> 318 </Fragment>
319 319
320 <Fragment> 320 <Fragment>
321 <CustomAction Id="WixQueryOsWellKnownSID" BinaryKey="WixCA" DllEntry="WixQueryOsWellKnownSID" Execute="firstSequence" Return="check" SuppressModularization="yes" /> 321 <CustomAction Id="WixQueryOsWellKnownSID" BinaryKey="UtilCA" DllEntry="WixQueryOsWellKnownSID" Execute="firstSequence" Return="check" SuppressModularization="yes" />
322 322
323 <InstallExecuteSequence> 323 <InstallExecuteSequence>
324 <Custom Action="WixQueryOsWellKnownSID" After="AppSearch" Overridable="yes">VersionNT &gt; 400 OR (VersionNT = 400 AND ServicePackLevel &gt; 3)</Custom> 324 <Custom Action="WixQueryOsWellKnownSID" After="AppSearch" Overridable="yes">VersionNT &gt; 400 OR (VersionNT = 400 AND ServicePackLevel &gt; 3)</Custom>
@@ -360,7 +360,7 @@
360 </Fragment> 360 </Fragment>
361 361
362 <Fragment> 362 <Fragment>
363 <CustomAction Id="WixQueryOsDriverInfo" BinaryKey="WixCA" DllEntry="WixQueryOsDriverInfo" Execute="firstSequence" Return="check" SuppressModularization="yes" /> 363 <CustomAction Id="WixQueryOsDriverInfo" BinaryKey="UtilCA" DllEntry="WixQueryOsDriverInfo" Execute="firstSequence" Return="check" SuppressModularization="yes" />
364 364
365 <InstallExecuteSequence> 365 <InstallExecuteSequence>
366 <Custom Action="WixQueryOsDriverInfo" After="AppSearch" Overridable="yes">VersionNT &gt; 400 OR (VersionNT = 400 AND ServicePackLevel &gt; 3)</Custom> 366 <Custom Action="WixQueryOsDriverInfo" After="AppSearch" Overridable="yes">VersionNT &gt; 400 OR (VersionNT = 400 AND ServicePackLevel &gt; 3)</Custom>
@@ -384,47 +384,47 @@
384 <!-- ShellExec custom actions (for when only one is needed; multiple executions need their own IDs) --> 384 <!-- ShellExec custom actions (for when only one is needed; multiple executions need their own IDs) -->
385 <Fragment> 385 <Fragment>
386 <PropertyRef Id="WixShellExecBinaryId" /> 386 <PropertyRef Id="WixShellExecBinaryId" />
387 <CustomAction Id="WixShellExecBinary" BinaryKey="WixCA" DllEntry="WixShellExecBinary" Execute="immediate" Return="check" Impersonate="yes" /> 387 <CustomAction Id="WixShellExecBinary" BinaryKey="UtilCA" DllEntry="WixShellExecBinary" Execute="immediate" Return="check" Impersonate="yes" />
388 </Fragment> 388 </Fragment>
389 389
390 <Fragment> 390 <Fragment>
391 <PropertyRef Id="WixShellExecTarget" /> 391 <PropertyRef Id="WixShellExecTarget" />
392 <CustomAction Id="WixShellExec" BinaryKey="WixCA" DllEntry="WixShellExec" Execute="immediate" Return="check" Impersonate="yes" /> 392 <CustomAction Id="WixShellExec" BinaryKey="UtilCA" DllEntry="WixShellExec" Execute="immediate" Return="check" Impersonate="yes" />
393 </Fragment> 393 </Fragment>
394 394
395 <Fragment> 395 <Fragment>
396 <PropertyRef Id="WixUnelevatedShellExecTarget" /> 396 <PropertyRef Id="WixUnelevatedShellExecTarget" />
397 <CustomAction Id="WixUnelevatedShellExec" BinaryKey="WixCA" DllEntry="WixUnelevatedShellExec" Execute="immediate" Return="check" Impersonate="yes" /> 397 <CustomAction Id="WixUnelevatedShellExec" BinaryKey="UtilCA" DllEntry="WixUnelevatedShellExec" Execute="immediate" Return="check" Impersonate="yes" />
398 </Fragment> 398 </Fragment>
399 399
400 <Fragment> 400 <Fragment>
401 <PropertyRef Id="QtExecCmdLine" /> 401 <PropertyRef Id="QtExecCmdLine" />
402 <CustomAction Id="QtExec" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="immediate" Return="check" Impersonate="yes" /> 402 <CustomAction Id="QtExec" BinaryKey="UtilCA" DllEntry="CAQuietExec" Execute="immediate" Return="check" Impersonate="yes" />
403 </Fragment> 403 </Fragment>
404 404
405 <Fragment> 405 <Fragment>
406 <PropertyRef Id="QtExec64CmdLine" /> 406 <PropertyRef Id="QtExec64CmdLine" />
407 <CustomAction Id="QtExec64" BinaryKey="WixCA" DllEntry="CAQuietExec64" Execute="immediate" Return="check" Impersonate="yes" /> 407 <CustomAction Id="QtExec64" BinaryKey="UtilCA" DllEntry="CAQuietExec64" Execute="immediate" Return="check" Impersonate="yes" />
408 </Fragment> 408 </Fragment>
409 409
410 <Fragment> 410 <Fragment>
411 <PropertyRef Id="WixQuietExecCmdLine" /> 411 <PropertyRef Id="WixQuietExecCmdLine" />
412 <CustomAction Id="WixQuietExec" BinaryKey="WixCA" DllEntry="WixQuietExec" Execute="immediate" Return="check" Impersonate="yes" /> 412 <CustomAction Id="WixQuietExec" BinaryKey="UtilCA" DllEntry="WixQuietExec" Execute="immediate" Return="check" Impersonate="yes" />
413 </Fragment> 413 </Fragment>
414 414
415 <Fragment> 415 <Fragment>
416 <PropertyRef Id="WixQuietExec64CmdLine" /> 416 <PropertyRef Id="WixQuietExec64CmdLine" />
417 <CustomAction Id="WixQuietExec64" BinaryKey="WixCA" DllEntry="WixQuietExec64" Execute="immediate" Return="check" Impersonate="yes" /> 417 <CustomAction Id="WixQuietExec64" BinaryKey="UtilCA" DllEntry="WixQuietExec64" Execute="immediate" Return="check" Impersonate="yes" />
418 </Fragment> 418 </Fragment>
419 419
420 <!-- SilentExec custom actions differ from QtExec in that they do not log the commandline or output of the exe --> 420 <!-- SilentExec custom actions differ from QtExec in that they do not log the commandline or output of the exe -->
421 <Fragment> 421 <Fragment>
422 <PropertyRef Id="WixSilentExecCmdLine" /> 422 <PropertyRef Id="WixSilentExecCmdLine" />
423 <CustomAction Id="WixSilentExec" BinaryKey="WixCA" DllEntry="WixSilentExec" Execute="immediate" Return="check" Impersonate="yes" /> 423 <CustomAction Id="WixSilentExec" BinaryKey="UtilCA" DllEntry="WixSilentExec" Execute="immediate" Return="check" Impersonate="yes" />
424 </Fragment> 424 </Fragment>
425 425
426 <Fragment> 426 <Fragment>
427 <PropertyRef Id="WixSilentExec64CmdLine" /> 427 <PropertyRef Id="WixSilentExec64CmdLine" />
428 <CustomAction Id="WixSilentExec64" BinaryKey="WixCA" DllEntry="WixSilentExec64" Execute="immediate" Return="check" Impersonate="yes" /> 428 <CustomAction Id="WixSilentExec64" BinaryKey="UtilCA" DllEntry="WixSilentExec64" Execute="immediate" Return="check" Impersonate="yes" />
429 </Fragment> 429 </Fragment>
430</Wix> 430</Wix>
diff --git a/src/wixlib/UtilExtension_Platform.wxi b/src/wixlib/UtilExtension_Platform.wxi
index 8328577f..4f76c687 100644
--- a/src/wixlib/UtilExtension_Platform.wxi
+++ b/src/wixlib/UtilExtension_Platform.wxi
@@ -31,7 +31,7 @@
31 </Fragment> 31 </Fragment>
32 32
33 <Fragment> 33 <Fragment>
34 <CustomAction Id="WixRegisterRestartResources$(var.Suffix)" BinaryKey="WixCA" DllEntry="WixRegisterRestartResources$(var.Suffix)" Execute="immediate" Return="check" SuppressModularization="yes" /> 34 <CustomAction Id="WixRegisterRestartResources$(var.Suffix)" BinaryKey="UtilCA$(var.Suffix)" DllEntry="WixRegisterRestartResources$(var.Suffix)" Execute="immediate" Return="check" SuppressModularization="yes" />
35 35
36 <InstallExecuteSequence> 36 <InstallExecuteSequence>
37 <Custom Action="WixRegisterRestartResources$(var.Suffix)" Before="InstallValidate" Overridable="yes" /> 37 <Custom Action="WixRegisterRestartResources$(var.Suffix)" Before="InstallValidate" Overridable="yes" />