From 88c8c142187d3baf27e0dee490d0aedcf0f553b7 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Mon, 18 Apr 2022 21:19:25 -0500 Subject: [PATCH 1/3] windows: Fix RoInitialize() failure after a CoInitializeEx() call using apartment threading This mirrors the same codepath in WIN_CoInitialize() which handles STA and MTA. --- src/core/windows/SDL_windows.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/core/windows/SDL_windows.c b/src/core/windows/SDL_windows.c index 5cf0ad90d3a74..50e128fcc88f2 100644 --- a/src/core/windows/SDL_windows.c +++ b/src/core/windows/SDL_windows.c @@ -148,7 +148,19 @@ WIN_RoInitialize(void) typedef HRESULT (WINAPI *RoInitialize_t)(RO_INIT_TYPE initType); RoInitialize_t RoInitializeFunc = (RoInitialize_t)WIN_LoadComBaseFunction("RoInitialize"); if (RoInitializeFunc) { - return RoInitializeFunc(RO_INIT_MULTITHREADED); + /* RO_INIT_SINGLETHREADED is equivalent to COINIT_APARTMENTTHREADED */ + HRESULT hr = RoInitializeFunc(RO_INIT_SINGLETHREADED); + if (hr == RPC_E_CHANGED_MODE) { + hr = RoInitializeFunc(RO_INIT_MULTITHREADED); + } + + /* S_FALSE means success, but someone else already initialized. */ + /* You still need to call RoUninitialize in this case! */ + if (hr == S_FALSE) { + return S_OK; + } + + return hr; } else { return E_NOINTERFACE; } From 704bd4d640dfa32f7666e92b19431e0ec91a5566 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Wed, 20 Apr 2022 20:58:29 -0500 Subject: [PATCH 2/3] WGI: Keep a reference to the MTA to avoid crashing on COM teardown Fixes #5552 Fixes #5270 --- .../windows/SDL_windows_gaming_input.c | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/joystick/windows/SDL_windows_gaming_input.c b/src/joystick/windows/SDL_windows_gaming_input.c index 5300cfa5bc02c..39fadeab33467 100644 --- a/src/joystick/windows/SDL_windows_gaming_input.c +++ b/src/joystick/windows/SDL_windows_gaming_input.c @@ -444,6 +444,29 @@ WGI_JoystickInit(void) return SDL_SetError("RoInitialize() failed"); } +#ifndef __WINRT__ + { + /* There seems to be a bug in Windows where a dependency of WGI can be unloaded from memory prior to WGI itself. + * This results in Windows_Gaming_Input!GameController::~GameController() invoking an unloaded DLL and crashing. + * As a workaround, we will keep a reference to the MTA to prevent COM from unloading DLLs later. + * See https://github.com/libsdl-org/SDL/issues/5552 for more details. + */ + static PVOID cookie = NULL; + if (!cookie) { + typedef HRESULT (WINAPI *CoIncrementMTAUsage_t)(PVOID* pCookie); + CoIncrementMTAUsage_t CoIncrementMTAUsageFunc = (CoIncrementMTAUsage_t)WIN_LoadComBaseFunction("CoIncrementMTAUsage"); + if (CoIncrementMTAUsageFunc) { + if (FAILED(CoIncrementMTAUsageFunc(&cookie))) { + return SDL_SetError("CoIncrementMTAUsage() failed"); + } + } else { + /* CoIncrementMTAUsage() is present since Win8, so we should never make it here. */ + return SDL_SetError("CoIncrementMTAUsage() not found"); + } + } + } +#endif + #ifdef __WINRT__ WindowsCreateStringReferenceFunc = WindowsCreateStringReference; RoGetActivationFactoryFunc = RoGetActivationFactory; From 732595386146ff5c1246dc45448ec3c78cf3ca50 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 21 Apr 2022 01:38:53 -0500 Subject: [PATCH 3/3] WGI: Only call RoUninitialize() if RoInitialize() succeeded --- src/joystick/windows/SDL_windows_gaming_input.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/joystick/windows/SDL_windows_gaming_input.c b/src/joystick/windows/SDL_windows_gaming_input.c index 39fadeab33467..6c5ec552868ed 100644 --- a/src/joystick/windows/SDL_windows_gaming_input.c +++ b/src/joystick/windows/SDL_windows_gaming_input.c @@ -68,6 +68,7 @@ static struct { EventRegistrationToken controller_added_token; EventRegistrationToken controller_removed_token; int controller_count; + SDL_bool ro_initialized; WindowsGamingInputControllerState *controllers; } wgi; @@ -443,6 +444,7 @@ WGI_JoystickInit(void) if (FAILED(WIN_RoInitialize())) { return SDL_SetError("RoInitialize() failed"); } + wgi.ro_initialized = SDL_TRUE; #ifndef __WINRT__ { @@ -871,9 +873,12 @@ WGI_JoystickQuit(void) __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_remove_RawGameControllerRemoved(wgi.statics, wgi.controller_removed_token); __x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics_Release(wgi.statics); } - SDL_zero(wgi); - WIN_RoUninitialize(); + if (wgi.ro_initialized) { + WIN_RoUninitialize(); + } + + SDL_zero(wgi); } static SDL_bool