Use RoInitialize/RoUninitialize for Windows.Gaming.Input

Thanks @walbourn!

Fixes https://github.com/libsdl-org/SDL/issues/5270
This commit is contained in:
Sam Lantinga 2022-04-01 14:58:33 -07:00
parent 1c9299b00d
commit 8ebef12d31
4 changed files with 68 additions and 78 deletions

View file

@ -26,6 +26,7 @@
#include "SDL_error.h" #include "SDL_error.h"
#include <objbase.h> /* for CoInitialize/CoUninitialize (Win32 only) */ #include <objbase.h> /* for CoInitialize/CoUninitialize (Win32 only) */
#include <roapi.h> /* For RoInitialize/RoUninitialize (Win32 only) */
#ifndef _WIN32_WINNT_VISTA #ifndef _WIN32_WINNT_VISTA
#define _WIN32_WINNT_VISTA 0x0600 #define _WIN32_WINNT_VISTA 0x0600
@ -104,51 +105,52 @@ void
WIN_CoUninitialize(void) WIN_CoUninitialize(void)
{ {
#ifndef __WINRT__ #ifndef __WINRT__
/* Don't uninitialize COM because of what appears to be a bug in Microsoft WGI reference counting. CoUninitialize();
* #endif
* If you plug in a non-Xbox controller and let the application run for 30 seconds, then it crashes in CoUninitialize() }
* with this stack trace:
Windows.Gaming.Input.dll!GameController::~GameController(void) Unknown void *
Windows.Gaming.Input.dll!GameController::`vector deleting destructor'(unsigned int) Unknown WIN_LoadComBaseFunction(const char *name)
Windows.Gaming.Input.dll!Microsoft::WRL::Details::RuntimeClassImpl<struct Microsoft::WRL::RuntimeClassFlags<1>,1,1,0,struct Windows::Gaming::Input::IGameController,struct Windows::Gaming::Input::IGameControllerBatteryInfo,struct Microsoft::WRL::CloakedIid<struct Windows::Gaming::Input::Internal::IGameControllerPrivate>,class Microsoft::WRL::FtmBase>::Release(void) Unknown {
Windows.Gaming.Input.dll!Windows::Gaming::Input::Custom::Details::AggregableRuntimeClass<struct Windows::Gaming::Input::IGamepad,struct Windows::Gaming::Input::IGamepad2,struct Microsoft::WRL::CloakedIid<struct Windows::Gaming::Input::Custom::IGameControllerInputSink>,struct Microsoft::WRL::CloakedIid<struct Windows::Gaming::Input::Custom::IGipGameControllerInputSink>,struct Microsoft::WRL::CloakedIid<struct Windows::Gaming::Input::Custom::IHidGameControllerInputSink>,struct Microsoft::WRL::CloakedIid<struct Windows::Gaming::Input::Custom::IXusbGameControllerInputSink>,class Microsoft::WRL::Details::Nil,class Microsoft::WRL::Details::Nil,class Microsoft::WRL::Details::Nil>::Release(void) Unknown static SDL_bool s_bLoaded;
Windows.Gaming.Input.dll!Microsoft::WRL::ComPtr<`WaitForCompletion<Windows::Foundation::IAsyncOperationWithProgressCompletedHandler<Windows::Storage::Streams::IBuffer *,unsigned int>,Windows::Foundation::IAsyncOperationWithProgress<Windows::Storage::Streams::IBuffer *,unsigned int>>'::`2'::FTMEventDelegate>::~ComPtr<`WaitForCompletion<Windows::Foundation::IAsyncOperationWithProgressCompletedHandler<Windows::Storage::Streams::IBuffer *,unsigned int>,Windows::Foundation::IAsyncOperationWithProgress<Windows::Storage::Streams::IBuffer *,unsigned int>>'::`2'::FTMEventDelegate>() Unknown static HMODULE s_hComBase;
Windows.Gaming.Input.dll!`eh vector destructor iterator'(void *,unsigned int,int,void (*)(void *)) Unknown
Windows.Gaming.Input.dll!Windows::Gaming::Input::Custom::Details::GameControllerCollection<class Windows::Gaming::Input::RawGameController,struct Windows::Gaming::Input::IRawGameController>::~GameControllerCollection<class Windows::Gaming::Input::RawGameController,struct Windows::Gaming::Input::IRawGameController>(void) Unknown if (!s_bLoaded) {
Windows.Gaming.Input.dll!Windows::Gaming::Input::Custom::Details::GameControllerCollection<class Windows::Gaming::Input::RawGameController,struct Windows::Gaming::Input::IRawGameController>::`vector deleting destructor'(unsigned int) Unknown s_hComBase = LoadLibraryEx(TEXT("combase.dll"), NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
Windows.Gaming.Input.dll!Microsoft::WRL::Details::RuntimeClassImpl<struct Microsoft::WRL::RuntimeClassFlags<1>,1,1,0,struct Windows::Foundation::Collections::IIterable<class Windows::Gaming::Input::ArcadeStick *>,struct Windows::Foundation::Collections::IVectorView<class Windows::Gaming::Input::ArcadeStick *>,class Microsoft::WRL::FtmBase>::Release(void) Unknown s_bLoaded = SDL_TRUE;
Windows.Gaming.Input.dll!Windows::Gaming::Input::Custom::Details::CustomGameControllerFactoryBase<class Windows::Gaming::Input::FlightStick,class Windows::Gaming::Input::FlightStick,struct Windows::Gaming::Input::IFlightStick,struct Windows::Gaming::Input::IFlightStickStatics,class Microsoft::WRL::Details::Nil>::~CustomGameControllerFactoryBase<class Windows::Gaming::Input::FlightStick,class Windows::Gaming::Input::FlightStick,struct Windows::Gaming::Input::IFlightStick,struct Windows::Gaming::Input::IFlightStickStatics,class Microsoft::WRL::Details::Nil>(void) Unknown }
Windows.Gaming.Input.dll!Windows::Gaming::Input::Custom::Details::CustomGameControllerFactoryBase<class Windows::Gaming::Input::FlightStick,class Windows::Gaming::Input::FlightStick,struct Windows::Gaming::Input::IFlightStick,struct Windows::Gaming::Input::IFlightStickStatics,class Microsoft::WRL::Details::Nil>::`vector deleting destructor'(unsigned int) Unknown if (s_hComBase) {
Windows.Gaming.Input.dll!Microsoft::WRL::ActivationFactory<struct Microsoft::WRL::Implements<class Microsoft::WRL::FtmBase,struct Microsoft::WRL::CloakedIid<struct Windows::Gaming::Input::Custom::ICustomGameControllerFactory> >,struct Windows::Gaming::Input::IFlightStickStatics,class Microsoft::WRL::Details::Nil,0>::Release(void) Unknown return GetProcAddress(s_hComBase, name);
Windows.Gaming.Input.dll!Microsoft::WRL::ComPtr<`WaitForCompletion<Windows::Foundation::IAsyncOperationWithProgressCompletedHandler<Windows::Storage::Streams::IBuffer *,unsigned int>,Windows::Foundation::IAsyncOperationWithProgress<Windows::Storage::Streams::IBuffer *,unsigned int>>'::`2'::FTMEventDelegate>::~ComPtr<`WaitForCompletion<Windows::Foundation::IAsyncOperationWithProgressCompletedHandler<Windows::Storage::Streams::IBuffer *,unsigned int>,Windows::Foundation::IAsyncOperationWithProgress<Windows::Storage::Streams::IBuffer *,unsigned int>>'::`2'::FTMEventDelegate>() Unknown } else {
Windows.Gaming.Input.dll!NtList<struct FactoryManager::FactoryListEntry>::~NtList<struct FactoryManager::FactoryListEntry>(void) Unknown return NULL;
Windows.Gaming.Input.dll!FactoryManager::`vector deleting destructor'(unsigned int) Unknown }
Windows.Gaming.Input.dll!Microsoft::WRL::ActivationFactory<struct Microsoft::WRL::Implements<class Microsoft::WRL::FtmBase,struct Windows::Gaming::Input::Custom::IGameControllerFactoryManagerStatics>,struct Windows::Gaming::Input::Custom::IGameControllerFactoryManagerStatics2,struct Microsoft::WRL::CloakedIid<struct Windows::Gaming::Input::Internal::IGameControllerFactoryManagerStaticsPrivate>,0>::Release(void) Unknown }
Windows.Gaming.Input.dll!Microsoft::WRL::Details::TerminateMap(class Microsoft::WRL::Details::ModuleBase *,unsigned short const *,bool) Unknown
Windows.Gaming.Input.dll!Microsoft::WRL::Module<1,class Microsoft::WRL::Details::DefaultModule<1> >::~Module<1,class Microsoft::WRL::Details::DefaultModule<1> >(void) Unknown HRESULT
Windows.Gaming.Input.dll!Microsoft::WRL::Details::DefaultModule<1>::`vector deleting destructor'(unsigned int) Unknown WIN_RoInitialize(void)
Windows.Gaming.Input.dll!`dynamic atexit destructor for 'Microsoft::WRL::Details::StaticStorage<Microsoft::WRL::Details::DefaultModule<1>,0,int>::instance_''() Unknown {
Windows.Gaming.Input.dll!__CRT_INIT@12() Unknown #ifdef __WINRT__
Windows.Gaming.Input.dll!__DllMainCRTStartup() Unknown return S_OK;
ntdll.dll!_LdrxCallInitRoutine@16() Unknown #else
ntdll.dll!LdrpCallInitRoutine() Unknown typedef HRESULT (*RoInitialize_t)(RO_INIT_TYPE initType);
ntdll.dll!LdrpProcessDetachNode() Unknown RoInitialize_t RoInitializeFunc = (RoInitialize_t)WIN_LoadComBaseFunction("RoInitialize");
ntdll.dll!LdrpUnloadNode() Unknown if (RoInitializeFunc) {
ntdll.dll!LdrpDecrementModuleLoadCountEx() Unknown return RoInitializeFunc(RO_INIT_MULTITHREADED);
ntdll.dll!LdrUnloadDll() Unknown } else {
KernelBase.dll!FreeLibrary() Unknown return E_NOINTERFACE;
combase.dll!FreeLibraryWithLogging(LoadOrFreeWhy why, HINSTANCE__ * hMod, const wchar_t * pswzOptionalFileName) Line 193 C++ }
combase.dll!CClassCache::CDllPathEntry::CFinishObject::Finish() Line 3311 C++ #endif
combase.dll!CClassCache::CFinishComposite::Finish() Line 3421 C++ }
combase.dll!CClassCache::CleanUpDllsForProcess() Line 7009 C++
[Inline Frame] combase.dll!CCCleanUpDllsForProcess() Line 8773 C++ void
combase.dll!ProcessUninitialize() Line 2243 C++ WIN_RoUninitialize(void)
combase.dll!DecrementProcessInitializeCount() Line 993 C++ {
combase.dll!wCoUninitialize(COleTls & Tls, int fHostThread) Line 4126 C++ #ifndef __WINRT__
combase.dll!CoUninitialize() Line 3945 C++ typedef void (*RoUninitialize_t)(void);
*/ RoUninitialize_t RoUninitializeFunc = (RoUninitialize_t)WIN_LoadComBaseFunction("RoUninitialize");
/*CoUninitialize();*/ if (RoUninitializeFunc) {
RoUninitializeFunc();
}
#endif #endif
} }

