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

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

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

3 ответов


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

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

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

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

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

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

  class CMyObject: public IExportedMethods { ...

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

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

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

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

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


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

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

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

как сделать последовательные двоичные файлы 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 - transfert распределение собственности (что происходит без вашего согласия при использовании выделения и/или освобождения встроенного кода или STL)
3 - не раздражайтесь проблемами привязанный к тому, что каждый модуль имеет свою собственную версию среды выполнения (т. е. выделение памяти и некоторые глобальные данные, используемые API C или C++)

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


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


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

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

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