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

发布于 2024-09-26 22:41:01 字数 528 浏览 3 评论 0原文

想象一下,我们有一个包含 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吗?一种特殊情况是导出多态类并添加新的虚函数。

提前致谢。

PS 我正在使用 MS Visual Studio 2008

Imagine we have a solution with 2 projects: MakeDll (a dll app), which creates a dll, and UseDll (an exe app), which uses the dll. Now I know there are basically two ways, one is pleasant, other is not. The pleasant way is that UseDll link statically to MakeDll.lib, and just dllimports functions and classes and uses them. The unpleasant way is to use LoadLibrary and GetProcAddress which I don't even imagine how is done with overloaded functions or class members, in other words anything else but extern "C" functions.

My questions are the following (all regarding the first option)

  1. What exactly does the MakeDll.lib
    contain?
  2. When is MakeDll.dll loaded into my application, and when unloaded? Can I control that?
  3. If I change MakeDll.dll, can I use the new version (provided it is a superset of the old one in terms of interface) without rebuilding UseDll.exe? A special case is when a polymorphic class is exported and a new virtual function is added.

Thanks in advance.

P.S. I am using MS Visual Studio 2008

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

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

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

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

  1. It basically contains a list of the functions in the DLL, both by name and by ordinal (though almost nobody uses ordinals anymore). The linker uses that to create an import table in UseDLL.exe -- i.e., a reference that says (in essence): "this file depends on function xxx from MakeDll.dll". When the loader loads that executable, it looks at the import table, and (recursively) loads all the DLLs it lists, and (at least conceptually) uses GetProcAddress to find the functions, so it can put their addresses into the executable where they're needed.

  2. It's normally loaded during the process of loading your executable. You can use the /delayload switch to delay its being loaded until a function from that DLL is called.

  3. In general, yes. In the specific case of adding a virtual function, it'll depend on the class' vtable layout staying the same other than the new function being added. Unless you take steps to either assure or verify that yourself, depending on it is a really bad idea.

旧时模样 2024-10-03 22:41:01
  1. MakeDll.lib 包含导出函数及其 RVA 的螺柱列表到 MakeDll.dll

  2. MakeDll.dll 根据加载类型加载到应用程序中为相关 dll 定义。 (例如DELAYLOAD)。 Raymond Chen 有一篇关于此的有趣的文章

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

  1. MakeDll.lib contains a list of studs for the exported functions and their RVAs into the MakeDll.dll

  2. MakeDll.dll is loaded into the application based on what type of loading is defined for the dll in question. (e.g. DELAYLOAD). Raymond Chen has an interesting article on that.

  3. You can use the new updated version of MakeDll.dll as long as all the RVA offsets used in UseDll.exe have not changed. In the event you change a vtable layout for a polymorphic class, as in add a new function in the middle of the previously defined vtable, you will need to recompile UseDll.exe. Other than that you can use the updated dll with the previously compiled UseDll.exe.

永言不败 2024-10-03 22:41:01

令人不快的方法是使用 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 所做的那样)。前者的设置非常直观,但需要纪律,而第二个......将重新发明轮子!

The unpleasant way is to use LoadLibrary and GetProcAddress which I don't even imagine how is done with overloaded functions or class members, in other words anything else but extern "C" functions.

Yes, this is unpleaseant but is not as bad as it sounds. If you choose to go through with this option, you'll want to do something like the following:

// 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(); }

I've successfully deployed such setups with different C++ compilers on both ends (i.e. G++ and MSVC interchanged in all 4 possible combinations).

You may change Something's implementation all you want. However, you may not change the interface without re-compiling on both sides! When you think about it, this is faily intuitive: both sides rely on the other's definition of ISomething. What you may do to add this extra flexibility is use numbered interfaces (as DirectX does) or go with a set of interfaces and test for capabilities (as COM does). The former is really intuitive to set up but requires discipline and the second well... would be re-inventing the wheel!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文