DLL-Связывание. Поиск DLL. Манифесты.
От: Rakafon Украина http://rakafon.blogspot.com/
Дата: 30.09.09 08:55
Оценка:
Добрый день, коллеги!
Меня вот какой мучает вопрос.
Каким образом можно конфигурировать и менять механизм поиска зависимых DLL модулей, прописанных в разделе импорта EXE или DLL модуля?

Поскольку в разделе импорта любого модуля указано только имя DLL, а пути нету, то загрузчику приходится самому искать ее на диске. По старинке, поиск DLL осуществляется в следующей последовательности:
  • Каталог, содержащий EXE-файл.
  • Текущий каталог процесса.
  • Системный каталог Windows.
  • Основной каталог Windows.
  • Каталоги, указанные в переменной окружения PATH.

    Так вот ... могу ли я в этот механизм внести свои изменения/дополнения ...? Может быть с помощью манифестов ...? Ведь в манифестах прописывают инфу о том где искать CRT-шные DLL-ки, какую версию Common Controls подгружать и так далее ... Но скока я не рыл по поводу манифестов, везде инфа только о том как подключать Shared DLL типа msvcr**.dll, msvcp**.dll, atl**.dll, cmnctrls.dll, настраивать security, и так далее, а вот толкового мануала о том, что за инфу вообще можно запихать в манифест, как можно ссылаться там на сторонние DLL модули и пр., никак и нигде найти не могу ... :(
    Я просто хочу чтоб все DLL приложения не располагались в одном каталоге, а были распределены по логическим подпапкам, при этом понятно связывались с EXE статически, а не динамически, существовали в единственном экземпляре каждая, не была засрана переменная Path и т.д.
    Коллеги, не знаете как это можно забабахать? Можно ли это сделать с помощью манифестов?
  • "Дайте мне возможность выпускать и контролировать деньги в государстве и – мне нет дела до того, кто пишет его законы." (c) Мейер Ансельм Ротшильд , банкир.
    DllHell
    DLL-Связывание. Поиск DLL. Манифесты.
    От: Юрий Жмеренецкий ICQ 380412032
    Дата: 30.09.09 11:20
    Оценка: 87 (19) +1
    #Имя: FAQ.winapi.dll.manifest
    Здравствуйте, Rakafon, Вы писали:

    R>Так вот ... могу ли я в этот механизм внести свои изменения/дополнения ...? Может быть с помощью манифестов ...? Ведь в манифестах прописывают инфу о том где искать CRT-шные DLL-ки, какую версию Common Controls подгружать и так далее ... Но скока я не рыл по поводу манифестов, везде инфа только о том как подключать Shared DLL типа msvcr**.dll, msvcp**.dll, atl**.dll, cmnctrls.dll, настраивать security, и так далее, а вот толкового мануала о том, что за инфу вообще можно запихать в манифест, как можно ссылаться там на сторонние DLL модули и пр., никак и нигде найти не могу ...

    R>Я просто хочу чтоб все DLL приложения не располагались в одном каталоге, а были распределены по логическим подпапкам, при этом понятно связывались с EXE статически, а не динамически, существовали в единственном экземпляре каждая, не была засрана переменная Path и т.д.
    R>Коллеги, не знаете как это можно забабахать? Можно ли это сделать с помощью манифестов?

    Можно. Манифесты это только верхушка айсберга под названием isolated applications & side-by-side assemblies (не путать со сборками дотнета). Это решение прдлагается MS для уменьшения конфликтов версий, избавления от DLL-hell'а, и прочих подобных аспектов (в частности это замена механизма DLL/COM redirection).

    Терминология:

    Isolated application — это обычное приложение, которое 'изолировано' от возможных изменений каких-либо внешних компонентов.

    Side-by-side assembly (сборка) — один файл (обычная DLL, COM-сервер, библиотека типов) или группа файлов.

    * Сборка может быть установлена как private — для использования только одним приложением. Такая сборка устанавливается в папку с приложением.

    * Сборка может быть установлена как shared — такие сборки хранятся в папке %windir%\WinSxS и доступны для использования любыми приложениями. Shared сборка обязательно должда быть подписанной. Private сборка также может быть подписана (но это не обязательно).

    Манифесты соответственно делятся на два типа — манифесты для приложений (или компонентов) и манифесты для сборок. Манифесты первого типа описывают (в том числе) зависимости от сборок. Манифесты второго типа описывают (в том числе) содержимое сборок.

    Для решения поставленной задачи нужно: собрать private сборку — сначала производится сборка всех компонентов, в нашем случае это один dll-файл (пусть будет TestDLLStatic.dll) Компилируется как обычная dll-библиотека (возможно она будет обладать своим манифестом, но это не важно).

    Каждая сборка должна иметь имя, по которой ее можно идентифицировать. Этим именем так же будет названа папка, в которой хранится содержимое сборки (в MSND есть описание алгоритма поиска сборок, там учитываются и другие случаи, вроде название локали). Также нужен манифест для описания содержимого. Пусть именем будет 'MyDll', тогда манифест может иметь следующую структуру:


    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
        <noInheritable/>
        <assemblyIdentity type="win32" name="MyDll" version="1.0.0.0" processorArchitecture="x86" />
        <file name="TestDLLStatic.dll" /> 
    </assembly>



    Т.е. список файлов (их может быть несколько), имя, версия и т.п. Подробный список разрешенных атрибутов есть в MSDN. Атрбут publicKeyToken отсутствует (это важно), т.к. сборку мы не будем подписывать.

    Теперь структура папок:

    TestDLLHost.exe (это приложение будет использовать нашу сборку)
    MyDll  - папка
     |- MyDll.manifest  - манифест, описанный выше
     \- TestDLLStatic.dll


    Название папки и файла-манифеста должны быть такими же как и атрибут 'name' используемый в манифесте.

    Теперь о том как использовать эту сборку: для приложения TestDLLHost.exe должен существовать свой манифест, в котором нужно указать завивимость от нашей сборки: Такой манифест может быть внешним (отдельный файл) или внедренным. MS Visual Studio по умолчанию (правда не помню с какой версии) использует внедренный манифест — в таком случае можно воспользоваться директивой 'pragma' (в cpp файле):

    #pragma comment(linker, "\"/manifestdependency:type='Win32' name='MyDll' version='1.0.0.0' processorArchitecture='X86'\"")


    Это приведет к тому что в секцию dependency/dependentAssembly получившегося манифеста будет добавлена зависимость от собрки MyDll:

      <dependency>
        <dependentAssembly>
          <assemblyIdentity type='win32' name='MyDll' version='1.0.0.0' processorArchitecture='x86' />
        </dependentAssembly>
      </dependency>


    Важно — требуется полное совпадение атрибутов, используемых в 'pragma' и атрибутов в сборке, т.е. type/version/processorArchitecture. Если автоматическая генерация манифестов выключена и используется внешний манифест, то в него необходимо добавать секцию dependency вручную. Все.

    Hint: Файлы из MSVC CRT инсталлируются в качестве shared сборки, но ее можно превратить в private и носить с собой (release версию) — таким образом не нужно при установке проверять наличие соответствующей версии (и устанавливать в случае необходимости). Как это делается: В папку с приложением копируется папка из (например) '<X>:\Program Files\Microsoft Visual Studio 9.0\VC\redist\x86' — 'Microsoft.VC90.CRT':

    TestDLLHost.exe
    Microsoft.VC90.CRT (папка)
      |- msvcm90.dll
      |- msvcp90.dll
      |- msvcr90.dll
      \- Microsoft.VC90.CRT.manifest


    После этого в файле 'Microsoft.VC90.CRT.manifest' необходимо удалить атрибут publicKeyToken. После чего на нее нужно сослаться из манифеста приложения, добавив секцию dependency/dependentAssembly (но уже без атрибута publicKeyToken).

    Но нужно понимать, что если приложение будет иcпользовать другую dll и эта dll будет использовать CRT из shared сборки, то фактически в процессе окажется две копии runtim'a со всеми вытекающими последствиями.
    dll manifest
    Re[2]: DLL-Связывание. Поиск DLL. Манифесты.
    От: Rakafon Украина http://rakafon.blogspot.com/
    Дата: 30.09.09 14:05
    Оценка: +1
    Здравствуйте, Юрий Жмеренецкий, Вы писали:
    ЮЖ>Для решения поставленной задачи нужно: собрать private сборку — сначала производится сборка всех компонентов, в нашем случае это один dll-файл (пусть будет TestDLLStatic.dll) Компилируется как обычная dll-библиотека
    ЮЖ>/* skiped */

    Ох, огромное Вам спасибо!
    Вы более чем развёрнуто ответили на мой вопрос!
    ... а то я уже тут было начал c GetEnvironmentVariable/SetEnvironmentVariable мудрить непотреб всякий!
    + ещё подсказали как msvcp**.dll и msvcr**.dll можно с собой таскать правильным образом, а то я вечно Redistributable с собой таскаю и инсталлю, а там, как известно ещё и левые, мне не нужные, dll-ки валяются типа MFC, OpenMP, MSDIA, etc.
    Спасибо большое!
    "Дайте мне возможность выпускать и контролировать деньги в государстве и – мне нет дела до того, кто пишет его законы." (c) Мейер Ансельм Ротшильд , банкир.
    Re[2]: DLL-Связывание. Поиск DLL. Манифесты.
    От: dcb-BanDos Россия  
    Дата: 30.09.09 14:38
    Оценка: 1 (1) +3
    Здравствуйте, Юрий Жмеренецкий, Вы писали:

    Это дело надо однозначно в FAQ
    Ничто не ограничивает полет мысли программиста так, как компилятор.
    Re[2]: DLL-Связывание. Поиск DLL. Манифесты.
    От: Rakafon Украина http://rakafon.blogspot.com/
    Дата: 30.09.09 15:28
    Оценка:
    + ещё вопросик ... :):)

    ... Допустим у меня слудующая структура приложения:
    MainApp                         /* директория приложения                */
     |- MainApp.manifest            /* манифест для дирректории MainApp     */
     |- MainApp.exe                 /* главный экзешник                     */
     |- MainApp.exe.manifest        /* манифест для MainApp.exe             */
     |- MyPlugins                   /* директория с плагинами               */
     |-  |- MyPlugins.manifest      /* манифест для дирректории MyPlugins   */
     |-  |- Plugin1.dll
     |-  |- Plugin1.dll.manifest    /* манифест для Plugin1.dll             */
     |-  |- Plugin2.dll
     |-  \- Plugin2.dll.manifest    /* манифест для Plugin2.dll             */
     |- SomeDlls                    /* директория с dll-ками                */
     |-  |- SomeDlls.manifest       /* манифест для дирректории SomeDlls    */
     |-  \- Logger.dll
     |- PluginManager.dll           /* DLL в той же диретории, что экзешник */
     \- PluginManager.dll.mainfest  /* манифест для PluginManager.dll       */


    Т.е. рядом с экзешником лежит PluginManager.dll и две папки: MyPlugins и SomeDlls.
    При этом, к примеру, MainApp.exe зависит от всех DLL в этом приложении, Plugin1.dll зависит от PluginManager.dll и Logger.dll, Plugin2.dll от Logger.dll, а PluginManager.dll — от Logger.dll.
    Тогда, получается, что содержание манифестов должно быть следующим (понятно, что внешние манифесты DLL и EXE могут быть встроенными):

    MainApp.manifest:
    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
        <noInheritable/>
        <assemblyIdentity type="win32" name="MainApp" version="1.0.0.0" processorArchitecture="x86" />
        <file name="MainApp.exe" /> 
        <file name="PluginManager.dll" />
    </assembly>


    MyPlugins.manifest:
    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
        <noInheritable/>
        <assemblyIdentity type="win32" name="MyPlugins" version="1.0.0.0" processorArchitecture="x86" />
        <file name="Plugin1.dll" /> 
        <file name="Plugin2.dll" /> 
    </assembly>


    SomeDlls.manifest:
    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
        <noInheritable/>
        <assemblyIdentity type="win32" name="SomeDlls" version="1.0.0.0" processorArchitecture="x86" />
        <file name="Logger.dll" /> 
    </assembly>


    MainApp.exe.manifest:
    <?xml version='1.0' encoding='UTF-8' standalone='yes'?>
    <assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
      <dependency>
        <dependentAssembly>
          <assemblyIdentity type='win32' name='SomeDlls' version='1.0.0.0' processorArchitecture='x86' />
        </dependentAssembly>
      </dependency>
      <dependency>
        <dependentAssembly>
          <assemblyIdentity type='win32' name='MyPlugins' version='1.0.0.0' processorArchitecture='x86' />
        </dependentAssembly>
      </dependency>
    </assembly>


    Plugin1.dll.manifest:
    <?xml version='1.0' encoding='UTF-8' standalone='yes'?>
    <assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
      <dependency>
        <dependentAssembly>
          <assemblyIdentity type='win32' name='MainApp' version='1.0.0.0' processorArchitecture='x86' />
        </dependentAssembly>
      </dependency>
      <dependency>
        <dependentAssembly>
          <assemblyIdentity type='win32' name='SomeDlls' version='1.0.0.0' processorArchitecture='x86' />
        </dependentAssembly>
      </dependency>
    </assembly>


    Plugin2.dll.manifest:
    <?xml version='1.0' encoding='UTF-8' standalone='yes'?>
    <assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
      <dependency>
        <dependentAssembly>
          <assemblyIdentity type='win32' name='SomeDlls' version='1.0.0.0' processorArchitecture='x86' />
        </dependentAssembly>
      </dependency>
    </assembly>


    PluginManager.dll.mainfest:
    <?xml version='1.0' encoding='UTF-8' standalone='yes'?>
    <assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
      <dependency>
        <dependentAssembly>
          <assemblyIdentity type='win32' name='SomeDlls' version='1.0.0.0' processorArchitecture='x86' />
        </dependentAssembly>
      </dependency>
    </assembly>



    Всё верно?
    Ничего не упустил?
    В файле "MainApp.exe.manifest" не надо никак указывать зависимость на PluginManager.dll?
    Ничего страшного, если все эти сборки "MainApp", "SomeDlls" и "MyPlugins" так разбросаны по подпапкам, что одни сборки ссылаются на те, которые находятся на уровень выше по файловой системе? Например как Plugin2.dll из сборки "MyPlugins" ссылается на сборку "SomeDlls", которая находится выше чем Plugin2.dll.
    Во время запуска MainApp.exe, Side-by-Side загрузчик верно одуплит в каких диреториях находятся все эти сборки?
    "Дайте мне возможность выпускать и контролировать деньги в государстве и – мне нет дела до того, кто пишет его законы." (c) Мейер Ансельм Ротшильд , банкир.
    Assembly Manifest DLL Side-by-Side
    Re[2]: DLL-Связывание. Поиск DLL. Манифесты.
    От: Unhandled_Exception Россия  
    Дата: 30.09.09 16:13
    Оценка:
    Здравствуйте, Юрий Жмеренецкий, Вы писали:

    ЮЖ> [...]


    супер!

    может быть вы сможете ответить на мой давний вопрос:

    http://rsdn.ru/forum/tools/3155344.aspx
    Автор: Unhandled_Exception
    Дата: 29.10.08


    спасибо!
    Re[3]: DLL-Связывание. Поиск DLL. Манифесты.
    От: Юрий Жмеренецкий ICQ 380412032
    Дата: 30.09.09 17:09
    Оценка:
    Здравствуйте, Rakafon, Вы писали:

    R>... Допустим у меня слудующая структура приложения:

    R>
    R>MainApp                         /* директория приложения                */
    R> |- MainApp.manifest            /* манифест для дирректории MainApp     */
    R> |- MainApp.exe                 /* главный экзешник                     */
    R> |- MainApp.exe.manifest        /* манифест для MainApp.exe             */
    R> |- MyPlugins                   /* директория с плагинами               */
    R> |-  |- MyPlugins.manifest      /* манифест для дирректории MyPlugins   */
    R> |-  |- Plugin1.dll
    R> |-  |- Plugin1.dll.manifest    /* манифест для Plugin1.dll             */
    R> |-  |- Plugin2.dll
    R> |-  \- Plugin2.dll.manifest    /* манифест для Plugin2.dll             */
    R> |- SomeDlls                    /* директория с dll-ками                */
    R> |-  |- SomeDlls.manifest       /* манифест для дирректории SomeDlls    */
    R> |-  \- Logger.dll
    R> |- PluginManager.dll           /* DLL в той же диретории, что экзешник */
    R> \- PluginManager.dll.mainfest  /* манифест для PluginManager.dll       */
    R>


    R>Т.е. рядом с экзешником лежит PluginManager.dll и две папки: MyPlugins и SomeDlls.

    R>При этом, к примеру, MainApp.exe зависит от всех DLL в этом приложении

    Зависит в каком смысле?

    R>Plugin1.dll зависит от PluginManager.dll и Logger.dll, Plugin2.dll от Logger.dll, а PluginManager.dll — от Logger.dll.

    R>Тогда, получается, что содержание манифестов должно быть следующим (понятно, что внешние манифесты DLL и EXE могут быть встроенными):

    R>MainApp.manifest:

    R>
    R><?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    R><assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    R>    <noInheritable/>
    R>    <assemblyIdentity type="win32" name="MainApp" version="1.0.0.0" processorArchitecture="x86" />
    R>    <file name="MainApp.exe" /> 
    R>    <file name="PluginManager.dll" />
    R></assembly>
    R>


    MainApp.exe — это не сборка, это приложение, поэтому MainApp.manifest не нужен.


    R>Ничего страшного, если все эти сборки "MainApp", "SomeDlls" и "MyPlugins" так разбросаны по подпапкам, что одни сборки ссылаются на те, которые находятся на уровень выше по файловой системе? Например как Plugin2.dll из сборки "MyPlugins" ссылается на сборку "SomeDlls", которая находится выше чем Plugin2.dll.

    R>Во время запуска MainApp.exe, Side-by-Side загрузчик верно одуплит в каких диреториях находятся все эти сборки?

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

    Но есть другой путь — можно отталкиваться от порядка инициализации контекстов активации: убрать вообще манифест для Plugin1.dll/Plugin2.dll, тогда (если я правильно понял про зависимости указанных dll's) запуск MainApp.exe приведет к загрузке PluginManager.dll [в этот момент с стеке активных контекстов будет присутствовать запись для PluginManager.dll ], а его загрузка приведет к загрузке Plugin1.dll и зависимости Plugin1.dll от PluginManager.dll будут разрешены автоматически (при отсутствии манифеста для Plugin1.dll). Зависимость Plugin2.dll от Logger.dll можно разрешить таким же способом. Зависимость PluginManager.dll от Logger.dll также, но при условии что можно гарантировать загрузку Logger.dll раньше чем PluginManager.dll.

    PS: Имхо, logger можно и в корень вынести.
    Re[3]: DLL-Связывание. Поиск DLL. Манифесты.
    От: Юрий Жмеренецкий ICQ 380412032
    Дата: 30.09.09 18:10
    Оценка:
    Здравствуйте, Unhandled_Exception, Вы писали:

    U_E>может быть вы сможете ответить на мой давний вопрос:


    U_E>http://rsdn.ru/forum/tools/3155344.aspx
    Автор: Unhandled_Exception
    Дата: 29.10.08


    Увы, не отвечу. Похоже ту часть, о которой идет речь добавляет WinSxS manger при установки сборки и использует для внутренних целей (вроде верификации сборки). IAssemblyName::GetProperty не распознает эту информацию.
    Re[4]: DLL-Связывание. Поиск DLL. Манифесты.
    От: Rakafon Украина http://rakafon.blogspot.com/
    Дата: 01.10.09 07:48
    Оценка:
    Здравствуйте, Юрий Жмеренецкий, Вы писали:
    ЮЖ>PS: Имхо, logger можно и в корень вынести.

    ... да ситуация с Logger'ом и вообще с вышеописанным примером надуманная, чисто примера ради.
    Я именно и имел ввиду, работает ли разрешение зависимостей не только сверху вниз, но и снизу вверх.
    Скажем можно придумать менее надуманный пример, когда в корне будет лежать CRT-сборка, а в папке "MyPlugins" будут dll-ки, которые зависят от этой CRT, например:
    MainApp                         /* директория приложения                */
     |- MainApp.exe                 /* главный экзешник                     */
     |- MainApp.exe.manifest        /* манифест для MainApp.exe             */
     |- Microsoft.VC90.CRT          /* CRT-сборка                           */
     |-  |- msvcm90.dll
     |-  |- msvcp90.dll
     |-  |- msvcr90.dll
     |-  \- Microsoft.VC90.CRT.manifest
     |- MyPlugins                   /* директория с плагинами               */
     |-  |- MyPlugins.manifest      /* MyPlugins-сборка                     */
     |-  |- Plugin1.dll
     |-  |- Plugin1.dll.manifest    /* манифест для Plugin1.dll             */
     |-  |- Plugin2.dll
     |-  \- Plugin2.dll.manifest    /* манифест для Plugin2.dll             */
     |- PluginManager.dll           /* DLL в той же диретории, что экзешник */
     |- PluginManager.dll.mainfest  /* манифест для PluginManager.dll       */
     |- Logger.dll                  /* DLL в той же диретории, что экзешник */
     \- Logger.dll.mainfest         /* манифест для Logger.dll.mainfest     */


    То есть, допустим, я решил не ставить VC++ 2008 Redistributable, а решил таскать CRT 2008-ой студии рядом с приложением, при этом MainApp.exe имеет dependencies на PluginManager.dll, Logger.dll, Plugin1.dll и Plugin2.dll, причём MainApp.exe и каждая dll-ка слинкованы с CRT 2008-ой студии динамически и соответственно имеют dependencies на msvcp90.dll и msvcr90.dll из сборки Microsoft.VC90.CRT. Это значит для каждого EXE и DLL модуля в его манифесте мне необходимо записать зависимость от Microsoft.VC90.CRT, где-то примерно так:
    <dependency>
        <dependentAssembly>
            <assemblyIdentity type='win32' name='Microsoft.VC90.CRT' version='x.x.x.x' processorArchitecture='x86' />
        </dependentAssembly>
    </dependency>

    ... и получается, что для Plugin1.dll и Plugin2.dll разрешение зависимостей от msvcp90.dll и msvcr90.dll будет проходить снизу вверх. При этом, наверное, когда SxS загрузчик разрешит зависимости MainApp.exe (при запуске приложения) и раздуплит где брать msvcp90.dll и msvcr90.dll, то Plugin1.dll и Plugin2.dll он подгрузит без проблем, т.к. ему уже будет известно месторасположение сборки Microsoft.VC90.CRT и заново её искать нет смысла.

    А если представить ситуацию, что от CRT будут зависеть только Plugin1.dll и Plugin2.dll, а EXE и все остальные модули вообще не будут зависеть от CRT (не будут её использовать вообще), а Microsoft.VC90.CRT сборка будет размещена в корневой директории приложения, то при загрузке MainApp.exe, будут подгружаться Plugin1.dll и Plugin2.dll, и следовательно надо будет разрешать зависимости библиотек Plugin1.dll и Plugin2.dll именно снизу вверх. Получается так? Разрешит ли SxS загрузчик такие зависимости, если Microsoft.VC90.CRT сборка лежит рядышком с экзешником?
    "Дайте мне возможность выпускать и контролировать деньги в государстве и – мне нет дела до того, кто пишет его законы." (c) Мейер Ансельм Ротшильд , банкир.
    Assembly Manifest DLL Side-by-Side
    Re[5]: DLL-Связывание. Поиск DLL. Манифесты.
    От: Юрий Жмеренецкий ICQ 380412032
    Дата: 01.10.09 09:31
    Оценка:
    Здравствуйте, Rakafon, Вы писали:

    R>Здравствуйте, Юрий Жмеренецкий, Вы писали:

    ЮЖ>>PS: Имхо, logger можно и в корень вынести.

    R>... да ситуация с Logger'ом и вообще с вышеописанным примером надуманная, чисто примера ради.

    R>Я именно и имел ввиду, работает ли разрешение зависимостей не только сверху вниз, но и снизу вверх.
    R>Скажем можно придумать менее надуманный пример, когда в корне будет лежать CRT-сборка, а в папке "MyPlugins" будут dll-ки, которые зависят от этой CRT, например:
    R>
    R>MainApp                         /* директория приложения                */
    R> |- MainApp.exe                 /* главный экзешник                     */
    R> |- MainApp.exe.manifest        /* манифест для MainApp.exe             */
    R> |- Microsoft.VC90.CRT          /* CRT-сборка                           */
    R> |-  |- msvcm90.dll
    R> |-  |- msvcp90.dll
    R> |-  |- msvcr90.dll
    R> |-  \- Microsoft.VC90.CRT.manifest
    R> |- MyPlugins                   /* директория с плагинами               */
    R> |-  |- MyPlugins.manifest      /* MyPlugins-сборка                     */
    R> |-  |- Plugin1.dll
    R> |-  |- Plugin1.dll.manifest    /* манифест для Plugin1.dll             */
    R> |-  |- Plugin2.dll
    R> |-  \- Plugin2.dll.manifest    /* манифест для Plugin2.dll             */
    R> |- PluginManager.dll           /* DLL в той же диретории, что экзешник */
    R> |- PluginManager.dll.mainfest  /* манифест для PluginManager.dll       */
    R> |- Logger.dll                  /* DLL в той же диретории, что экзешник */
    R> \- Logger.dll.mainfest         /* манифест для Logger.dll.mainfest     */
    R>


    R>То есть, допустим, я решил не ставить VC++ 2008 Redistributable, а решил таскать CRT 2008-ой студии рядом с приложением, при этом MainApp.exe имеет dependencies на PluginManager.dll, Logger.dll, Plugin1.dll и Plugin2.dll, причём MainApp.exe и каждая dll-ка слинкованы с CRT 2008-ой студии динамически и соответственно имеют dependencies на msvcp90.dll и msvcr90.dll из сборки Microsoft.VC90.CRT. Это значит для каждого EXE и DLL модуля в его манифесте мне необходимо записать зависимость от Microsoft.VC90.CRT, где-то примерно так:

    R>
    R><dependency>
    R>    <dependentAssembly>
    R>        <assemblyIdentity type='win32' name='Microsoft.VC90.CRT' version='x.x.x.x' processorArchitecture='x86' />
    R>    </dependentAssembly>
    R></dependency>
    R>


    Это не будет работать. Если
    1) в манифесте для dll присутствует ссылка на 'Microsoft.VC90.CRT'
    2) и рядом (в той же папке) с этой dll нет папки 'Microsoft.VC90.CRT' с соответствующим содержимым, то такая dll не загрузится (т.е. попытка загрузки закончится ошибкой).

    Это поведение невозможно изменить даже с помощью ручного управления контекстами активации.

    R>... и получается, что для Plugin1.dll и Plugin2.dll разрешение зависимостей от msvcp90.dll и msvcr90.dll будет проходить снизу вверх.


    Зависимости всегда разрешаются в порядке загрузки (или вызова некоторых функций). Сначала запускается MainApp.exe — это приводит к поиску зависимостей, перечисленных в MainApp.exe.manifest (и только в нем). Загрузка каждого файла будет сопровождаться сменой активного контекста активации. При загрузке зависимой dll сначала будет произведена попытка загрузить ее зависимости, и при наличии манифеста (точнее ссылки в нем) необнаружение зависимой сборки приведет к ошибке. Но если манифест отсутствует (или в нем отсутствует соответствующая ссылка) то поиск будет продолжен в активном контексте.

    R>При этом, наверное, когда SxS загрузчик разрешит зависимости MainApp.exe (при запуске приложения) и раздуплит где брать msvcp90.dll и msvcr90.dll, то Plugin1.dll и Plugin2.dll он подгрузит без проблем, т.к. ему уже будет известно месторасположение сборки Microsoft.VC90.CRT и заново её искать нет смысла.


    Это верно только в частном случае (см. выше).

    R>А если представить ситуацию, что от CRT будут зависеть только Plugin1.dll и Plugin2.dll, а EXE и все остальные модули вообще не будут зависеть от CRT (не будут её использовать вообще), а Microsoft.VC90.CRT сборка будет размещена в корневой директории приложения, то при загрузке MainApp.exe, будут подгружаться Plugin1.dll и Plugin2.dll, и следовательно надо будет разрешать зависимости библиотек Plugin1.dll и Plugin2.dll именно снизу вверх. Получается так? Разрешит ли SxS загрузчик такие зависимости, если Microsoft.VC90.CRT сборка лежит рядышком с экзешником?


    Нет, т.к. для Plugin1.dll и Plugin2.dll указание зависимости от private сборок в манифесте эквивалентно — "искать только в той папке, где находится сама dll" (даже если MainApp.exe будет зависеть от CRT, и на момент загрузки Plugin1.dll CRT будет загружена).

    Поэтому, чтобы разрулить ситуацию с CRT в корневой папке и нескольких (использующих ее) dll в подпапках — нужно добавить зависимость от CRT только для корневого модуля (обычно exe).
    Re[6]: DLL-Связывание. Поиск DLL. Манифесты.
    От: Rakafon Украина http://rakafon.blogspot.com/
    Дата: 01.10.09 09:51
    Оценка:
    Здравствуйте, Юрий Жмеренецкий, Вы писали:
    ЮЖ>Зависимости всегда разрешаются в порядке загрузки (или вызова некоторых функций). Сначала запускается MainApp.exe — это приводит к поиску зависимостей, перечисленных в MainApp.exe.manifest (и только в нем). Загрузка каждого файла будет сопровождаться сменой активного контекста активации. При загрузке зависимой dll сначала будет произведена попытка загрузить ее зависимости, и при наличии манифеста (точнее ссылки в нем) необнаружение зависимой сборки приведет к ошибке. Но если манифест отсутствует (или в нем отсутствует соответствующая ссылка) то поиск будет продолжен в активном контексте.
    То есть, поиск будет продолжен в контексте загрузки MainApp.exe. Так?

    ЮЖ>Поэтому, чтобы разрулить ситуацию с CRT в корневой папке и нескольких (использующих ее) dll в подпапках — нужно добавить зависимость от CRT только для корневого модуля (обычно exe).


    Ясно, т.е. чтобы приложение нормально работало в вышеописанной ситуации достаточно указать зависимость на сборку Microsoft.VC90.CRT только в манифесте приложения MainApp.exe, а все остальные DLL-ки зависящие от Microsoft.VC90.CRT (в том числе и в подпапках), загрузятся без проблем, вернее отсутствие специальной записи о ссылке на Microsoft.VC90.CRT в манифестах DLL-ек, а также тот факт, что msvcr90.dll уже спрецирована на адресное пространство процесса приведут к решению, что msvcr90.dll уже есть и её искать и грузить не надо.

    Спасибо большое! Вы освободили мой затуманенный разум!
    "Дайте мне возможность выпускать и контролировать деньги в государстве и – мне нет дела до того, кто пишет его законы." (c) Мейер Ансельм Ротшильд , банкир.
    SxS Assembly Manifest DLL Side-by-Side
    Re: DLL-Связывание. Поиск DLL. Манифесты.
    От: Rakafon Украина http://rakafon.blogspot.com/
    Дата: 02.10.09 12:35
    Оценка:
    Здравствуйте, Юрий Жмеренецкий, Вы писали:
    ЮЖ>Hint: Файлы из MSVC CRT инсталлируются в качестве shared сборки, но ее можно превратить в private и носить с собой (release версию) — таким образом не нужно при установке проверять наличие соответствующей версии (и устанавливать в случае необходимости). Как это делается: В папку с приложением копируется папка из (например) '<X>:\Program Files\Microsoft Visual Studio 9.0\VC\redist\x86' — 'Microsoft.VC90.CRT':
    ЮЖ>После этого в файле 'Microsoft.VC90.CRT.manifest' необходимо удалить атрибут publicKeyToken. После чего на нее нужно сослаться из манифеста приложения, добавив секцию dependency/dependentAssembly (но уже без атрибута publicKeyToken).

    Насчёт того, чтобы таскать CRT, ATL и другие части redist'а с собой ...
    Один коллега в сообщении "Side-by-side assemblies и их проблемы" своём журнале указал на файлик redist.txt, который располагается в корне инсталляции Visual Studio, например "%PROGRAMFILES%\Microsoft Visual Studio 8\redist.txt" или "%PROGRAMFILES%\Microsoft Visual Studio 9.0\redist.txt".

    Кроме прочего, в этом файле написано:

    Visual C++ Runtime files

    Subject to the license terms for the software, you may redistribute the .EXE files (unmodified) listed below.
    These files can be run as prerequisites during installation.

    vcredist_x86.exe
    vcredist_x64.exe
    vcredist_IA64.exe

    For your convenience, we have provided the following folders for use when redistributing VC++ runtime files. Subject to the license terms for the software, you may redistribute the folder (unmodified) in the application local folder as a sub-folder with no change to the folder name. You may also redistribute all the files (*.dll and *.manifest) within a folder, listed below the folder for your convenience, as an entire set.

    \VC\redist\x86\Microsoft.VC90.ATL\
    atl90.dll
    Microsoft.VC90.ATL.manifest

    \VC\redist\x86\Microsoft.VC90.CRT\
    msvcm90.dll
    msvcp90.dll
    msvcr90.dll
    Microsoft.VC90.CRT.manifest


    Ключевое слово "unmodified".
    То есть получается, что "удалить атрибут publicKeyToken" — не есть правомерно. А это значит, что сделать PRIVATE сборку из Microsoft.VC90.CRT легально нельзя!
    Я прав?
    "Дайте мне возможность выпускать и контролировать деньги в государстве и – мне нет дела до того, кто пишет его законы." (c) Мейер Ансельм Ротшильд , банкир.
    SxS Assembly Manifest DLL Side-by-Side
    Re[2]: DLL-Связывание. Поиск DLL. Манифесты.
    От: SchweinDeBurg Россия http://zarezky.spb.ru/
    Дата: 02.10.09 12:52
    Оценка: 1 (1) :)
    Здравствуйте, Rakafon, Вы писали:

    R>For your convenience, we have provided the following folders for use when redistributing VC++ runtime files. Subject to the license terms for the software, you may redistribute the folder (unmodified) in the application local folder as a sub-folder with no change to the folder name. You may also redistribute all the files (*.dll and *.manifest) within a folder, listed below the folder for your convenience, as an entire set.


    R>Ключевое слово "unmodified".

    R>То есть получается, что "удалить атрибут publicKeyToken" — не есть правомерно. А это значит, что сделать PRIVATE сборку из Microsoft.VC90.CRT легально нельзя!
    R>Я прав?

    Формально, скорее всего, да (ИМХО). Но, с другой стороны, я не думаю, что авторы Howto: Deploy VC2008 apps without installing vcredist_x86.exe и Create projects easily with private MFC, ATL and CRT assemblies (последний вообще является MS MVP C++) кто-нибудь привлекал или собирается привлекать к какой-либо ответственности.
    [ posted via RSDN@Home 1.1.4 stable SR1 r568, accompanied by silence ]
    - Искренне ваш, Поросенок Пафнутий ~ ICQ#116846877 http://web.icq.com/whitepages/online?icq=116846877&img=21
    In Windows, there’s always a catch… © Paul DiLascia
    Re[3]: DLL-Связывание. Поиск DLL. Манифесты.
    От: Rakafon Украина http://rakafon.blogspot.com/
    Дата: 02.10.09 13:18
    Оценка:
    Здравствуйте, SchweinDeBurg, Вы писали:
    SDB>Формально, скорее всего, да (ИМХО). Но, с другой стороны, я не думаю, что авторы Howto: Deploy VC2008 apps without installing vcredist_x86.exe и Create projects easily with private MFC, ATL and CRT assemblies (последний вообще является MS MVP C++) кто-нибудь привлекал или собирается привлекать к какой-либо ответственности.

    Ну эти товарищи, конечно, мне по жопе не постучат, если я сделаю из Microsoft.VC90.CRT приватную сборку. Но, например, имхо, зарегистрировать такой софт в Windows Vista Compatibility Center вряд ли получится ...
    "Дайте мне возможность выпускать и контролировать деньги в государстве и – мне нет дела до того, кто пишет его законы." (c) Мейер Ансельм Ротшильд , банкир.
    Re[3]: *** Флуд ***
    От: Rakafon Украина http://rakafon.blogspot.com/
    Дата: 02.10.09 13:30
    Оценка:
    Здравствуйте, SchweinDeBurg, Вы писали:
    SDB>... не думаю, что авторы Howto: Deploy VC2008 apps without installing vcredist_x86.exe и Create projects easily with private MFC, ATL and CRT assemblies (последний вообще является MS MVP C++) кто-нибудь привлекал или собирается привлекать к какой-либо ответственности.

    + ещё ...


    Объясните плиз, что означает аббревиатура MVP?


    P.S. ... кстати ни в статье, ни в профиле Martin'a Richter'a я не нашёл места где бы встретилось словосочетание MS MVP, только MVP отдельно без MS ... Видимо всё-таки этот чел не из мелкософта ...
    "Дайте мне возможность выпускать и контролировать деньги в государстве и – мне нет дела до того, кто пишет его законы." (c) Мейер Ансельм Ротшильд , банкир.
    Re[4]: *** Флуд ***
    От: SchweinDeBurg Россия http://zarezky.spb.ru/
    Дата: 02.10.09 13:35
    Оценка:
    Здравствуйте, Rakafon, Вы писали:

    R>Объясните плиз, что означает аббревиатура MVP?


    Microsoft Most Valuable Professional

    Alexander Lozhechkin &mdash; Кто такие MVP
    [ posted via RSDN@Home 1.1.4 stable SR1 r568, accompanied by silence ]
    - Искренне ваш, Поросенок Пафнутий ~ ICQ#116846877 http://web.icq.com/whitepages/online?icq=116846877&img=21
    In Windows, there’s always a catch… © Paul DiLascia
    Re[5]: *** Флуд ***
    От: Rakafon Украина http://rakafon.blogspot.com/
    Дата: 02.10.09 13:37
    Оценка:
    Здравствуйте, SchweinDeBurg, Вы писали:
    SDB>Microsoft Most Valuable Professional
    SDB>Alexander Lozhechkin &mdash; Кто такие MVP
    Сенкъюверимач!
    :)
    "Дайте мне возможность выпускать и контролировать деньги в государстве и – мне нет дела до того, кто пишет его законы." (c) Мейер Ансельм Ротшильд , банкир.
    Re[2]: DLL-Связывание. Поиск DLL. Манифесты.
    От: Юрий Жмеренецкий ICQ 380412032
    Дата: 04.10.09 05:10
    Оценка:
    Здравствуйте, Rakafon, Вы писали:

    [...]

    R>То есть получается, что "удалить атрибут publicKeyToken" — не есть правомерно. А это значит, что сделать PRIVATE сборку из Microsoft.VC90.CRT легально нельзя!

    R>Я прав?

    Прав, нужно смотреть лицензию для конкретной версии. Здесь еще есть про CRT.
    Re[4]: DLL-Связывание. Поиск DLL. Манифесты.
    От: Аноним  
    Дата: 06.10.09 20:33
    Оценка:
    Здравствуйте, Rakafon, Вы писали:

    R>Здравствуйте, SchweinDeBurg, Вы писали:

    SDB>>Формально, скорее всего, да (ИМХО). Но, с другой стороны, я не думаю, что авторы Howto: Deploy VC2008 apps without installing vcredist_x86.exe и Create projects easily with private MFC, ATL and CRT assemblies (последний вообще является MS MVP C++) кто-нибудь привлекал или собирается привлекать к какой-либо ответственности.

    R>Ну эти товарищи, конечно, мне по жопе не постучат, если я сделаю из Microsoft.VC90.CRT приватную сборку. Но, например, имхо, зарегистрировать такой софт в Windows Vista Compatibility Center вряд ли получится ...

    R>


    Я никак не могу понять одного, зачем удалять атрибут publicKeyToken из манифеста. Ведь положив неизмененное содержимое Microsoft.VC90.CRT в папку со своим приложением, ничего не нарушается, и приложение работает.

    Чтобы сборка была не приватной, ее нужно установить в WinSxS, и сделать это можно только с помощью Windows Installer. Если же сборка лежит вместе с приложением, то она является приватной, и совершенно не важно, есть в манифесте этой сборки publicKeyToken или нет его там. Отличие в том, что без него, сборка не может быть неприватной.


    Чтобы расставить все точки над ı, давайте почитаем, что же говорит Microsoft об этом: Redistributing Visual C++ Files, а точнее How to: Deploy using XCopy. Часть Deploying Visual C++ library DLLs as private assemblies наиболее интересна:

    1. Create a folder structure on the development computer that matches the folder structure to be used on the target computer. For this example, create a \bin folder and copy myapplication.exe there. Then create a \bin\mylibrary folder and copy MyLibrary.dll there.

    2. On the development computer, from %PROGDIR%\Microsoft Visual Studio 8\VC\Redist\x86, copy Microsoft.VC80.CRT and Microsoft.VC80.MFC to \bin and to \bin\MyLibrary.

    3. Copy the \bin folder to the target computer. On a target computer with manifest-based binding support (Windows XP Home Edition, Windows XP Professional, Windows Server 2003, Windows Vista) no further preparation is necessary. On a computer without such support (Windows 98, Windows 98 Second Edition, Windows Millennium Edition, Windows 2000), Microsoft.VC80.CRT and Microsoft.VC80.MFC must be on the path.


    Аналоги этих же статей для Visual Studio 2008:

    И не нужно редактировать манифесты, удаляя publicKeyToken из них, — просто берем и используем.
    SxS assembly manifest DLL side-by-side
    Re[5]: DLL-Связывание. Поиск DLL. Манифесты.
    От: Юрий Жмеренецкий ICQ 380412032
    Дата: 07.10.09 04:45
    Оценка: +1
    Здравствуйте, http://alexyv.livejournal.com/, Вы писали:

    [...]

    HAL>Я никак не могу понять одного, зачем удалять атрибут publicKeyToken из манифеста.


    publicKeyToken является необязательной частью identity для application manifest, по которой производится поиск сборки. С таким же успехом можно имя поменять, но в коде инициализации crt есть привязка к имени.

    HAL>Ведь положив неизмененное содержимое Microsoft.VC90.CRT в папку со своим приложением, ничего не нарушается, и приложение работает.


    В таком случае, если в WinSxS присутствует необходимая сборка, то будет использована именно она (либо другая, с учетом механизма dependency redirection).

    HAL>... Если же сборка лежит вместе с приложением, то она является приватной, и совершенно не важно, есть в манифесте этой сборки publicKeyToken или нет его там.


    Наличие private сборки не является безусловным фактом ее использования.
    Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.