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 > 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 > 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 > 400 OR (VersionNT = 400 AND ServicePackLevel > 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 > 400 OR (VersionNT = 400 AND ServicePackLevel > 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 > 400 OR (VersionNT = 400 AND ServicePackLevel > 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 > 400 OR (VersionNT = 400 AND ServicePackLevel > 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" />