Как сделать совместимые двоичные файлы DLL для версий VS? - PullRequest
6 голосов
/ 24 октября 2008

Например, winsock libs прекрасно работает во всех версиях Visual Studio. Но у меня есть реальная проблема, чтобы обеспечить согласованный двоичный файл для всех версий. DLL, скомпилированная с VS 2005, не будет работать, если она связана с приложением, написанным в 2008 году. Я обновил 2k5 и 2k8 до SP1, но результаты не сильно изменились. Это работает кое-что хорошо. Но когда они включают это в приложение C #, приложение C # получает ошибки нарушения доступа, но с классическим приложением C ++ оно работает нормально.

Есть ли стратегия, которую я должен знать, когда я предоставляю dll?

Ответы [ 3 ]

10 голосов
/ 24 октября 2008

Во-первых, не передавайте ничего, кроме простых старых данных, через границы DLL. т.е. структуры в порядке. классы нет. Во-вторых, убедитесь, что право собственности не передается, т. Е. Любые структуры, пересекающие границу DLL, никогда не освобождаются за пределами DLL. Итак, если вы экспортируете dll-функцию X * GetX (), то есть соответствующая функция типа FreeX (X *), гарантирующая, что то же время выполнения, которое было выделено, отвечает за перераспределение.

Next: Получите ваши DLL-библиотеки для связи со статической средой выполнения. Составление проекта, включающего dls от нескольких сторонних производителей, каждый из которых связан и ожидает разные среды выполнения, потенциально отличающиеся от времени выполнения, ожидаемого приложением, - это трудная задача, потенциально вынуждающая программу установки устанавливать среды выполнения для 7.0, 7.1, 8.0 и 9.0 - некоторые из которых существуют в разных пакетах обновления, которые могут вызывать или не вызывать проблемы. Будьте добры - статически связывайте свои проекты dll.

- Изменить: Вы не можете экспортировать класс C ++ напрямую с этим подходом. Совместное использование определений классов между модулями означает, что вы ДОЛЖНЫ иметь однородную среду выполнения, поскольку разные компиляторы или версии компиляторов будут генерировать декорированные имена по-разному.

Вы можете обойти это ограничение, экспортировав свой класс вместо этого как интерфейс в стиле COM ... то есть, хотя вы не можете экспортировать класс независимо от среды выполнения, вы МОЖЕТЕ экспортировать "интерфейс" ", который вы можете легко сделать, объявив класс, содержащий только чисто виртуальные функции ...

  struct IExportedMethods {
    virtual long __stdcall AMethod(void)=0;
  };
  // with the win32 macros:
  interface IExportedMethods {
    STDMETHOD_(long,AMethod)(THIS)PURE;
  };

