我在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不能从主进程访问。 为什么这两个地址不同?
基本的规则是,DLL是全部加载在使用它的进程的地址空间中。 所以你的函数指针必须是相同的。
我尝试了一个小的变体你的代码片段。 在DLL的main()中,我使用了:
std::cout << "f in dll: " << (DWORD *)function << std::endl;
在程序的main()中,我使用了:
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的编译错误。 我不知道为什么它适合你,但我怀疑你打印你的stringstream的地址,而不是它的内容。 由于oss是一个局部变量,它在两个函数中都有不同的地址,因此引起了您的担心。
尝试替代方法 :
std::cout << "f in dll: " << oss.str() << std::endl;
你会得到同样的结果(地址打印出来)在双方。
在GCC上,您的代码会编译,但不会产生您期望的结果,因为此联机代码片段显示。
如果同一个DLL函数具有不同的地址,这意味着你有两个不同的进程,每个进程都有自己的地址空间。
当你使用可选的DllMain()
入口点,并在那里调用一个(不导出的) main()
函数时,它可以给人一种独特的过程印象,但事实并非如此。
我也想补充说,函数指针是函数poitners。 它没有黑魔法。 指向完全相同的函数的两个完全相同类型的指针将具有完全相同的地址。 这里是C ++标准中的规范性参考:
5.10 / 1:相同类型的两个指针比较相等的当且仅当它们都是空的, 都指向相同的函数 ,或者两者都代表相同的地址。
地址007BFD88不一定是从DLL(GetProcAddress()返回)导出函数的地址; 它只是一个函数指针。 C / C ++中的函数指针有一些有趣的属性,请检查: 为什么函数指针定义可以与任何数量的&符号或星号“*”一起工作?