Использование библиотек DLL C++ с разными версиями компилятора

этот вопрос связан с " Как сделать последовательные двоичные файлы dll в версиях VS ?"

  • у нас есть приложения и библиотеки DLL с помощью VC6 и нового приложения с VC9. VC9-приложение должно использовать Dll, скомпилированные с VC6, большинство которые написаны на C и one in С.++
  • lib C++ проблематичен из-за название украшения / проблемы с калечением.
  • компиляция всего с VC9 в настоящее время не вариант, как там оказаться некоторые побочные эффекты. Решить эти проблемы было бы достаточно времени всепоглощающий.
  • Я могу изменить библиотеку C++, однако она должна быть скомпилирована с VC6.
  • C++ lib по существу является OO-оболочкой для другой библиотеки C. VC9-app использует некоторые статические функции, а также некоторые нестатические.

в то время как статические функции можно обрабатывать с помощью чего-то вроде

// Header file
class DLL_API Foo
{
    int init();
}

extern "C"
{
    int DLL_API Foo_init();
}

// Implementation file
int Foo_init()
{
    return Foo::init();
}

это не так просто с нестатическими методами.

Как я понимаю, Крис Бечке-х предложение использовать COM-подобный интерфейс не поможет мне, потому что имена членов интерфейса все равно будут оформлены и, следовательно, недоступны из двоичного файла, созданного с другим компилятором. я прав?

единственным решением было бы написать интерфейс DLL в стиле C с помощью обработчиков объектов или я что-то пропустил? В этом случае, я думаю, у меня было бы меньше усилий с прямым использованием обернутой C-библиотеки.

5 ответов


имена элементов интерфейса не быть украшенным - они просто смещения в vtable. Вы можете определить интерфейс (используя структуру C, а не COM-интерфейс) в файле заголовка, таким образом:

struct IFoo {
    int Init() = 0;
};

затем вы можете экспортировать функцию из DLL, без искажения:

class CFoo : public IFoo { /* ... */ };
extern "C" IFoo * __stdcall GetFoo() { return new CFoo(); }

это будет работать нормально, при условии, что вы используете компилятор, который генерирует совместимые vtables. Microsoft c++ сгенерировал тот же формат vtable С (по крайней мере, I думаю) MSVC6.1 для DOS, где vtable-это простой список указателей на функции (с thunking в случае множественного наследования). GNU C++ (если я правильно помню) генерирует vtables с указателями функций и относительными смещениями. Они несовместимы друг с другом.


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

Я предполагаю, что вы можете пройти мимо имени mangling (и соглашения о вызове), что не сложно, если вы используете компилятор с совместимым mangling (я думаю, что VC6 широко совместим с VS2008), или если вы используете extern "C".

где вы столкнетесь с проблемами, когда вы выделяете что-то с помощью new (или malloc) из DLL, а затем вы возвращаете это вызывающему объекту. Звонивший delete (или free) попытается освободить объект из другой кучи. Это будет ужасно неправильно.

вы можете либо сделать COM-style IFoo::Release, или MyDllFree() вещь. Оба из них, поскольку они возвращаются в DLL, будут использовать правильную реализацию delete (или free()), поэтому они удалят правильный объект.

или вы можете убедиться, что используете LocalAlloc (для пример), так что EXE и DLL используют одну и ту же кучу.


