从 .dll 访问 .exe 中定义的函数的正确方法

Correct way to access functions defined in .exe from .dll

我有一个带有可执行文件和 DLL 的 VS 解决方案。

在可执行文件 (MAIN) 中:

__declspec(dllexport) void testExe()
{
    printf("Hello from EXE");
}

__declspec(dllimport) void DoStuff();

int main()
{
    DoStuff();
}

在 .dll (DLL) 中

__declspec(dllimport) void testExe();

__declspec(dllexport) void testDll()
{
    printf("Hello from Dll");
}

__declspec(dllexport) void DoStuff()
{
    testExe();
    testDll();
}

我在 MAIN.exe 中链接了 Dll.lib,但仍然出现链接错误:

error LNK2019: unresolved external symbol "__declspec(dllimport) void __cdecl testExe(void)" referenced in function "void __cdecl DoStuff(void)"

我怎样才能做到这一点?

不要从 EXE 导出函数。从接受函数指针作为输入的 DLL 中导出函数,然后让 EXE 在运行时调用该函数。

执行文件:

__declspec(dllimport) void SetFunc(void (*)());
__declspec(dllimport) void DoStuff(); 

void testExe()
{
    printf("Hello from EXE");
}

int main()
{
    SetFunc(testExe);
    DoStuff();
}

DLL:

typedef void (*lpFuncType)();

lpFuncType pExeFunc = NULL;

void testDll()
{
    printf("Hello from Dll");
}

__declspec(dllexport) void SetFunc(lpFuncType func)
{
    pExeFunc = func;
}

__declspec(dllexport) void DoStuff()
{
    if (pExeFunc) pExeFunc();
    testDll();
}

循环依赖是这里的一个问题。你必须打破这个循环,否则你将有复杂而脆弱的构建过程来创建这个循环。

打破这个循环的一种方法是 Remy Lebeau 的回答。

另一种方法是引入另一个包含 testExe() 的 dll,这个 dll 将链接到可执行文件和您的初始 dll,其中包含 testDll()。优点是您根本不必更改代码,只需将可执行文件拆分为可执行文件和额外的 dll。

使用GetModuleHandleW(NULL)获取正在执行的EXE的模块句柄。然后用GetProcAddress得到函数地址

注意 C++ 名称修改和调用约定(使用 __stdcall 将添加类似 @4 的内容)更改函数名称。为避免更改函数名称,请在导出前使用 extern "C"

示例:

在 main.cpp 中用于 EXE:

//Want to use extern "C" so that the function name doesn't get mangled using C++ mangling rules
extern "C"
{
    __declspec(dllexport) int DoStuff(int param1, int param2);
}

从EXE导入的DLL中:

//Declare a function pointer type named "DoStuff_FUNC", this makes it a lot easier to import functions
typedef (int DoStuff_FUNC)(int param1, int param2);

int DoStuff_WithinDll(int param1, int param2)
{
    //Get the module handle from the executing EXE
    HMODULE module = GetModuleHandleW(NULL);
    //Type-cast "FARPROC" to our desired function type
    DoStuff_FUNC DoStuff = (DoStuff_FUNC)GetProcAddress(module, "DoStuff");
    if (DoStuff != NULL)
    {
        return DoStuff(param1, param2);
    }
    return -1;
}