【问题标题】:Why can I use functions not exported by dll loaded at runtime为什么我可以使用运行时加载的 dll 未导出的函数
【发布时间】:2021-06-03 22:06:58
【问题描述】:

我有一个相当简单的插件系统,可以在运行时加载和重新加载 dll。没有任何涉及的静态链接。 .exe 仅包含加载 dll 所需的代码。它目前通过使用 LoadLibrary/GetProcAddress/FreeLibrary 函数在 Windows 下执行此操作。 (在 VisualStudio 中)没有设置额外的库目录、项目引用或库。

然而,我正在做的是包含一些来自 dll 项目的头文件,我稍后会动态加载这些头文件(以防万一)。现在我知道我可能应该只使用 GetProcAddress 查询的接口函数来调用 dll,但这是我第一次这样做,所以我只是在搞清楚。

现在假设我得到了一个 CreateWindow 函数的函数指针,如下所示:

void LoadPlugin(PlatformPlugin* plugin)
{
  plugin->Handle = LoadLibrary(plugin->PLUGIN_FILE_TEMP);
  plugin->CreateWindow = (PLUGIN_PLATFORM_CreateWindow)GetProcAddress(plugin->Handle,  "CreateWindow");
}

CreateWindow 函数返回一个指向 Window 对象的指针,该对象主要包含虚函数,并由特定于平台的 Window 类(如 WindowsWindow)继承:

class Window
{
public:
  virtual unsigned int GetWidth() const = 0;
  virtual unsigned int GetHeight() const = 0;
};

class WindowsWindow : public Window
{
 public:
   virtual unsigned int GetWidth() const override;
   virtual unsigned int GetHeight() const override;
};

当然,还有一个包含定义的 .cpp 文件。 '问题'来了:当调用 CreateWindow 时,我得到了有效的指针,当在它上面调用 GetWidth 时,我也得到了正确的结果。但您可能已经注意到,我从未导出任何 Window 类或函数。据我了解,我不应该调用 GetWidth,但我可以。 Dependencywalker 证实了这一点,并将 CreateWindow 作为导出函数显示给我。 反正我怎么称呼它?包含的头文件只是为编译器提供了理论上调用函数所需的信息,但由于头文件实际上并不包含函数实现,链接器如何知道我的函数的地址?

【问题讨论】:

    标签: c++ dll loadlibrary hot-reload


    【解决方案1】:

    虚函数的地址存储在对象的vtable 中。因此,您不需要导出的符号来调用这些函数,只需一个有效的对象(或其中的地址)和 vtable 布局的知识(由您的头文件提供)。

    然后编译器可以索引到vtable中找到要调用的地址并调用它。

    【讨论】:

    • 我明白了。那么假设非虚拟功能实际上不起作用是否正确?正如我最初预期的那样
    • 是的,没错。要调用它们,您需要从 DLL 中导出它们。
    • 非虚拟inline 函数将起作用。在 Windows 中,COM 和 C++ 虚拟表惊人地相似(由于 COM 技术和 C++ 语言的历史发展)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-15
    • 1970-01-01
    • 1970-01-01
    • 2012-08-01
    • 1970-01-01
    • 2021-12-23
    相关资源
    最近更新 更多