使用GetProcAddress调用外部函数会导致应用程序崩溃

Calling an external function with GetProcAddress crashes the app

本文关键字:应用程序 崩溃 函数 GetProcAddress 调用 外部 使用      更新时间:2023-10-16

我一直试图用GetProcAddress函数调用一个外部函数,但每次调用该函数时,它都会崩溃控制台。我一直在寻找,但在每一篇文章中,我都得到了相同的最终解决方案,但当我在DLL中尝试时,它会崩溃应用程序。

这是代码:

#include <Windows.h>
#include <vector>
#include "SDKplugin.h"
typedef void (*logprintf_t)(char* format, ...);
logprintf_t logprintf;
// static void* m_AMXExports[44];
typedef bool (PLUGIN_CALL *ServerPluginLoad_t)(void **data);
typedef void (PLUGIN_CALL *ServerPluginUnload_t)();
typedef unsigned int (PLUGIN_CALL *ServerPluginSupports_t)();
typedef void (PLUGIN_CALL *ServerPluginProcessTick_t)();
typedef int (PLUGIN_CALL *ServerPluginAmxLoad_t)(AMX *amx);
typedef int (PLUGIN_CALL *ServerPluginAmxUnload_t)(AMX *amx);
struct Plugins
{
    void* AppData[256];
    SUPPORTS_FLAGS FlagSupport;
    HMODULE Module;
    ServerPluginLoad_t      LOAD;
    ServerPluginUnload_t    UNLOAD;
    ServerPluginSupports_t  SUPPORTS;
    ServerPluginProcessTick_t   PROCESSTICK;
    // AMX Plugin Interface
    ServerPluginAmxLoad_t   AMXLOAD;
    ServerPluginAmxUnload_t AMXUNLOAD;
};
Plugins* ServerPlugins;
void **ppPluginData ;
extern void *pAMXFunctions;
//native LoadLibrary(libraryname[]);
static cell AMX_NATIVE_CALL my_LoadLibrary(AMX* amx, cell* params)
{
    bool validfunc = false;
    char *path;
    amx_StrParam(amx, params[1], path);
    logprintf("Loading plugin %s", path);
    ServerPlugins = new Plugins();
    ServerPlugins->Module = LoadLibraryA(path);
    if (ServerPlugins->Module == NULL)
    {
        delete ServerPlugins;
        logprintf("Failed loading plugin %s (Error: %d)", path, GetLastError());
        return 0;
    }
    logprintf("NULL");
    ServerPlugins->LOAD = (ServerPluginLoad_t)GetProcAddress(ServerPlugins->Module, "Load");
    ServerPlugins->UNLOAD = (ServerPluginUnload_t)GetProcAddress(ServerPlugins->Module, "Unload");
    ServerPlugins->SUPPORTS = (ServerPluginSupports_t)GetProcAddress(ServerPlugins->Module, "Supports");
    if (ServerPlugins->LOAD == NULL || ServerPlugins->SUPPORTS == NULL || ServerPlugins->UNLOAD == NULL)
    {
        logprintf(" Plugin doesnt conform to architecture");
        FreeLibrary(ServerPlugins->Module);
        delete ServerPlugins;
        return false;
    }
    logprintf("NULL 1");
    ServerPlugins->FlagSupport = (SUPPORTS_FLAGS)ServerPlugins->SUPPORTS();
    if ((ServerPlugins->FlagSupport & SUPPORTS_VERSION_MASK) > SUPPORTS_VERSION)
    {
        logprintf("Unsupported Version; unloading.");
        FreeLibrary(ServerPlugins->Module);
        delete ServerPlugins;
        return false;
    }
    logprintf("NULL 2");
    if ((ServerPlugins->FlagSupport & SUPPORTS_AMX_NATIVES) > SUPPORTS_VERSION)
    {
        ServerPlugins->AMXLOAD = (ServerPluginAmxLoad_t)GetProcAddress(ServerPlugins->Module, "AmxLoad");
        ServerPlugins->AMXUNLOAD = (ServerPluginAmxUnload_t)GetProcAddress(ServerPlugins->Module, "AmxUnload");
    }
    else
    {
        ServerPlugins->AMXLOAD = NULL;
        ServerPlugins->AMXUNLOAD = NULL;
        logprintf("Any Abstract Machine has been loaded");
    }
    logprintf("NULL 3");
    if ((ServerPlugins->FlagSupport & SUPPORTS_PROCESS_TICK) != 0)
    {
        ServerPlugins->PROCESSTICK = (ServerPluginProcessTick_t)GetProcAddress(ServerPlugins->Module, "ProcessTick");
    }
    else
    {
        ServerPlugins->PROCESSTICK = NULL;
    }
    logprintf("NULL 4"); //debugging
    ServerPlugins->AppData[PLUGIN_DATA_AMX_EXPORTS] = pAMXFunctions;
    ServerPlugins->AppData[PLUGIN_DATA_LOGPRINTF] = &logprintf;
    if (!(ServerPlugins->LOAD)(ServerPlugins->AppData)) //i didnt put it as &ServerPlugins->AppData because it causes an error
    {
        logprintf("Initialized failed loading plugin %s", path);
        FreeLibrary(ServerPlugins->Module);
        logprintf("NULL 5");
        delete ServerPlugins;
        return false;
    }
    logprintf("Plugin %s loaded", path);
    return true;
}
//native UnloadLibrary(libraryname[]);
static cell AMX_NATIVE_CALL my_UnloadLibrary(AMX*amx, cell*params)
{
    char *path;
    amx_StrParam(amx, params[1], path);
    ServerPlugins->Module = GetModuleHandle((LPCTSTR)path);
    if (ServerPlugins->Module != NULL)
    {
        ServerPlugins->UNLOAD = (ServerPluginUnload_t)GetProcAddress(ServerPlugins->Module, "Unload");
        if (ServerPlugins->UNLOAD != NULL)
        {
            ServerPlugins->UNLOAD();
            FreeLibrary(GetModuleHandleA(path));
            logprintf("Library %s has been unloaded correctly", path);
            return 1;
        }
        else
        {
            logprintf("Unloading library %s failed (Error: %d)", GetLastError());
            return 0;
        }
    }
    return 1;
}

