Условно использовать 32/64 бит ссылки при создании в Visual Studio

У меня есть проект, который строит в 32/64-бит и имеет соответствующие 32/64-битные зависимости. Я хочу иметь возможность переключать конфигурации и использовать правильную ссылку, но я не знаю, как сказать Visual Studio использовать соответствующую архитектуре зависимость.

Возможно, я поступил неправильно, но я хочу иметь возможность переключаться между x86 и x64 в раскрывающемся списке конфигурации и иметь соответствующую DLL-ссылку, которая должна быть правильной.

Ответ 1

Вот что я сделал в предыдущем проекте, который потребует ручной редакции файла .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-разрядной платформе.

Первоначально я написал это для внутреннего вики на работе, однако я изменил его и опубликовал полный процесс в своем блоге, если вас интересуют подробные пошаговые инструкции.

Ответ 2

AFAIK, если для вашего проекта требуются ссылки, которые являются 32-разрядными или 64-битными (то есть сборками COM-interop), и вы не заинтересованы в ручном редактировании файла .csproj, тогда вам придется создавать отдельные 32 -битные и 64-битные проекты.

Я должен отметить, что следующее решение не проверено, но должно работать. Если вы хотите вручную отредактировать файл .csproj, вы сможете достичь желаемого результата с помощью одного проекта. Файл .csproj - это просто MSBuild script, поэтому для полной ссылки посмотрите здесь. После того как вы откроете файл .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

Ответ 3

Вы можете использовать условие для ItemGroup для ссылок dll в файле проекта.
Это заставит визуальную студию перепроверять состояние и ссылки всякий раз, когда вы меняете активную конфигурацию.
Просто добавьте условие для каждой конфигурации.

Пример:

 <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>

Ответ 4

Я ссылаюсь на библиотеки x86, расположенные, например. \component\v3_NET4, в моем проекте. Конкретные DLL файлы для x86/x64 расположены в подпапках с именем "x86" и "x64" соответственно.

Затем я использую pre-build script, который копирует соответствующие DLL (x86/x64) в ссылочную папку на основе $(PlatformName).

xcopy /s /e /y "$(SolutionDir)..\component\v3_NET4\$(PlatformName)\*" "$(SolutionDir)..\component\v3_NET4"

Работает для меня.

Ответ 5

Я столкнулся с такой же проблемой и потратил немало времени на поиски достойного решения. Большинство людей предлагают ручное редактирование файлов решений Visual Studio, которые довольно утомительны, подвержены ошибкам и запутываются при изучении этих отредактированных файлов в графическом интерфейсе Visual Studio. Когда я уже сдался, решение пришло в себя. Это очень похоже на то, что Мик рекомендует в своем ответе выше.

В менеджере учетных записей, как обычно, я создал две отдельные цели сборки для платформ x86 и x64. Затем я добавил ссылку на сборку x86 для моего проекта. С этой точки зрения я полагал, что проект сконфигурирован только для сборки x86 и никогда не будет создан для конфигурации x64, если только я не сделаю его вручную, как было предложено выше в Hugo.

Через некоторое время я в конце концов забыл об ограничении и случайно начал сборку x64. Конечно, сборка не удалась. Но важным было сообщение об ошибке, которое я получил. Сообщение об ошибке сообщило, что сборка, названная точно так же, как моя ссылка на сборку x86 отсутствует в папке, предназначенной как цель сборки x64 для моего решения.

Заметив это, я вручную скопировал соответствующую сборку x64 в этот каталог. Слава! Моя сборка x64 чудом преуспела в том, что надлежащая сборка найдена и связана неявно. Для изменения моего решения было задано несколько минут, чтобы установить целевой каталог сборки для сборки x64 в эту папку. После этих шагов решение автоматически создается для x86 и x64 без ручного редактирования файлов MSBuild.

Подводя итог:

  • Создание целевых объектов x86 и x64 в одном проекте
  • Добавьте все соответствующие ссылки проекта на сборки x86.
  • Установить один общий целевой каталог сборки для всех сборок x64.
  • Если у вас есть готовые сборки x64, просто скопируйте их один раз в свой целевой каталог сборки x64.

