Ориентация как на 32-битную, так и на 64-битную версию с Visual Studio в одном решении / проекте

111

У меня небольшая дилемма относительно того, как настроить мои сборки визуальной студии для множественного таргетинга.

Предыстория: c # .NET v2.0 с вызовом p / в сторонние 32-битные библиотеки DLL, SQL compact v3.5 SP1 с проектом установки. Прямо сейчас целевая платформа установлена ​​на x86, поэтому ее можно запускать в Windows x64.

Сторонняя компания только что выпустила 64-битные версии своих DLL, и я хочу создать специальную 64-битную программу.

Это поднимает некоторые вопросы, на которые у меня еще нет ответов. Я хочу иметь точно такую ​​же базу кода. Я должен строить со ссылками на 32-битный набор DLL или 64-битный DLL. (И сторонние производители, и SQL Server Compact)

Можно ли решить эту проблему с помощью двух новых наборов конфигураций (Debug64 и Release64)?

Должен ли я создать 2 отдельных проекта установки (стандартные проекты Visual Studio, без Wix или какой-либо другой утилиты), или это можно решить в одном и том же .msi?

Любые идеи и / или рекомендации приветствуются.

Магнус Йоханссон
источник
@Magnus Johansson: вы можете использовать две конфигурации, чтобы выполнить половину своей цели. MSI немного сложнее.
user7116

Ответы:

83

Да, вы можете настроить таргетинг на x86 и x64 с одной и той же базой кода в одном проекте. В общем, все будет просто работать, если вы создадите правильные конфигурации решения в VS.NET (хотя P / Invoke для полностью неуправляемых библиотек DLL, скорее всего, потребует некоторого условного кода): элементы, которые, как я обнаружил, требуют особого внимания:

  • Ссылки на внешние управляемые сборки с тем же именем, но с их собственной конкретной разрядностью (это также относится к сборкам взаимодействия с COM)
  • Пакет MSI (который, как уже отмечалось, должен быть ориентирован на x86 или x64)
  • Любые настраиваемые действия на основе классов установщика .NET в вашем пакете MSI

Проблема ссылки на сборку не может быть решена полностью в VS.NET, так как это позволит вам только один раз добавить ссылку с заданным именем в проект. Чтобы обойти это, отредактируйте файл проекта вручную (в VS щелкните правой кнопкой мыши файл проекта в обозревателе решений, выберите «Выгрузить проект», затем снова щелкните правой кнопкой мыши и выберите «Изменить»). После добавления ссылки, скажем, на версию сборки x86, ваш файл проекта будет содержать что-то вроде:

<Reference Include="Filename, ..., processorArchitecture=x86">
  <HintPath>C:\path\to\x86\DLL</HintPath>
</Reference>

Оберните этот тег Reference внутри тега ItemGroup, указав конфигурацию решения, к которой он применяется, например:

<ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
   <Reference ...>....</Reference>
</ItemGroup>

Затем скопируйте и вставьте весь тег ItemGroup и отредактируйте его, чтобы он содержал сведения о вашей 64-битной DLL, например:

<ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
  <Reference Include="Filename, ..., processorArchitecture=AMD64">
     <HintPath>C:\path\to\x64\DLL</HintPath>
   </Reference>
</ItemGroup>

После перезагрузки вашего проекта в VS.NET диалоговое окно «Справочник по сборке» будет немного сбито с толку этими изменениями, и вы можете столкнуться с некоторыми предупреждениями о сборках с неправильным целевым процессором, но все ваши сборки будут работать нормально.

Следующим шагом будет решение проблемы с MSI, и, к сожалению, для этого потребуется инструмент, отличный от VS.NET: для этой цели я предпочитаю Advanced Installer от Caphyon , так как он реализует основной трюк (создайте общий MSI, а также 32-разрядный и 64-разрядные MSI, а также использовать средство запуска установки .EXE для извлечения нужной версии и выполнения необходимых исправлений во время выполнения) очень и очень хорошо.

Вероятно, вы можете добиться тех же результатов с помощью других инструментов или набора инструментов Windows Installer XML (WiX) , но Advanced Installer упрощает работу (и при этом вполне доступен), что я никогда особо не рассматривал альтернативы.

Одна вещь, для которой вам может потребоваться WiX, даже при использовании Advanced Installer, - это настраиваемые действия вашего .NET Installer Class. Хотя тривиально указать определенные действия, которые должны выполняться только на определенных платформах (с использованием условий выполнения VersionNT64 и НЕ VersionNT64 соответственно), встроенные настраиваемые действия AI будут выполняться с использованием 32-разрядной платформы, даже на 64-разрядных машинах. .

Это может быть исправлено в будущем выпуске, но на данный момент (или при использовании другого инструмента для создания ваших MSI-файлов с той же проблемой) вы можете использовать поддержку управляемых настраиваемых действий WiX 3.0 для создания DLL действий с надлежащей разрядностью, которая будет выполняться с использованием соответствующего Framework.


Изменить: начиная с версии 8.1.2 Advanced Installer правильно поддерживает 64-разрядные пользовательские действия. С момента моего первоначального ответа его цена, к сожалению, немного выросла, хотя он по-прежнему чрезвычайно выгоден по сравнению с InstallShield и ему подобными ...


Изменить: если ваши библиотеки DLL зарегистрированы в GAC, вы также можете использовать стандартные теги ссылок таким образом (например, SQLite):

<ItemGroup Condition="'$(Platform)' == 'x86'">
    <Reference Include="System.Data.SQLite, Version=1.0.80.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=x86" />