PLUGIN_EXPORT bool PLUGIN_CALL Load(void **ppData)
{
    pAMXFunctions = ppData[PLUGIN_DATA_AMX_EXPORTS];
    logprintf = (logprintf_t)ppData[PLUGIN_DATA_LOGPRINTF];
    return 1;
}
PLUGIN_EXPORT void PLUGIN_CALL Unload()
{
}
PLUGIN_EXPORT unsigned int PLUGIN_CALL Supports()
{
    return SUPPORTS_VERSION | SUPPORTS_AMX_NATIVES;
}
AMX_NATIVE_INFO projectNatives[] =
{
    { "LoadLibrary", my_LoadLibrary },
    { "UnloadLibrary", my_UnloadLibrary }
};
PLUGIN_EXPORT int PLUGIN_CALL AmxLoad(AMX *amx)
{
    return amx_Register(amx, projectNatives, -1);
}
PLUGIN_EXPORT int PLUGIN_CALL AmxUnload(AMX *amx)
{
    return AMX_ERR_NONE;
}

convertCharArrayToLPCWSTR()中存在内存泄漏。您永远不会释放您分配的wchar_t*。不需要convertCharArrayToLPCWSTR()函数本身,只需按原样将char*路径传递给LoadLibraryA()即可:

char *path;
amx_StrParam(amx, params[1], path);
...
ServerPlugins->Module = LoadLibraryA(path);

您没有检查GetProcAddress("Unload")是否成功加载了ServerPlugins->UNLOAD

您正在将GetProcAddress("Load")用于ServerPlugins->LOADServerPlugins->AMXLOAD,将GetProcAddress("Unload")用于ServerPlugins->UNLOADServerPlugins->AMXUNLOAD。这对我来说很可疑。DLL真的对AMX和非AMX入口点使用相同的导出吗?如果是这样,那么考虑到ServerPluginLoad_tServerPluginAmxLoad_t具有非常不同的签名,并且ServerPlugin(Amx)Unload_t也是如此,这是非常糟糕的设计。这是一个正在等待发生的损坏的调用堆栈。让DLL导出单独的AmxLoad()AmxUnload()函数会更安全。

因此,SUPPORTS_AMX_NATIVESSUPPORTS_PROCESS_TICK标志是多余的,因为GetProcAddress()会告诉您这些导出是否可用。

至于调用ServerPlugins->LOAD时的崩溃,在将数据传递给Load()之前,我没有看到您用任何数据初始化ppData。当然不是PLUGIN_DATA_AMX_EXPORTSPLUGIN_DATA_LOGPRINTF时隙,至少:

ppData[PLUGIN_DATA_AMX_EXPORTS] = pAMXFunctions;
ppData[PLUGIN_DATA_LOGPRINTF] = &logprintf;
if (!(ServerPlugins->LOAD)(ppData))

因此,即使对Load()的调用本身没有崩溃,DLL在稍后尝试使用其在Load()中分配的本地pAMXFunctionslogprintf指针时仍可能崩溃。

既然如此,为什么要将这样的东西作为void*数组而不是struct来传递呢?那会更安全,例如:

struct PluginInitData
{
    void* pAMXFunctions;
    logprintf_t logprintf;
    ...
};
typedef bool (__stdcall *ServerPluginLoad_t)(PluginInitData* data);
PluginInitData pInitData;
pInitData.pAMXFunctions = pAMXFunctions;
pInitData.logprintf = &logprintf;
...
if (!(ServerPlugins->LOAD)(&pInitData))

extern "C" bool __stdcall Load(PluginInitData* data)
{
    pAMXFunctions = data->pAMXFunctions;
    logprintf = data->logprintf;
    ...
    return true;
}

更新:您已经修复了我提到的大多数问题,但现在我发现您的my_UnloadLibrary()函数实现错误根本不要调用GetModuleHandle()GetProcAddress(),使用先前在my_LoadLibrary()中初始化的现有ModuleUNLOAD指针。

static cell AMX_NATIVE_CALL my_LoadLibrary(AMX* amx, cell* params)
{
    char *path;
    amx_StrParam(amx, params[1], path);
    ...
    ServerPlugins->Path = path;
    ...
}
static cell AMX_NATIVE_CALL my_UnloadLibrary(AMX*amx, cell*params)
{
    if (ServerPlugins)
    {
        if (ServerPlugins->UNLOAD != NULL)
            ServerPlugins->UNLOAD();
        if (ServerPlugins->Module != NULL)
        {
            FreeLibrary(ServerPlugins->Module);
            ServerPlugins->Module = NULL;
        }
        logprintf("Library %s has been unloaded", ServerPlugins->Path);
        delete ServerPlugins;
        ServerPlugins = NULL;
    }
    return 1;
}

如果您仍然有Load()崩溃的问题,那么您只需要使用编译器的调试器来了解运行时实际发生了什么。到目前为止显示的代码不应该崩溃,所以要么是调用约定不匹配,要么是数据对齐不匹配,或者是内存损坏,等等。我们无法为您运行调试器。