【问题标题】:Why/when is __declspec( dllimport ) not needed?为什么/何时不需要 __declspec( dllimport )?
【发布时间】:2011-05-28 05:29:17
【问题描述】:

在一个使用 server.dll 和 client.exe 的项目中,我已经 dllexported 来自服务器 dll 的服务器符号,而 没有 dllimported 将它放入客户端 exe。

仍然,应用程序链接并启动,没有任何问题。是不是不需要dllimport,那么???

详情:

我有这个“服务器”dll:

// server.h
#ifdef SERVER_EXPORTS
  #define SERVER_API __declspec(dllexport)
#else
  #define SERVER_API // =====> not using dllimport!
#endif
class  SERVER_API CServer {
   static long s;
   public:
   CServer();
};

// server.cpp
CServer::CServer(){}

long CServer::s;

还有这个客户端可执行文件:

#include <server.h>
int main() {
   CServer s;
}

服务器命令行:

cl.exe /Od  /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_USRDLL" 
 /D "SERVER_EXPORTS" /D "_UNICODE" /D "UNICODE" /D "_WINDLL" 
 /Gm /EHsc /RTC1 /MDd /Yu"stdafx.h" 
 /Fp"Debug\server.pch" /Fo"Debug\\" /Fd"Debug\vc80.pdb" 
 /W3 /nologo /c /Wp64 /ZI /TP /errorReport:prompt

cl.exe /OUT:"U:\libs\Debug\server.dll" /INCREMENTAL:NO /NOLOGO /DLL 
/MANIFEST /MANIFESTFILE:"Debug\server.dll.intermediate.manifest" 
/DEBUG /PDB:"u:\libs\Debug\server.pdb" 
/SUBSYSTEM:WINDOWS /MACHINE:X86 /ERRORREPORT:PROMPT 
kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib 
shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib

客户端命令行:

cl.exe /Od /I "..\server" 
 /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" 
 /Gm /EHsc /RTC1 /MDd /Fo"Debug\\" /Fd"Debug\vc80.pdb" /W3 /c /Wp64 /ZI /TP 
 .\client.cpp

cl.exe /OUT:"U:\libs\Debug\Debug\client.exe" /INCREMENTAL 
/LIBPATH:"U:\libs\Debug" 
/MANIFEST /MANIFESTFILE:"Debug\client.exe.intermediate.manifest" 
/DEBUG /PDB:"u:\libs\debug\debug\client.pdb" 
/SUBSYSTEM:CONSOLE /MACHINE:X86 
server.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib 
advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib

【问题讨论】:

  • 其实是个好问题。 MSDN Docs --- msdn.microsoft.com/en-us/library/3y1sfaz2(VS.80).aspx --- 并没有告诉我使用extern(具有正确的调用约定和名称修改)和指定导入库是否有任何好处。
  • __declspec(dllexport) 对类和类成员非常、非常 脆弱。单独的 server.dll 的目的是什么?当与/delayload:server.dll 配对时,类上唯一的__declspec(dllexport) 做得好的就是减少进程启动I/O。任何其他感知到的优势(例如,想象能够在不重新编译应用程序的情况下修补 DLL 逻辑)实际上违反了单一定义规则并且不可靠。
  • @xtofl:解耦很好,源码其实也可以解耦。但是类和类成员上的__declspec(dllexport) 会导致二进制文件 紧密耦合。换句话说,通过使用静态库并且在任何地方都没有__declspec(dllexport),您将拥有相同级别的耦合,并且部署负担要少得多。
  • @Sergey:最好将所有代码都放在一个 .DLL 模块中,只为 7 个应用程序(甚至参数)中的每一个导出与 main 函数相对应的纯 C 函数到单个 .exe,ala busybox)。至少在 Windows 上,这是我们在这里讨论的平台,Qt 不提供二进制兼容性。您必须使用应用程序使用的特定编译器和命令行选项自己构建 Qt 库,以避免违反 ODR。在这种情况下,您不妨再次使用静态库。
  • @marshalcraft 这不是博客——把你的咆哮留到别处吧。请:)。

标签: c++ dllimport visual-c++-2005 declspec


【解决方案1】:

这不是必需的。这是一种优化,是对编译器的提示,即 DLL 将直接导出函数指针,而不仅仅是 DLL 的 IAT 中的一个条目。名为 foo() 的函数的导出函数指针将是 __imp_foo。这允许它生成更好的代码,节省来自 IAT 的函数指针加载和间接跳转。这是时间优化,而不是空间。

这个blog post有详细信息。

【讨论】:

  • 来自链接的文章:对于数据,需要正确性。
  • 谢谢!您指出了正确的博客文章。不过,您的措辞有点令人困惑 - 我认为 importing 源代码会导致在 importing 代码中调用 __imp_foo thunk。
【解决方案2】:

我也想知道这个。我还删除了 __declspec(dllimport) 指令,并且非常惊讶地发现依赖于另一个 dll (glib) 中的函数的 dll (gmodule) 编译并运行(特别是在 wireshark 中)没有问题。这是MS的引述:

__declspec(dllimport) is ALWAYS required to access exported DLL data.

不知道为什么 MS 这么说,因为在其他页面上他们声明该指令是不必要的。无论如何,我的库不仅在没有 dllimport 的情况下运行,而且我已经很久没有看到“__imp”符号了,而以前我一直在偶然发现它(或者它在我身上)。这是怎么回事?答案就在这里:

That's why using __declspec(dllimport) is better: because the linker doesn't generate a thunk if it's not required. There's no thunk and no jmp instruction, so the code is smaller and faster. You can also get the same effect WITHOUT __declspec(dllimport) by using whole program optimization. For more information, see /GL (Whole Program Optimization).

现在说得通了。我在所有项目中都使用 /GL (+ /LTCG)。这就是主题问题的答案

when is __declspec( dllimport ) not needed?

当使用整个程序优化时。

【讨论】:

    猜你喜欢
    • 2013-03-26
    • 2012-02-10
    • 1970-01-01
    • 1970-01-01
    • 2011-06-10
    • 1970-01-01
    • 2011-01-18
    相关资源
    最近更新 更多