master
cpp 382 lines 12.7 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 WindowsUpdateIntegration.cpp
8
9 Abstract:
10
11 This file contains objects related to invoking the Windows Update Agent API.
12
13 --*/
14
15 #include "precomp.h"
16 #include "WindowsUpdateIntegration.h"
17
18 namespace wsl::windows::common {
19
20 namespace anon {
21 struct DefaultWindowsUpdateClassFactory : public WindowsUpdateClassFactory
22 {
23 wil::com_ptr<IUpdateSession> CreateUpdateSession() const override
24 {
25 wil::com_ptr<IUpdateSession> result;
26 THROW_IF_FAILED(CoCreateInstance(CLSID_UpdateSession, nullptr, CLSCTX_INPROC_SERVER, IID_IUpdateSession, (void**)&result));
27 return result;
28 }
29
30 wil::com_ptr<IUpdateCollection> CreateUpdateCollection() const override
31 {
32 wil::com_ptr<IUpdateCollection> result;
33 THROW_IF_FAILED(CoCreateInstance(CLSID_UpdateCollection, nullptr, CLSCTX_INPROC_SERVER, IID_IUpdateCollection, (void**)&result));
34 return result;
35 }
36 };
37
38 struct DownloadProgressChangedCallback
39 : public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, IDownloadProgressChangedCallback>
40 {
41 DownloadProgressChangedCallback(std::function<void(uint32_t)> progress) : m_progress(std::move(progress))
42 {
43 }
44
45 IFACEMETHOD(Invoke)(IDownloadJob*, IDownloadProgressChangedCallbackArgs* callbackArgs) override
46 {
47 wil::com_ptr<IDownloadProgress> progress;
48 RETURN_IF_FAILED(callbackArgs->get_Progress(&progress));
49
50 LONG percent{};
51 RETURN_IF_FAILED(progress->get_PercentComplete(&percent));
52
53 m_progress(static_cast<uint32_t>(percent));
54 return S_OK;
55 }
56
57 private:
58 std::function<void(uint32_t)> m_progress;
59 };
60
61 struct DownloadCompletedCallback
62 : public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, IDownloadCompletedCallback>
63 {
64 DownloadCompletedCallback()
65 {
66 }
67
68 IFACEMETHOD(Invoke)(IDownloadJob*, IDownloadCompletedCallbackArgs*) override
69 {
70 m_completed.SetEvent();
71 return S_OK;
72 }
73
74 void Wait()
75 {
76 m_completed.wait();
77 }
78
79 private:
80 wil::slim_event_manual_reset m_completed;
81 };
82
83 struct InstallationProgressChangedCallback
84 : public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, IInstallationProgressChangedCallback>
85 {
86 InstallationProgressChangedCallback(std::function<void(uint32_t)> progress) : m_progress(std::move(progress))
87 {
88 }
89
90 IFACEMETHOD(Invoke)(IInstallationJob*, IInstallationProgressChangedCallbackArgs* callbackArgs) override
91 {
92 wil::com_ptr<IInstallationProgress> progress;
93 RETURN_IF_FAILED(callbackArgs->get_Progress(&progress));
94
95 LONG percent{};
96 RETURN_IF_FAILED(progress->get_PercentComplete(&percent));
97
98 m_progress(static_cast<uint32_t>(percent));
99 return S_OK;
100 }
101
102 private:
103 std::function<void(uint32_t)> m_progress;
104 };
105
106 struct InstallationCompletedCallback
107 : public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, IInstallationCompletedCallback>
108 {
109 InstallationCompletedCallback()
110 {
111 }
112
113 IFACEMETHOD(Invoke)(IInstallationJob*, IInstallationCompletedCallbackArgs*) override
114 {
115 m_completed.SetEvent();
116 return S_OK;
117 }
118
119 void Wait()
120 {
121 m_completed.wait();
122 }
123
124 private:
125 wil::slim_event_manual_reset m_completed;
126 };
127 } // namespace anon
128
129 WindowsUpdateContext::WindowsUpdateContext() : WindowsUpdateContext(std::make_unique<anon::DefaultWindowsUpdateClassFactory>())
130 {
131 }
132
133 WindowsUpdateContext::WindowsUpdateContext(std::unique_ptr<WindowsUpdateClassFactory> factory) :
134 m_factory(std::move(factory)), m_product(WslProductIdentifier())
135 {
136 m_session = m_factory->CreateUpdateSession();
137
138 auto applicationID = wil::make_bstr(L"Windows Subsystem for Linux");
139 THROW_IF_FAILED(m_session->put_ClientApplicationID(applicationID.get()));
140
141 m_activity = std::make_unique<ActivityType>();
142 TraceLoggingWriteStart(
143 *m_activity,
144 "WindowsUpdateContext",
145 TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage),
146 TraceLoggingValue(WSL_PACKAGE_VERSION, "wslVersion"),
147 TraceLoggingWideString(m_product.c_str(), "product"));
148 }
149
150 std::wstring WindowsUpdateContext::WslProductIdentifier()
151 {
152 return STRING_TO_WIDE_STRING(DCAT_PRODUCT_NAME);
153 }
154
155 void WindowsUpdateContext::EnsureProductRegistryEntry(bool reset) const
156 {
157 if (reset || !wsl::windows::common::helpers::VersionRegisteredWithDcat())
158 {
159 wsl::windows::common::helpers::RegisterWithDcat(false);
160 }
161 }
162
163 size_t WindowsUpdateContext::SearchForUpdates()
164 {
165 TraceLoggingWriteTagged(
166 *m_activity,
167 "SearchForUpdates",
168 TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
169 TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage),
170 TraceLoggingWideString(m_product.c_str(), "product"));
171
172 THROW_IF_FAILED(m_session->CreateUpdateSearcher(&m_searcher));
173
174 std::wstring queryString = std::format(L"Product='{}'", m_product);
175 auto queryBSTR = wil::make_bstr(queryString.c_str());
176
177 wil::com_ptr<ISearchResult> searchResult;
178 THROW_IF_FAILED(m_searcher->Search(queryBSTR.get(), &searchResult));
179
180 OperationResultCode resultCode{};
181 THROW_IF_FAILED(searchResult->get_ResultCode(&resultCode));
182
183 THROW_HR_IF(WSLC_E_WU_SEARCH_FAILED, resultCode != OperationResultCode::orcSucceeded && resultCode != OperationResultCode::orcSucceededWithErrors);
184
185 if (resultCode == OperationResultCode::orcSucceededWithErrors)
186 {
187 wil::com_ptr<IUpdateExceptionCollection> warnings;
188 if (SUCCEEDED_LOG(searchResult->get_Warnings(&warnings)) && warnings)
189 {
190 LONG warningCount{};
191 if (SUCCEEDED_LOG(warnings->get_Count(&warningCount)))
192 {
193 for (LONG i = 0; i < warningCount; ++i)
194 {
195 wil::com_ptr<IUpdateException> warning;
196 if (FAILED_LOG(warnings->get_Item(i, &warning)) || !warning)
197 {
198 continue;
199 }
200
201 wil::unique_bstr message;
202 LONG hr{};
203 UpdateExceptionContext context{};
204 warning->get_Message(&message);
205 warning->get_HResult(&hr);
206 warning->get_Context(&context);
207
208 TraceLoggingWriteTagged(
209 *m_activity,
210 "SearchWarning",
211 TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
212 TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage),
213 TraceLoggingHResult(hr, "warningHResult"),
214 TraceLoggingUInt32(static_cast<uint32_t>(context), "warningContext"),
215 TraceLoggingWideString(message.get(), "warningMessage"));
216 }
217 }
218 }
219 }
220
221 THROW_IF_FAILED(searchResult->get_Updates(&m_updates));
222 size_t result = GetUpdateCount();
223
224 TraceLoggingWriteTagged(
225 *m_activity,
226 "SearchForUpdatesResult",
227 TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
228 TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage),
229 TraceLoggingInt32(resultCode, "OperationResultCode"),
230 TraceLoggingLong(static_cast<LONG>(result), "updateCount"));
231
232 return result;
233 }
234
235 size_t WindowsUpdateContext::GetUpdateCount() const
236 {
237 LONG result{};
238 if (m_updates)
239 {
240 THROW_IF_FAILED(m_updates->get_Count(&result));
241 }
242 return static_cast<size_t>(result);
243 }
244
245 void WindowsUpdateContext::DownloadUpdates(const std::function<void(uint32_t)>& progress) const
246 {
247 // Collect all of the updates that are not currently downloaded
248 wil::com_ptr<IUpdateCollection> toDownload = m_factory->CreateUpdateCollection();
249
250 for (size_t i = 0, count = GetUpdateCount(); i < count; ++i)
251 {
252 wil::com_ptr<IUpdate> update;
253 THROW_IF_FAILED(m_updates->get_Item(static_cast<LONG>(i), &update));
254 VARIANT_BOOL downloaded = VARIANT_FALSE;
255 THROW_IF_FAILED(update->get_IsDownloaded(&downloaded));
256 if (downloaded == VARIANT_FALSE)
257 {
258 THROW_IF_FAILED(toDownload->Add(update.get(), nullptr));
259 }
260 }
261
262 // All updates are already downloaded — nothing to do.
263 LONG toDownloadCount{};
264 THROW_IF_FAILED(toDownload->get_Count(&toDownloadCount));
265
266 TraceLoggingWriteTagged(
267 *m_activity,
268 "DownloadUpdates",
269 TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
270 TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage),
271 TraceLoggingLong(toDownloadCount, "downloadCount"));
272
273 if (toDownloadCount == 0)
274 {
275 if (progress)
276 {
277 progress(100);
278 }
279 return;
280 }
281
282 wil::com_ptr<IUpdateDownloader> updateDownloader;
283 THROW_IF_FAILED(m_session->CreateUpdateDownloader(&updateDownloader));
284
285 THROW_IF_FAILED(updateDownloader->put_Updates(toDownload.get()));
286
287 Microsoft::WRL::ComPtr<anon::DownloadProgressChangedCallback> downloadProgress;
288 if (progress)
289 {
290 downloadProgress = wil::MakeOrThrow<anon::DownloadProgressChangedCallback>(progress);
291 }
292 auto downloadCompleted = wil::MakeOrThrow<anon::DownloadCompletedCallback>();
293 wil::com_ptr<IDownloadJob> downloadJob;
294
295 THROW_IF_FAILED(updateDownloader->BeginDownload(downloadProgress.Get(), downloadCompleted.Get(), VARIANT{}, &downloadJob));
296 downloadCompleted->Wait();
297 THROW_IF_FAILED(downloadJob->CleanUp());
298
299 wil::com_ptr<IDownloadResult> result;
300 THROW_IF_FAILED(updateDownloader->EndDownload(downloadJob.get(), &result));
301
302 HRESULT downloadHResult{};
303 THROW_IF_FAILED(result->get_HResult(&downloadHResult));
304 THROW_IF_FAILED(downloadHResult);
305 }
306
307 void WindowsUpdateContext::InstallUpdates(const std::function<void(uint32_t)>& progress) const
308 {
309 TraceLoggingWriteTagged(
310 *m_activity, "InstallUpdates", TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
311 wil::com_ptr<IUpdateInstaller> updateInstaller;
312 THROW_IF_FAILED(m_session->CreateUpdateInstaller(&updateInstaller));
313
314 THROW_IF_FAILED(updateInstaller->put_Updates(m_updates.get()));
315
316 Microsoft::WRL::ComPtr<anon::InstallationProgressChangedCallback> installationProgress;
317 if (progress)
318 {
319 installationProgress = wil::MakeOrThrow<anon::InstallationProgressChangedCallback>(progress);
320 }
321 auto installationCompleted = wil::MakeOrThrow<anon::InstallationCompletedCallback>();
322 wil::com_ptr<IInstallationJob> installationJob;
323
324 THROW_IF_FAILED(updateInstaller->BeginInstall(installationProgress.Get(), installationCompleted.Get(), VARIANT{}, &installationJob));
325 installationCompleted->Wait();
326 THROW_IF_FAILED(installationJob->CleanUp());
327
328 wil::com_ptr<IInstallationResult> result;
329 THROW_IF_FAILED(updateInstaller->EndInstall(installationJob.get(), &result));
330
331 HRESULT installationHResult{};
332 THROW_IF_FAILED(result->get_HResult(&installationHResult));
333 THROW_IF_FAILED(installationHResult);
334 }
335
336 void WindowsUpdateContext::RunUpdateFlow(UpdateOptions options, const std::function<void(uint32_t)>& progress)
337 {
338 TraceLoggingWriteTagged(
339 *m_activity,
340 "RunUpdateFlow",
341 TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
342 TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage),
343 TraceLoggingUInt32(static_cast<std::underlying_type_t<UpdateOptions>>(options), "options"));
344
345 static_assert(
346 DownloadProgressPercent + InstallProgressPercent == 100, "Download and Install progress values must add up to 100.");
347
348 if (progress)
349 {
350 progress(0);
351 }
352
353 if (options != UpdateOptions::None)
354 {
355 EnsureProductRegistryEntry(options == UpdateOptions::ResetProductRegistration);
356 }
357
358 size_t updateCount = SearchForUpdates();
359 if (!updateCount)
360 {
361 if (progress)
362 {
363 progress(100);
364 }
365 return;
366 }
367
368 std::function<void(uint32_t)> downloadProgress;
369 if (progress)
370 {
371 downloadProgress = [&](uint32_t percent) { progress((percent * DownloadProgressPercent) / 100); };
372 }
373 DownloadUpdates(downloadProgress);
374
375 std::function<void(uint32_t)> installProgress;
376 if (progress)
377 {
378 installProgress = [&](uint32_t percent) { progress(DownloadProgressPercent + ((percent * InstallProgressPercent) / 100)); };
379 }
380 InstallUpdates(installProgress);
381 }
382 } // namespace wsl::windows::common