Ну, я думаю предложение Криса Бекке это просто прекрасно. Я бы не использовал Роджер, который использует интерфейс только по имени и, как он упоминает, может столкнуться с проблемами несовместимой компиляторной обработки абстрактных классов и виртуальных методов. Роджер указывает на привлекательный com-последовательный случай в его на.

  1. точка боли: вам нужно научиться делать запросы com-интерфейса и правильно обращаться с IUnknown, опираясь на по крайней мере IUnknown:AddRef и IUnknown:Release. Если реализации интерфейсов могут поддерживать более одного интерфейса или если методы также могут возвращать интерфейсы, вам также может потребоваться освоиться с IUnknown:QueryInterface.

  2. вот ключевая идея. Все программы, использующие реализацию интерфейса (но не реализующие его), используют общий #include "*.H " файл, определяющий интерфейс как структуру (C) или класс C / C++ (VC++) или struct (не VC++, а C++). Этот.* H файл автоматически адаптируется соответствующим образом в зависимости от того, компилируется ли программа языка C или программа языка C++. Вам не нужно знать об этой части, чтобы просто использовать *.H-файл. Что за *.H файл определяет структуру или тип интерфейса, скажем, IFoo, с его виртуальными функциями-членами (и только функциями, без прямой видимости для членов данных в этом подходе).

  3. файл заголовка построен в честь двоичный стандарт COM таким образом, который работает для C и работает для C++ независимо от используемого компилятора C++. (Народ Java JNI понял это.) Это означает, что он работает между отдельно скомпилированными модулями любого происхождения, пока структура, состоящая полностью из указателей ввода функций (vtable), сопоставляется с памятью одинаково всеми из них (поэтому они должны быть все x86 32-битные или все x64, например).

  4. в DLL, которая реализует COM интерфейс через какой-то класс-оболочку, вам нужна только заводская точка входа. Что-то вроде

    extern " C " HRESULT MkIFooImplementation (void * * ppv);

который возвращает HRESULT (вам также нужно узнать о них), а также вернет *pv в месте, которое вы предоставляете для получения указателя интерфейса IFoo. (Я бегло просматриваю, и здесь вам понадобятся более подробные сведения. Не доверяйте моему синтаксису) фактический стереотип функции что вы используете для этого и объявлен в *.H-файл.

  1. дело в том, что заводская запись, которая всегда является undecorated extern "C", выполняет все необходимое создание класса оболочки, а затем доставляет указатель интерфейса Ifoo в указанное вами местоположение. Это означает, что все управление памятью для создания класса, и все управление памятью для его доработки и т. д., произойдет в DLL, где вы создаете оболочку. Это единственное место, где ты придется разобраться с этими деталями.

  2. когда вы получаете результат OK от заводской функции, вам был выдан указатель интерфейса, и он уже зарезервирован для вас (существует неявная операция IFoo:Addref, уже выполненная от имени указателя интерфейса, который вы были доставлены).

  3. когда вы закончите с интерфейсом, вы отпустите его с вызовом метода IFoo: Release интерфейса. Это окончательный релиз реализация (в случае, если вы сделали больше копий Addref'D), которая снесет класс и его поддержку интерфейса в заводской DLL. Это то, что позволяет вам правильно полагаться на согласованное динамическое распределение и выпуск stoorage за интерфейсом, независимо от того, использует ли DLL, содержащая заводскую функцию, те же библиотеки, что и вызывающий код.

  4. вы, вероятно, должны реализовать IUnknown:QueryInterface (как метод IFoo: QueryInterface) тоже, даже если он всегда терпит неудачу. Если вы хотите быть более сложным с использованием модели двоичного интерфейса COM, поскольку у вас больше опыта, вы можете научиться предоставлять полные реализации QueryInterface.

Это, вероятно, слишком много информации, но я хотел бы отметить, что многие проблемы, с которыми вы сталкиваетесь о гетерогенных реализациях DLL, решаются в определении двоичного интерфейса COM, и даже если вам не нужно все это, тот факт, что он предоставляет отработанные решения, является ценный. По моему опыту, как только вы освоитесь с этим, вы никогда не забудете, насколько мощным это может быть в ситуациях взаимодействия C++ и C++.

Я не набросал ресурсы, которые вам могут понадобиться, чтобы проконсультироваться для примеров и что вы должны узнать, чтобы сделать *.H файлы и фактически реализовать заводские функции оболочки библиотек, которые вы хотите поделиться. Если хочешь копнуть глубже, кричи.


есть и другие вещи, которые вам нужно учитывать, например, какие времена выполнения используются различными библиотеками. Если объекты не разделяются, это нормально, но на первый взгляд это кажется маловероятным.
Предложения Криса Беккера довольно точны-с помощью фактический COM интерфейс может помочь вам получить двоичную совместимость вам нужно. Ваш пробег может отличаться:)


не весело. вы находитесь в ДЛЯ много разочарования, вы, вероятно, должны дать это:

единственным решением было бы написать Интерфейс DLL в стиле C с помощью обработчиков к предметам или я пропал? что-то? В таком случае, полагаю, я вероятно, было бы меньше усилий с непосредственно используя обернутую C-библиотеку.

очень внимательно посмотреть. удача.