为什么我可以使用运行时加载的 dll 未导出的函数

Why can I use functions not exported by dll loaded at runtime

我有一个相当简单的插件系统,可以在运行时加载和重新加载 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 函数 returns 一个指向 Window 对象的指针,该对象主要包含虚函数并被特定平台继承 Window class像 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 文件。 'issue' 出现了:当调用 CreateWindow 时,我得到了有效的指针,当调用它的 GetWidth 时,我也得到了正确的结果。但正如您可能已经注意到的那样,我从未导出任何 Window classes 或函数。据我了解,我不应该调用 GetWidth,但我可以。 Dependencywalker 证实了这一点,并仅向我展示了 CreateWindow 作为导出函数。 我怎样才能调用它呢?包含的头文件只为编译器提供理论上调用函数所需的信息,但由于头文件实际上不包含函数实现,链接器如何知道我函数的地址?

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

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