创建和使用dll:__ declspec(dllimport)与GetProcAddress

时间:2010-10-09 16:57:55

标签: c++ visual-studio dll

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

我的问题如下(所有关于第一个选项)

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

    P.S。我正在使用MS Visual Studio 2008

3 个答案:

答案 0 :(得分:3)

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

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

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

答案 1 :(得分:1)

  1. MakeDll.lib包含导出的函数及其RVAs到MakeDll.dll

  2. 的精选列表
  3. MakeDll.dll根据为相关dll定义的加载类型加载到应用程序中。 (例如DELAYLOAD)。 Raymond Chen对此有interesting article

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

  5. 一起使用

答案 2 :(得分:1)

  

令人不快的方式是使用LoadLibrary和GetProcAddress,我甚至无法想象如何使用重载函数或类成员,换句话说除了extern“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所做)。前者设置非常直观,但需要纪律,第二口井......将重新发明轮子!