Способ загрузки DLL из центрального хранилища

У нас много продуктов, и в каждом приложении есть несколько общих библиотек DLL. Прямо сейчас мы копируем каждую общую DLL в каталог bin каждого продукта и рассматриваем их как приватную сборку. Это излишне увеличивает размер msi каждого продукта, и когда возникает проблема в DLL, мы должны создать msi каждого продукта, содержащего DLL, и развернуть ее.

Есть ли какой-либо способ указать приложению продукта использовать общий частный каталог, который будет использоваться для загрузки DLL [с использованием схемы манифеста.. ]? [Примечание: Добавление частного каталога в PATH env не даст решения, как если бы в каталоге SYSTEM существовала DLL с таким же именем, которая взяла бы привилегию на наш личный каталог]

-Kartlee

6 ответов

Вы не указываете, является ли ваша среда.NET или прямой Win32.

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

В терминах Win32 можно загрузить Dll из общего расположения одним из двух способов:

  • Используйте LoadLibrary с явными полными путями. Это означает, что вы не можете использовать статическое связывание - все функции dll, используемые во всех продуктах, должны быть доступны через GetProcAddress. Вы не можете импортировать классы C++ из dll, загруженных через LoadLibrary - они должны быть статически связаны для работы, поэтому этот подход может или не может быть жизнеспособным. Не очень сложно написать заголовочные файлы shim, которые маскируются под интерфейс dll и выполняют своевременную загрузку dll и GetProcAddress, необходимые для каждого вызова.

  • Другой вариант - превратить DLL в так называемые "бок о бок сборки" и установить их в хранилище WinSxS. Не пугайся громкого имени. "сборка бок о бок" означает "файл Dll плюс файл манифеста с информацией о версии". Каждое из различных приложений затем помещало бы "строгое имя" - которое включает информацию о версии - в манифест приложения для каждой используемой библиотеки DLL, и загрузчик Win32 Dll будет использовать это для выбора правильного экземпляра общей библиотеки DLL из хранилища WinSxS. Базовый процесс описан в статье MSDN Рекомендации по созданию параллельных сборок


В Windows версии 6.1 и выше (Windows Server 2008 и иронично названная Windows 7) файлы конфигурации приложения СЕЙЧАС поддерживают элемент исследования в файлах конфигурации приложения.

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


Хорошо, я провел некоторое тестирование на Windows 7, и это работает:

Предполагая, что у вас есть приложение app1.exe, установленное в \Program Files\App1, которое зависит от некоторых распространенных dll "thedll.dll"

В папке приложения (\Program Files\App1) создайте файл App1.exe.config и передайте ему следующее содержимое:

<configuration>   
   <windows>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
        <probing privatePath="..\AcmeCommon"/>
    </assemblyBinding>
  </windows>
</configuration>

Теперь создайте папку с именем \Program Files\AcmeCommon и в ней папку acme.thedll и скопируйте файл DLL.dll в \Program Files\AcmeCommon\acme.thedll

Также создайте файл в AcmeCommon \ acme.thedll с именем acme.thedll.manifest - это будет манифест сборки, описывающий сборку с именем acme.thedll.

Содержимое acme.thedll.manifest будет:

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <assemblyIdentity name="acme.thedll" version="1.2.3.4" processorArchitecture="x86"  type="win32"/>
    <file name="thedll.dll"/>
</assembly>

Теперь у нас есть общая dll, в общем месте, как нативная сборка sxs. У нас есть приложение с файлом конфигурации, который на сервере Windows 7 и 2008 (и выше) сообщит ему о поиске сборок в общем месте. Но приложение все еще пытается связать dll как dll, а не через сборку.

Чтобы приложение загрузило сборку, нам нужно добавить файл манифеста в приложение. Если вы используете Visual Studio, ваши приложения, вероятно, уже настроены на создание и внедрение манифестов с помощью настроек проекта компоновщика и инструмента манифеста. В этом случае самый простой способ сообщить приложению о сборке - это перестроить его после добавления следующего кода хотя бы в один заголовочный файл или файл c / cpp в проекте:

#pragma comment(linker,"/manifestdependency:\"type='win32' name='acme.thedll' version='1.2.3.4' processorArchitecture='x86' language='*'\"")

Если вы используете более старую среду сборки, в которой манифесты создаются вручную, вам необходимо объединить следующий xml с app1.exe.manifest в папке App1:

<dependency>
  <dependentassembly>
    <assemblyidentity type="win32" name="acme.thedll" version="1.2.3.4"   processorArchitecture="x86" language="*"/>
  </dependentassembly>
</dependency>

Это должно замкнуть круг: при загрузке приложения загрузчик win32 загрузит манифест приложения (app1.exe.manifest или встроенный как ресурс RT_MANIFEST) и узнает о сборке "acme.thedll". Он также загрузит файл конфигурации приложения (app1.exe.config) и узнает о личном пути для поиска сборок. Затем он загрузит и добавит "acme.thedll.manifest" в "контекст активации" приложения. Затем, когда загрузчик попытается загрузить файл "thedll.dll", он выполнит поиск в db контекста активации, найдет его в сборке acme.thedll и загрузит его из расположения сборок.

Я следую за ответом Криса. Убедитесь, что дело правильно на манифестах и ​​конфигах. В противном случае они потерпят неудачу. Я смог получить сборку для загрузки, но DLL не будет выбрана. В моем случае Windows DLL в system32 выбирается вместо моей с тем же именем. В Dependency Walker моя DLL загружается, но во время выполнения с помощью Process Explorer загружается копия Windows. Есть идеи?

Если вы говорите о.NET, вы можете:

  • Загрузите вашу DLL напрямую из базы данных, используя Assembly.Load(byte[])
  • Используя Assembly.TypeResolve событие
  • Используя TypeProvider учебный класс
  • Определив каталог зонда в вашем конфигурационном файле

Подобно:

<configuration>   
    <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
            <probing privatePath="bin"/>
        </assemblyBinding>
    </runtime>
</configuration>

Я не уверен, что правильно понял вопрос, но если вы находитесь в.Net, есть глобальный кэш сборок (GAC): http://en.wikipedia.org/wiki/Global_Assembly_Cache

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

Это может вам не помочь, но... вы МОЖЕТЕ загрузить dll из произвольных каталогов И при этом по-прежнему полагаться на обычное динамическое связывание с ними, если вы можете контролировать, когда dll загружаются через динамическое связывание, и убедиться, что вы уже загрузили dll явно использовать полный путь до его динамической загрузки.

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

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

Я не уверен, что это то, что вы ищете, но там, где я сейчас работаю, мы используем общий UNC-путь для всех наших DLL.

У нас есть что-то похожее на...

\\ server01 \ productionLibrary для производственных библиотек чтения, каждая в своем собственном каталоге.

а также

\\server01\developmentLibrary, которая отражает производственную библиотеку, и это то, что разработчики используют при разработке.

Когда мы объединяем код после завершения тестирования, мы внедряем его в производственную библиотеку. Все проекты ссылаются на производственную библиотеку, когда они встроены в файлы MSI для развертывания. Наша автоматизированная система встраивает проекты в MSI и проверяет, что все библиотеки DLL указывают на производственную библиотеку, поэтому нет никаких шансов, что она случайно будет использовать копию для разработки.

Надеюсь это поможет.

Другие вопросы по тегам