2010-11-30 5 views
0

У меня есть двухчастная программа, в которой основное ядро ​​должно иметь зарегистрированный адаптер, а затем перезвонить в регистр. Части находятся в отдельных DLL, а ядро ​​не знает особенностей адаптера (помимо методов и параметров раньше времени). Я попытался установить его с помощью следующего кода и ПОЛУЧИТЕ Segfault каждый раз, когда ядро ​​пытается вызвать метод адаптера:Segfault при вызове виртуальной функции в другой 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 при построении первого модуля (ядра), а ядро ​​помечено как экспорт, адаптер как импорт при построении адаптера.

Код работает до тех пор, пока не будет вызываться Core :: DoStuff. Пройдя через сборку, кажется, что она разрешает функцию из vftable, но адрес, в котором он заканчивается, равен 0x0013nnnn, а мои модули находятся в диапазоне 0x0084nnn и выше. Исходный отладчик Visual Studio и intellisense показывают, что записи в vftable одинаковы, а подходящий - на очень низкий адрес (один также переходит в 0xF-что-то, что кажется нечетным).

Редактирование для ясности: Выполнение никогда не вводится в класс или модуль адаптера. Предполагаемый адрес для вызова недействителен, и выполнение там теряется, что приводит к segfault. Это не проблема с каким-либо кодом в классе адаптера, поэтому я оставил это.

Я перестраивал обе библиотеки более одного раза в режиме отладки. Это чистый C++, ничего необычного. Я не уверен, почему это не сработает, но мне нужно перезвонить в другой класс и лучше избегать использования функции struct ptrs.

Есть ли способ использовать аккуратные обратные вызовы, подобные этому между модулями, или это как-то невозможно?

+0

Почему люди загромождают код с помощью «this->»? – 2010-11-30 21:54:06

+0

Это не фактический код, просто применимые части слегка переформулированы (структура не изменилась). Они находятся в некоторых частях реального кода, где это может быть неправильно прочитано. – ssube 2010-11-30 21:55:55

+0

@VJo: явный всегда лучше, чем неявный – Falmarri 2010-11-30 21:56:20

ответ

0

Проблема закончилась тем, что была глупой ошибкой с моей стороны.

В другой функции в коде я случайно использовал функцию Core * вместо Adapter * и не поймал ее в своем прочтении. Каким-то образом компилятор тоже не поймал его (он должен был потерпеть неудачу, но не было дано неявное предупреждение о броске, возможно, потому, что оно было подсчитанной точкой отсчета).

Это попыталось превратить Core * в адаптер и получить vftable от этого мутантного объекта, который потерпел неудачу и привел к segfault. Я исправил его как подходящий тип, и теперь все работает отлично.

0

Вы сказали, что все ваши методы объявлены экспортом DLL. Методы (функции-члены) не должны быть отмечены таким образом, достаточно экспортировать класс. Я не знаю, если это вредно, если вы это сделаете.

0
  1. Вы используете много указателей. Где их жизнь управляется?
  2. Adapter_Specific наследуется конфиденциальным от адаптера в вашем примере
  3. адаптера имеет виртуальный метод так, вероятно, нуждается в виртуальный деструкторе слишком
  4. Адаптер также не имеет конструктора не по умолчанию, так что конструктор Adapter_Specific не будет компилироваться. Возможно, вам понадобится построить базовый класс с тем же параметром. Это происходит не автоматически. Однако см. Пункт 6.
  5. Конструкторы, которые принимают ровно один параметр (кроме конструктора копирования), обычно должны быть объявлены явным.
  6. Адаптер базового класса принимает параметр, который он не использует.
0

__declspec(dllexport) по классам и классам - очень плохая идея. Гораздо лучше использовать интерфейс (базовый класс, содержащий только виртуальные функции, он по существу тот же, что и «конструктор указателей функций», который вам не нужен, за исключением того, что компилятор обрабатывает все детали), и используйте __declspec(dllexport) только для глобальных функций, таких как как заводские функции. Особенно не вызывайте конструкторы и деструкторы непосредственно через границы DLL, потому что вы получите несовпадающие распределители, выставляете обычную функцию, которая обертывает специальные функции.

Смежные вопросы