View file

@ -63,10 +63,17 @@ extern int WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr);
/* Sets an error message based on GetLastError(). Always return -1. */ /* Sets an error message based on GetLastError(). Always return -1. */
extern int WIN_SetError(const char *prefix); extern int WIN_SetError(const char *prefix);
/* Load a function from combase.dll */
void *WIN_LoadComBaseFunction(const char *name);
/* Wrap up the oddities of CoInitialize() into a common function. */ /* Wrap up the oddities of CoInitialize() into a common function. */
extern HRESULT WIN_CoInitialize(void); extern HRESULT WIN_CoInitialize(void);
extern void WIN_CoUninitialize(void); extern void WIN_CoUninitialize(void);
/* Wrap up the oddities of RoInitialize() into a common function. */
extern HRESULT WIN_RoInitialize(void);
extern void WIN_RoUninitialize(void);
/* Returns SDL_TRUE if we're running on Windows Vista and newer */ /* Returns SDL_TRUE if we're running on Windows Vista and newer */
extern BOOL WIN_IsWindowsVistaOrGreater(void); extern BOOL WIN_IsWindowsVistaOrGreater(void);

View file

@ -565,22 +565,19 @@ RAWINPUT_InitWindowsGamingInput(RAWINPUT_DeviceContext *ctx)
if (!wgi_state.initialized) { if (!wgi_state.initialized) {
static const IID SDL_IID_IGamepadStatics = { 0x8BBCE529, 0xD49C, 0x39E9, { 0x95, 0x60, 0xE4, 0x7D, 0xDE, 0x96, 0xB7, 0xC8 } }; static const IID SDL_IID_IGamepadStatics = { 0x8BBCE529, 0xD49C, 0x39E9, { 0x95, 0x60, 0xE4, 0x7D, 0xDE, 0x96, 0xB7, 0xC8 } };
HRESULT hr; HRESULT hr;
HMODULE hModule;
/* I think this takes care of RoInitialize() in a way that is compatible with the rest of SDL */ if (FAILED(WIN_RoInitialize())) {
if (FAILED(WIN_CoInitialize())) {
return; return;
} }
wgi_state.initialized = SDL_TRUE; wgi_state.initialized = SDL_TRUE;
wgi_state.dirty = SDL_TRUE; wgi_state.dirty = SDL_TRUE;
hModule = LoadLibraryA("combase.dll"); {
if (hModule != NULL) {
typedef HRESULT (WINAPI *WindowsCreateStringReference_t)(PCWSTR sourceString, UINT32 length, HSTRING_HEADER *hstringHeader, HSTRING* string); typedef HRESULT (WINAPI *WindowsCreateStringReference_t)(PCWSTR sourceString, UINT32 length, HSTRING_HEADER *hstringHeader, HSTRING* string);
typedef HRESULT (WINAPI *RoGetActivationFactory_t)(HSTRING activatableClassId, REFIID iid, void** factory); typedef HRESULT (WINAPI *RoGetActivationFactory_t)(HSTRING activatableClassId, REFIID iid, void** factory);
WindowsCreateStringReference_t WindowsCreateStringReferenceFunc = (WindowsCreateStringReference_t)GetProcAddress(hModule, "WindowsCreateStringReference"); WindowsCreateStringReference_t WindowsCreateStringReferenceFunc = (WindowsCreateStringReference_t)WIN_LoadComBaseFunction("WindowsCreateStringReference");
RoGetActivationFactory_t RoGetActivationFactoryFunc = (RoGetActivationFactory_t)GetProcAddress(hModule, "RoGetActivationFactory"); RoGetActivationFactory_t RoGetActivationFactoryFunc = (RoGetActivationFactory_t)WIN_LoadComBaseFunction("RoGetActivationFactory");
if (WindowsCreateStringReferenceFunc && RoGetActivationFactoryFunc) { if (WindowsCreateStringReferenceFunc && RoGetActivationFactoryFunc) {
PCWSTR pNamespace = L"Windows.Gaming.Input.Gamepad"; PCWSTR pNamespace = L"Windows.Gaming.Input.Gamepad";
HSTRING_HEADER hNamespaceStringHeader; HSTRING_HEADER hNamespaceStringHeader;
@ -591,7 +588,6 @@ RAWINPUT_InitWindowsGamingInput(RAWINPUT_DeviceContext *ctx)
RoGetActivationFactoryFunc(hNamespaceString, &SDL_IID_IGamepadStatics, (void **)&wgi_state.gamepad_statics); RoGetActivationFactoryFunc(hNamespaceString, &SDL_IID_IGamepadStatics, (void **)&wgi_state.gamepad_statics);
} }
} }
FreeLibrary(hModule);
} }
} }
} }
@ -657,7 +653,7 @@ RAWINPUT_QuitWindowsGamingInput(RAWINPUT_DeviceContext *ctx)
__x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_Release(wgi_state.gamepad_statics); __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_Release(wgi_state.gamepad_statics);
wgi_state.gamepad_statics = NULL; wgi_state.gamepad_statics = NULL;
} }
WIN_CoUninitialize(); WIN_RoUninitialize();
wgi_state.initialized = SDL_FALSE; wgi_state.initialized = SDL_FALSE;
} }
} }

