我有一个由两部分组成的程序,其中主核需要注册适配器,然后回调到寄存器中。这些部分位于单独的DLL中,并且核心不知道适配器的细节(除了提前的方法和参数)。我尝试使用以下代码进行设置,并在每次核心尝试调用适配器方法时收到段错误:
Core.hpp / cpp(合并和简化):
class Core
{
public:
Core()
: mAdapter(NULL)
{ }
void DoStuff(int param)
{
if ( this->mAdapter )
{
this->mAdapter->Prepare(param);
}
}
void Register(Adapter * adapter)
{
this->mAdapter = adapter;
}
private:
Adapter * mAdapter;
};
Adapter.hpp / cpp(在核心库中):
class Adapter
{
public:
Adapter(Core * core) { }
virtual void Prepare(int param)
{
// For testing, this is defined here
throw("Non-overridden adapter function called.");
}
};
AdapterSpecific.hpp / cpp(第二个库):
class Adapter_Specific
: Adapter
{
public:
Adapter_Specific(Core * core)
{
core->Register(this);
}
void Prepare(int param) { ... }
};
在构建第一个模块(核心)时,所有类和方法都标记为DLL导出,核心标记为export,适配器在构建适配器时作为导入。
代码运行正常,直到调用Core :: DoStuff。从遍历程序集开始,它似乎从vftable中解析了函数,但它最终得到的地址是0x0013nnnn,我的模块在0x0084nnnn及以上范围内。 Visual Studio的源调试器和intellisense显示vftable中的条目是相同的,并且适当的条目确实转到一个非常低的地址(一个也转到0xF-something,这似乎同样奇怪)。
为了清晰起见,编辑:执行永远不会重新进入adapter_specific类或模块。调用的假定地址无效,执行在那里丢失,导致段错误。这不是适配器类中任何代码的问题,这就是我将其排除在外的原因。
我在调试模式下不止一次重建了两个库。这是纯粹的C ++,没什么特别的。我不确定为什么它不起作用,但我需要回调到另一个类,而宁愿避免使用函数ptrs的结构。
有没有办法在模块之间使用这样的简单回调,或者它在某种程度上是不可能的?
答案 0 :(得分:0)
你说你的所有方法都被声明为DLL导出。方法(成员函数)不必以这种方式标记,导出类就足够了。如果你这样做,我不知道这是否有害。
答案 1 :(得分:0)
答案 2 :(得分:0)
这个问题最终成了我的一个愚蠢的错误。
在代码中的另一个函数中,我不小心为Core *
而不是Adapter *
提供了一个函数,并且在我的阅读中没有捕获它。不知怎的,编译器也没有捕获它(它应该失败,但没有给出隐式转换警告,可能是因为它是一个引用计数点)。
试图将Core *
转换为适配器并从该突变对象获取vftable,该对象失败并导致段错误。我把它修好为合适的类型,现在一切正常。
答案 3 :(得分:0)
__declspec(dllexport)
是一个非常糟糕的主意。最好使用一个接口(只包含虚函数的基类,它与你不想要的“函数指针结构”基本相同,除了编译器处理所有细节),并且只使用__declspec(dllexport)
用于工厂功能等全局功能。特别是不要直接跨DLL边界调用构造函数和析构函数,因为你会得到不匹配的分配器,暴露一个包含特殊函数的普通函数。