На прошлой неделе столкнулся с интересной проблемой DLL Hell'а.
Чтобы решить проблему с библиотеками, в Windows XP появился механизм side-by-side assemblies. В системе может быть установлено несколько версий одной и той же библиотеки, а каждое приложение должно явным образом указать, какую версию следует загрузить. Такой механизм явного объявления зависимостей устраняет ошибки из-за загрузки несовместимой версии библиотеки.
Но не все так гладко… Начиная с Visual Studio 2005, библиотеки С++ runtime поставляются в виде side-by-side assemblies, а компилятор/линкер автоматически создают manifest файл, в котором указываются эти зависимости.
Проблемы возникают, когда приложение использует DLL, которые собираются не из исходного кода. Например, если приложение и используемые библиотеки были откомпилированы разными версиями Visual Studio, в системе должны будут находится две (или даже больше) разных версий C++ runtime.
Вот именно с такой проблемой я и столкнулся. Сначала обнаружилось, что у нас две зависимости: от версии 8.0.50608.0 и от 8.0.50727.762. А через несколько дней среди зависимостей вдруг появилась еще и третья версия 8.0.50727.4053.
Раньше библиотеки Visual C++ вместе с манифестом лежали в папке с остальными библиотеками, и всё работало. (То есть они использовались как private assembly.) Недавно выяснилось впрочем, что не совсем всё работало: обнаружилось, что на тестовых машинах были установлены библиотеки версии 8.0.50727.42. А если вдруг их не оказывалось, то ничего не запускалось.
Две разных версии assembly не могут одновременно находится в папке с приложением, потому что файлы называются одинаково. Как обойти это, описано в статье A solution to two references to different versions of CRT, MFC, ATL in one application manifest file. Workaround #2 не помогал, потому что зависимости росли не из EXE-файла, а из DLL. В комментариях нашлось решение и для DLL, которое более подробно описано здесь. После добавления файликов <dll-name>.2.config приложение заработало.
Но с появлением зависимости от третьей версии, которая появилась после установки обновления, пришлось бы создавать такие файлы почти для каждой DLL-библиотеки, а их не мало. В итоге, чтобы жизнь была проще, был выбран первый вариант решения: использование Merge-модулей, тем более для установки уже использовался Windows Installer. Merge-модули устанавливают не только сами библиотеки, но и policy-файл, который «перенаправляет» предыдущие версии на новую версию. Таким образом, поставка всего одной версии библиотек и установка глобальной политики перенаправления версий решило все проблемы с зависимостями и избавило от создания кучи config-файлов с одинаковым содержанием.
На эти исследования пришлось потратить почти два дня, зато теперь в арсенале имеются новые знания.