66#include < winget/Runtime.h>
77#include < AppInstallerFileLogger.h>
88#include < AppInstallerErrors.h>
9+ #include < winrt/Windows.ApplicationModel.h>
910
1011namespace AppInstaller ::MSStore
1112{
@@ -176,6 +177,133 @@ namespace AppInstaller::MSStore
176177
177178 return S_FALSE;
178179 }
180+
181+ // Used to detect a signal that a package update is being requested so that we can early out
182+ // on an attempt to update ourself. This is only needed for elevated processes because the
183+ // standard shutdown signals are not sent to elevated processes in the same manner.
184+ struct PackageUpdateMonitor
185+ {
186+ PackageUpdateMonitor ()
187+ {
188+ if (Runtime::IsRunningAsAdmin () && Runtime::IsRunningInPackagedContext ())
189+ {
190+ m_catalog = winrt::Windows::ApplicationModel::PackageCatalog::OpenForCurrentPackage ();
191+ m_updatingEvent = m_catalog.PackageUpdating (
192+ winrt::auto_revoke, [this ](winrt::Windows::ApplicationModel::PackageCatalog, winrt::Windows::ApplicationModel::PackageUpdatingEventArgs args)
193+ {
194+ // Deployment always sends a value of 0 before doing any work and a value of 100 when completely done.
195+ constexpr double minProgress = 0 ;
196+ auto progress = args.Progress ();
197+ if (progress > minProgress)
198+ {
199+ m_isUpdating = true ;
200+ }
201+ });
202+ }
203+ }
204+
205+ bool IsUpdating () const
206+ {
207+ return m_isUpdating;
208+ }
209+
210+ private:
211+ winrt::Windows::ApplicationModel::PackageCatalog m_catalog = nullptr ;
212+ decltype (winrt::Windows::ApplicationModel::PackageCatalog{ nullptr }.PackageUpdating(winrt::auto_revoke, nullptr )) m_updatingEvent;
213+ std::atomic_bool m_isUpdating = false ;
214+ };
215+
216+ HRESULT WaitForOperation (const std::wstring& productId, bool isSilentMode, IVectorView<AppInstallItem>& installItems, IProgressCallback& progress, const PackageUpdateMonitor& monitor)
217+ {
218+ auto cancelIfOperationFailed = wil::scope_exit (
219+ [&]()
220+ {
221+ try
222+ {
223+ AppInstallManager installManager;
224+ installManager.Cancel (productId);
225+ }
226+ CATCH_LOG ();
227+ });
228+
229+ for (auto const & installItem : installItems)
230+ {
231+ AICLI_LOG (Core, Info, <<
232+ " Started MSStore package execution. ProductId: " << Utility::ConvertToUTF8 (installItem.ProductId ()) <<
233+ " PackageFamilyName: " << Utility::ConvertToUTF8 (installItem.PackageFamilyName ()));
234+
235+ if (isSilentMode)
236+ {
237+ installItem.InstallInProgressToastNotificationMode (AppInstallationToastNotificationMode::NoToast);
238+ installItem.CompletedInstallToastNotificationMode (AppInstallationToastNotificationMode::NoToast);
239+ }
240+ }
241+
242+ HRESULT errorCode = S_OK;
243+
244+ // We are aggregating all AppInstallItem progresses into one.
245+ // Averaging every progress for now until we have a better way to find overall progress.
246+ uint64_t overallProgressMax = 100 * static_cast <uint64_t >(installItems.Size ());
247+ uint64_t currentProgress = 0 ;
248+
249+ while (currentProgress < overallProgressMax)
250+ {
251+ currentProgress = 0 ;
252+
253+ for (auto const & installItem : installItems)
254+ {
255+ const auto & status = installItem.GetCurrentStatus ();
256+ currentProgress += static_cast <uint64_t >(status.PercentComplete ());
257+
258+ errorCode = status.ErrorCode ();
259+
260+ if (!SUCCEEDED (errorCode))
261+ {
262+ return errorCode;
263+ }
264+ }
265+
266+ // It may take a while for Store client to pick up the install request.
267+ // So we show indefinite progress here to avoid a progress bar stuck at 0.
268+ if (currentProgress > 0 )
269+ {
270+ progress.OnProgress (currentProgress, overallProgressMax, ProgressType::Percent);
271+ }
272+
273+ if (progress.IsCancelledBy (CancelReason::User))
274+ {
275+ for (auto const & installItem : installItems)
276+ {
277+ installItem.Cancel ();
278+ }
279+ }
280+
281+ // If app shutdown then we have 30s to keep installing, keep going and hope for the best.
282+ else if (progress.IsCancelledBy (CancelReason::AppShutdown) || monitor.IsUpdating ())
283+ {
284+ for (auto const & installItem : installItems)
285+ {
286+ // Insert spiderman meme.
287+ if (installItem.ProductId () == std::wstring{ s_AppInstallerProductId })
288+ {
289+ AICLI_LOG (Core, Info, << " Asked to shutdown while installing AppInstaller." );
290+ progress.OnProgress (overallProgressMax, overallProgressMax, ProgressType::Percent);
291+ cancelIfOperationFailed.release ();
292+ return S_OK;
293+ }
294+ }
295+ }
296+
297+ Sleep (100 );
298+ }
299+
300+ if (SUCCEEDED (errorCode))
301+ {
302+ cancelIfOperationFailed.release ();
303+ }
304+
305+ return errorCode;
306+ }
179307 }
180308
181309 HRESULT MSStoreOperation::StartAndWaitForOperation (IProgressCallback& progress)
@@ -195,6 +323,8 @@ namespace AppInstaller::MSStore
195323
196324 HRESULT MSStoreOperation::InstallPackage (IProgressCallback& progress)
197325 {
326+ PackageUpdateMonitor monitor;
327+
198328 AppInstallManager installManager;
199329 AppInstallOptions installOptions;
200330
@@ -248,11 +378,13 @@ namespace AppInstaller::MSStore
248378 installOptions).get ();
249379 }
250380
251- return WaitForOperation (installItems, progress);
381+ return WaitForOperation (m_productId, m_isSilentMode, installItems, progress, monitor );
252382 }
253383
254384 HRESULT MSStoreOperation::UpdatePackage (IProgressCallback& progress)
255385 {
386+ PackageUpdateMonitor monitor;
387+
256388 AppInstallManager installManager;
257389 AppUpdateOptions updateOptions;
258390 updateOptions.AllowForcedAppRestart (m_force);
@@ -300,98 +432,6 @@ namespace AppInstaller::MSStore
300432 installItems = winrt::single_threaded_vector (std::move (installItemVector)).GetView ();
301433 }
302434
303- return WaitForOperation (installItems, progress);
304- }
305-
306- HRESULT MSStoreOperation::WaitForOperation (IVectorView<AppInstallItem>& installItems, IProgressCallback& progress)
307- {
308- auto cancelIfOperationFailed = wil::scope_exit (
309- [&]()
310- {
311- try
312- {
313- AppInstallManager installManager;
314- installManager.Cancel (m_productId);
315- }
316- CATCH_LOG ();
317- });
318-
319- for (auto const & installItem : installItems)
320- {
321- AICLI_LOG (Core, Info, <<
322- " Started MSStore package execution. ProductId: " << Utility::ConvertToUTF8 (installItem.ProductId ()) <<
323- " PackageFamilyName: " << Utility::ConvertToUTF8 (installItem.PackageFamilyName ()));
324-
325- if (m_isSilentMode)
326- {
327- installItem.InstallInProgressToastNotificationMode (AppInstallationToastNotificationMode::NoToast);
328- installItem.CompletedInstallToastNotificationMode (AppInstallationToastNotificationMode::NoToast);
329- }
330- }
331-
332- HRESULT errorCode = S_OK;
333-
334- // We are aggregating all AppInstallItem progresses into one.
335- // Averaging every progress for now until we have a better way to find overall progress.
336- uint64_t overallProgressMax = 100 * static_cast <uint64_t >(installItems.Size ());
337- uint64_t currentProgress = 0 ;
338-
339- while (currentProgress < overallProgressMax)
340- {
341- currentProgress = 0 ;
342-
343- for (auto const & installItem : installItems)
344- {
345- const auto & status = installItem.GetCurrentStatus ();
346- currentProgress += static_cast <uint64_t >(status.PercentComplete ());
347-
348- errorCode = status.ErrorCode ();
349-
350- if (!SUCCEEDED (errorCode))
351- {
352- return errorCode;
353- }
354- }
355-
356- // It may take a while for Store client to pick up the install request.
357- // So we show indefinite progress here to avoid a progress bar stuck at 0.
358- if (currentProgress > 0 )
359- {
360- progress.OnProgress (currentProgress, overallProgressMax, ProgressType::Percent);
361- }
362-
363- if (progress.IsCancelledBy (CancelReason::User))
364- {
365- for (auto const & installItem : installItems)
366- {
367- installItem.Cancel ();
368- }
369- }
370-
371- // If app shutdown then we have 30s to keep installing, keep going and hope for the best.
372- else if (progress.IsCancelledBy (CancelReason::AppShutdown))
373- {
374- for (auto const & installItem : installItems)
375- {
376- // Insert spiderman meme.
377- if (installItem.ProductId () == std::wstring{ s_AppInstallerProductId })
378- {
379- AICLI_LOG (Core, Info, << " Asked to shutdown while installing AppInstaller." );
380- progress.OnProgress (overallProgressMax, overallProgressMax, ProgressType::Percent);
381- cancelIfOperationFailed.release ();
382- return S_OK;
383- }
384- }
385- }
386-
387- Sleep (100 );
388- }
389-
390- if (SUCCEEDED (errorCode))
391- {
392- cancelIfOperationFailed.release ();
393- }
394-
395- return errorCode;
435+ return WaitForOperation (m_productId, m_isSilentMode, installItems, progress, monitor);
396436 }
397437}
0 commit comments