Вопрос

Я написал некоторый код, который использует библиотеку с открытым исходным кодом для выполнения некоторой тяжелой работы.Эта работа была выполнена в Linux с помощью модульных тестов и cmake, которые помогли перенести ее на Windows.Существует требование, чтобы он был запущен на обеих платформах.

Мне нравится Linux, и мне нравится cmake, и мне нравится, что я могу автоматически генерировать файлы Visual Studios.Как и сейчас, в Windows все будет компилироваться, связываться и генерировать тестовые исполняемые файлы.

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

Насколько я понимаю,:

В версии VS 2005 Microsoft создала параллельные библиотеки DLL.Мотивацией для этого является то, что раньше несколько приложений устанавливали разные версии одной и той же библиотеки dll, что приводило к сбою ранее установленных и работающих приложений (т.Е. к "Аду DLL").Параллельные библиотеки dll исправляют это, поскольку теперь к каждому исполняемому файлу / dll добавляется "файл манифеста", который указывает, какая версия должна быть выполнена.

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

Похоже, Microsoft выпускает новый набор системных библиотек DLL с каждым выпуском Visual Studios.Кроме того, как я упоминал ранее, я разработчик, пытающийся создать ссылку на стороннюю библиотеку.Часто эти вещи распространяются в виде "предварительно скомпилированной библиотеки DLL".Теперь, что происходит, когда предварительно скомпилированная библиотека dll, скомпилированная с помощью одной версии Visual studios, связана с приложением, использующим другую версию Visual Studios?

Судя по тому, что я прочитал в Интернете, случаются плохие вещи.К счастью, я не зашел так далеко - я бегал в "MSVCR80.dll не найден" проблемы, когда при запуске исполняемого и так началось мое погружение в этот вопрос весь манифест.

В конце концов я пришел к выводу, что единственный способ заставить это работать (помимо статического связывания всего) заключается в том, что все сторонние библиотеки должны быть скомпилированы с использованием одной и той же версии Visual Studios - т. Е. не использовать предварительно скомпилированные библиотеки dll - загрузите исходный код, создайте новую библиотеку dll и используйте ее вместо этого.

Действительно ли это так?Я что-то пропустил?

Более того, если кажется, что это так, то я не могу не думать, что Microsoft сделала это специально по гнусным причинам.

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

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

Наконец, подведем итог:Можно ли использовать предварительно скомпилированные двоичные файлы с этой новой схемой манифеста?Если это так, то в чем была моя ошибка?Если это не так, действительно ли Microsoft считает, что это упрощает разработку приложений?

Обновление - Более краткий вопрос: Как Linux избегает использования файлов манифеста?

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

Решение

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

Это одно и то же на всех платформах.Это не что-то, что изобрела Microsoft.

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

a.dll
    dllexport void* createBla() { return malloc( 100 ); }

b.dll
    void consumeBla() { void* p = createBla(); free( p ); }

Когда a.dll и b.dll связаны с разными rumtimes, это приводит к сбою, потому что функции среды выполнения реализуют свою собственную кучу.

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

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

Для справки :

  • не выделяйте / освобождайте память / объекты через границы модуля
  • не используйте сложные объекты в вашем интерфейсе dll.(например,std:: строка, ...)
  • не используйте сложные механизмы C ++ за пределами библиотеки dll.(typeinfo, исключения C ++, ...)
  • ...

Но это не проблема с манифестами.

Манифест содержит информацию о версии среды выполнения, используемой модулем, и встраивается компоновщиком в двоичный файл (exe / dll).Когда приложение загружено и необходимо устранить его зависимости, загрузчик просматривает информацию манифеста, встроенную в исполняемый файл, и использует соответствующую версию библиотеки DLL среды выполнения из папки WinSxS.Вы не можете просто скопировать среду выполнения или другие модули в папку WinSxS.Вы должны установить среду выполнения, предлагаемую Корпорацией Майкрософт.Существуют пакеты MSI, поставляемые корпорацией Майкрософт, которые могут быть запущены при установке программного обеспечения на тестовый компьютер / компьютер конечного пользователя.

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


