SDL: Fixed crash if a broken EZFRD64.DLL is installed

From a813b86c859fa75102988a3cbd0e072c8eaf6e91 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Tue, 15 Sep 2026 16:39:54 -0700
Subject: [PATCH] Fixed crash if a broken EZFRD64.DLL is installed

It turns out GameInput crashes the same way as DirectInput
---
 src/core/windows/SDL_gameinput.cpp        |  4 +++
 src/core/windows/SDL_windows.c            | 31 +++++++++++++++++++++++
 src/core/windows/SDL_windows.h            |  3 +++
 src/joystick/windows/SDL_dinputjoystick.c | 26 +------------------
 4 files changed, 39 insertions(+), 25 deletions(-)

diff --git a/src/core/windows/SDL_gameinput.cpp b/src/core/windows/SDL_gameinput.cpp
index baf7ed2165433..fb3b942d3206d 100644
--- a/src/core/windows/SDL_gameinput.cpp
+++ b/src/core/windows/SDL_gameinput.cpp
@@ -38,6 +38,10 @@ bool SDL_InitGameInput(IGameInput **ppGameInput)
             return false;
         }
 
+        if (WIN_HasBrokenEZFRD64DLL()) {
+            return SDL_SetError("GameInput disabled to prevent application crashing");
+        }
+
         typedef HRESULT (WINAPI *pfnGameInputCreate)(IGameInput **gameInput);
         pfnGameInputCreate pGameInputCreate = (pfnGameInputCreate)SDL_LoadFunction(g_hGameInputDLL, "GameInputCreate");
         if (!pGameInputCreate) {
diff --git a/src/core/windows/SDL_windows.c b/src/core/windows/SDL_windows.c
index b8b27e5ced878..fa82be15775e8 100644
--- a/src/core/windows/SDL_windows.c
+++ b/src/core/windows/SDL_windows.c
@@ -768,4 +768,35 @@ char *WIN_GetModulePath(HMODULE handle)
     return retval;
 }
 
+bool WIN_HasBrokenEZFRD64DLL(void)
+{
+    static bool checked = false;
+    static bool has_broken_EZFRD64_DLL = false;
+
+#ifdef _WIN64
+    if (!checked) {
+        if (SDL_GetHintBoolean("SDL_CHECK_BROKEN_EZFRD64", true)) {
+            // The 64-bit version of EZFRD64.DLL crashes after being loaded,
+            // which happens implicitly when querying the device capabilities,
+            // so make sure we don't do that if there's a possibility of crashing
+            static const char *directories[] = {
+                "C:/Windows/USB_Vibration",
+                "C:/Windows/USB Vibration"
+            };
+            for (int i = 0; i < SDL_arraysize(directories) && !has_broken_EZFRD64DLL; ++i) {
+                int count = 0;
+                char **files = SDL_GlobDirectory(directories[i], "*/EZFRD64.DLL", SDL_GLOB_CASEINSENSITIVE, &count);
+                if (count > 0) {
+                    SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Broken EZFRD64.DLL detected, disabling GameInput and DirectInput force feedback");
+                    has_broken_EZFRD64_DLL = true;
+                }
+                SDL_free(files);
+            }
+        }
+        checked = true;
+    }
+#endif
+    return has_broken_EZFRD64_DLL;
+}
+
 #endif // defined(SDL_PLATFORM_WINDOWS)
diff --git a/src/core/windows/SDL_windows.h b/src/core/windows/SDL_windows.h
index 54f3f2c96c72a..882ce33dfb945 100644
--- a/src/core/windows/SDL_windows.h
+++ b/src/core/windows/SDL_windows.h
@@ -218,6 +218,9 @@ extern const char *WIN_CheckDefaultArgcArgv(int *pargc, char ***pargv, void **pa
 // Does all the win32 tapdancing to make GetModuleFileName work. Returns a SDL_malloc'd UTF-8 string, or NULL on failure.
 extern char *WIN_GetModulePath(HMODULE handle);
 
+// Return true if this system has a broken EZFRD64.DLL installed
+extern bool WIN_HasBrokenEZFRD64DLL(void);
+
 // Ends C function definitions when using C++
 #ifdef __cplusplus
 }
diff --git a/src/joystick/windows/SDL_dinputjoystick.c b/src/joystick/windows/SDL_dinputjoystick.c
index c6712600f8dc1..70115fb05f7f3 100644
--- a/src/joystick/windows/SDL_dinputjoystick.c
+++ b/src/joystick/windows/SDL_dinputjoystick.c
@@ -45,7 +45,6 @@ extern HWND SDL_HelperWindow;
 // local variables
 static bool coinitialized = false;
 static LPDIRECTINPUT8 dinput = NULL;
-static bool has_broken_EZFRD64DLL = false;
 
 // Taken from Wine - Thanks!
 static DIOBJECTDATAFORMAT dfDIJoystick2[] = {
@@ -438,29 +437,6 @@ bool SDL_DINPUT_JoystickInit(void)
         dinput = NULL;
         return SetDIerror("IDirectInput::Initialize", result);
     }
-
-#ifdef _WIN64
-    if (SDL_GetHintBoolean("SDL_JOYSTICK_CHECK_EZFRD64", true)) {
-        // The 64-bit version of EZFRD64.DLL crashes after being loaded,
-        // which happens implicitly when querying the device capabilities,
-        // so make sure we don't do that if there's a possibility of crashing
-        static const char *directories[] = {
-            "C:/Windows/USB_Vibration",
-            "C:/Windows/USB Vibration"
-        };
-        for (int i = 0; i < SDL_arraysize(directories) && !has_broken_EZFRD64DLL; ++i) {
-            int count = 0;
-            char **files = SDL_GlobDirectory(directories[i], "*/EZFRD64.DLL", SDL_GLOB_CASEINSENSITIVE, &count);
-            if (count > 0) {
-                has_broken_EZFRD64DLL = true;
-            }
-            SDL_free(files);
-        }
-        if (has_broken_EZFRD64DLL) {
-            SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Broken EZFRD64.DLL detected, disabling DirectInput force feedback");
-        }
-    }
-#endif
     return true;
 }
 
@@ -806,7 +782,7 @@ bool SDL_DINPUT_JoystickOpen(SDL_Joystick *joystick, JoyStick_DeviceData *joysti
         return SetDIerror("IDirectInputDevice8::SetDataFormat", result);
     }
 
-    if (!has_broken_EZFRD64DLL) {
+    if (!WIN_HasBrokenEZFRD64DLL()) {
         // Get device capabilities to see if we are force feedback capable
         result =
             IDirectInputDevice8_GetCapabilities(joystick->hwdata->InputDevice,