从 .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;
}
我有一个带有可执行文件和 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;
}