可视化C++编译器/链接器在使用运行时 DLL 加载时执行的操作
visual What C++ compiler/linker does when using runtime DLL loading?
我想了解 DLL 机制以及编译器在运行时加载 DLL 时的作用(即我不将使用生成的.lib
(。
请考虑以下C++代码:
DLL 接口头文件
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif
class MYDLL_API Base
{
public:
Base();
virtual ~Base();
virtual int get_number() const;
virtual const char* what() const = 0;
private:
int i_;
};
class MYDLL_API Child : public Base
{
public:
Child();
virtual ~Child();
virtual int get_number() const override;
virtual const char* what() const override;
private:
int j_;
};
extern "C" {
MYDLL_API Base* __cdecl initializeObject();
}
DLL 实现源文件
#include "MyDLL.hh"
Base::Base()
: i_(42)
{}
Base::~Base()
{}
int Base::get_number() const
{
return i_;
}
Child::Child()
: Base()
, j_(24)
{}
Child::~Child()
{}
int Child::get_number() const
{
return j_;
}
const char* Child::what() const
{
return "Hello!";
}
Base* initializeObject()
{
return new Child();
}
此 DLL 的目标是具有由 Base
类定义的公共接口,但它允许在运行时加载的不同 DLL 中编译的特定实现(此处公开了 Child 类以用于示例的目的(。
在这个阶段,如果我天真地包含 DLL 的标头:
#include "MyDLL.hh"
int main()
{
Base* b = new Child();
std::cout << b->get_number() << std::endl;
std::cout << b->what() << std::endl;
delete b;
getchar();
return 0;
}
链接器抱怨LNK2019
和LNK2001
错误:它无法解析符号。因此,它的行为符合预期(我没有使用.lib
(。
现在,请考虑以下用于在运行时加载 DLL 的代码:
#include "MyDLL.hh"
typedef Base* (*initFuncType)();
int main()
{
HINSTANCE handle = LoadLibrary(L"MyDLL.dll");
initFuncType init = nullptr;
init = (initFuncType)(GetProcAddress(handle, "initializeObject"));
if (init)
{
Base* b = init(); //< Use init() !
std::cout << b->get_number() << std::endl;
std::cout << b->what() << std::endl;
delete b;
}
getchar();
FreeLibrary(handle);
return 0;
}
这一次它起作用了,联动完成了。
- 第一个问题:发生了什么?编译器和链接器发生了哪些变化?在 initializeObject(( 上使用函数指针解决了这个问题。
我不太了解的另一个问题是当我删除virtual
和override
get_number()
时:
int get_number() const;
由于_main
函数中未解析的Base::get_number(void) const
符号,我有一个LNK2019
错误。我知道 virtual
关键字将动态解析成员函数(在运行时(。在我们的例子中,DLL尚未加载,get_number
符号不可用。
第二个问题:这是否意味着必须始终使用 DLL 运行时链接
virtual
方法?第三个问题:如何使用 Windows API 导出C++函数?这样我就可以删除
extern "C" { ... }
的东西。
感谢您的阅读!我希望我能读到有趣的答案!:)
有两种方法可以链接 dll 文件。
第二种方式(它的工作方式(是 C 绑定方法,您可以在其中查询 dll 以获取特定的函数名称,它会向您返回一个函子。
使用第二种方式,您将无法扩展基类,因为它们未定义(您没有任何代码可以复制粘贴,因此可以在链接时进行(。
为了有一个可以扩展其类的 dll,您需要使用动态绑定。您需要编译.dll并提供符号库(或导出库(。您可以在项目属性的 VS 工作室中使用此选项。
机制如下:
- 编译 Dll 项目 -> 输出 : myLib.dll , myLib.lib
- 在主项目中使用从 myLib.lib 导出的符号(主项目将 myLib.lib 作为依赖项(
- 在运行时,由于绑定,您的程序会知道它需要 myLib.dll 才能工作,因此它将加载它(如果找到,否则会出现运行时错误(
使用导出库的另一个优点是可以导出C++函数(导出时会损坏(。
很难对损坏的函数进行 C 绑定。
另一方面,与动态绑定相比,C 绑定不会让你的程序尖叫.dll如果找不到 myLib,你只会得到一个指向函数的空指针。
- Python ctypes:不会按预期加载 dll
- 在 Linux 平台的 C++ 中动态加载 DLL
- 为什么 LoadLibrary 失败,而 LoadLibraryA 成功加载 DLL?
- 为什么有些延迟加载 DLL 会立即使用 FFMPEG 卸载?
- 了解加载库无法加载 DLL 的原因
- 应用程序仅在qt调试器中加载dll,而不是独立加载
- dll 生成成功,但在加载 dll 时执行错误
- QT 加载.dll不调用解析
- 通过重新加载DLL,避免使用DLL预加载漏洞,任何更好的方法
- 问题在具有依赖性时从其他路径上加载DLL的DLL
- C++插件不能延迟加载dll时
- Java加载DLL,该DLL从JNI中的另一个DLL导出方法
- C++ 动态加载 DLL 错误
- GHCi 无法在 Windows 上加载.dll lib(C++ lib)
- 使用C++应用程序加载 DLL
- System.DllNotFoundException:无法使用 dotnet core 加载 DLL
- 当应用程序在不同系统上运行时,无法加载DLL异常
- 如何停止二级IMPLICITLY加载DLL的DLL劫持
- 无法加载dll libglorycolx2010.dll.该应用程序未能启动,因为其并排配置不正确.Hresult:0x
- 如何在Acrobat JavaScript中加载DLL文件