释放DLL并退出线程时,FreeLibraryAndExitThread会导致程序崩溃。

3
我正在编写一个DLL,将其注入游戏中以进行一些反向工程。有一段时间我能够成功地注入、弹出和重新注入程序。我使用FreeLibraryAndExitThread来卸载。
在添加XInput到程序中以便捕获用户输入后,当我调用FreeLibraryAndExitThread时,游戏会崩溃并显示访问冲突。从这篇文章中可以得知,我猜测使用XInput在我卸载时留下了一些“活”的内容,并导致了崩溃。说实话,我不知道如何解决这个问题。
以下是在退出时导致程序崩溃的代码:
    XINPUT_STATE state;
    ZeroMemory(&state, sizeof(XINPUT_STATE));

    //The problematic line of code
    bool gamepad = XInputGetState(0, &state) == ERROR_SUCCESS; 

    WORD buttonsHeld = state.Gamepad.wButtons;
    WORD buttonsPressed = (~previousButtonState) & state.Gamepad.wButtons;
    WORD buttonsReleased = previousButtonState & (~state.Gamepad.wButtons);

当我删除对 XInputGetState 的调用时,一切正常,我能够卸载 DLL 而不崩溃。

这是我用来卸载程序并退出的方式:

    FreeLibraryAndExitThread(hDLL, 0);

其中hDLL是来自DllMain的参数hinstDLL。我还尝试使用GetModuleHandleEx而不是使用hinstDLL

我的猜测是:

  • 使用XInputGetState会使我的程序加载第二个XInput的DLL,或者

  • XInputGetState在调用时创建了对我的DLL的某种引用,并且当我删除我的DLL时,它尝试访问不再存在的内存。

编辑:我进行了一些挖掘,问题似乎是添加对XInputGetState的调用会导致我的DLL加载XINPUT1_4.dll。我尝试使用FreeLibrary卸载它,但没有效果。

编辑:我进一步缩小了范围——问题是由游戏中的某个线程尝试返回到XINPUT1_4.dll的代码部分造成的,该部分已被卸载,从而导致崩溃。我不知道如何解决这个问题。

最终编辑:这是一个简单的解决方法,我需要调用LoadLibrary(L"XINPUT1_4.dll")来加载导致问题的DLL。


如果游戏已经在使用XInput来实现自己的目的,那么它应该有自己打开xinput1_4.dll的引用,因此加载和释放你自己的引用不应该从内存中卸载该DLL,因为它的引用计数不会降为0。 - Remy Lebeau
尝试在您的DLL中显式加载xinput1_4.dll(使用LoadLibrary,GetProcAddress),而不是静态链接它。如果在您的DLL卸载时保留它已加载,则可能不会出现问题。 - Harry Johnston
1
游戏实际上加载了一个较旧的XInput版本,它加载的是1.3版本。这会有什么影响吗? - Nerual
1
实际上,这个方法可行!DllMain创建了一个线程,该线程调用LoadLibrary("XINPUT1_4.dll");,并且在卸载时它也能正常工作!非常感谢! :) - Nerual
1个回答

3
这是解决方案:
问题在于调用XInputGetState会导致我的DLL自动加载XINPUT1_4.dll,当我调用FreeLibraryAndExitThread时,我的DLL卸载也会强制卸载XInput DLL。程序中的代码(可能来自XInput 1.4的线程)试图执行不再存在的代码,从而导致访问冲突。
因此,解决方案很简单,只需在初始化DLL线程后调用LoadLibrary(L'XINPUT1_4.dll'),这样当我的DLL被卸载时,XInput DLL仍然保留在内存中,因为LoadLibrary增加了引用计数。
(当DLL的引用计数达到0时,它将被卸载。第一次加载时,它被初始化为1,LoadLibrary将其增加1,而调用FreeLibraryAndExitThread将其减少1。因此,最终引用计数大于0,并且随着我的DLL被卸载而保留在内存中)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接