Неправильный макет VTable для класса, экспортируемый DLL: запрос на разъяснение в отношении строительства заголовков и VTable

StackOverflow https://stackoverflow.com/questions/3453402

Вопрос

Хотя проблема под рукой решается, у него немного запутано относительно того, какие данные используются для построения VTables для класса и где хранятся макет для VTable. Если кто-то может предоставить уточнение или указать мне на некоторую информацию, которая может наделить мое любопытство, я бы очень признателен.

Фон

  1. Два отдельных проекта VC6.0: один для EXE, один для DLL.
  2. Приложение содержит .lib, .dll и файлы .h из выпуска проекта DLL.
  3. Когда новый релиз готов, файлы .lib, .dll и .h скопирован в проект EXE из проекта DLL.
  4. Я унаследовал эту схему.

Проблема:

Недавно я внес изменения в DLL и скопировал над .Lib и .dll, но забыл скопировать файлы заголовка. Отказ Была изменяется некоторая иерархия и, в результате, VTable для одного конкретного класса (позвони InternalClass) изменился.

EXE делает не напрямую вызывать какие-либо методы на InternalClass. Отказ Вместо этого он создает экземпляр другого класса (позвони InterfaceClass) который инкапсулирует указатель на InternalClass Объект и вызывает различные методы против этого указателя.

Во время выполнения звонки изнутри InterfaceClass методы InternalClass методы фактически вызывали неправильные методы (то есть InterfaceClass будет вызываться InternalClass::A и InternalClass::B на самом деле запустится). Глядя на то как м, оказывается, что фиксация или Thunk (извините, если это неправильный жаргон!) Использовал правильный компенсировать в vtable. Однако, сам VTable содержал список указателей, которые вы ожидаете от старый заголовок файлов.

Фактический вопрос:

Я понял свою ошибку, скопировал файлы заголовка, перекомпилированные и все было хорошо. Однако, Я остался с одной ножкой:

Когда находится расположение для определения VTables и какая информация используется для построения VTable для этих классов во время выполнения? То есть мне кажется, что правильный VTable должен быть собран, когда DLL была скомпилирована так, чтобы смещения и т. Д. Могут быть использованы для вызовов от InterfaceClass к InternalClass. Отказ Почему тогда было устаревшим VTable, используемым во время выполнения? Это макет также Определяется отдельно, когда EXE скомпилирован с помощью заголовков у него есть?

Я не уверен, что это совсем ясно. Дайте мне знать, если то, что я прошу, это слишком запутано. Спасибо!

Это было полезно?

Решение

Хм, Сверхмощные подробности реализации 12-летнего компилятора. При использовании __DECLSPEC (DLLEXPORT) на классе линкер экспортирует участников класса. Не VTable. Которые реконструируются в клиенте класса, когда компилятор анализирует объявление класса в файле заголовка. Линкер заполняет этот локальный VTable с экспортированными адресами участников.

Будет работать лучше, если компилятор просто экспортировал VTable из DLL? Возможно, но экспортировка таких деталей реализации ужасно ломко. Страх рисовать себя в угол, который вы не можете выбраться из-за обратной совместимости - это сильный мотиватор, я представляю.

Другие советы

То, как ваше описание взаимодействия этих двух классов не совсем понятно на одной очень важной детали: ваш так называемый InternalClass быть экспортированным из DLL или нет? Или поставить вопрос другого способа: код в этих копированных файлах заголовков использует указатели на InternalClass или есть все Знание этого класса, скрытого исключительно в DLL?

Если этот класс в вопросе на самом деле полностью скрыт в DLL, то я не уверен, как может произойти эта проблема. Но из того, что вы описываете, это действительно звучит так, как будто InternalClass подвергается воздействию DLL.

Если эти заголовки, которые копируются сверху, содержат код, похожие в концепции для этого:

class InterfaceClass
{
    InternalClass* Internal;

    void DoSomething()
    {
        Internal->DoSomething();
    }
};

Тогда InternalClass Должно быть выставлено, в противном случае проект EXE не сможет скомпилировать заголовок. (Так что это не совсем внутреннее в конце концов.)

И если это так, то он легко объясняет вашу проблему. EXE строится против другой декларации InternalClass чем была построена DLL. Это означает, что каждый означает, что каждый компилирует свою собственную версию виртуальной таблицы, и эти версии разные.

Затем во время выполнения, код EXE передан объекту, созданному из в DLL, и, таким образом, с его указателем VTBAL, указывающей на версию DLL. Затем код EXE пытается работать через этот указатель VTable, как если бы он указал на версию EXE VTable. И очевидно, что вызывает проблемы.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top