【问题标题】:Creating and using dll's: __declspec(dllimport) vs. GetProcAddress创建和使用 dll:__declspec(dllimport) 与 GetProcAddress
【发布时间】:2010-10-09 16:57:55
【问题描述】:

假设我们有一个包含 2 个项目的解决方案:MakeDll(一个 dll 应用程序),它创建一个 dll 和 UseDll(一个 exe 应用程序),它使用 dll。现在我知道基本上有两种方法,一种是愉快的,另一种是不愉快的。令人愉快的方法是 UseDll 静态链接到 MakeDll.lib,并且只需 dll 导入函数和类并使用它们。不愉快的方法是使用 LoadLibrary 和 GetProcAddress,我什至无法想象如何处理重载函数或类成员,换句话说,除了外部“C”函数之外的任何其他东西。

我的问题如下(都是关于第一个选项)

  1. MakeDll.lib 究竟是什么 包含?
  2. MakeDll.dll 何时加载到我的应用程序中,何时卸载?我能控制吗?
  3. 如果我更改 MakeDll.dll,我是否可以在不重新构建 UseDll.exe 的情况下使用新版本(前提是它在接口方面是旧版本的超集)?一种特殊情况是导出多态类并添加新的虚函数。

提前致谢。

附:我正在使用 MS Visual Studio 2008

【问题讨论】:

    标签: c++ visual-studio dll


    【解决方案1】:
    1. 它基本上包含 DLL 中的函数列表,包括名称和序数(尽管几乎没有人再使用序数了)。链接器使用它在 UseDLL.exe 中创建一个导入表——即一个引用(本质上):“这个文件依赖于 MakeDll.dll 中的函数 xxx”。当加载器加载该可执行文件时,它会查看导入表,并(递归地)加载它列出的所有 DLL,并且(至少在概念上)使用GetProcAddress 来查找函数,因此它可以将它们的地址放入可执行文件中他们是需要的。

    2. 它通常在加载可执行文件的过程中加载。您可以使用 /delayload 开关延迟其加载,直到调用该 DLL 中的函数。

    3. 一般来说,是的。在添加虚函数的特定情况下,它将取决于类的 vtable 布局保持不变,而不是添加新函数。除非您采取措施来确保或验证自己,否则依赖它是一个非常糟糕的主意。

    【讨论】:

      【解决方案2】:
      1. MakeDll.lib 包含导出函数及其 RVA 到 MakeDll.dll 的螺柱列表

      2. MakeDll.dll 根据为相关 dll 定义的加载类型加载到应用程序中。 (例如DELAYLOAD)。 Raymond Chen 有一个interesting article

      3. 只要 UseDll.exe 中使用的所有 RVA 偏移量未更改,您就可以使用 MakeDll.dll 的新更新版本。如果您更改多态类的vtable 布局,例如在先前定义的vtable 中间添加一个新函数,您将需要重新编译UseDll.exe。除此之外,您可以将更新后的 dll 与之前编译的 UseDll.exe 一起使用。

      【讨论】:

        【解决方案3】:

        不愉快的方法是使用 LoadLibrary 和 GetProcAddress,我什至无法想象如何处理重载函数或类成员,换句话说,除了外部“C”函数之外的任何其他东西。

        是的,这令人不快,但并不像听起来那么糟糕。如果您选择使用此选项,则需要执行以下操作:

        // Common.h: interface common to both sides.
        // Note: 'extern "C"' disables name mangling on methods.
        extern "C" class ISomething
        {
            // Public virtual methods...
        
                // Object MUST delete itself to ensure memory allocator
                // coherence. If linking to different libraries on either
                // sides and don't do this, you'll get a hard crash or worse.
                // Note: 'const' allows you to make constants and delete
                // without a nasty 'const_cast'.
            virtual void destroy () const = 0;
        };
        
        // MakeDLL.c: interface implementation.
        class Something : public ISomething
        {
            // Overrides + oher stuff...
        
            virtual void destroy () const { delete this; }
        };
        
        extern "C" ISomething * create () { return new Something(); }
        

        我已经成功地在两端使用不同的 C++ 编译器部署了此类设置(即 G++ 和 MSVC 在所有 4 种可能的组合中互换)。

        您可以随意更改Something 的实现。但是,您可以在不重新编译双方的情况下更改界面!仔细想想,这很不直观:双方都依赖对方对ISomething 的定义。您可以添加这种额外灵活性的方法是使用编号接口(如 DirectX 所做的那样)或使用一组接口并测试功能(如 COM 所做的那样)。前者的设置非常直观,但需要纪律,而第二个井......将重新发明轮子!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-01-18
          • 2013-03-26
          • 2013-07-29
          • 1970-01-01
          相关资源
          最近更新 更多