После завершения этих шагов ваше решение будет правильно построено для конфигураций x86 и x64.

Это сработало для меня в Visual Studio 2010.NET 4.0 С#. Очевидно, это своего рода недокументированное внутреннее поведение Visual Studio, которое может быть изменено в версиях 2012, 2013 и 2015 годов. Если кто-то попробует другие версии, поделитесь своим опытом.

Ответ 6

Одна сборка .Net с зависимостями x86/x64

В то время как все остальные ответы дают вам решение для создания различных сборок в соответствии с платформой, я даю вам возможность иметь только конфигурацию "AnyCPU" и создать сборку, которая работает с вашими библиотеками x86 и x64.

Вы должны написать некоторый сантехнический код для этого. Я не мог заставить это работать с app.config. Если кто-то знает, как решить эту проблему через app.config, мне бы очень хотелось знать.

Разрешение правильных x86/x64-dll во время выполнения

Этапы:

  1. Используйте AnyCPU в csproj
  2. Решите, будете ли вы ссылаться только на библиотеки x86 или x64 в вашем csprojs. Адаптируйте настройки UnitTests к выбранным вами настройкам архитектуры. Это важно для отладки/запуска тестов внутри VisualStudio.
  3. В справочных свойствах установите Копировать локальный & Специальная версия - ложная
  4. Избавьтесь от предупреждений архитектуры, добавив эту строку в первую PropertyGroup во всех ваших файлах csproj, где вы ссылаетесь на x86/x64: <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
  5. Добавьте этот сценарий 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

    -> Когда вы запустите приложение сейчас, вы получите исключение что сборка не найдена.

  6. Зарегистрируйте событие 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;
    }
    
  7. Если у вас есть модульные тесты, создайте TestClass с методом, который имеет AssemblyInitializeAttribute, а также зарегистрируйте вышеупомянутый TryResolveArchitectureDependency-Handler. (Иногда это не будет выполняться, если вы запускаете одиночные тесты внутри Visual Studio, ссылки будут разрешаться не из бункера UnitTest. Поэтому решение на шаге 2 важно.)

Преимущества:

  • Одна установка/сборка для обеих платформ

Недостатки:  - Нет ошибок во время компиляции, когда dll x86/x64 не совпадают.  - Вы все равно должны запустить тест в обоих режимах!

При желании создайте второй исполняемый файл, предназначенный исключительно для архитектуры x64, с помощью Corflags.exe в сценарии после сборки

Другие варианты, чтобы попробовать:  - Вам не понадобится обработчик событий AssemblyResolve, если вы в противном случае убедитесь, что библиотеки dll будут скопированы в вашу двоичную папку при запуске (архитектура процесса оценки → переместите соответствующие библиотеки dll из x64/x86 в папку bin и обратно.)  - В установщике оцените архитектуру и удалите двоичные файлы для неправильной архитектуры и переместите правильные в папку bin.

Ответ 7

Я закончил тем, что использовал то, что считаю более простым решением, вроде инверсии Микки. Проект представляет собой приложение форм С#, Visual Studio 2015, с целями x86 и x64. Я ссылался на одну из сборок .NET, я использовал 32-битную. В свойствах ссылки я установил "Копировать локально" на false. Затем я просто вручную помещаю соответствующую (32- или 64-битную) сборку .Net в каждый целевой каталог. Фактическая эталонная разрядность не имеет значения, если предположить, что они имеют одинаковые возможности, поскольку она просто определяет внешний интерфейс. Вы также можете добавить шаг копирования после сборки, если хотите стать модным. Обратите внимание, что этот проект также имеет ссылку COM, то же самое работает. Ссылка определяет объекты/интерфейсы, поэтому разрядность эталонной DLL не имеет значения. Если зарегистрированы как 32-битные, так и 64-битные библиотеки DLL COM, приложение выполнит поиск в соответствующем месте в реестре и создаст правильный 32- или 64-битный объект COM. Работает для меня!