Подтвердить что ты не робот

Работа с файлами манифеста Visual С++

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

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

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

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

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

Это хорошо и хорошо. Приложения больше не должны падать таинственно. Однако...

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

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

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

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

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

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

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

Для справки:

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

Почему это плохо?

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

Явный подход

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

Почему Linux не имеет этого механика?

У меня есть несколько догадок. (Это на самом деле просто гадание...)

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

Ответ 2

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

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

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

Ответ 3

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

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

Ответ 4

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

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

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

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