В своем определении класса вы наследуете от этого интерфейса:

  class CMyObject: public IExportedMethods { ...

Вы можете экспортировать такие интерфейсы, создав методы фабрики C:

  extern "C" __declspec(dllexport) IExportedClass* WINAPI CreateMyExportedObject(){
    return new CMyObject; 
  }

Это очень легкий способ экспорта версии компилятора и независимых от класса версий. Обратите внимание, что вы все еще не можете удалить один из них. Вы должны включить функцию release как член dll или интерфейса. Как член интерфейса это может выглядеть так:

  interface IExportedMethods {
    STDMETHOD_(void) Release(THIS) PURE; };
  class CMyObject : public IExportedMethods {
    STDMETHODIMP_(void) Release(){
      delete this;
    }
  };

Вы можете взять эту идею и продолжить работать с ней - унаследовать свой интерфейс от IUnknown, реализовать методы AddRef и Release с подсчетом ссылок, а также возможность QueryInterface для интерфейсов v2 или других функций. И, наконец, используйте DllCreateClassObject в качестве средства для создания вашего объекта и получения необходимой регистрации COM. Все это не является обязательным, однако вы можете легко получить простое определение интерфейса, доступ к которому осуществляется через функцию C.

5 голосов
/ 24 октября 2008

Я не согласен с точкой зрения Криса Бекке, хотя вижу преимущества его подхода.

Недостаток в том, что вы не можете создавать библиотеки служебных объектов, потому что вам запрещено делиться ими между библиотеками.

Расширение решения Криса

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

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

В Windows Win32 API они решали проблему через «обработчики». Вы делаете то же самое по:

1 - Никогда не подвергать структуру. Разоблачать только указатели (то есть указатель void *)
2 - Доступ к этим данным структуры осуществляется через функции , принимающие указатель в качестве первого параметра
3 - указатель этой структуры данные о распределении / освобождении находятся через функции

Таким образом, вы можете избежать перекомпиляции всего, когда ваша структура изменится.

C ++ способ сделать это - PImpl. См http://en.wikipedia.org/wiki/Opaque_pointer

Он имеет то же поведение, что и концепция void * выше, но с помощью PImpl вы можете использовать как RAII, инкапсуляцию, так и прибыль от строгой безопасности типов. Для этого потребуется совместимое оформление (тот же компилятор), но не одно и то же время выполнения или версия (если между версиями одинаковое оформление).

Другое решение?

Надеясь смешать библиотеки DLL из разных версий компиляторов / компиляторов - это либо повод для катастрофы (как вы объяснили в своем вопросе), либо утомительно, поскольку вы позволили отказаться от большинства (если не от всех) решений C ++ в своем коде базовое кодирование C или оба.

Мое решение будет:

1 - убедитесь, что все ваши модули скомпилированы с одинаковым компилятором / версией . Период.
2 - Убедитесь, что все ваши модули скомпилированы для ссылки динамически с тем же временем выполнения
3 - Убедитесь, что у вас есть «инкапсуляция» всех сторонних модулей , над которыми у вас нет контроля (невозможно компилировать с вашим компилятором), как совершенно справедливо объяснил Крис Бекке в Как сделать согласованные двоичные файлы DLL для версий VS? .

Обратите внимание, что не удивительно и не возмутительно, что все модули вашего приложения скомпилированы для одного и того же компилятора и одной и той же версии компилятора.

Не позволяйте никому говорить, что смешивание компиляторов - это хорошо. Это не. Свобода микширования компилятора для большинства людей - та же самая свобода, которой можно наслаждаться, выпрыгивая с вершины здания: вы можете делать это свободно, но обычно вы просто этого не хотите.

Мое решение позволяет вам:

1 - экспортировать классы и, таким образом, создавать настоящие некастрированные библиотеки C ++ (как вы должны делать, например, с __declspec (dllexport) Visual C ++)
2 - передать владение распределением (что происходит без вашего согласия при использовании встроенного и / или освобождающего встроенного кода или STL)
3 - не раздражаться проблемами , связанными с тем фактом, что каждый модуль имеет свою собственную версию среды выполнения (т.е. выделение памяти и некоторые глобальные данные, используемые API C или C ++)

Обратите внимание, что это означает, что вы не должны смешивать отладочную версию своих модулей с выпусками других модулей. Ваше приложение полностью отлажено или полностью выпущено.

2 голосов
/ 24 октября 2008

относительно проблемы прохождения структуры, эта вещь безопасна, пока вы выравниваете свою структуру, такую ​​как:


#pragma pack(push,4)
typedef myStruct {
  int a;
  char b;
  float c;
}myStruct;
#pragma pack(pop)

Вы можете поместить это объявление в заголовочный файл и включить его в оба проекта. Таким образом, у вас не возникнет проблем при прохождении структур.

Кроме того, убедитесь, что вы статически связаны с библиотеками времени выполнения и не пытайтесь делать такие вещи, как выделение памяти (ptr = malloc (1024)) в модуле, а затем освобождение этой памяти в другом модуле (бесплатно (ptr)).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...