Работа с файлами манифеста Visual Studios C ++ - PullRequest
31 голосов
/ 26 февраля 2009

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Ответы [ 4 ]

27 голосов
/ 26 февраля 2009

Все компоненты в вашем приложении должны иметь одинаковое время выполнения. Если это не так, вы сталкиваетесь со странными проблемами, такими как утверждение операторов 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, ...)
  • не используйте сложные механизмы C ++ через границы dll. (typeinfo, исключения C ++, ...)
  • ...

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

Манифест подход

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

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

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

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

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

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

3 голосов
/ 26 февраля 2009

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

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

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

1 голос
/ 26 февраля 2009

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

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

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

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

...