@joebigelow / wix / commits / f7020c0d

Import implementation of UtilCA from old repo's WixCA/scasched/scaexec. (#3)

Sean Hall committed Dec 15, 2018 at 21:46 UTC f7020c0d16baf2b960e7123e233e20c519f6a340
38 files changed +11209 -23
src/ca/BroadcastSettingChange.cpp new
+45
@@ -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 +/********************************************************************
7 +WixBroadcastSettingChange
8 +
9 + Send WM_SETTINGCHANGE message to all top-level windows indicating
10 + that unspecified settings have changed.
11 +********************************************************************/
12 +extern "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 +
22 +LExit:
23 + return WcaFinalize(ERROR_SUCCESS);
24 +}
25 +
26 +
27 +/********************************************************************
28 +WixBroadcastEnvironmentChange
29 +
30 + Send WM_SETTINGCHANGE message to all top-level windows indicating
31 + that environment variables have changed.
32 +********************************************************************/
33 +extern "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 +
43 +LExit:
44 + return WcaFinalize(ERROR_SUCCESS);
45 +}
src/ca/CheckReboot.cpp new
+36
@@ -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 +/********************************************************************
7 +WixCheckRebootRequired - entry point for WixCheckRebootRequired Custom Action
8 +
9 + called as Type 1 CustomAction (binary DLL) from Windows Installer
10 + in InstallExecuteSequence after InstallFinalize
11 +********************************************************************/
12 +extern "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 +
31 +LExit:
32 +
33 + if (FAILED(hr))
34 + er = ERROR_INSTALL_FAILURE;
35 + return WcaFinalize(er);
36 +}
src/ca/CloseApps.cpp new
+570
@@ -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
10 +LPCWSTR wzQUERY_CLOSEAPPS = L"SELECT `WixCloseApplication`, `Target`, `Description`, `Condition`, `Attributes`, `Property`, `TerminateExitCode`, `Timeout` FROM `WixCloseApplication` ORDER BY `Sequence`";
11 +enum eQUERY_CLOSEAPPS { QCA_ID = 1, QCA_TARGET, QCA_DESCRIPTION, QCA_CONDITION, QCA_ATTRIBUTES, QCA_PROPERTY, QCA_TERMINATEEXITCODE, QCA_TIMEOUT };
12 +
13 +// CloseApplication.Attributes
14 +enum 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 +
26 +struct 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 +******************************************************************/
39 +BOOL 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 +******************************************************************/
90 +static 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 +
135 +LExit:
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 +******************************************************************/
145 +void 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 +******************************************************************/
175 +void 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 +******************************************************************/
209 +void 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 +******************************************************************/
235 +void 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 +******************************************************************/
258 +extern "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 +
440 +LExit:
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 +******************************************************************/
475 +extern "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 +
559 +LExit:
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 +}
src/ca/CustomMsiErrors.h new
+32
@@ -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
src/ca/FormatFiles.cpp new
+221
@@ -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 +
5 +const UINT COST_FILEFORMATTING = 2000;
6 +
7 +
8 +//
9 +// WixSchedFormatFiles - immediate CA to schedule format files CAs
10 +//
11 +extern "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 +
118 +LExit:
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 +//
126 +extern "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 +
217 +LExit:
218 + ReleaseStr(psz);
219 +
220 + return WcaFinalize(er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er);
221 +}
src/ca/OsInfo.cpp new
+487
@@ -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 +/********************************************************************
13 +WixQueryOsInfo - 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 +********************************************************************/
19 +extern "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 +
139 +LExit:
140 + if (FAILED(hr))
141 + er = ERROR_INSTALL_FAILURE;
142 + return WcaFinalize(er);
143 +}
144 +
145 +/********************************************************************
146 +WixQueryOsDirs - 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 +********************************************************************/
151 +extern "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 +
279 +LExit:
280 + if (FAILED(hr))
281 + er = ERROR_INSTALL_FAILURE;
282 + return WcaFinalize(er);
283 +}
284 +
285 +
286 +/********************************************************************
287 +SetPropertyWellKnownSID
288 +
289 + Set a property with the localized name of a well known windows SID
290 +********************************************************************/
291 +static 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 +
328 +LExit:
329 + if (NULL != psid)
330 + {
331 + ::LocalFree(psid);
332 + }
333 + ReleaseStr(pwzPropertyValue);
334 + return hr;
335 +}
336 +
337 +/********************************************************************
338 +WixQueryOsWellKnownSID - 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 +********************************************************************/
344 +extern "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 +
363 +LExit:
364 + if (FAILED(hr))
365 + {
366 + er = ERROR_INSTALL_FAILURE;
367 + }
368 + return WcaFinalize(er);
369 +}
370 +
371 +
372 +/********************************************************************
373 +DetectWDDMDriver
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 +********************************************************************/
380 +static 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 +
404 +LExit:
405 + if (NULL != hModule)
406 + {
407 + FreeLibrary(hModule);
408 + }
409 +
410 + return hr;
411 +}
412 +
413 +/********************************************************************
414 +DetectIsCompositionEnabled
415 +
416 + Set a property based on the return value of DwmIsCompositionEnabled().
417 +********************************************************************/
418 +static 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 +
448 +LExit:
449 + if (NULL != hModule)
450 + {
451 + FreeLibrary(hModule);
452 + }
453 + return hr;
454 +}
455 +
456 +/********************************************************************
457 +WixQueryOsDriverInfo - 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 +********************************************************************/
463 +extern "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 +
481 +LExit:
482 + if (FAILED(hr))
483 + {
484 + er = ERROR_INSTALL_FAILURE;
485 + }
486 + return WcaFinalize(er);
487 +}
src/ca/RemoveFoldersEx.cpp new
+197
@@ -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 +
5 +LPCWSTR vcsRemoveFolderExQuery = L"SELECT `WixRemoveFolderEx`, `Component_`, `Property`, `InstallMode` FROM `WixRemoveFolderEx`";
6 +enum eRemoveFolderExQuery { rfqId = 1, rfqComponent, rfqProperty, feqMode };
7 +
8 +static 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 +
89 +LExit:
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 +
101 +extern "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 +
178 +LExit:
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 +}
src/ca/RestartManager.cpp new
+185
@@ -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 +
9 +enum eRmuResourceType
10 +{
11 + etInvalid,
12 + etFilename,
13 + etApplication,
14 + etServiceName,
15 +
16 + // Mask types from Attributes.
17 + etTypeMask = 0xf,
18 +};
19 +
20 +LPCWSTR vcsRestartResourceQuery =
21 + L"SELECT `WixRestartResource`.`WixRestartResource`, `WixRestartResource`.`Component_`, `WixRestartResource`.`Resource`, `WixRestartResource`.`Attributes` "
22 + L"FROM `WixRestartResource`";
23 +enum eRestartResourceQuery { rrqRestartResource = 1, rrqComponent, rrqResource, rrqAttributes };
24 +
25 +/********************************************************************
26 +WixRegisterRestartResources - Immediate CA to register resources with RM.
27 +
28 +Enumerates components before InstallValidate and registers resources
29 +to be restarted by Restart Manager if the component action
30 +is anything other than None.
31 +
32 +Do not disable file system redirection.
33 +
34 +********************************************************************/
35 +extern "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 +
174 +LExit:
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 +}
src/ca/TouchFile.cpp new
+308
@@ -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 +
5 +LPCWSTR vcsTouchFileQuery = L"SELECT `WixTouchFile`, `Component_`, `Path`, `Attributes` FROM `WixTouchFile`";
6 +enum TOUCH_FILE_QUERY { tfqId = 1, tfqComponent, tfqPath, tfqTouchFileAttributes };
7 +
8 +enum 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 +
18 +static 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 +
38 +LExit:
39 + if (fReenableFileSystemRedirection)
40 + {
41 + WcaRevertWow64FSRedirection();
42 + }
43 +
44 + return SUCCEEDED(hr);
45 +}
46 +
47 +
48 +static 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 +
73 +LExit:
74 + return hr;
75 +}
76 +
77 +
78 +static 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 +
107 +LExit:
108 + if (fReenableFileSystemRedirection)
109 + {
110 + WcaRevertWow64FSRedirection();
111 + }
112 +
113 + return SUCCEEDED(hr);
114 +}
115 +
116 +
117 +static 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 +
198 +LExit:
199 + ReleaseStr(sczExecuteData);
200 + ReleaseStr(sczRollbackData);
201 + ReleaseStr(sczPath);
202 + ReleaseStr(sczComponent);
203 + ReleaseStr(sczId);
204 +
205 + return hr;
206 +}
207 +
208 +
209 +extern "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 +
222 +LExit:
223 + DWORD er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
224 + return WcaFinalize(er);
225 +}
226 +
227 +
228 +extern "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 +
241 +LExit:
242 + DWORD er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
243 + return WcaFinalize(er);
244 +}
245 +
246 +
247 +extern "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 +
301 +LExit:
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 +}
src/ca/XmlConfig.cpp new
+1099
@@ -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 +
14 +enum eXmlAction
15 +{
16 + xaUnknown = 0,
17 + xaOpenFile,
18 + xaOpenFilex64,
19 + xaWriteValue,
20 + xaWriteDocument,
21 + xaDeleteValue,
22 + xaCreateElement,
23 + xaDeleteElement,
24 +};
25 +
26 +enum eXmlPreserveDate
27 +{
28 + xdDontPreserve = 0,
29 + xdPreserve
30 +};
31 +
32 +LPCWSTR 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`";
36 +enum eXmlConfigQuery { xfqXmlConfig = 1, xfqFile, xfqElementPath, xfqVerifyPath, xfqName, xfqValue, xfqXmlFlags, xfqComponent, xfqCompAttributes };
37 +
38 +struct 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 +
63 +static 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 +
97 +LExit:
98 + return hr;
99 +}
100 +
101 +static 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 +
127 +LExit:
128 + return hr;
129 +}
130 +
131 +
132 +static 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 +
226 +LExit:
227 + ReleaseStr(pwzData);
228 +
229 + return hr;
230 +}
231 +
232 +static 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 +
310 +LExit:
311 +
312 + return hr;
313 +}
314 +
315 +
316 +static 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 + }
367 +LExit:
368 + ReleaseMem(pbData);
369 +
370 + return hr;
371 +}
372 +
373 +
374 +static 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 +
422 +LExit:
423 + return hr;
424 +}
425 +
426 +
427 +/******************************************************************
428 + SchedXmlConfig - entry point for XmlConfig Custom Action
429 +
430 +********************************************************************/
431 +extern "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 +
564 +LExit:
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 +*******************************************************************/
582 +extern "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 +
956 +LExit:
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 +*******************************************************************/
995 +extern "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 +
1079 +LExit:
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 +
src/ca/XmlFile.cpp new
+942
@@ -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 +
13 +extern BOOL vfMsxml30;
14 +
15 +enum eXmlAction
16 +{
17 + xaOpenFile = 1,
18 + xaOpenFilex64,
19 + xaWriteValue,
20 + xaDeleteValue,
21 + xaCreateElement,
22 + xaDeleteElement,
23 + xaBulkWriteValue,
24 +};
25 +
26 +enum eXmlPreserveDate
27 +{
28 + xdDontPreserve = 0,
29 + xdPreserve
30 +};
31 +
32 +enum eXmlSelectionLanguage
33 +{
34 + xsXSLPattern = 0,
35 + xsXPath = 1,
36 +};
37 +
38 +LPCWSTR 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`";
42 +enum eXmlFileQuery { xfqXmlFile = 1, xfqFile, xfqXPath, xfqName, xfqValue, xfqXmlFlags, xfqComponent, xfqCompAttributes };
43 +
44 +struct 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 +
86 +static 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 +
112 +LExit:
113 + return hr;
114 +}
115 +
116 +
117 +static 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 +
195 +LExit:
196 + ReleaseStr(pwzData);
197 +
198 + return hr;
199 +}
200 +
201 +
202 +static 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 + }
262 +LExit:
263 + ReleaseMem(pbData);
264 +
265 + return hr;
266 +}
267 +
268 +
269 +static 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 +
287 +LExit:
288 + return hr;
289 +}
290 +
291 +
292 +/******************************************************************
293 + SchedXmlFile - entry point for XmlFile Custom Action
294 +
295 +********************************************************************/
296 +extern "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 +
461 +LExit:
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 +*******************************************************************/
475 +extern "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 +
804 +LExit:
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 +*******************************************************************/
840 +extern "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 +
924 +LExit:
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 +
src/ca/caSuffix.h new
+11
@@ -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
src/ca/cost.h new
+9
@@ -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 +
5 +const UINT COST_SECUREOBJECT = 1000;
6 +const UINT COST_SERVICECONFIG = 1000;
7 +const UINT COST_XMLFILE = 1000;
8 +const UINT COST_CLOSEAPP = 500;
9 +const UINT COST_INTERNETSHORTCUT = 2000;
src/ca/exitearlywithsuccess.cpp new
+27
@@ -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 +/******************************************************************
7 +WixExitEarlyWithSuccess - 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 +********************************************************************/
22 +extern "C" UINT __stdcall WixExitEarlyWithSuccess(
23 + __in MSIHANDLE /*hInstall*/
24 + )
25 +{
26 + return ERROR_NO_MORE_ITEMS;
27 +}
src/ca/netshortcuts.cpp new
+434
@@ -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 +
5 +LPCWSTR vcsShortcutsQuery =
6 + L"SELECT `Component_`, `Directory_`, `Name`, `Target`, `Attributes`, `IconFile`, `IconIndex` "
7 + L"FROM `WixInternetShortcut`";
8 +enum eShortcutsQuery { esqComponent = 1, esqDirectory, esqFilename, esqTarget, esqAttributes, esqIconFile, esqIconIndex };
9 +enum eShortcutsAttributes { esaLink = 0, esaURL = 1 };
10 +
11 +/******************************************************************
12 + WixSchedInternetShortcuts - entry point
13 +
14 +********************************************************************/
15 +extern "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 +
163 +LExit:
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 +*******************************************************************/
198 +static 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 +
253 +LExit:
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 +*******************************************************************/
266 +static 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 +
299 +LExit:
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 +*******************************************************************/
312 +extern "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 +
370 +LExit:
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 +*******************************************************************/
390 +extern "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 +
428 +LExit:
429 + ReleaseStr(pwzCustomActionData);
430 + ReleaseStr(pwzShortcutPath);
431 +
432 + er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er;
433 + return WcaFinalize(er);
434 +}
src/ca/precomp.h
+42
@@ -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.
3
4
5 +#if _WIN32_MSI < 150
6 +#define _WIN32_MSI 150
7 +#endif
8 +
9 #include <windows.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>
24
25 #define MAXUINT USHRT_MAX
26 #include <Setup.Configuration.h>
27
28 #include "wcautil.h"
29 +#include "wcawow64.h"
30 +#include "wcawrapquery.h"
31 +#include "aclutil.h"
32 +#include "dirutil.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"
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"
src/ca/qtexecca.cpp new
+312
@@ -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
8 +const LPCWSTR CAQUIET_TIMEOUT_PROPERTY = L"QtExecCmdTimeout";
9 +const LPCWSTR CAQUIET_ARGUMENTS_PROPERTY = L"QtExecCmdLine";
10 +const LPCWSTR CAQUIET64_ARGUMENTS_PROPERTY = L"QtExec64CmdLine";
11 +// end deprecated section
12 +
13 +// WixCA name quiet commandline argument properties
14 +const LPCWSTR WIX_QUIET_ARGUMENTS_PROPERTY = L"WixQuietExecCmdLine";
15 +const LPCWSTR WIX_QUIET64_ARGUMENTS_PROPERTY = L"WixQuietExec64CmdLine";
16 +
17 +// WixCA quiet timeout properties
18 +const LPCWSTR WIX_QUIET_TIMEOUT_PROPERTY = L"WixQuietExecCmdTimeout";
19 +const LPCWSTR WIX_QUIET64_TIMEOUT_PROPERTY = L"WixQuietExec64CmdTimeout";
20 +
21 +// WixCA silent commandline argument properties
22 +const LPCWSTR WIX_SILENT_ARGUMENTS_PROPERTY = L"WixSilentExecCmdLine";
23 +const LPCWSTR WIX_SILENT64_ARGUMENTS_PROPERTY = L"WixSilentExec64CmdLine";
24 +
25 +// WixCA silent timeout properties
26 +const LPCWSTR WIX_SILENT_TIMEOUT_PROPERTY = L"WixSilentExecCmdTimeout";
27 +const LPCWSTR WIX_SILENT64_TIMEOUT_PROPERTY = L"WixSilentExec64CmdTimeout";
28 +
29 +HRESULT 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 +
68 +LExit:
69 + return hr;
70 +}
71 +
72 +#define ONEMINUTE 60000
73 +
74 +DWORD 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 +
92 +LExit:
93 + ReleaseStr(pwzData);
94 +
95 + return dwTimeout;
96 +
97 +}
98 +
99 +HRESULT 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 +
118 +LExit:
119 + ReleaseStr(pwzCommand);
120 +
121 + return hr;
122 +}
123 +
124 +HRESULT 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 +
157 +LExit:
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.
175 +extern "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 +
189 +LExit:
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
199 +extern "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 +
213 +LExit:
214 + if (FAILED(hr))
215 + {
216 + er = ERROR_INSTALL_FAILURE;
217 + }
218 +
219 + return WcaFinalize(er);
220 +}
221 +
222 +extern "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 +
236 +LExit:
237 + if (FAILED(hr))
238 + {
239 + er = ERROR_INSTALL_FAILURE;
240 + }
241 +
242 + return WcaFinalize(er);
243 +}
244 +
245 +extern "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 +
259 +LExit:
260 + if (FAILED(hr))
261 + {
262 + er = ERROR_INSTALL_FAILURE;
263 + }
264 +
265 + return WcaFinalize(er);
266 +}
267 +
268 +extern "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 +
282 +LExit:
283 + if (FAILED(hr))
284 + {
285 + er = ERROR_INSTALL_FAILURE;
286 + }
287 +
288 + return WcaFinalize(er);
289 +}
290 +
291 +extern "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 +
305 +LExit:
306 + if (FAILED(hr))
307 + {
308 + er = ERROR_INSTALL_FAILURE;
309 + }
310 +
311 + return WcaFinalize(er);
312 +}
src/ca/sca.h new
+19
@@ -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
5 +enum 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
src/ca/scacost.h new
+18
@@ -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 +
5 +const UINT COST_PERFMON_REGISTER = 1000;
6 +const UINT COST_PERFMON_UNREGISTER = 1000;
7 +
8 +const UINT COST_SMB_CREATESMB = 10000;
9 +const UINT COST_SMB_DROPSMB = 5000;
10 +const UINT COST_USER_ADD = 10000;
11 +const UINT COST_USER_DELETE = 10000;
12 +
13 +const UINT COST_PERFMONMANIFEST_REGISTER = 1000;
14 +const UINT COST_PERFMONMANIFEST_UNREGISTER = 1000;
15 +
16 +const UINT COST_EVENTMANIFEST_REGISTER = 1000;
17 +const UINT COST_EVENTMANIFEST_UNREGISTER = 1000;
18 +
src/ca/scaexec.cpp new
+807
@@ -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 + * ****************************************************************/
13 +extern "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 +
90 +LExit:
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 + * ****************************************************************/
116 +extern "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 +
146 +LExit:
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 +
158 +static 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 +
221 +LExit:
222 + ReleaseObject(pGroup);
223 + ReleaseBSTR(bstrUser);
224 + ReleaseBSTR(bstrGroup);
225 +
226 + return hr;
227 +}
228 +
229 +static 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 +
287 +LExit:
288 + ReleaseObject(pGroup);
289 + ReleaseBSTR(bstrUser);
290 + ReleaseBSTR(bstrGroup);
291 +
292 + return hr;
293 +}
294 +
295 +
296 +static 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 +
346 +LExit:
347 + if (hPolicy)
348 + {
349 + ::LsaClose(hPolicy);
350 + }
351 +
352 + ReleaseSid(psid);
353 + ReleaseStr(pwzUser);
354 + return hr;
355 +}
356 +
357 +
358 +static 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 +
419 +static 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 + * *****************************************************************/
474 +extern "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 +
616 +LExit:
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 + * *****************************************************************/
657 +extern "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 +
786 +LExit:
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 +}
src/ca/scamanifest.cpp new
+377
@@ -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 +
5 +LPCWSTR vcsPerfmonManifestQuery = L"SELECT `Component_`, `File`, `ResourceFileDirectory` FROM `PerfmonManifest`";
6 +LPCWSTR vcsEventManifestQuery = L"SELECT `Component_`, `File` FROM `EventManifest`";
7 +enum ePerfMonManifestQuery { pfmComponent = 1, pfmFile, pfmResourceFileDir };
8 +enum eEventManifestQuery { emComponent = 1, emFile};
9 +
10 +BOOL 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 +********************************************************************/
29 +extern "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 +
110 +LExit:
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 +********************************************************************/
126 +extern "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 +
199 +LExit:
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 +********************************************************************/
214 +extern "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 +
280 +LExit:
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 +********************************************************************/
296 +extern "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 +
369 +LExit:
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 +
src/ca/scaperf.cpp new
+310
@@ -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 +
5 +LPCWSTR vcsPerfCounterDataQuery = L"SELECT `PerformanceCategory`, `Component_`, `Name`, `IniData`, `ConstantData` FROM `PerformanceCategory`";
6 +enum ePerfCounterDataQuery { pcdqId = 1, pcdqComponent, pcdqName, pcdqIniData, pcdqConstantData };
7 +
8 +LPCWSTR vcsPerfMonQuery = L"SELECT `Component_`, `File`, `Name` FROM `Perfmon`";
9 +enum ePerfMonQuery { pmqComponent = 1, pmqFile, pmqName };
10 +
11 +
12 +static 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 +********************************************************************/
23 +extern "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 +
37 +LExit:
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 +********************************************************************/
48 +extern "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 +
62 +LExit:
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 +********************************************************************/
72 +extern "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 +
130 +LExit:
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 +********************************************************************/
145 +extern "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 +
203 +LExit:
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 +
214 +static 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 +
302 +LExit:
303 + ReleaseStr(pwzCustomActionData);
304 + ReleaseStr(pwzData);
305 + ReleaseStr(pwzName);
306 + ReleaseStr(pwzComponent);
307 + ReleaseStr(pwzId);
308 +
309 + return hr;
310 +}
src/ca/scaperfexec.cpp new
+423
@@ -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 +
5 +typedef DWORD (STDAPICALLTYPE *PFNPERFCOUNTERTEXTSTRINGS)(LPWSTR lpCommandLine, BOOL bQuietModeArg);
6 +
7 +static HRESULT ExecutePerfCounterData(
8 + __in MSIHANDLE hInstall,
9 + __in BOOL fInstall
10 + );
11 +static 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 +*******************************************************************/
26 +extern "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 +
40 +LExit:
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 +*******************************************************************/
52 +extern "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 +
66 +LExit:
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 +*******************************************************************/
79 +extern "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;
146 +LExit:
147 + ReleaseStr(pwzData);
148 +
149 + if (FAILED(hr))
150 + er = ERROR_INSTALL_FAILURE;
151 + return WcaFinalize(er);
152 +}
153 +
154 +
155 +extern "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;
200 +LExit:
201 + ReleaseStr(pwzData);
202 +
203 + if (FAILED(hr))
204 + er = ERROR_INSTALL_FAILURE;
205 + return WcaFinalize(er);
206 +}
207 +
208 +
209 +static 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 +
326 +LExit:
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 +
354 +static 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 +
414 +LExit:
415 + if (INVALID_HANDLE_VALUE != hFile)
416 + {
417 + ::CloseHandle(hFile);
418 + }
419 + ReleaseStr(pszData);
420 + ReleaseStr(pwzFile);
421 +
422 + return hr;
423 +}
src/ca/scasched.cpp new
+127
@@ -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 +/********************************************************************
7 +ConfigureSmb - CUSTOM ACTION ENTRY POINT for installing fileshare settings
8 +
9 +********************************************************************/
10 +extern "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 +
36 +LExit:
37 + if (pssList)
38 + ScaSmbFreeList(pssList);
39 +
40 + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
41 + return WcaFinalize(er);
42 +}
43 +
44 +
45 +/********************************************************************
46 +ConfigureSmb - CUSTOM ACTION ENTRY POINT for installing fileshare settings
47 +
48 +********************************************************************/
49 +extern "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 +
75 +LExit:
76 + if (pssList)
77 + ScaSmbFreeList(pssList);
78 +
79 + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
80 + return WcaFinalize(er);
81 +}
82 +
83 +
84 +/********************************************************************
85 +ConfigureUsers - CUSTOM ACTION ENTRY POINT for installing users
86 +
87 +********************************************************************/
88 +extern "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 +
114 +LExit:
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
src/ca/scasmb.h new
+46
@@ -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
9 +struct 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 +
17 +struct 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
42 +HRESULT ScaSmbRead(SCA_SMB** ppssList);
43 +HRESULT ScaSmbExPermsRead(SCA_SMB* pss);
44 +HRESULT ScaSmbUninstall(SCA_SMB* pssList);
45 +HRESULT ScaSmbInstall(SCA_SMB* pssList);
46 +void ScaSmbFreeList(SCA_SMB* pssList);
src/ca/scasmbexec.cpp new
+316
@@ -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 +********************************************************************/
11 +HRESULT 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 +
107 +LExit:
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 +********************************************************************/
124 +void 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
141 +NERR_Success = 0
142 +NERR_DuplicateShare = 2118
143 +NERR_BufTooSmall = 2123
144 +NERR_NetNameNotFound = 2310
145 +NERR_RedirectedPath = 2117
146 +NERR_UnknownDevDir = 2116
147 +*/
148 +
149 +/********************************************************************
150 + DoesShareExists - Does a share of this name exist on this computer?
151 +
152 +********************************************************************/
153 +HRESULT 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 +********************************************************************/
185 +HRESULT 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 +
256 +LExit:
257 + if (pACL)
258 + {
259 + ::LocalFree(pACL);
260 + }
261 +
262 + ReleaseMem(pSD);
263 +
264 + return hr;
265 +}
266 +
267 +
268 +/********************************************************************
269 + ScaEnsureSmbExists
270 +
271 +********************************************************************/
272 +HRESULT 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 +********************************************************************/
291 +HRESULT 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 +
314 +LExit:
315 + return hr;
316 +}
src/ca/scasmbexec.h new
+27
@@ -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 +
5 +struct 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 +
13 +struct 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 +
26 +HRESULT ScaEnsureSmbExists(SCA_SMBP* pssp);
27 +HRESULT ScaDropSmb(SCA_SMBP* pssp);
src/ca/scasmbsched.cpp new
+639
@@ -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 +********************************************************************/
10 +SCA_SMB* NewSmb()
11 +{
12 + SCA_SMB* pss = (SCA_SMB*)MemAlloc(sizeof(SCA_SMB), TRUE);
13 + Assert(pss);
14 + return pss;
15 +}
16 +
17 +
18 +SCA_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 +
26 +SCA_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 +
47 +SCA_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 +
70 +void 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 +
82 +void 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
95 +LPCWSTR vcsSmbQuery = L"SELECT `FileShare`, `ShareName`, `Description`, `Directory_`, "
96 + L"`Component_`, `User_`, `Permissions` FROM `FileShare`";
97 +
98 +enum 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 +********************************************************************/
114 +HRESULT 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 +
258 +LExit:
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 +********************************************************************/
275 +HRESULT 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 +
337 +LExit:
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 +********************************************************************/
356 +HRESULT 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 +
429 +LExit:
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 +********************************************************************/
446 +HRESULT 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 +
461 +LExit:
462 + return hr;
463 +}
464 +
465 +
466 +/********************************************************************
467 + SchedDropSmb - schedule one instance of a file share removal
468 +
469 +********************************************************************/
470 +HRESULT 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 +
524 +LExit:
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 +********************************************************************/
541 +HRESULT 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 +
556 +LExit:
557 + return hr;
558 +}
559 +
560 +LPCWSTR vcsSmbExUserPermsQuery = L"SELECT `FileShare_`,`User_`,`Permissions` "
561 + L"FROM `FileSharePermissions` WHERE `FileShare_`=?";
562 +
563 +enum 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 +********************************************************************/
576 +HRESULT 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 +
629 +LExit:
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 +}
src/ca/scauser.cpp new
+676
@@ -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 +
5 +LPCWSTR vcsUserQuery = L"SELECT `User`, `Component_`, `Name`, `Domain`, `Password` FROM `User` WHERE `User`=?";
6 +enum eUserQuery { vuqUser = 1, vuqComponent, vuqName, vuqDomain, vuqPassword };
7 +
8 +LPCWSTR vcsGroupQuery = L"SELECT `Group`, `Component_`, `Name`, `Domain` FROM `Group` WHERE `Group`=?";
9 +enum eGroupQuery { vgqGroup = 1, vgqComponent, vgqName, vgqDomain };
10 +
11 +LPCWSTR vcsUserGroupQuery = L"SELECT `User_`, `Group_` FROM `UserGroup` WHERE `User_`=?";
12 +enum eUserGroupQuery { vugqUser = 1, vugqGroup };
13 +
14 +LPCWSTR vActionableQuery = L"SELECT `User`,`Component_`,`Name`,`Domain`,`Password`,`Attributes` FROM `User` WHERE `Component_` IS NOT NULL";
15 +enum eActionableQuery { vaqUser = 1, vaqComponent, vaqName, vaqDomain, vaqPassword, vaqAttributes };
16 +
17 +
18 +static HRESULT AddUserToList(
19 + __inout SCA_USER** ppsuList
20 + );
21 +
22 +static HRESULT AddGroupToList(
23 + __inout SCA_GROUP** ppsgList
24 + );
25 +
26 +
27 +HRESULT __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 +
96 +LExit:
97 + ReleaseStr(pwzData);
98 +
99 + return hr;
100 +}
101 +
102 +HRESULT __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 +
172 +LExit:
173 + ReleaseStr(pwzData);
174 +
175 + return hr;
176 +}
177 +
178 +
179 +HRESULT __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 +
236 +LExit:
237 + ReleaseStr(pwzData);
238 +
239 + return hr;
240 +}
241 +
242 +
243 +void 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 +
259 +void 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 +
274 +HRESULT 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 +
396 +LExit:
397 + ReleaseStr(pwzData);
398 +
399 + return hr;
400 +}
401 +
402 +
403 +static 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 +
419 +LExit:
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
426 +static 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 +
460 +LExit:
461 + return hr;
462 +}
463 +
464 +
465 +/* ****************************************************************
466 +ScaUserExecute - Schedules user account creation or removal based on
467 +component state.
468 +
469 +******************************************************************/
470 +HRESULT 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 +
631 +LExit:
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 +
647 +static 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 +
658 +LExit:
659 + return hr;
660 +}
661 +
662 +
663 +static 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 +
674 +LExit:
675 + return hr;
676 +}
src/ca/scauser.h new
+67
@@ -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 +
5 +enum USER_EXISTS
6 +{
7 + USER_EXISTS_YES,
8 + USER_EXISTS_NO,
9 + USER_EXISTS_INDETERMINATE
10 +};
11 +
12 +// structs
13 +struct 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 +
24 +struct 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
43 +HRESULT __stdcall ScaGetUser(
44 + __in LPCWSTR wzUser,
45 + __out SCA_USER* pscau
46 + );
47 +HRESULT __stdcall ScaGetUserDeferred(
48 + __in LPCWSTR wzUser,
49 + __in WCA_WRAPQUERY_HANDLE hUserQuery,
50 + __out SCA_USER* pscau
51 + );
52 +HRESULT __stdcall ScaGetGroup(
53 + __in LPCWSTR wzGroup,
54 + __out SCA_GROUP* pscag
55 + );
56 +void ScaUserFreeList(
57 + __in SCA_USER* psuList
58 + );
59 +void ScaGroupFreeList(
60 + __in SCA_GROUP* psgList
61 + );
62 +HRESULT ScaUserRead(
63 + __inout SCA_USER** ppsuList
64 + );
65 +HRESULT ScaUserExecute(
66 + __in SCA_USER *psuList
67 + );
src/ca/secureobj.cpp new
+902
@@ -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
6 +LPCWSTR 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`";
9 +enum eQUERY_SECUREOBJECTS { QSO_SECUREOBJECT = 1, QSO_TABLE, QSO_DOMAIN, QSO_USER, QSO_PERMISSION, QSO_COMPONENT, QSO_COMPATTRIBUTES };
10 +
11 +LPCWSTR wzQUERY_REGISTRY = L"SELECT `Registry`.`Registry`, `Registry`.`Root`, `Registry`.`Key` FROM `Registry` WHERE `Registry`.`Registry`=?";
12 +enum eQUERY_OBJECTCOMPONENT { QSOC_REGISTRY = 1, QSOC_REGROOT, QSOC_REGKEY };
13 +
14 +LPCWSTR wzQUERY_SERVICEINSTALL = L"SELECT `ServiceInstall`.`Name` FROM `ServiceInstall` WHERE `ServiceInstall`.`ServiceInstall`=?";
15 +enum eQUERY_SECURESERVICEINSTALL { QSSI_NAME = 1 };
16 +
17 +enum eOBJECTTYPE { OT_UNKNOWN, OT_SERVICE, OT_FOLDER, OT_FILE, OT_REGISTRY };
18 +
19 +static 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 +
51 +static 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 +
82 +static 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 + }
156 +LExit:
157 + ReleaseStr(pwzCustomActionData);
158 +
159 + if (psd)
160 + {
161 + ::LocalFree(psd);
162 + }
163 +
164 + return hr;
165 +}
166 +
167 +static 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 +
302 +LExit:
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 +******************************************************************/
315 +extern "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 +
451 +LExit:
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 +******************************************************************/
471 +extern "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 +
553 +LExit:
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 +******************************************************************/
573 +extern "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 +
757 +LExit:
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 +
785 +extern "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 +
886 +LExit:
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 +}
src/ca/serviceconfig.cpp new
+821
@@ -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
6 +LPCWSTR wzQUERY_SERVICECONFIG = L"SELECT `ServiceName`, `Component_`, `NewService`, `FirstFailureActionType`, `SecondFailureActionType`, `ThirdFailureActionType`, `ResetPeriodInDays`, `RestartServiceDelayInSeconds`, `ProgramCommandLine`, `RebootMessage` FROM `ServiceConfig`";
7 +enum 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
10 +LPCWSTR c_wzActionTypeNone = L"none";
11 +LPCWSTR c_wzActionTypeReboot = L"reboot";
12 +LPCWSTR c_wzActionTypeRestart = L"restart";
13 +LPCWSTR c_wzActionTypeRunCommand = L"runCommand";
14 +
15 +// prototypes
16 +static SC_ACTION_TYPE GetSCActionType(
17 + __in LPCWSTR pwzActionTypeName
18 + );
19 +
20 +static HRESULT GetSCActionTypeString(
21 + __in SC_ACTION_TYPE type,
22 + __out_ecount(cchActionTypeString) LPWSTR wzActionTypeString,
23 + __in DWORD cchActionTypeString
24 + );
25 +
26 +static HRESULT GetService(
27 + __in SC_HANDLE hSCM,
28 + __in LPCWSTR wzService,
29 + __in DWORD dwOpenServiceAccess,
30 + __out SC_HANDLE* phService
31 + );
32 +
33 +static 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 +/******************************************************************
48 +SchedServiceConfig - entry point for SchedServiceConfig Custom Action
49 +
50 +called as Type 1 CustomAction (binary DLL) from Windows Installer
51 +in InstallExecuteSequence before CaExecServiceConfig
52 +********************************************************************/
53 +extern "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 +
177 +LExit:
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 +/******************************************************************
188 +CaExecServiceConfig - entry point for ServiceConfig Custom Action.
189 +
190 +NOTE: deferred CustomAction since it modifies the machine
191 +NOTE: CustomActionData == wzServiceName\tfNewService\twzFirstFailureActionType\twzSecondFailureActionType\twzThirdFailureActionType\tdwResetPeriodInDays\tdwRestartServiceDelayInSeconds\twzProgramCommandLine\twzRebootMessage\twzServiceName\tfNewService\t...
192 +*******************************************************************/
193 +extern "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 +
394 +LExit:
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 +/******************************************************************
429 +RollbackServiceConfig - entry point for ServiceConfig rollback
430 + Custom Action.
431 +
432 +NOTE: CustomActionScript Data == wzServiceName\twzFirstFailureActionType\twzSecondFailureActionType\twzThirdFailureActionType\tdwResetPeriodInDays\tdwRestartServiceDelayInSeconds\twzProgramCommandLine\twzRebootMessage\twzServiceName\t...
433 +*******************************************************************/
434 +extern "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 +
552 +LExit:
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 +/**********************************************************
585 +GetSCActionType - helper function to return the SC_ACTION_TYPE
586 +for a given string matching the allowed set.
587 +REBOOT, RESTART, RUN_COMMAND and NONE
588 +**********************************************************/
589 +static 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 +
618 +static 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 +
648 +LExit:
649 + return hr;
650 +}
651 +
652 +
653 +static 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 +
684 +LExit:
685 + if (lpMsgBuf) // Allocated with FormatString.
686 + {
687 + ::LocalFree(lpMsgBuf);
688 + }
689 +
690 + return hr;
691 +}
692 +
693 +
694 +static 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 +
806 +LExit:
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 +}
src/ca/shellexecca.cpp new
+271
@@ -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 +
5 +HRESULT 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 +
71 +LExit:
72 + ReleaseStr(sczWorkingDirectory);
73 + return hr;
74 +}
75 +
76 +extern "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 +
102 +LExit:
103 + ReleaseStr(pwzTarget);
104 +
105 + if (FAILED(hr))
106 + {
107 + er = ERROR_INSTALL_FAILURE;
108 + }
109 + return WcaFinalize(er);
110 +}
111 +
112 +extern "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 +
138 +LExit:
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 +//
151 +HRESULT 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 +
188 +LExit:
189 + ReleaseStr(pwzSql);
190 +
191 + return hr;
192 +}
193 +
194 +extern "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 +
257 +LExit:
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 +}
src/ca/test.cpp new
+269
@@ -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 +
7 +extern HMODULE g_hInstCADLL;
8 +
9 +
10 +// structs
11 +
12 +struct UITHREAD_CONTEXT
13 +{
14 + HANDLE hInitializedEvent;
15 + HINSTANCE hInstance;
16 + HWND hWnd;
17 +};
18 +
19 +
20 +// internal function declarations
21 +
22 +static HRESULT CreateMessageWindow(
23 + __out HWND* phWnd
24 + );
25 +
26 +static void CloseMessageWindow(
27 + __in HWND hWnd
28 + );
29 +
30 +static DWORD WINAPI ThreadProc(
31 + __in LPVOID pvContext
32 + );
33 +
34 +static LRESULT CALLBACK WndProc(
35 + __in HWND hWnd,
36 + __in UINT uMsg,
37 + __in WPARAM wParam,
38 + __in LPARAM lParam
39 + );
40 +
41 +
42 +/******************************************************************
43 +WixFailWhenDeferred - 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 +********************************************************************/
50 +extern "C" UINT __stdcall WixFailWhenDeferred(
51 + __in MSIHANDLE hInstall
52 + )
53 +{
54 + return ::MsiGetMode(hInstall, MSIRUNMODE_SCHEDULED) ? ERROR_INSTALL_FAILURE : ERROR_SUCCESS;
55 +}
56 +
57 +/******************************************************************
58 +WixWaitForEvent - 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 +********************************************************************/
65 +extern "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 +
124 +LExit:
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 +
149 +static 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 +
175 +LExit:
176 + ReleaseHandle(rgWaitHandles[1]);
177 + ReleaseHandle(rgWaitHandles[0]);
178 +
179 + return hr;
180 +}
181 +
182 +static void CloseMessageWindow(
183 + __in HWND hWnd
184 + )
185 +{
186 + if (::IsWindow(hWnd))
187 + {
188 + ::PostMessageW(hWnd, WM_CLOSE, 0, 0);
189 + }
190 +}
191 +
192 +static 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 +
240 +LExit:
241 + if (fRegistered)
242 + {
243 + ::UnregisterClassW(WIXCA_UITHREAD_CLASS_WINDOW, pContext->hInstance);
244 + }
245 +
246 + return hr;
247 +}
248 +
249 +static 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 +}
src/ca/utilca.def
+82 -1
@@ -4,4 +4,85 @@
4 LIBRARY "utilca"
5
6 EXPORTS
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
src/ca/utilca.vcxproj
+34 -1
@@ -38,18 +38,51 @@
38 </ImportGroup>
39
40 <PropertyGroup>
41 - <ProjectAdditionalLinkLibraries>msi.lib</ProjectAdditionalLinkLibraries>
41 + <ProjectAdditionalLinkLibraries>activeds.lib;adsiid.lib;msi.lib;netapi32.lib;shlwapi.lib</ProjectAdditionalLinkLibraries>
42 </PropertyGroup>
43
44 <ItemGroup>
45 + <ClCompile Include="BroadcastSettingChange.cpp" />
46 + <ClCompile Include="CheckReboot.cpp" />
47 + <ClCompile Include="CloseApps.cpp" />
48 <ClCompile Include="dllmain.cpp">
49 <PrecompiledHeader>Create</PrecompiledHeader>
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" />
71 <ClCompile Include="utilca.cpp" />
72 + <ClCompile Include="XmlConfig.cpp" />
73 + <ClCompile Include="XmlFile.cpp" />
74 </ItemGroup>
75
76 <ItemGroup>
77 + <ClInclude Include="caSuffix.h" />
78 + <ClInclude Include="cost.h" />
79 + <ClInclude Include="CustomMsiErrors.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" />
86 </ItemGroup>
87
88 <ItemGroup>
src/wixlib/UtilExtension.wxs
+20 -20
@@ -63,7 +63,7 @@
63 </Fragment>
64
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
68 <InstallExecuteSequence>
69 <Custom Action="WixFailWhenDeferred" Before="InstallFinalize" Overridable="yes">WIXFAILWHENDEFERRED=1 AND VersionNT &gt; 400</Custom>
@@ -71,7 +71,7 @@
71 </Fragment>
72
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
76 <InstallExecuteSequence>
77 <Custom Action="WixWaitForEvent" Before="InstallFinalize" Overridable="yes" />
@@ -79,7 +79,7 @@
79 </Fragment>
80
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
84 <InstallExecuteSequence>
85 <Custom Action="WixWaitForEventDeferred" After="InstallInitialize" Overridable="yes" />
@@ -87,7 +87,7 @@
87 </Fragment>
88
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
92 <InstallExecuteSequence>
93 <Custom Action="WixExitEarlyWithSuccess" After="FindRelatedProducts" Overridable="yes">NEWERVERSIONDETECTED AND VersionNT &gt; 400</Custom>
@@ -95,7 +95,7 @@
95 </Fragment>
96
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
100 <InstallExecuteSequence>
101 <Custom Action="WixRemoveFoldersEx" Before="CostInitialize" />
@@ -103,7 +103,7 @@
103 </Fragment>
104
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
108 <InstallExecuteSequence>
109 <Custom Action="WixBroadcastSettingChange" After="InstallFinalize" Overridable="yes" />
@@ -111,7 +111,7 @@
111 </Fragment>
112
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
116 <InstallExecuteSequence>
117 <Custom Action="WixBroadcastEnvironmentChange" After="InstallFinalize" Overridable="yes" />
@@ -120,7 +120,7 @@
120
121 <!-- WiX OS-detection properties and custom action -->
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
125 <InstallExecuteSequence>
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>
214
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
218 <InstallExecuteSequence>
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>
319
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
323 <InstallExecuteSequence>
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>
361
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
365 <InstallExecuteSequence>
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) -->
385 <Fragment>
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>
389
390 <Fragment>
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>
394
395 <Fragment>
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>
399
400 <Fragment>
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>
404
405 <Fragment>
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>
409
410 <Fragment>
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>
414
415 <Fragment>
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>
419
420 <!-- SilentExec custom actions differ from QtExec in that they do not log the commandline or output of the exe -->
421 <Fragment>
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>
425
426 <Fragment>
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>
430 </Wix>
src/wixlib/UtilExtension_Platform.wxi
+1 -1
@@ -31,7 +31,7 @@
31 </Fragment>
32
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
36 <InstallExecuteSequence>
37 <Custom Action="WixRegisterRestartResources$(var.Suffix)" Before="InstallValidate" Overridable="yes" />