Умовно використовуйте 32/64-бітну посилання під час створення в Visual Studio


124

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

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


Дуже незрозуміло, що це за мова? Чи проект DLL в рішенні?
Ганс Пасант

Вибачте, це .NET, я пишу на C #.
Джонатан Йе

4
Гаразд, я вирішив це німим рішенням: створив додатковий файл csproj, який посилається лише на DLL x64 (і видалив конфігурацію 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-бітній платформі.

У мене це було написано спочатку для внутрішньої Вікі на роботі, проте я його змінив і опублікував повний процес у своєму блозі , якщо вас цікавлять детальні покрокові інструкції.


1
Приємно. Я пішов із використанням умовного на ItemGroup відповідно до запропонованої нижче пропозиції, але використовуючи $ (PROCESSOR_ARCHITEW6432) та $ (PROCESSOR_ARCHITECTURE) для умов, як тут. Зауважте, що я виявив, що $ (PROCESSOR_ARCHITECTURE) повертає x86 на обох 32 і 64 бітових платформах, але $ (PROCESSOR_ARCHITEW6432) повертає AMD64 лише на 64 біт. Щось зауважимо, якщо ви спробуєте протестувати на x86 (тому що я вважаю, AMD64 є похідною від x86).
tjmoore

Дякую за цю інформацію @tjmoore. На якому O / S ви це помітили? Я просто перевірив свою ще раз (Win7SP1) і каже AMD64 за $ (PROCESSOR_ARCHITECTURE), але, безумовно, хотів би мати якомога повнішу і ретельну інформацію.
Гюго

7
Смішно, мій пошук приводить мене сюди, і мені це потрібно лише тому, що я також використовую LeadTools ... +1
Ed S.

Рішення працює для конфігурації за замовчуванням, але не з мого тестування, якщо ви не змінюєте конфігурацію з конфігурації зі списку Visual Studio (2012 у моєму випадку) Конфігурація рішення.
Сара Вайнбергер

Замість використання $ (PROCESSOR_ARCHITEW6432) я чомусь використовував $ (Platform), $ (PROCESSOR_ARCHITEW6432) не працював.
Dzyann

60

AFAIK, якщо ваш проект вимагає посилань, які є 32-бітними або 64-бітовими (тобто COM-interop збірки), і ви не маєте інтересу в ручному редагуванні файлу .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 у файлі проекту.
Це призведе до того, що візуальна студія повторно перевірить стан та посилання, коли ви зміните активну конфігурацію.
Просто додайте умову для кожної конфігурації.

Приклад:

 <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, розташовані, наприклад, \ компонент \ v3_NET4. Конкретні DLL-файли для x86 / x64 розміщені у підпапках, названих "x86" та "x64", відповідно.

Тоді я використовую сценарій попереднього збирання, який копіює відповідні DLL (x86 / x64) у папку, на яку посилається, на основі $ (PlatformName).

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

Працює для мене.


3

Одна .Net збірка із залежностями x86 / x64

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

Дозвіл правильних x86 / x64-dlls під час виконання

Кроки:

  1. Використовуйте AnyCPU в csproj
  2. Вирішіть, чи посилаєтесь лише на x86 або x64 dlls у своєму csprojs. Адаптуйте параметри UnitTests до вибраних вами параметрів архітектури. Це важливо для налагодження / запуску тестів всередині VisualStudio.
  3. У розділі Довідкові властивості встановіть значення Копіювати локальну та конкретну версію в хибну
  4. Позбавтеся від попереджень архітектури, додавши цей рядок до першої PropertyGroup у всіх своїх файлах csproj, де ви посилаєтеся на x86 / x64: <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
  5. Додайте цей сценарій після збирання до свого запуску проекту, використовуйте та змінюйте шляхи цього сценарію, щоб він копіював усі ваші x86 / x64 dlls у відповідні підпапки вашого накопичувача \ 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. (Це не буде виконуватися іноді, якщо ви запускаєте одиничні тести всередині візуальної студії, посилання вирішуватимуться не з бін UnitTest. Тому рішення на кроці 2 важливо.)

Переваги:

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

Недоліки: - Немає помилок під час компіляції, коли x86 / x64 dlls не збігаються. - Ви все одно повинні пройти тест в обох режимах!

За бажанням створіть другий виконуваний файл, ексклюзивний для архітектури x64 за допомогою Corflags.exe в сценарії після побудови

Інші варіанти для випробування: - Вам не знадобиться обробник події AssemblyResolve, якщо ви впевнені в іншому випадку, що dlls будуть скопійовані у вашій бінарній папці на початку (Оцінити архітектуру процесу -> перемістити відповідні dlls з x64 / x86 у папку бін та назад.) - В Інсталяторі оцініть архітектуру та видаліть бінарні файли для неправильної архітектури та перемістіть правильні до папки бін.


2

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

У менеджері облікових записів я створив дві окремі цілі збірки для платформ x86 та x64, як зазвичай. Далі я додав до свого проекту посилання на складання x86. З цього моменту я вважав, що проект налаштований лише на збірку x86 і ніколи не будуватиметься для конфігурації x64, якщо я не зроблю вручну його редагування, як запропонував Hugo вище.

Через деякий час я врешті-решт забув обмеження і випадково почав збирати x64. Звичайно, збірка не вдалася. Але важливим було повідомлення про помилку, яке я отримав. Повідомлення про помилку повідомляє, що в папці, призначеній як ціль збірки x64 для мого рішення, відсутня збірка, названа так, як моя посилання x86.

Помітивши це, я вручну скопіював належну збірку 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. Для мене працює!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.