(Обновлено до вопроса "Как Linux избегает использования файлов манифеста")

Что такое файл манифеста?

Файлы манифеста были введены для размещения информации об устранении неоднозначности рядом с существующим исполняемым файлом / библиотекой динамических ссылок или непосредственно встроены в этот файл.

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

(Есть несколько других вещей, которые вы можете сделать с файлами манифеста, напримернекоторые метаданные могут быть помещены здесь)

Зачем это делается?

Версия не является частью имени библиотеки dll по историческим причинам.Таким образом, "comctl32.dll" называется именно так во всех его версиях.(Таким образом, comctl32 под Win2k отличается от comctl32 в XP или Vista).Чтобы указать, какая версия вам действительно нужна (и на которой вы тестировали), вы помещаете информацию о версии в файл "appname.exe.manifest" (или вставляете этот файл / информацию).

Почему это было сделано именно так?

Многие программы устанавливали свои библиотеки DLL в каталог system32 на systemrootdir.Это было сделано для того, чтобы исправления ошибок в совместно используемых библиотеках можно было легко развертывать для всех зависимых приложений.А во времена ограниченной памяти общие библиотеки сокращали объем памяти, когда несколько приложений использовали одни и те же библиотеки.

Этой концепцией злоупотребляли многие программисты, когда они устанавливали все свои библиотеки dll в этот каталог;иногда происходит перезапись более новых версий совместно используемых библиотек более старыми.Иногда библиотеки незаметно меняли свое поведение, так что зависшие приложения выходили из строя.

Это приводит к подходу "Распространять все библиотеки DLL в каталоге приложения".

Почему это было так плохо?

При появлении ошибок все библиотеки DLL, разбросанные по нескольким каталогам, должны были быть обновлены.(gdiplus.dll) в других случаях этого не было вообще возможно (Компоненты Windows)

Очевидный подход

Такой подход решает все вышеперечисленные проблемы.Вы можете установить библиотеки DLL в центральном месте, где программист не сможет вмешиваться.Здесь библиотеки DLL могут быть обновлены (путем обновления библиотеки DLL в папке WinSxS), и загрузчик загружает "правильную" библиотеку DLL.(сопоставление версий выполняется dll-загрузчиком).

Почему в Linux нет такой механики?

У меня есть несколько предположений.(На самом деле это просто предположение ...)

  • Большинство программ имеют открытый исходный код, поэтому перекомпиляция для исправления ошибок не является проблемой для целевой аудитории
  • Поскольку существует только одна "среда выполнения" (среда выполнения gcc), проблема с разделением среды выполнения / границами библиотек возникает не так часто
  • Многие компоненты используют C на уровне интерфейса, где эти проблемы просто не возникают, если все сделано правильно
  • Версия библиотеки в большинстве случаев встроена в имя ее файла.
  • Большинство приложений статически привязаны к своим библиотекам, поэтому никакого DLL-ада возникнуть не может.
  • Среда выполнения GCC поддерживалась на очень высоком уровне, чтобы эти проблемы не могли возникнуть.

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

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

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

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

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

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

В конце концов я пришел к выводу, что единственный способ заставить это работать (помимо статического связывания всего) заключается в том, что все сторонние библиотеки должны быть скомпилированы с использованием одной и той же версии Visual Studios - т. Е. не использовать предварительно скомпилированные библиотеки dll - загрузите исходный код, создайте новую библиотеку dll и используйте ее вместо этого.

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

...насколько я понимаю.:)

(Моя сфера деятельности не связана с Windows, хотя время от времени мы боремся с библиотеками DLL в Windows с точки зрения пользователя, однако нам приходится использовать определенные версии компиляторов и получать версии программного обеспечения сторонних производителей, которые все созданы с помощью одного и того же компилятора.К счастью, все поставщики, как правило, остаются в курсе последних событий, поскольку они оказывают подобную поддержку уже много лет.)

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