diff options
Diffstat (limited to 'src/burn/engine/msuengine.cpp')
-rw-r--r-- | src/burn/engine/msuengine.cpp | 529 |
1 files changed, 529 insertions, 0 deletions
diff --git a/src/burn/engine/msuengine.cpp b/src/burn/engine/msuengine.cpp new file mode 100644 index 00000000..6003123b --- /dev/null +++ b/src/burn/engine/msuengine.cpp | |||
@@ -0,0 +1,529 @@ | |||
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 | // constants | ||
7 | |||
8 | #define WU_S_REBOOT_REQUIRED 0x00240005L | ||
9 | #define WU_S_ALREADY_INSTALLED 0x00240006L | ||
10 | |||
11 | |||
12 | // function definitions | ||
13 | static HRESULT EnsureWUServiceEnabled( | ||
14 | __in BOOL fStopWusaService, | ||
15 | __out SC_HANDLE* pschWu, | ||
16 | __out BOOL* pfPreviouslyDisabled | ||
17 | ); | ||
18 | static HRESULT SetServiceStartType( | ||
19 | __in SC_HANDLE sch, | ||
20 | __in DWORD stratType | ||
21 | ); | ||
22 | static HRESULT StopWUService( | ||
23 | __in SC_HANDLE schWu | ||
24 | ); | ||
25 | |||
26 | |||
27 | extern "C" HRESULT MsuEngineParsePackageFromXml( | ||
28 | __in IXMLDOMNode* pixnMsuPackage, | ||
29 | __in BURN_PACKAGE* pPackage | ||
30 | ) | ||
31 | { | ||
32 | HRESULT hr = S_OK; | ||
33 | |||
34 | // @KB | ||
35 | hr = XmlGetAttributeEx(pixnMsuPackage, L"KB", &pPackage->Msu.sczKB); | ||
36 | ExitOnFailure(hr, "Failed to get @KB."); | ||
37 | |||
38 | // @DetectCondition | ||
39 | hr = XmlGetAttributeEx(pixnMsuPackage, L"DetectCondition", &pPackage->Msu.sczDetectCondition); | ||
40 | ExitOnFailure(hr, "Failed to get @DetectCondition."); | ||
41 | |||
42 | LExit: | ||
43 | return hr; | ||
44 | } | ||
45 | |||
46 | extern "C" void MsuEnginePackageUninitialize( | ||
47 | __in BURN_PACKAGE* pPackage | ||
48 | ) | ||
49 | { | ||
50 | ReleaseNullStr(pPackage->Msu.sczKB); | ||
51 | ReleaseNullStr(pPackage->Msu.sczDetectCondition); | ||
52 | } | ||
53 | |||
54 | extern "C" HRESULT MsuEngineDetectPackage( | ||
55 | __in BURN_PACKAGE* pPackage, | ||
56 | __in BURN_VARIABLES* pVariables | ||
57 | ) | ||
58 | { | ||
59 | HRESULT hr = S_OK; | ||
60 | BOOL fDetected = FALSE; | ||
61 | |||
62 | // evaluate detect condition | ||
63 | if (pPackage->Msu.sczDetectCondition && *pPackage->Msu.sczDetectCondition) | ||
64 | { | ||
65 | hr = ConditionEvaluate(pVariables, pPackage->Msu.sczDetectCondition, &fDetected); | ||
66 | ExitOnFailure(hr, "Failed to evaluate MSU package detect condition."); | ||
67 | } | ||
68 | |||
69 | // update detect state | ||
70 | pPackage->currentState = fDetected ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT; | ||
71 | |||
72 | if (pPackage->fCanAffectRegistration) | ||
73 | { | ||
74 | pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; | ||
75 | } | ||
76 | |||
77 | LExit: | ||
78 | return hr; | ||
79 | } | ||
80 | |||
81 | // | ||
82 | // PlanCalculate - calculates the execute and rollback state for the requested package state. | ||
83 | // | ||
84 | extern "C" HRESULT MsuEnginePlanCalculatePackage( | ||
85 | __in BURN_PACKAGE* pPackage | ||
86 | ) | ||
87 | { | ||
88 | HRESULT hr = S_OK; | ||
89 | BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
90 | BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
91 | BOOL fAllowUninstall = FALSE; | ||
92 | |||
93 | // We can only uninstall MSU packages if they have a KB and we are on Win7 or newer. | ||
94 | fAllowUninstall = pPackage->Msu.sczKB && *pPackage->Msu.sczKB && ::IsWindows7OrGreater(); | ||
95 | |||
96 | // execute action | ||
97 | switch (pPackage->currentState) | ||
98 | { | ||
99 | case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: | ||
100 | switch (pPackage->requested) | ||
101 | { | ||
102 | case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; | ||
103 | case BOOTSTRAPPER_REQUEST_STATE_REPAIR: | ||
104 | execute = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
105 | break; | ||
106 | |||
107 | case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough; | ||
108 | case BOOTSTRAPPER_REQUEST_STATE_CACHE: | ||
109 | execute = fAllowUninstall && pPackage->fUninstallable ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; | ||
110 | break; | ||
111 | |||
112 | case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: | ||
113 | execute = fAllowUninstall ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; | ||
114 | break; | ||
115 | |||
116 | default: | ||
117 | execute = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
118 | break; | ||
119 | } | ||
120 | break; | ||
121 | |||
122 | case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: | ||
123 | switch (pPackage->requested) | ||
124 | { | ||
125 | case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; | ||
126 | case BOOTSTRAPPER_REQUEST_STATE_REPAIR: | ||
127 | execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; | ||
128 | break; | ||
129 | |||
130 | default: | ||
131 | execute = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
132 | break; | ||
133 | } | ||
134 | break; | ||
135 | |||
136 | default: | ||
137 | hr = E_INVALIDARG; | ||
138 | ExitOnRootFailure(hr, "Invalid package state."); | ||
139 | } | ||
140 | |||
141 | // Calculate the rollback action if there is an execute action. | ||
142 | if (BOOTSTRAPPER_ACTION_STATE_NONE != execute) | ||
143 | { | ||
144 | switch (pPackage->currentState) | ||
145 | { | ||
146 | case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: | ||
147 | switch (pPackage->requested) | ||
148 | { | ||
149 | case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; | ||
150 | case BOOTSTRAPPER_REQUEST_STATE_ABSENT: | ||
151 | rollback = BOOTSTRAPPER_ACTION_STATE_INSTALL; | ||
152 | break; | ||
153 | |||
154 | default: | ||
155 | rollback = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
156 | break; | ||
157 | } | ||
158 | break; | ||
159 | |||
160 | case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: | ||
161 | switch (pPackage->requested) | ||
162 | { | ||
163 | case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; | ||
164 | case BOOTSTRAPPER_REQUEST_STATE_REPAIR: | ||
165 | rollback = fAllowUninstall ? BOOTSTRAPPER_ACTION_STATE_UNINSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; | ||
166 | break; | ||
167 | |||
168 | default: | ||
169 | rollback = BOOTSTRAPPER_ACTION_STATE_NONE; | ||
170 | break; | ||
171 | } | ||
172 | break; | ||
173 | |||
174 | default: | ||
175 | hr = E_INVALIDARG; | ||
176 | ExitOnRootFailure(hr, "Invalid package expected state."); | ||
177 | } | ||
178 | } | ||
179 | |||
180 | // return values | ||
181 | pPackage->execute = execute; | ||
182 | pPackage->rollback = rollback; | ||
183 | |||
184 | LExit: | ||
185 | return hr; | ||
186 | } | ||
187 | |||
188 | // | ||
189 | // PlanAdd - adds the calculated execute and rollback actions for the package. | ||
190 | // | ||
191 | extern "C" HRESULT MsuEnginePlanAddPackage( | ||
192 | __in BURN_PACKAGE* pPackage, | ||
193 | __in BURN_PLAN* pPlan, | ||
194 | __in BURN_LOGGING* pLog, | ||
195 | __in BURN_VARIABLES* pVariables, | ||
196 | __in HANDLE hCacheEvent | ||
197 | ) | ||
198 | { | ||
199 | HRESULT hr = S_OK; | ||
200 | BURN_EXECUTE_ACTION* pAction = NULL; | ||
201 | |||
202 | // add wait for cache | ||
203 | if (hCacheEvent) | ||
204 | { | ||
205 | hr = PlanExecuteCacheSyncAndRollback(pPlan, pPackage, hCacheEvent); | ||
206 | ExitOnFailure(hr, "Failed to plan package cache syncpoint"); | ||
207 | } | ||
208 | |||
209 | hr = DependencyPlanPackage(NULL, pPackage, pPlan); | ||
210 | ExitOnFailure(hr, "Failed to plan package dependency actions."); | ||
211 | |||
212 | // add execute action | ||
213 | if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute) | ||
214 | { | ||
215 | hr = PlanAppendExecuteAction(pPlan, &pAction); | ||
216 | ExitOnFailure(hr, "Failed to append execute action."); | ||
217 | |||
218 | pAction->type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE; | ||
219 | pAction->msuPackage.pPackage = pPackage; | ||
220 | pAction->msuPackage.action = pPackage->execute; | ||
221 | |||
222 | LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, &pAction->msuPackage.sczLogPath); // ignore errors. | ||
223 | } | ||
224 | |||
225 | // add rollback action | ||
226 | if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback) | ||
227 | { | ||
228 | hr = PlanAppendRollbackAction(pPlan, &pAction); | ||
229 | ExitOnFailure(hr, "Failed to append rollback action."); | ||
230 | |||
231 | pAction->type = BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE; | ||
232 | pAction->msuPackage.pPackage = pPackage; | ||
233 | pAction->msuPackage.action = pPackage->rollback; | ||
234 | |||
235 | LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, &pAction->msuPackage.sczLogPath); // ignore errors. | ||
236 | } | ||
237 | |||
238 | LExit: | ||
239 | return hr; | ||
240 | } | ||
241 | |||
242 | extern "C" HRESULT MsuEngineExecutePackage( | ||
243 | __in BURN_EXECUTE_ACTION* pExecuteAction, | ||
244 | __in BURN_VARIABLES* pVariables, | ||
245 | __in BOOL fRollback, | ||
246 | __in BOOL fStopWusaService, | ||
247 | __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, | ||
248 | __in LPVOID pvContext, | ||
249 | __out BOOTSTRAPPER_APPLY_RESTART* pRestart | ||
250 | ) | ||
251 | { | ||
252 | HRESULT hr = S_OK; | ||
253 | int nResult = IDNOACTION; | ||
254 | LPWSTR sczCachedDirectory = NULL; | ||
255 | LPWSTR sczMsuPath = NULL; | ||
256 | LPWSTR sczWindowsPath = NULL; | ||
257 | LPWSTR sczSystemPath = NULL; | ||
258 | LPWSTR sczWusaPath = NULL; | ||
259 | LPWSTR sczCommand = NULL; | ||
260 | SC_HANDLE schWu = NULL; | ||
261 | BOOL fWuWasDisabled = FALSE; | ||
262 | STARTUPINFOW si = { }; | ||
263 | PROCESS_INFORMATION pi = { }; | ||
264 | GENERIC_EXECUTE_MESSAGE message = { }; | ||
265 | DWORD dwExitCode = 0; | ||
266 | BOOL fUseSysNativePath = FALSE; | ||
267 | BURN_PACKAGE* pPackage = pExecuteAction->msuPackage.pPackage; | ||
268 | BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload; | ||
269 | |||
270 | #if !defined(_WIN64) | ||
271 | hr = ProcWow64(::GetCurrentProcess(), &fUseSysNativePath); | ||
272 | ExitOnFailure(hr, "Failed to determine WOW64 status."); | ||
273 | #endif | ||
274 | |||
275 | *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; | ||
276 | |||
277 | // get wusa.exe path | ||
278 | if (fUseSysNativePath) | ||
279 | { | ||
280 | hr = PathGetKnownFolder(CSIDL_WINDOWS, &sczWindowsPath); | ||
281 | ExitOnFailure(hr, "Failed to find Windows directory."); | ||
282 | |||
283 | hr = PathConcat(sczWindowsPath, L"SysNative\\", &sczSystemPath); | ||
284 | ExitOnFailure(hr, "Failed to append SysNative directory."); | ||
285 | } | ||
286 | else | ||
287 | { | ||
288 | hr = PathGetKnownFolder(CSIDL_SYSTEM, &sczSystemPath); | ||
289 | ExitOnFailure(hr, "Failed to find System32 directory."); | ||
290 | } | ||
291 | |||
292 | hr = PathConcat(sczSystemPath, L"wusa.exe", &sczWusaPath); | ||
293 | ExitOnFailure(hr, "Failed to allocate WUSA.exe path."); | ||
294 | |||
295 | // build command | ||
296 | switch (pExecuteAction->msuPackage.action) | ||
297 | { | ||
298 | case BOOTSTRAPPER_ACTION_STATE_INSTALL: | ||
299 | // get cached MSU path | ||
300 | hr = CacheGetCompletedPath(TRUE, pPackage->sczCacheId, &sczCachedDirectory); | ||
301 | ExitOnFailure(hr, "Failed to get cached path for package: %ls", pPackage->sczId); | ||
302 | |||
303 | // Best effort to set the execute package cache folder variable. | ||
304 | VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE); | ||
305 | |||
306 | hr = PathConcat(sczCachedDirectory, pPackagePayload->sczFilePath, &sczMsuPath); | ||
307 | ExitOnFailure(hr, "Failed to build MSU path."); | ||
308 | |||
309 | // format command | ||
310 | hr = StrAllocFormatted(&sczCommand, L"\"%ls\" \"%ls\" /quiet /norestart", sczWusaPath, sczMsuPath); | ||
311 | ExitOnFailure(hr, "Failed to format MSU install command."); | ||
312 | break; | ||
313 | |||
314 | case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: | ||
315 | // format command | ||
316 | hr = StrAllocFormatted(&sczCommand, L"\"%ls\" /uninstall /kb:%ls /quiet /norestart", sczWusaPath, pPackage->Msu.sczKB); | ||
317 | ExitOnFailure(hr, "Failed to format MSU uninstall command."); | ||
318 | break; | ||
319 | |||
320 | default: | ||
321 | hr = E_UNEXPECTED; | ||
322 | ExitOnFailure(hr, "Failed to get action arguments for MSU package."); | ||
323 | } | ||
324 | |||
325 | if (pExecuteAction->msuPackage.sczLogPath && *pExecuteAction->msuPackage.sczLogPath) | ||
326 | { | ||
327 | hr = StrAllocConcat(&sczCommand, L" /log:", 0); | ||
328 | ExitOnFailure(hr, "Failed to append log switch to MSU command-line."); | ||
329 | |||
330 | hr = StrAllocConcat(&sczCommand, pExecuteAction->msuPackage.sczLogPath, 0); | ||
331 | ExitOnFailure(hr, "Failed to append log path to MSU command-line."); | ||
332 | } | ||
333 | |||
334 | LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, LoggingActionStateToString(pExecuteAction->msuPackage.action), sczMsuPath ? sczMsuPath : pPackage->Msu.sczKB, sczCommand); | ||
335 | |||
336 | hr = EnsureWUServiceEnabled(fStopWusaService, &schWu, &fWuWasDisabled); | ||
337 | ExitOnFailure(hr, "Failed to ensure WU service was enabled to install MSU package."); | ||
338 | |||
339 | // create process | ||
340 | si.cb = sizeof(si); | ||
341 | if (!::CreateProcessW(sczWusaPath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) | ||
342 | { | ||
343 | ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", sczWusaPath); | ||
344 | } | ||
345 | |||
346 | do | ||
347 | { | ||
348 | message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; | ||
349 | message.dwAllowedResults = MB_OKCANCEL; | ||
350 | message.progress.dwPercentage = 50; | ||
351 | nResult = pfnGenericMessageHandler(&message, pvContext); | ||
352 | hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); | ||
353 | ExitOnRootFailure(hr, "Bootstrapper application aborted during MSU progress."); | ||
354 | |||
355 | // wait for process to terminate | ||
356 | hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode); | ||
357 | if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr) | ||
358 | { | ||
359 | ExitOnFailure(hr, "Failed to wait for executable to complete: %ls", sczWusaPath); | ||
360 | } | ||
361 | } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr); | ||
362 | |||
363 | // get process exit code | ||
364 | if (!::GetExitCodeProcess(pi.hProcess, &dwExitCode)) | ||
365 | { | ||
366 | ExitWithLastError(hr, "Failed to get process exit code."); | ||
367 | } | ||
368 | |||
369 | // We'll normalize the restart required error code from wusa.exe just in case. Most likely | ||
370 | // that on reboot we'll actually get WU_S_REBOOT_REQUIRED. | ||
371 | if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == static_cast<HRESULT>(dwExitCode)) | ||
372 | { | ||
373 | dwExitCode = ERROR_SUCCESS_REBOOT_REQUIRED; | ||
374 | } | ||
375 | |||
376 | // handle exit code | ||
377 | switch (dwExitCode) | ||
378 | { | ||
379 | case S_OK: __fallthrough; | ||
380 | case S_FALSE: __fallthrough; | ||
381 | case WU_S_ALREADY_INSTALLED: | ||
382 | hr = S_OK; | ||
383 | break; | ||
384 | |||
385 | case ERROR_SUCCESS_REBOOT_REQUIRED: __fallthrough; | ||
386 | case WU_S_REBOOT_REQUIRED: | ||
387 | *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; | ||
388 | hr = S_OK; | ||
389 | break; | ||
390 | |||
391 | default: | ||
392 | hr = static_cast<HRESULT>(dwExitCode); | ||
393 | break; | ||
394 | } | ||
395 | |||
396 | LExit: | ||
397 | ReleaseStr(sczCachedDirectory); | ||
398 | ReleaseStr(sczMsuPath); | ||
399 | ReleaseStr(sczSystemPath); | ||
400 | ReleaseStr(sczWindowsPath); | ||
401 | ReleaseStr(sczWusaPath); | ||
402 | ReleaseStr(sczCommand); | ||
403 | |||
404 | ReleaseHandle(pi.hProcess); | ||
405 | ReleaseHandle(pi.hThread); | ||
406 | |||
407 | if (fWuWasDisabled) | ||
408 | { | ||
409 | SetServiceStartType(schWu, SERVICE_DISABLED); | ||
410 | } | ||
411 | |||
412 | // Best effort to clear the execute package cache folder variable. | ||
413 | VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE); | ||
414 | |||
415 | return hr; | ||
416 | } | ||
417 | |||
418 | extern "C" void MsuEngineUpdateInstallRegistrationState( | ||
419 | __in BURN_EXECUTE_ACTION* pAction, | ||
420 | __in HRESULT hrExecute | ||
421 | ) | ||
422 | { | ||
423 | BURN_PACKAGE* pPackage = pAction->msuPackage.pPackage; | ||
424 | |||
425 | if (FAILED(hrExecute) || !pPackage->fCanAffectRegistration) | ||
426 | { | ||
427 | ExitFunction(); | ||
428 | } | ||
429 | |||
430 | if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->msuPackage.action) | ||
431 | { | ||
432 | pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; | ||
433 | } | ||
434 | else | ||
435 | { | ||
436 | pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; | ||
437 | } | ||
438 | |||
439 | LExit: | ||
440 | return; | ||
441 | } | ||
442 | |||
443 | static HRESULT EnsureWUServiceEnabled( | ||
444 | __in BOOL fStopWusaService, | ||
445 | __out SC_HANDLE* pschWu, | ||
446 | __out BOOL* pfPreviouslyDisabled | ||
447 | ) | ||
448 | { | ||
449 | HRESULT hr = S_OK; | ||
450 | SC_HANDLE schSCM = NULL; | ||
451 | SC_HANDLE schWu = NULL; | ||
452 | SERVICE_STATUS serviceStatus = { }; | ||
453 | QUERY_SERVICE_CONFIGW* pConfig = NULL; | ||
454 | |||
455 | schSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS); | ||
456 | ExitOnNullWithLastError(schSCM, hr, "Failed to open service control manager."); | ||
457 | |||
458 | schWu = ::OpenServiceW(schSCM, L"wuauserv", SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS | SERVICE_STOP ); | ||
459 | ExitOnNullWithLastError(schWu, hr, "Failed to open WU service."); | ||
460 | |||
461 | if (!::QueryServiceStatus(schWu, &serviceStatus) ) | ||
462 | { | ||
463 | ExitWithLastError(hr, "Failed to query status of WU service."); | ||
464 | } | ||
465 | |||
466 | // Stop service if requested to. | ||
467 | if (SERVICE_STOPPED != serviceStatus.dwCurrentState && fStopWusaService) | ||
468 | { | ||
469 | hr = StopWUService(schWu); | ||
470 | } | ||
471 | |||
472 | // If the service is not running then it might be disabled so let's check. | ||
473 | if (SERVICE_RUNNING != serviceStatus.dwCurrentState) | ||
474 | { | ||
475 | hr = SvcQueryConfig(schWu, &pConfig); | ||
476 | ExitOnFailure(hr, "Failed to read configuration for WU service."); | ||
477 | |||
478 | // If WU is disabled, change it to a demand start service (but touch nothing else). | ||
479 | if (SERVICE_DISABLED == pConfig->dwStartType) | ||
480 | { | ||
481 | hr = SetServiceStartType(schWu, SERVICE_DEMAND_START); | ||
482 | ExitOnFailure(hr, "Failed to mark WU service to start on demand."); | ||
483 | |||
484 | *pfPreviouslyDisabled = TRUE; | ||
485 | } | ||
486 | } | ||
487 | |||
488 | *pschWu = schWu; | ||
489 | schWu = NULL; | ||
490 | |||
491 | LExit: | ||
492 | ReleaseMem(pConfig); | ||
493 | ReleaseServiceHandle(schWu); | ||
494 | ReleaseServiceHandle(schSCM); | ||
495 | |||
496 | return hr; | ||
497 | } | ||
498 | |||
499 | static HRESULT SetServiceStartType( | ||
500 | __in SC_HANDLE sch, | ||
501 | __in DWORD startType | ||
502 | ) | ||
503 | { | ||
504 | HRESULT hr = S_OK; | ||
505 | |||
506 | if (!::ChangeServiceConfigW(sch, SERVICE_NO_CHANGE, startType, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, NULL)) | ||
507 | { | ||
508 | ExitWithLastError(hr, "Failed to set service start type."); | ||
509 | } | ||
510 | |||
511 | LExit: | ||
512 | return hr; | ||
513 | } | ||
514 | |||
515 | static HRESULT StopWUService( | ||
516 | __in SC_HANDLE schWu | ||
517 | ) | ||
518 | { | ||
519 | HRESULT hr = S_OK; | ||
520 | SERVICE_STATUS serviceStatus = { }; | ||
521 | |||
522 | if(!::ControlService(schWu, SERVICE_CONTROL_STOP, &serviceStatus)) | ||
523 | { | ||
524 | ExitWithLastError(hr, "Failed to stop wusa service."); | ||
525 | } | ||
526 | |||
527 | LExit: | ||
528 | return hr; | ||
529 | } | ||