Попытка не нуждаться в двух отдельных решениях для программы x86 и x64
У меня есть программа, которая должна работать как в среде x86, так и в среде x64. Он использует драйверы ODBC от Oracle. У меня есть ссылка на Oracle.DataAccess.DLL. Эта DLL-библиотека отличается в зависимости от того, является ли система x64 или x86.
В настоящее время у меня есть два отдельных решения, и я поддерживаю код для обоих. Это ужасно. Мне было интересно, что является правильным решением?
Моя платформа установлена на "Любой процессор". и я понимаю, что VS должен скомпилировать DLL на промежуточный язык, так что не имеет значения, использую ли я версию x86 или x64. Тем не менее, если я пытаюсь использовать x64 DLL, я получаю сообщение об ошибке "Не удалось загрузить файл или сборку" Oracle.DataAccess, версия =2.102.3.2, Culture= нейтральный, PublicKeyToken=89b483f429c47342'или одну из его зависимостей. Была предпринята попытка загрузить программу с неверным форматом."
Я работаю на 32-битной машине, поэтому сообщение об ошибке имеет смысл, но меня удивляет, как мне эффективно разрабатывать эту программу, когда она должна работать на x64.
Благодарю.
5 ответов
Это рабочее решение вашей проблемы:
Добавьте 2 DLL (x86 и x64) к вашему решению в подпапке. Сделайте их "Копировать, если новее"
Ссылка на правильную DLL, которую вы используете для разработки для отладки из 2 DLL, которые вы добавили. Сделать это Копировать Local=false.
Это означает, что при запуске приложения DLL не загружается автоматически. Он не будет загружен, пока вы не используете тип из этой сборки. Как только это произойдет, в.Net будет запущено событие, которое спросит, где он может найти вашу сборку.
Поэтому перед первым использованием этой сборки убедитесь, что вы присоединяетесь к этому событию.
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
В содержимом обработчика убедитесь, что вы загружаете DLL (x86 или x64), когда она запрашивает ее.
static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) {
if (args.Name.Equals("MyFullAssemblyName")) {
var path = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
if (IntPtr.Size > 4) {
var dll = System.IO.Path.Combine(path, @"MySubDir\MyDLL_x64.dll");
return System.Reflection.Assembly.LoadFile(dll);
}
else {
var dll = System.IO.Path.Combine(path, @"MySubDir\MyDLL.dll");
return System.Reflection.Assembly.LoadFile(dll);
}
}
return null;
}
Вуаля. Теперь вы можете запускать ваше приложение как 32-битное, так и 64-битное.
В качестве альтернативы добавлению DLL в подпапку, вы можете сделать их вложенными ресурсами, а затем загрузить их следующим образом:
static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) {
if (args.Name.Equals("MyFullAssemblyName")) {
var ass = Assembly.GetExecutingAssembly();
if (IntPtr.Size > 4) {
var strm = ass.GetManifestResourceStream("the.resource.name.for.MyDLL_x64.dll");
var data = new byte[strm.Length];
strm.Read(data, 0, data.Length);
return Assembly.Load(data);
}
else {
var strm = ass.GetManifestResourceStream("the.resource.name.for.MyDLL.dll");
var data = new byte[strm.Length];
strm.Read(data, 0, data.Length);
return Assembly.Load(data);
}
}
return null;
}
Это не работает для всех сборок. Некоторые "гибридные" сборки имеют тенденцию давать сбой, если они не загружены с диска (это можно решить, записав их на диск непосредственно перед загрузкой).
Это чисто проблема развертывания, вам никогда не придется обслуживать разные проекты. Хотя это неловко, и я жалею на Oracle за то, что они сами не позаботились об этом. Другое соображение заключается в том, что эта сборка действительно должна создаваться на целевой машине. Некоторые варианты
- Создайте два установщика, один для x64 и один для x86. Клиент выбирает правильный, основываясь на операционной системе, которую он использует. Достаточно просто, вы просто копируете нужный файл.
- Разверните обе сборки в GAC. Теперь это автоматически, .NET выбирает правильную на любом типе машины. Крупные компании должны почти всегда использовать GAC, чтобы развертывать обновления безопасности, не зная, почему Oracle этого не делает.
- Разверните сборки в подкаталоге x86 и x64 каталога установки. Вам нужно будет написать обработчик события AppDomain.AssemblyResolve, который на основе значения IntPtr.Size выбирает правильный каталог.
- Измените целевую платформу в вашем проекте EXE на x86. Учитывая, что ваш код должен работать как на 32-битной, так и на 64-битной машине, нет причин для сборки AnyCPU.
Если вы работаете на 32-битной машине, вам нужно загрузить 32-битную версию Oracle DLL. 32-битная программа не может ссылаться на 64-битную DLL. И 64-битная программа не может ссылаться на 32-битную DLL.
"Любой ЦП" является правильной целью, если у вас есть несколько версий внешней DLL. Хитрость заключается в том, чтобы убедиться, что правильная Oracle DLL находится и загружается. Лучше всего найти 64-разрядную версию DLL в вашей 32-разрядной системе и переименовать ее, чтобы среда выполнения не могла ее найти.
Вы должны быть в состоянии настроить одно и то же решение для сборки версий x86/x64 отдельно. Вам также может понадобиться добавить шаги после сборки, чтобы скопировать правильную версию DLL в соответствующие папки вывода...
По крайней мере, если вам нужно построить 2 решения - используйте один и тот же источник (добавляйте файлы как ссылки на второе решение, а не копируйте во второе решение).
Использование AnyCPU с нативными ранними привязками просто не сработает, для этого вам нужны два отдельных решения и сборки, как вы видели. Вы должны овладеть 64-битной системой, чтобы разработать или хотя бы протестировать скомпилированные библиотеки x64.
Однако, с поздним связыванием, вы можете использовать свойства AnyCPU и System, чтобы выяснить, с какой архитектурой вы работаете, и связать с правильной dll, если вы оставите имя как Oracle.DataAccess.x86.dll. Если они установлены в GAC, это еще проще, вы можете выполнить связывание, даже не потрудившись сначала проверить архитектуру, но я считаю, что вам все равно придется поздно связываться.
Обратите внимание, что VMware может запускать 64-разрядную гостевую систему на 32-разрядной машине, если вы действительно не хотите переустанавливать Windows.