我在Windows上看到,可执行文件的模块映射在同一个地址空间中。 我不明白为什么
typedef int (__stdcall *fptr)();
int main(void)
{
HINSTANCE h;
fptr f;
std::stringstream oss;
h = LoadLibrary("test.dll");
if (! h)
return EXIT_FAILURE;
f = (fptr)GetProcAddress(h, "function");
if (! f)
return EXIT_FAILURE;
oss << (DWORD *)f;
std::cout <<"main: "<< oss << std::endl;
_getch();
return EXIT_SUCCESS;
}
和
extern "C" {
void __declspec(dllexport) function() {
return ;
}
}
int main(HMODULE m)
{
std::stringstream oss;
oss << (DWORD *)function;
std::cout << "dll: " << oss << std::endl;
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
main(hModule);
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
得出这个结果:
> "test.exe"
dll: 007BFDB8
main: 0039F944
此外,无法从主进程访问地址007BFD88。 为什么两个地址不同?
答案 0 :(得分:1)
基本规则是DLL在使用它的进程的地址空间中加载 allways 。所以你的函数指针必须是相同的。
我尝试使用一个小变体的代码片段。在DLL的main()中,我使用过:
std::cout << "f in dll: " << (DWORD *)function << std::endl;
在节目的主要部分()中,我使用过:
std::cout << "f in main: " << (DWORD *)f << std::endl;
完全相同的地址出来了!永远!
在您的代码段中,您不必直接宣传功能地址,但使用oss
:
std::stringstream oss;
oss << (DWORD *)function;
std::cout << "f in dll: " << oss << std::endl; // ???
在我的编译器(MSVC13)上,我收到了oss的编译错误。我不知道它为什么适合你,但我怀疑你打印的是字符串流的地址,而不是它的内容。由于oss是一个局部变量,它在两个函数中都有不同的地址,因此引起您的担忧。
试试alternative:
std::cout << "f in dll: " << oss.str() << std::endl;
并且您将再次获得双方相同的结果(地址打印出来)。
在GCC上,您的代码会编译,但不会产生您期望的结果online snippet。
如果相同的DLL函数具有不同的地址,则意味着您存在两个不同的进程,每个进程都有自己的地址空间。
当您使用可选的DllMain()
入口点,并在那里调用(非导出的)main()
函数时,它可能会给人一种独特过程的印象,但这真的很棒不是这样的。
我还想补充一点,函数指针是函数poitners。它没有黑魔法。两个完全相同类型的指针,指向完全相同的函数,将具有完全相同的地址。这里是C ++标准中的规范性引用:
5.10 / 1:相同类型的两个指针比较相等,当且仅当它们都为空,都指向相同的函数,或者两者都是 代表相同的地址。
答案 1 :(得分:0)
地址007BFD88不必是DLL中导出函数的地址(GetProcAddress()返回);它只是一个函数指针。 C / C ++中的函数指针有一些有趣的属性,请查看:Why do function pointer definitions work with any number of ampersands '&' or asterisks '*'?