Skip to content

Commit 3a98698

Browse files
committed
Fix Win11 TAP teardown ordering
1 parent 9f572bb commit 3a98698

1 file changed

Lines changed: 105 additions & 54 deletions

File tree

‎Src/StartMenu/StartMenuDLL/Win11StartButton.cpp‎

Lines changed: 105 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ static const GUID CLSID_OpenShellStartButtonTap =
2222
{ 0x7d15741f, 0x2f3b, 0x4971, { 0xb8, 0x91, 0x6a, 0x5d, 0x42, 0xd7, 0x1a, 0x34 } };
2323

2424
static const UINT WM_OS_STARTBUTTON_APPLY = WM_APP + 0x35B;
25-
static const UINT WM_OS_STARTBUTTON_DESTROY = WM_APP + 0x35C;
25+
static const UINT WM_OS_STARTBUTTON_SHUTDOWN = WM_APP + 0x35C;
2626

2727
static volatile LONG g_StartButtonActive = 0;
2828
static volatile LONG g_ConnectStarted = 0;
@@ -175,47 +175,72 @@ class CWin11StartButtonTap: public IObjectWithSite, public IVisualTreeServiceCal
175175

176176
m_Site = site;
177177

178-
// InitializeXamlDiagnosticsEx loads the TAP DLL by path even though
179-
// StartMenuDLL is already loaded. Balance that injection reference so
180-
// Open-Shell can unload and initialize the DLL again after an explicit Exit.
181-
if (!m_InjectionReferenceBalanced)
182-
{
183-
HMODULE module = GetThisModule();
184-
if (module)
185-
{
186-
FreeLibrary(module);
187-
m_InjectionReferenceBalanced = true;
188-
}
189-
}
190-
178+
// Keep the module reference acquired by InitializeXamlDiagnosticsEx for
179+
// the whole TAP session. It is balanced only after the callback and all
180+
// XAML/COM references have been released. StartMenuDLL has its own module
181+
// reference while Open-Shell is active, so balancing the diagnostics
182+
// reference during teardown cannot unload code that is still executing.
183+
//
191184
// Exit may race a connection attempt. Do not attach a new callback after
192185
// shutdown has already begun.
193186
if (!InterlockedCompareExchange(&g_StartButtonActive, 0, 0))
194187
{
195188
m_Site.Release();
189+
BalanceInjectionReference();
196190
InterlockedExchange(&g_ConnectStarted, 0);
197191
return S_OK;
198192
}
199193

200194
HRESULT hr = site->QueryInterface(__uuidof(IVisualTreeService), (void**)&m_Visual);
201195
if (FAILED(hr) || !m_Visual)
196+
{
197+
m_Visual.Release();
198+
m_Site.Release();
199+
BalanceInjectionReference();
200+
InterlockedExchange(&g_ConnectStarted, 0);
202201
return hr;
202+
}
203203

204204
if (!CreateDispatchWindow())
205-
return HRESULT_FROM_WIN32(GetLastError());
206-
207-
AcquireSRWLockExclusive(&g_TapLock);
208-
g_Tap = this;
209-
ReleaseSRWLockExclusive(&g_TapLock);
205+
{
206+
DWORD error = GetLastError();
207+
m_Visual.Release();
208+
m_Site.Release();
209+
BalanceInjectionReference();
210+
InterlockedExchange(&g_ConnectStarted, 0);
211+
return HRESULT_FROM_WIN32(error);
212+
}
210213

211214
// Advise replays the existing tree. OnVisualTreeChange only records
212215
// element handles; all property access is dispatched afterwards.
213216
hr = m_Visual->AdviseVisualTreeChange(static_cast<IVisualTreeServiceCallback*>(this));
214217
if (SUCCEEDED(hr))
215218
{
216219
m_Advised = true;
220+
221+
// Publish the TAP only after the subscription is fully established.
222+
// Otherwise an Advise failure can leave g_Tap pointing at an object
223+
// that the XAML runtime is about to release.
224+
AcquireSRWLockExclusive(&g_TapLock);
225+
g_Tap = this;
226+
ReleaseSRWLockExclusive(&g_TapLock);
227+
217228
RequestApply(false);
218229
}
230+
else
231+
{
232+
if (m_Dispatch)
233+
{
234+
HWND dispatch = m_Dispatch;
235+
m_Dispatch = NULL;
236+
SetWindowLongPtr(dispatch, GWLP_USERDATA, 0);
237+
DestroyWindow(dispatch);
238+
}
239+
m_Visual.Release();
240+
m_Site.Release();
241+
BalanceInjectionReference();
242+
InterlockedExchange(&g_ConnectStarted, 0);
243+
}
219244
LogToFile(STARTUP_LOG, L"Win11StartButton: visual tree advise 0x%08X", hr);
220245
return hr;
221246
}
@@ -295,41 +320,26 @@ class CWin11StartButtonTap: public IObjectWithSite, public IVisualTreeServiceCal
295320
PostMessage(m_Dispatch, WM_OS_STARTBUTTON_APPLY, 0, 0);
296321
}
297322

