Использование C ++ DLL с различными версиями компилятора - PullRequest
6 голосов
/ 01 декабря 2008

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

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

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

// 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 ]

5 голосов
/ 01 декабря 2008

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

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

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

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

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

3 голосов
/ 02 декабря 2008

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

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

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

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

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

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

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

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

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

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

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

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

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

3 голосов
/ 01 декабря 2008

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

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

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

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

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

1 голос
/ 01 декабря 2008

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

0 голосов
/ 01 декабря 2008

не весело, чувак. Вы сильно разочарованы, вы, вероятно, должны сказать следующее:

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

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

...