66#include " COMContext.h"
77#include " Command.h"
88#include " ExecutionContext.h"
9+ #include " Public/ShutdownMonitoring.h"
910#include < winget/Checkpoint.h>
1011#include < winget/Reboot.h>
1112#include < winget/UserSettings.h>
@@ -19,262 +20,6 @@ namespace AppInstaller::CLI::Execution
1920
2021 namespace
2122 {
22- // Type to contain the CTRL signal and window messages handler.
23- struct SignalTerminationHandler
24- {
25- static SignalTerminationHandler& Instance ()
26- {
27- static SignalTerminationHandler s_instance;
28- return s_instance;
29- }
30-
31- void AddContext (Context* context)
32- {
33- std::lock_guard<std::mutex> lock{ m_contextsLock };
34-
35- auto itr = std::find (m_contexts.begin (), m_contexts.end (), context);
36- THROW_HR_IF (E_NOT_VALID_STATE, itr != m_contexts.end ());
37- m_contexts.push_back (context);
38- }
39-
40- void RemoveContext (Context* context)
41- {
42- std::lock_guard<std::mutex> lock{ m_contextsLock };
43-
44- auto itr = std::find (m_contexts.begin (), m_contexts.end (), context);
45- THROW_HR_IF (E_NOT_VALID_STATE, itr == m_contexts.end ());
46- m_contexts.erase (itr);
47- }
48-
49- void StartAppShutdown ()
50- {
51- // Lifetime manager sends CTRL-C after the WM_QUERYENDSESSION is processed.
52- // If we disable the CTRL-C handler, the default handler will kill us.
53- TerminateContexts (CancelReason::AppShutdown, true );
54-
55- #ifndef AICLI_DISABLE_TEST_HOOKS
56- m_appShutdownEvent.SetEvent ();
57- #endif
58- }
59-
60- #ifndef AICLI_DISABLE_TEST_HOOKS
61- HWND GetWindowHandle () { return m_windowHandle.get (); }
62-
63- bool WaitForAppShutdownEvent ()
64- {
65- return m_appShutdownEvent.wait (60000 );
66- }
67- #endif
68-
69- private:
70- SignalTerminationHandler ()
71- {
72- if (Runtime::IsRunningAsAdmin () && Runtime::IsRunningInPackagedContext ())
73- {
74- m_catalog = winrt::Windows::ApplicationModel::PackageCatalog::OpenForCurrentPackage ();
75- m_updatingEvent = m_catalog.PackageUpdating (
76- winrt::auto_revoke, [this ](winrt::Windows::ApplicationModel::PackageCatalog, winrt::Windows::ApplicationModel::PackageUpdatingEventArgs args)
77- {
78- // There are 3 events being hit with 0%, 1% and 38%
79- // Typically the window message is received between the first two.
80- constexpr double minProgress = 0 ;
81- auto progress = args.Progress ();
82- if (progress > minProgress)
83- {
84- SignalTerminationHandler::Instance ().StartAppShutdown ();
85- }
86- });
87- }
88- else
89- {
90- // Create message only window.
91- m_messageQueueReady.create ();
92- m_windowThread = std::thread (&SignalTerminationHandler::CreateWindowAndStartMessageLoop, this );
93- if (!m_messageQueueReady.wait (100 ))
94- {
95- AICLI_LOG (CLI, Warning, << " Timeout creating winget window" );
96- }
97- }
98-
99- // Set up ctrl-c handler.
100- LOG_IF_WIN32_BOOL_FALSE (SetConsoleCtrlHandler (StaticCtrlHandlerFunction, TRUE ));
101-
102- #ifndef AICLI_DISABLE_TEST_HOOKS
103- m_appShutdownEvent.create ();
104- #endif
105- }
106-
107- ~SignalTerminationHandler ()
108- {
109- // At this point the thread is gone, but it will get angry
110- // if there's no call to join.
111- if (m_windowThread.joinable ())
112- {
113- m_windowThread.join ();
114- }
115- }
116-
117- static BOOL WINAPI StaticCtrlHandlerFunction (DWORD ctrlType)
118- {
119- return Instance ().CtrlHandlerFunction (ctrlType);
120- }
121-
122- static LRESULT WINAPI WindowMessageProcedure (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
123- {
124- AICLI_LOG (CLI, Verbose, << " Received window message type: " << uMsg);
125- switch (uMsg)
126- {
127- case WM_QUERYENDSESSION:
128- SignalTerminationHandler::Instance ().StartAppShutdown ();
129- return TRUE ;
130- case WM_ENDSESSION:
131- case WM_CLOSE:
132- DestroyWindow (hWnd);
133- break ;
134- case WM_DESTROY:
135- PostQuitMessage (0 );
136- break ;
137- default :
138- return DefWindowProc (hWnd, uMsg, wParam, lParam);
139- }
140- return FALSE ;
141- }
142-
143- BOOL CtrlHandlerFunction (DWORD ctrlType)
144- {
145- // TODO: Move this to be logged per active context when we have thread static globals
146- AICLI_LOG (CLI, Info, << " Got CTRL type: " << ctrlType);
147-
148- switch (ctrlType)
149- {
150- case CTRL_C_EVENT:
151- case CTRL_BREAK_EVENT:
152- return TerminateContexts (CancelReason::CtrlCSignal, false );
153- // According to MSDN, we should never receive these due to having gdi32/user32 loaded in our process.
154- // But handle them as a force terminate anyway.
155- case CTRL_CLOSE_EVENT:
156- case CTRL_LOGOFF_EVENT:
157- case CTRL_SHUTDOWN_EVENT:
158- return TerminateContexts (CancelReason::CtrlCSignal, true );
159- default :
160- return FALSE ;
161- }
162- }
163-
164- // Terminates the currently attached contexts.
165- // Returns FALSE if no contexts attached; TRUE otherwise.
166- BOOL TerminateContexts (CancelReason reason, bool force)
167- {
168- if (m_contexts.empty ())
169- {
170- return FALSE ;
171- }
172-
173- {
174- std::lock_guard<std::mutex> lock{ m_contextsLock };
175- for (auto & context : m_contexts)
176- {
177- context->Cancel (reason, force);
178- }
179- }
180-
181- return TRUE ;
182- }
183-
184- void CreateWindowAndStartMessageLoop ()
185- {
186- PCWSTR windowClass = L" wingetWindow" ;
187- HINSTANCE hInstance = GetModuleHandle (NULL );
188- if (hInstance == NULL )
189- {
190- LOG_LAST_ERROR_MSG (" Failed getting module handle" );
191- return ;
192- }
193-
194- WNDCLASSEX wcex = {};
195- wcex.cbSize = sizeof (wcex);
196-
197- wcex.style = CS_NOCLOSE;
198- wcex.lpfnWndProc = SignalTerminationHandler::WindowMessageProcedure;
199- wcex.cbClsExtra = 0 ;
200- wcex.cbWndExtra = 0 ;
201- wcex.hInstance = hInstance;
202- wcex.lpszClassName = windowClass;
203-
204- if (!RegisterClassEx (&wcex))
205- {
206- LOG_LAST_ERROR_MSG (" Failed registering window class" );
207- return ;
208- }
209-
210- m_windowHandle = wil::unique_hwnd (CreateWindow (
211- windowClass,
212- L" WingetMessageOnlyWindow" ,
213- WS_OVERLAPPEDWINDOW,
214- 0 , /* x */
215- 0 , /* y */
216- 0 , /* nWidth */
217- 0 , /* nHeight */
218- NULL , /* hWndParent */
219- NULL , /* hMenu */
220- hInstance,
221- NULL )); /* lpParam */
222-
223- if (m_windowHandle == nullptr )
224- {
225- LOG_LAST_ERROR_MSG (" Failed creating window" );
226- return ;
227- }
228-
229- ShowWindow (m_windowHandle.get (), SW_HIDE);
230-
231- // Force message queue to be created.
232- MSG msg;
233- PeekMessage (&msg, NULL , WM_USER, WM_USER, PM_NOREMOVE);
234- m_messageQueueReady.SetEvent ();
235-
236- // Message loop
237- BOOL getMessageResult;
238- while ((getMessageResult = GetMessage (&msg, m_windowHandle.get (), 0 , 0 )) != 0 )
239- {
240- if (getMessageResult == -1 )
241- {
242- LOG_LAST_ERROR ();
243- }
244- else
245- {
246- DispatchMessage (&msg);
247- }
248- }
249- }
250-
251- #ifndef AICLI_DISABLE_TEST_HOOKS
252- wil::unique_event m_appShutdownEvent;
253- #endif
254-
255- std::mutex m_contextsLock;
256- std::vector<Context*> m_contexts;
257- wil::unique_event m_messageQueueReady;
258- wil::unique_hwnd m_windowHandle;
259- std::thread m_windowThread;
260- winrt::Windows::ApplicationModel::PackageCatalog m_catalog = nullptr ;
261- decltype (winrt::Windows::ApplicationModel::PackageCatalog{ nullptr }.PackageUpdating(winrt::auto_revoke, nullptr )) m_updatingEvent;
262- };
263-
264- void SetSignalTerminationHandlerContext (bool add, Context* context)
265- {
266- THROW_HR_IF (E_POINTER, context == nullptr );
267-
268- if (add)
269- {
270- SignalTerminationHandler::Instance ().AddContext (context);
271- }
272- else
273- {
274- SignalTerminationHandler::Instance ().RemoveContext (context);
275- }
276- }
277-
27823 bool ShouldRemoveCheckpointDatabase (HRESULT hr)
27924 {
28025 switch (hr)
@@ -356,7 +101,7 @@ namespace AppInstaller::CLI::Execution
356101
357102 void Context::EnableSignalTerminationHandler (bool enabled)
358103 {
359- SetSignalTerminationHandlerContext (enabled, this );
104+ ShutdownMonitoring::TerminationSignalHandler::EnableListener (enabled, this );
360105 m_disableSignalTerminationHandlerOnExit = enabled;
361106 }
362107
@@ -487,16 +232,6 @@ namespace AppInstaller::CLI::Execution
487232 {
488233 return (m_shouldExecuteWorkflowTask ? m_shouldExecuteWorkflowTask (task) : true );
489234 }
490-
491- HWND GetWindowHandle ()
492- {
493- return SignalTerminationHandler::Instance ().GetWindowHandle ();
494- }
495-
496- bool WaitForAppShutdownEvent ()
497- {
498- return SignalTerminationHandler::Instance ().WaitForAppShutdownEvent ();
499- }
500235#endif
501236
502237 void ContextEnumBasedVariantMapActionCallback (const void * map, Data data, EnumBasedVariantMapAction action)
0 commit comments