View file

@ -260,10 +260,9 @@ static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_InvokeAdde
WindowsGetStringRawBufferFunc = WindowsGetStringRawBuffer; WindowsGetStringRawBufferFunc = WindowsGetStringRawBuffer;
WindowsDeleteStringFunc = WindowsDeleteString; WindowsDeleteStringFunc = WindowsDeleteString;
#else #else
HMODULE hModule = LoadLibraryA("combase.dll"); {
if (hModule != NULL) { WindowsGetStringRawBufferFunc = (WindowsGetStringRawBuffer_t)WIN_LoadComBaseFunction("WindowsGetStringRawBuffer");
WindowsGetStringRawBufferFunc = (WindowsGetStringRawBuffer_t)GetProcAddress(hModule, "WindowsGetStringRawBuffer"); WindowsDeleteStringFunc = (WindowsDeleteString_t)WIN_LoadComBaseFunction("WindowsDeleteString");
WindowsDeleteStringFunc = (WindowsDeleteString_t)GetProcAddress(hModule, "WindowsDeleteString");
} }
#endif /* __WINRT__ */ #endif /* __WINRT__ */
if (WindowsGetStringRawBufferFunc && WindowsDeleteStringFunc) { if (WindowsGetStringRawBufferFunc && WindowsDeleteStringFunc) {
@ -277,11 +276,6 @@ static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_InvokeAdde
WindowsDeleteStringFunc(hString); WindowsDeleteStringFunc(hString);
} }
} }
#ifndef __WINRT__
if (hModule != NULL) {
FreeLibrary(hModule);
}
#endif
__x_ABI_CWindows_CGaming_CInput_CIRawGameController2_Release(controller2); __x_ABI_CWindows_CGaming_CInput_CIRawGameController2_Release(controller2);
} }
if (!name) { if (!name) {
@ -444,23 +438,19 @@ WGI_JoystickInit(void)
WindowsCreateStringReference_t WindowsCreateStringReferenceFunc = NULL; WindowsCreateStringReference_t WindowsCreateStringReferenceFunc = NULL;
RoGetActivationFactory_t RoGetActivationFactoryFunc = NULL; RoGetActivationFactory_t RoGetActivationFactoryFunc = NULL;
#ifndef __WINRT__
HMODULE hModule;
#endif
HRESULT hr; HRESULT hr;
if (FAILED(WIN_CoInitialize())) { if (FAILED(WIN_RoInitialize())) {
return SDL_SetError("CoInitialize() failed"); return SDL_SetError("RoInitialize() failed");
} }
#ifdef __WINRT__ #ifdef __WINRT__
WindowsCreateStringReferenceFunc = WindowsCreateStringReference; WindowsCreateStringReferenceFunc = WindowsCreateStringReference;
RoGetActivationFactoryFunc = RoGetActivationFactory; RoGetActivationFactoryFunc = RoGetActivationFactory;
#else #else
hModule = LoadLibraryA("combase.dll"); {
if (hModule != NULL) { WindowsCreateStringReferenceFunc = (WindowsCreateStringReference_t)WIN_LoadComBaseFunction("WindowsCreateStringReference");
WindowsCreateStringReferenceFunc = (WindowsCreateStringReference_t)GetProcAddress(hModule, "WindowsCreateStringReference"); RoGetActivationFactoryFunc = (RoGetActivationFactory_t)WIN_LoadComBaseFunction("RoGetActivationFactory");
RoGetActivationFactoryFunc = (RoGetActivationFactory_t)GetProcAddress(hModule, "RoGetActivationFactory");
} }
#endif /* __WINRT__ */ #endif /* __WINRT__ */
if (WindowsCreateStringReferenceFunc && RoGetActivationFactoryFunc) { if (WindowsCreateStringReferenceFunc && RoGetActivationFactoryFunc) {
@ -519,11 +509,6 @@ WGI_JoystickInit(void)
} }
} }
} }
#ifndef __WINRT__
if (hModule != NULL) {
FreeLibrary(hModule);
}
#endif
if (wgi.statics) { if (wgi.statics) {
__FIVectorView_1_Windows__CGaming__CInput__CRawGameController *controllers; __FIVectorView_1_Windows__CGaming__CInput__CRawGameController *controllers;
@ -865,7 +850,7 @@ WGI_JoystickQuit(void)
} }
SDL_zero(wgi); SDL_zero(wgi);
WIN_CoUninitialize(); WIN_RoUninitialize();
} }
static SDL_bool static SDL_bool