Условно использовать 32- или 64-битную ссылку при сборке в Visual Studio

124

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

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

Джонатан Йи
источник
Очень непонятно, что это за язык? Есть ли в решении проект DLL?
Ханс Пассан,
Извините, это .NET, пишу на C #.
Джонатан Йи,
4
Хорошо, я решил это с помощью глупого решения: создал дополнительный файл csproj, который ссылается только на x64 DLL (и удалил конфигурацию x86 из csproj). Это работает, но если бы у кого-нибудь было более элегантное решение, которое не включало бы дополнительный csproj, я бы с удовольствием его увидел.
Джонатан Йи,

Ответы:

99

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

Хьюго
источник
1
Ницца. Я использовал условие для ItemGroup в соответствии с предложением ниже, но использовал $ (PROCESSOR_ARCHITEW6432) и $ (PROCESSOR_ARCHITECTURE) для условий, как здесь. Замечание: я обнаружил, что $ (PROCESSOR_ARCHITECTURE) возвращает x86 на 32- и 64-битных платформах, но $ (PROCESSOR_ARCHITEW6432) возвращает AMD64 только на 64-битной. Что следует отметить, если вы попытаетесь протестировать x86 (потому что AMD64 является производным от x86, я полагаю).
tjmoore
Спасибо за информацию @tjmoore. На какой ОС вы это заметили? Я только что снова проверил свой (Win7SP1) и сказал AMD64 для $ (PROCESSOR_ARCHITECTURE), но определенно хотел бы получить как можно более полную и исчерпывающую информацию.
Hugo
7
Забавно, мой поиск приводит меня сюда, и мне это нужно только потому, что я также использую LeadTools ... +1
Эд С.
Решение работает для конфигурации по умолчанию, но не из моего тестирования, если вы измените конфигурацию из конфигурации из раскрывающегося списка Visual Studio (2012 в моем случае).
Сара Вайнбергер
Вместо использования $ (PROCESSOR_ARCHITEW6432) я использовал $ (Platform) по какой-то причине $ (PROCESSOR_ARCHITEW6432) не работал.
Dzyann
60

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

Джастин Хольцер
источник
Отличный ответ !! Спас мой день! Большое спасибо.
Здравствуйте,
20

Вы можете использовать условие для 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>
Йохай Тиммер
источник
1
Спасибо большое! Это определенно должно быть приемлемым решением!
ManicBlowfish
Серьезно, этот ответ намного лучше и проще принятого.
Яндрос
1
Нормально ли после этого дублировать записи в Справочниках?
natenho
7

Я ссылаюсь на библиотеки DLL 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"

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

Micke
источник
3

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

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

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

шаги:

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

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

Феликс Кейл
источник
2

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

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

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

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

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

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

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

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

Борис Зинченко
источник
-1

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

Джефф Х
источник