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