Условно используйте 32/64 битную ссылку при сборке в Visual Studio
У меня есть проект, который строит в 32/64-битных и имеет соответствующие 32/64-битные зависимости. Я хочу иметь возможность переключать конфигурации и использовать правильную ссылку, но я не знаю, как сказать Visual Studio использовать зависимую от архитектуры зависимость.
Может быть, я иду по этому пути неправильно, но я хочу иметь возможность переключаться между x86 и x64 в раскрывающемся списке конфигурации, и чтобы ссылочная DLL была правильной разрядностью.
6 ответов
Вот что я сделал в предыдущем проекте, который потребует ручной редакции файла.csproj. Вам также нужны отдельные каталоги для разных двоичных файлов, в идеале родных и близких, и с тем же именем, что и целевая платформа.
После добавления ссылок на одну платформу в проект откройте.csproj в текстовом редакторе. Перед первым <ItemGroup>
элемент внутри <Project>
добавьте следующий код, который поможет определить, на какой платформе вы работаете (и строите).
<!-- Properties group for Determining 64bit Architecture -->
<PropertyGroup>
<CurrentPlatform>x86</CurrentPlatform>
<CurrentPlatform Condition="'$(PROCESSOR_ARCHITECTURE)'=='AMD64' or '$(PROCESSOR_ARCHITEW6432)'=='AMD64'">AMD64</CurrentPlatform>
</PropertyGroup>
Затем для ссылок, специфичных для вашей платформы, вы вносите следующие изменения:
<ItemGroup>
<Reference Include="Leadtools, Version=16.5.0.0, Culture=neutral, PublicKeyToken=9cf889f53ea9b907, processorArchitecture=x86">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\Lib\Leadtools\$(CurrentPlatform)\Leadtools.dll</HintPath>
</Reference>
<Reference Include="Leadtools.Codecs, Version=16.5.0.0, Culture=neutral, PublicKeyToken=9cf889f53ea9b907, processorArchitecture=x86">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\Lib\Leadtools\$(CurrentPlatform)\Leadtools.Codecs.dll</HintPath>
</Reference>
<Reference Include="Leadtools.ImageProcessing.Core, Version=16.5.0.0, Culture=neutral, PublicKeyToken=9cf889f53ea9b907, processorArchitecture=x86">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\Lib\Leadtools\$(CurrentPlatform)\Leadtools.ImageProcessing.Core.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data.Entity" />
<!-- Other project references -->
</ItemGroup>
Обратите внимание на использование $(CurrentPlatform)
свойство, которое мы определили выше. Вместо этого вы могли бы использовать условные обозначения, какие сборки включить для какой платформы. Вам также может понадобиться:
- Заменить
$(PROCESSOR_ARCHITEW6432)
а также$(PROCESSOR_ARCHITECTURE)
с$(Platform)
считать ТОЛЬКО целевой платформой проектов - Измените логику определения платформы, чтобы она соответствовала текущей машине, чтобы вы не создавали / не ссылались на 64-битный двоичный файл для выполнения на 32-битной платформе.
Первоначально я написал это для внутреннего Wiki на работе, однако я изменил его и разместил полный процесс в своем блоге, если вас интересуют подробные пошаговые инструкции.
AFAIK, если ваш проект требует ссылок, которые являются 32-битными или 64-битными (то есть сборками COM-взаимодействия), и вы не заинтересованы в ручном редактировании файла.csproj, то вам придется создать отдельный 32-битный и 64-битные проекты.
Я должен отметить, что следующее решение не проверено, но должно работать. Если вы хотите вручную редактировать файл.csproj, то вы сможете достичь желаемого результата с помощью одного проекта. Файл.csproj - это всего лишь скрипт MSBuild, поэтому для полной информации смотрите здесь. Открыв файл.csproj в редакторе, найдите <Reference>
элементы. Вы должны быть в состоянии разделить эти элементы на 3 отдельные группы элементов: ссылки, которые не зависят от платформы, ссылки, специфичные для x86, и ссылки, специфичные для x64.
Вот пример, который предполагает, что ваш проект настроен с целевыми платформами с именами "x86" и "x64"
<!-- this group contains references that are not platform specific -->
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<!-- any other references that aren't platform specific -->
</ItemGroup>
<!-- x86 specific references -->
<ItemGroup Condition=" '$(Platform)' == 'x86' ">
<Reference Include="MyComAssembly.Interop">
<HintPath>..\..\lib\x86\MyComAssembly.Interop.dll</HintPath>
</Reference>
<!-- any additional x86 specific references -->
</ItemGroup>
<!-- x64 specific referneces -->
<ItemGroup Condition=" '$(Platform)' == 'x64' ">
<Reference Include="MyComAssembly.Interop">
<HintPath>..\..\lib\x64\MyComAssembly.Interop.dll</HintPath>
</Reference>
<!-- any additional x64 specific references -->
</ItemGroup>
Теперь, когда вы устанавливаете конфигурацию сборки вашего проекта / решения для целевой платформы x86 или x64, она должна включать правильные ссылки в каждом случае. Конечно, вам нужно поиграть с <Reference>
элементы. Вы можете даже создать фиктивные проекты, в которых вы добавляете ссылки на x86 и x64, а затем просто копируете необходимые <Reference>
элементы из этих фиктивных файлов проекта в ваш "настоящий" файл проекта.
Редактировать 1
Вот ссылка на общие элементы проекта MSBuild, которые я случайно пропустил из исходного поста: http://msdn.microsoft.com/en-us/library/bb629388.aspx
Вы можете использовать условие для ItemGroup для ссылок dll в файле проекта.
Это заставит Visual Studio перепроверять условия и ссылки при каждом изменении активной конфигурации.
Просто добавьте условие для каждой конфигурации.
Пример:
<ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<Reference Include="DLLName">
<HintPath>..\DLLName.dll</HintPath>
</Reference>
<ProjectReference Include="..\MyOtherProject.vcxproj">
<Project>{AAAAAA-000000-BBBB-CCCC-TTTTTTTTTT}</Project>
<Name>MyOtherProject</Name>
</ProjectReference>
</ItemGroup>
Я ссылаюсь на библиотеки x86, расположенные, например, в \component\v3_NET4, в моем проекте. Специальные библиотеки DLL для x86/x64 находятся в подпапках с именами "x86" и "x64" соответственно.
Затем я использую сценарий предварительной сборки, который копирует соответствующие библиотеки DLL (x86/x64) в указанную папку на основе $(PlatformName).
xcopy /s /e /y "$(SolutionDir)..\component\v3_NET4\$(PlatformName)\*" "$(SolutionDir)..\component\v3_NET4"
Работает для меня.
Одна сборка.Net с зависимостями x86/x64
В то время как все остальные ответы дают вам решение для создания различных сборок в соответствии с платформой, я даю вам возможность иметь только конфигурацию "AnyCPU" и создать сборку, которая работает с вашими библиотеками x86 и x64.
Вы должны написать какой-то сантехнический код для этого. Я не мог заставить это работать с app.config. Если кто-то знает способ решить это через app.config, я бы очень хотел знать.
Разрешение правильных x86/x64-dll во время выполнения
шаги:
- Используйте AnyCPU в csproj
- Решите, будете ли вы ссылаться только на библиотеки x86 или x64 в вашем csprojs. Адаптируйте настройки UnitTests к выбранным вами настройкам архитектуры. Это важно для отладки / запуска тестов внутри VisualStudio.
- В свойствах Reference установите для свойства " Копировать локальную и определенную версию" значение false
- Избавьтесь от предупреждений архитектуры, добавив эту строку в первую группу PropertyGroup во всех ваших файлах csproj, где вы ссылаетесь на x86/x64:
<ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
Добавьте этот сценарий postbuild в ваш проект запуска, используйте и измените пути этого сценария, чтобы он копировал все ваши библиотеки x86/x64 в соответствующие подпапки вашей сборки bin \ x86 \ bin \ x64 \
xcopy /E /H /R /Y /I /D $(SolutionDir)\YourPathToX86Dlls $(TargetDir)\x86 xcopy /E /H /R /Y /I /D $(SolutionDir)\YourPathToX64Dlls $(TargetDir)\x64
-> Когда вы запускаете приложение сейчас, вы получаете исключение, что сборка не может быть найдена.
Зарегистрируйте событие AssemblyResolve прямо в начале точки входа вашего приложения
AppDomain.CurrentDomain.AssemblyResolve += TryResolveArchitectureDependency;
с помощью этого метода:
/// <summary> /// Event Handler for AppDomain.CurrentDomain.AssemblyResolve /// </summary> /// <param name="sender">The app domain</param> /// <param name="resolveEventArgs">The resolve event args</param> /// <returns>The architecture dependent assembly</returns> public static Assembly TryResolveArchitectureDependency(object sender, ResolveEventArgs resolveEventArgs) { var dllName = resolveEventArgs.Name.Substring(0, resolveEventArgs.Name.IndexOf(",")); var anyCpuAssemblyPath = $".\\{dllName}.dll"; var architectureName = System.Environment.Is64BitProcess ? "x64" : "x86"; var assemblyPath = $".\\{architectureName}\\{dllName}.dll"; if (File.Exists(assemblyPath)) { return Assembly.LoadFrom(assemblyPath); } return null; }
- Если у вас есть модульные тесты, создайте TestClass с методом, который имеет AssemblyInitializeAttribute, а также зарегистрируйте вышеупомянутый TryResolveArchitectureDependency-Handler. (Иногда это не будет выполняться, если вы запускаете одиночные тесты в Visual Studio, ссылки будут разрешаться не из бункера UnitTest. Поэтому решение на шаге 2 важно.)
Выгоды:
- Одна установка / сборка для обеих платформ
Недостатки: - Нет ошибок во время компиляции, когда DLL-библиотеки x86/x64 не совпадают. - Вы все равно должны запустить тест в обоих режимах!
При желании создайте второй исполняемый файл, который является эксклюзивным для архитектуры x64 с Corflags.exe в сценарии после сборки
Другие варианты: - Вам не понадобится обработчик событий AssemblyResolve, если вы в противном случае убедитесь, что библиотеки dll копируются в вашу двоичную папку при запуске (архитектура Evaluate Process -> перемещаете соответствующие библиотеки dll из x64/x86 в папку bin и обратно.) - В установщике оцените архитектуру и удалите двоичные файлы для неправильной архитектуры и переместите правильные в папку bin.
Я столкнулся с той же проблемой и провел довольно много времени в поисках достойного решения. Большинство людей предлагают ручное редактирование файлов решений Visual Studio, что довольно утомительно, подвержено ошибкам и приводит к путанице при последующем исследовании этих отредактированных файлов в графическом интерфейсе Visual Studio. Когда я уже сдался, решение пришло само собой. Это очень похоже на то, что Микке рекомендует в своем ответе выше.
Как обычно, в диспетчере учетных записей я создал две отдельные цели сборки для платформ x86 и x64. Затем я добавил ссылку на сборку x86 в свой проект. На этом этапе я полагал, что проект настроен только для сборки x86 и никогда не будет собираться для конфигурации x64, если я не сделаю его ручное редактирование, как предложено Хьюго выше.
Через некоторое время я в конце концов забыл об ограничении и случайно запустил сборку x64. Конечно, сборка не удалась. Но важным было сообщение об ошибке, которое я получил. Сообщение об ошибке говорит о том, что сборка, названная точно так, как моя сборка x86, на которую ссылаются, отсутствует в папке, предназначенной как цель сборки x64 для моего решения.
Заметив это, я вручную скопировал правильную сборку x64 в этот каталог. Слава! Моя сборка x64 чудесным образом преуспела, и надлежащая сборка была обнаружена и неявно связана. Это было считанные минуты, чтобы изменить мое решение, чтобы установить в эту папку целевой каталог сборки для сборки x64. После этих шагов решение собирается автоматически для x86 и x64 без какого-либо ручного редактирования файлов MSBuild.
Подводить итоги:
- Создайте цели x86 и x64 в одном проекте
- Добавьте все правильные ссылки проекта на сборки x86
- Установите один общий целевой каталог сборки для всех сборок x64
- Если у вас есть готовые сборки x64, просто скопируйте их один раз в целевой каталог сборки x64
После выполнения этих шагов ваше решение будет правильно построено для конфигураций x86 и x64.
Это сработало для меня в проекте Visual Studio 2010 .NET 4.0 C#. Очевидно, это своего рода недокументированное внутреннее поведение Visual Studio, которое может быть изменено в версиях 2012, 2013 и 2015 годов. Если кто-то попробует другие версии, поделитесь своим опытом.
Я закончил тем, что использовал то, что считаю более простым решением, вроде инверсии Микки. Проект представляет собой приложение форм C#, Visual Studio 2015, с целями x86 и x64. Я ссылался на одну из сборок.NET, я использовал 32-битную. В свойствах ссылки я установил "Копировать локально" на false. Затем я просто вручную помещаю соответствующую (32- или 64-битную) сборку.Net в каждый целевой каталог. Фактическая эталонная разрядность не имеет значения, если предположить, что они имеют одинаковые возможности, поскольку она просто определяет внешний интерфейс. Вы также можете добавить шаг копирования после сборки, если хотите стать модным. Обратите внимание, что этот проект также имеет ссылку COM, то же самое работает. Ссылка определяет объекты / интерфейсы, поэтому разрядность эталонной DLL не имеет значения. Если зарегистрированы как 32-битные, так и 64-битные библиотеки DLL COM, приложение выполнит поиск в соответствующем месте в реестре и создаст правильный 32- или 64-битный объект COM. Работает для меня!