如何在导出C++类的DLL中使用延迟加载

12

我有一个DLL one.dll,它使用从two.dll导出的TwoClass类,通过class __declspec(dllexport)。 我想让one.dll使用/delayload来加载two.dll,但是我得到了链接错误:

LINK : fatal error LNK1194: cannot delay-load 'two.dll' due to import
of data symbol '"__declspec(dllimport) const TwoClass::`vftable'"
(__imp_??_7TwoClass@@6B@)'; link without /DELAYLOAD:two.dll

在Release版本中出现了问题,在Debug版本中可以正常工作。(我不知道Release和Debug版本在vtable导出方面的区别,并且也找不到任何编译器开关或编译指示来控制它。)

在Release版本中如何使用/delayload与像这样导出类的DLL?

4个回答

7

看一下这里, 看起来这个人遇到了完全相同的问题,并找到了解决方法。

我设法使延迟加载在发布版本中正常工作,方法是禁用使用SomeClass类的翻译单元上的优化 - 以某种方式它消除了对导出vtable的依赖。


1
那里的解决办法是关闭优化,虽然能起作用,但并不理想——我需要在我的代码的大子集上这样做。如果有办法控制影响虚函数表的具体优化,那就太好了,但我找不到一个。 - RichieHindle
@RichieHindle - 同意,这就是为什么我没有称其为解决方案的原因 :) - davka
1
似乎这可能是由需要调整器thunks的优化引起的 - 参见https://dev59.com/k0jSa4cB1Zd3GeqPEE_2(这些类是否通过继承特别相关,MI?) - MSalters
2
将“删除未引用的代码和数据”选项设置为“否”允许我保持优化启用。参考 - notbad.jpeg

1
检查one.dll是否包含一个源文件,其中包括TwoClass.hxx但实际上未使用它。此外,请检查TwoClass是否符合编译器生成方法的条件(请参阅自动生成条件)。
在我的情况下,我实际上不需要TwoClass的编译器生成副本构造函数或赋值运算符,因此我将它们声明在private:部分而没有提供定义。这为one.dll创建了构建错误,指导我找到了不必要地包含TwoClass.hxx的源文件。删除不必要的包含后,我能够开启优化并使用/delayload编译和链接。
我假设不必要的#include语句误导了优化器将TwoClass的编译器生成方法复制到.obj文件中以链接到one.dll,即使这些.obj文件中没有使用它们。这些不必要的TwoClass编译器生成方法似乎会阻止使用/delayload进行链接。

0
定义一个工厂函数,它可以分发类的实例,就像在COM中一样。这也要求类的接口是公共的,但当某人导入一个类时,这也是一个常识。

1
是的。在类上使用__declspec(dllexport)是一个可怕的想法,更好的方法是暴露接口并让实现细节保持在模块内部隐藏。 - Ben Voigt
这是一种更好的风格,但这能解决OP在“delayload”方面的问题吗? - davka
1
@davka:是的。原因很简单:你只能通过这种方法导出函数,而不能导出数据(这最终是延迟加载中的问题)。 - 0xC0000022L

0

我曾经遇到过一个问题,就是一个类中包含了导出类的内联实现。

class __declspec(dllimport) VidExpInternal : public VidExpBase
{
public:
    VidExpInternal(TCHAR* msg=_T(""), int ln=__LINE__, TCHAR* filechar=_T(__FILE__)) :
        VidExpBase (msg,ln,filechar) {}

我已将内联实现移动到.cpp文件中 - 之后一切都很顺利。

class __declspec(dllimport) VidExpInternal : public VidExpBase
{
public:
    VidExpInternal(TCHAR* msg=_T(""), int ln=__LINE__, TCHAR* filechar=_T(__FILE__));

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