298-
void Shutdown( void )
323+
HRESULT Shutdown( void )
299324
{
300-
// Prevent new users of the TAP while teardown is in progress. The caller
301-
// holds a reference, so UnadviseVisualTreeChange cannot destroy this object
302-
// out from under the shutdown sequence.
325+
// SetSite creates the dispatch window on the XAML thread. Run the complete
326+
// XAML/COM teardown on that same thread rather than releasing apartment-
327+
// sensitive interfaces from whichever thread initiated Open-Shell Exit.
328+
if (!m_Dispatch)
329+
return E_UNEXPECTED;
330+
331+
HRESULT hr = (HRESULT)SendMessage(m_Dispatch, WM_OS_STARTBUTTON_SHUTDOWN, 0, 0);
332+
if (FAILED(hr))
333+
return hr;
334+
303335
AcquireSRWLockExclusive(&g_TapLock);
304336
if (g_Tap == this)
305337
g_Tap = NULL;
306338
ReleaseSRWLockExclusive(&g_TapLock);
307339

308-
// Restore the native XAML state before detaching the callback.
309-
RequestApply(true);
310-
311-
if (m_Visual && m_Advised)
312-
{
313-
HRESULT hr = m_Visual->UnadviseVisualTreeChange(static_cast<IVisualTreeServiceCallback*>(this));
314-
if (FAILED(hr))
315-
{
316-
// Keep the DLL resident rather than leave XAML with a callback into
317-
// unloaded code. Explorer restart remains the safe recovery path.
318-
HMODULE module = NULL;
319-
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
320-
(LPCTSTR)&GetThisModule, &module);
321-
LogToFile(STARTUP_LOG, L"Win11StartButton: visual tree unadvise failed 0x%08X", hr);
322-
return;
323-
}
324-
m_Advised = false;
325-
}
326-
327-
m_Visual.Release();
328-
m_Site.Release();
329340
InterlockedExchange(&g_ConnectStarted, 0);
330-
331-
if (m_Dispatch)
332-
SendMessage(m_Dispatch, WM_OS_STARTBUTTON_DESTROY, 0, 0);
341+
BalanceInjectionReference();
342+
return S_OK;
333343
}
334344

335345
private:
@@ -349,14 +359,53 @@ class CWin11StartButtonTap: public IObjectWithSite, public IVisualTreeServiceCal
349359
tap->ApplyState(enabled);
350360
return 0;
351361
}
352-
if (msg == WM_OS_STARTBUTTON_DESTROY && tap)
362+
if (msg == WM_OS_STARTBUTTON_SHUTDOWN && tap)
363+
return tap->ShutdownOnDispatchThread();
364+
return DefWindowProc(hwnd, msg, wParam, lParam);
365+
}
366+
367+
HRESULT ShutdownOnDispatchThread( void )
368+
{
369+
// The window is owned by the same thread on which SetSite ran. Keep all
370+
// operations touching the XAML diagnostics interfaces on this thread.
371+
ApplyState(false);
372+
373+
if (m_Visual && m_Advised)
353374
{
354-
tap->m_Dispatch = NULL;
355-
SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
356-
DestroyWindow(hwnd);
357-
return 0;
375+
HRESULT hr = m_Visual->UnadviseVisualTreeChange(static_cast<IVisualTreeServiceCallback*>(this));
376+
if (FAILED(hr))
377+
{
378+
LogToFile(STARTUP_LOG, L"Win11StartButton: visual tree unadvise failed 0x%08X", hr);
379+
return hr;
380+
}
381+
m_Advised = false;
382+
}
383+
384+
m_Visual.Release();
385+
m_Site.Release();
386+
387+
if (m_Dispatch)
388+
{
389+
HWND dispatch = m_Dispatch;
390+
m_Dispatch = NULL;
391+
SetWindowLongPtr(dispatch, GWLP_USERDATA, 0);
392+
DestroyWindow(dispatch);
393+
}
394+
return S_OK;
395+
}
396+
397+
void BalanceInjectionReference( void )
398+
{
399+
if (m_InjectionReferenceBalanced)
400+
return;
401+
402+
HMODULE module = GetThisModule();
403+
if (module)
404+
{
405+
// Mark first: FreeLibrary can run loader callbacks before it returns.
406+
m_InjectionReferenceBalanced = true;
407+
FreeLibrary(module);
358408
}
359-
return DefWindowProc(hwnd, msg, wParam, lParam);
360409
}
361410

362411
bool CreateDispatchWindow( void )
@@ -906,7 +955,9 @@ void StopWin11StartButtonMonitor( void )
906955
CWin11StartButtonTap *tap = GetTapRef();
907956
if (tap)
908957
{
909-
tap->Shutdown();
958+
HRESULT hr = tap->Shutdown();
959+
if (FAILED(hr))
960+
LogToFile(STARTUP_LOG, L"Win11StartButton: TAP shutdown failed 0x%08X", hr);
910961
tap->Release();
911962
}
912963
}

0 commit comments

Comments
 (0)