Как поздно привязать 32-битные/64-битные библиотеки во время выполнения
Вопрос
У меня проблема, похожая на описанную, но немного отличающаяся от нее. здесь (Загрузка сборок и их зависимостей).
У меня есть C++ DLL для 3D-рендеринга, которую мы продаем клиентам.Для пользователей .NET у нас будет оболочка CLR.C++ DLL может быть построена как в 32-, так и в 64-битной версии, но я думаю, это означает, что нам нужно иметь две оболочки CLR, поскольку CLR привязывается к определенной DLL?
Допустим, теперь у нашего клиента есть .NET-приложение, которое может быть 32- или 64-разрядным, и что это чистое .NET-приложение, поэтому среда CLR обрабатывает его из одного набора сборок.Вопрос в том, как код приложения может динамически выбирать между нашими 32- и 64-битными комбинациями CLR/DLL во время выполнения?
Более конкретно, применим ли и здесь предлагаемый ответ на вышеупомянутый вопрос (т.е.создать обработчик ResolveEvent)?
Решение
Наконец-то у меня есть ответ на этот вопрос, который, похоже, работает.
Скомпилируйте 32- и 64-разрядные версии (управляемую и неуправляемую) в отдельные папки.Затем приложение .NET во время выполнения выбирает, из какого каталога загружать сборки.
Проблема с использованием ResolveEvent заключается в том, что он вызывается только в том случае, если сборки не найдены, поэтому очень легко случайно получить 32-битные версии.Вместо этого используйте второй объект AppDomain, где мы можем изменить свойство ApplicationBase, чтобы оно указывало на нужную папку.В итоге вы получите такой код:
static void Main(String[] argv)
{
// Create a new AppDomain, but with the base directory set to either the 32-bit or 64-bit
// sub-directories.
AppDomainSetup objADS = new AppDomainSetup();
System.String assemblyDir = System.IO.Path.GetDirectoryName(Application.ExecutablePath);
switch (System.IntPtr.Size)
{
case (4): assemblyDir += "\\win32\\";
break;
case (8): assemblyDir += "\\x64\\";
break;
}
objADS.ApplicationBase = assemblyDir;
// We set the PrivateBinPath to the application directory, so that we can still
// load the platform neutral assemblies from the app directory.
objADS.PrivateBinPath = System.IO.Path.GetDirectoryName(Application.ExecutablePath);
AppDomain objAD = AppDomain.CreateDomain("", null, objADS);
if (argv.Length > 0)
objAD.ExecuteAssembly(argv[0]);
else
objAD.ExecuteAssembly("MyApplication.exe");
AppDomain.Unload(objAD);
}
В итоге у вас будет два exes — ваше обычное приложение и второе приложение-переключатель, которое выбирает, какие биты загружать.Примечание. Я не могу взять на себя ответственность за детали этого.Один из моих коллег догадался об этом, учитывая мою первоначальную подсказку.Если и когда он подпишется на StackOverflow, я передам ему ответ.
Другие советы
Мне удалось это сделать около года назад, но я уже не помню всех подробностей.По сути, вы можете использовать IntPtr.Size, чтобы определить, какую DLL загружать, а затем выполнить фактическую LoadLibrary через p/Invoke.На этом этапе у вас есть модуль в памяти, и вы должны иметь возможность просто вызывать функции p/Invoke изнутри него — одно и то же имя модуля не должно перезагружаться снова.
Однако я думаю, что в моем приложении C++ DLL зарегистрировалась как COM-сервер, а затем получила доступ к ее функциям через сгенерированную оболочку .NET, поэтому я не знаю, тестировал ли я когда-либо непосредственно p/Invoking.
Некоторое время назад я столкнулся с подобным сценарием.Набор инструментов, который я использовал, плохо себя вел в 64-битной среде, и мне не удалось найти способ динамически принудительно привязывать сборки как 32-битные.
Можно заставить ваши сборки работать в 32-битном режиме, но для этого необходимо исправить заголовок CLR (в Framework есть инструмент, который делает это), и если ваши сборки имеют строгие имена, это не сработает.
Боюсь, вам придется собрать и опубликовать два набора двоичных файлов для 32- и 64-битных платформ.