创建和使用DLL:__declspec(dllimport) vs. GetProcAddress

3
假设我们有一个包含两个项目的解决方案: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的情况下使用新版本(前提是界面是旧版本的超集)?特殊情况是当导出多态类并添加新的虚拟函数时。
谢谢。
P.S. 我正在使用MS Visual Studio 2008
3个回答

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

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

  3. 一般而言是的。具体添加虚拟函数的情况,它将取决于类的vtable布局保持不变,除了新函数被添加之外。除非您采取措施来确保或验证自己,否则依赖它是一个非常糟糕的想法。


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++编译器在两端部署了这样的设置(即在所有4种可能的组合中交换G++和MSVC)。

你可以随意更改Something的实现。然而,你不能在没有重新编译两端的情况下更改接口!仔细思考一下,这是相当直观的:双方都依赖于对方对ISomething的定义。为了增加这种额外的灵活性,你可以使用带编号的接口(如DirectX所做的)或者采用一组接口并测试其功能(如COM所做的)。前者很容易设置但需要纪律,而后者......将会重新发明轮子!


1
  1. MakeDll.lib 包含导出函数的存根列表和它们在 MakeDll.dll 中的 RVA。

  2. MakeDll.dll 根据所定义的 dll 类型进行加载,例如 DELAYLOAD。Raymond Chen 在这方面有一篇有趣的文章

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接