</ItemGroup>
<ItemGroup Condition="'$(Platform)' == 'x64'">
    <Reference Include="System.Data.SQLite, Version=1.0.80.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=AMD64" />
</ItemGroup>

Условие также сокращено до всех типов сборки, выпуска или отладки и просто определяет архитектуру процессора.

mdb
источник
В Visual Studio 2008 я обнаружил, что <ItemGroup> не могут быть вложенными. Это решение работало нормально, как только я сделал новые <ItemGroup> ниже группы остальных <Reference>. Мне также пришлось сменить x86 на AnyCPU, что, вероятно, связано с историей моего конкретного проекта.
Оливер Бок,
Этот расширенный установщик выглядит потрясающе.
Пэт
Это может быть глупый вопрос, но как получить доступ к файлу, чтобы отредактировать его вручную?
пп
1
Чтобы отредактировать файл в VS, щелкните правой кнопкой мыши проект в обозревателе решений и найдите «Выгрузить проект». После выгрузки проекта щелкните его правой кнопкой мыши еще раз и выберите «Изменить <имя файла проекта>». После редактирования файла проекта сохраните его, снова щелкните файл проекта правой кнопкой мыши и загрузите его. Если нет опечаток или ошибок, он загрузится снова. Если нет, VS в значительной степени расскажет вам, в чем проблема с файлом. Надеюсь, это поможет!
Джон Боуман,
Отличный ответ! Спасибо!
Джон Боуман,
27

Допустим, у вас есть сборки DLL для обеих платформ, и они находятся в следующем месте:

C:\whatever\x86\whatever.dll
C:\whatever\x64\whatever.dll

Вам просто нужно отредактировать файл .csproj из этого:

<HintPath>C:\whatever\x86\whatever.dll</HintPath>

К этому:

<HintPath>C:\whatever\$(Platform)\whatever.dll</HintPath>

После этого вы сможете создать свой проект, ориентированный на обе платформы, и MSBuild будет искать в правильном каталоге для выбранной платформы.

Тим Букер
источник
Было бы здорово, если бы это сработало, но это не так. По крайней мере не для меня.
Джон Шиэн
10
Разве это не должно быть: <HintPath> C: \ something \ $ (Platform) \ something.dll </HintPath>
Андреас
Работал нормально в Visual Studio 2008 для меня, но не копировал автоматически DLL в целевой каталог сборки, как это делает обычный <Reference>. Решение mdb сработало для меня лучше.
Оливер Бок
1

Не уверен в полном ответе на ваш вопрос, но подумал, что хотел бы указать на комментарий в разделе «Дополнительная информация» на странице загрузки SQL Compact 3.5 SP1, видя, что вы смотрите на x64 - надеюсь, это поможет.

Из-за изменений в SQL Server Compact SP1 и дополнительной поддержки 64-разрядной версии централизованно устанавливаемые и смешанные среды 32-разрядной версии SQL Server Compact 3.5 и 64-разрядной версии SQL Server Compact 3.5 SP1 могут создавать то, что кажется прерывистым. проблемы. Чтобы свести к минимуму вероятность конфликтов и обеспечить независимое от платформы развертывание управляемых клиентских приложений, централизованная установка 64-разрядной версии SQL Server Compact 3.5 SP1 с помощью файла установщика Windows (MSI) также требует установки 32-разрядной версии SQL Server. Компактный файл MSI 3.5 SP1. Для приложений, которым требуется только 64-разрядная версия, можно использовать частное развертывание 64-разрядной версии SQL Server Compact 3.5 SP1.

Я прочитал это как «включить 32-битные файлы SQLCE, а также 64- битные файлы» при распространении для 64-битных клиентов.

Делает жизнь интересной, я думаю ... должен сказать, что мне нравится фраза "что-то вроде прерывистых проблем" ... звучит немного как "вы что-то воображаете, но на всякий случай сделайте это ..."

gleng
источник
1

Одна сборка .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 в свой запускаемый проект, используйте и измените пути этого сценария sp, чтобы он копировал все ваши x86 / x64 dll в соответствующие подпапки вашего сборочного 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.

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

По поводу вашего последнего вопроса. Скорее всего, вы не сможете решить эту проблему с помощью одного MSI. Если вы используете реестр / системные папки или что-то подобное, сам MSI должен знать об этом, и вы должны подготовить 64-битный MSI для правильной установки на 32-битном компьютере.

Существует вероятность того, что вы можете установить свой продукт как 32-битное приложение и по-прежнему иметь возможность запускать его как 64-битное, но я думаю, что это может быть довольно сложно достичь.

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

Надеюсь это поможет. Вот ссылка на некоторую информацию, связанную с проблемами 32/64 бит: http://blog.typemock.com/2008/07/registry-on-windows-64-bit-double-your.html

Лиор Фридман
источник
0

Если вы используете настраиваемые действия, написанные на .NET, как часть установщика MSI, у вас возникает другая проблема.

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

Дополнительная информация и некоторые приемы ниндзя, чтобы обойти (в основном измените MSI, чтобы использовать 64-битную версию этой прокладки)

Создание MSI в Visual Studio 2005/2008 для работы на SharePoint 64

64-битные управляемые настраиваемые действия с Visual Studio

Райан
источник
0

Вы можете сгенерировать два решения по-разному и потом объединить их! Я сделал это для VS 2010. и это работает. У меня было 2 разных решения, созданных CMake, и я их объединил

voidMainReturn
источник
0

Вы можете использовать условие для 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>
Йохай Тиммер
источник