Visando 32 bits e 64 bits com Visual Studio na mesma solução / projeto

111

Eu tenho um pequeno dilema sobre como configurar minhas compilações de estúdio visual para multi-segmentação.

Histórico: c # .NET v2.0 com p / invocar em DLLs de 32 bits de terceiros, SQL compact v3.5 SP1, com um projeto de instalação. No momento, o destino da plataforma está definido como x86 para que possa ser executado no Windows x64.

A empresa terceirizada acaba de lançar versões de 64 bits de suas DLLs e eu quero construir um programa dedicado de 64 bits.

Isso levanta algumas questões para as quais ainda não tenho as respostas. Eu quero ter exatamente a mesma base de código. Devo construir com referências ao conjunto de DLLs de 32 bits ou DLL de 64 bits. (De terceiros e SQL Server Compact)

Isso pode ser resolvido com 2 novos conjuntos de configurações (Debug64 e Release64)?

Devo criar 2 projetos de configuração separados (projetos std. Visual studio, sem Wix ou qualquer outro utilitário), ou isso pode ser resolvido dentro do mesmo .msi?

Quaisquer ideias e / ou recomendações serão bem-vindas.

Magnus Johansson
fonte
@Magnus Johansson: você pode usar duas configurações para cumprir metade do seu objetivo. O MSI é um pouco mais difícil.
user7116

Respostas:

83

Sim, você pode direcionar tanto x86 quanto x64 com o mesmo código base no mesmo projeto. Em geral, as coisas simplesmente funcionarão se você criar as configurações de solução certas no VS.NET (embora P / Invoke para DLLs totalmente não gerenciadas provavelmente exija algum código condicional): os itens que descobri que requerem atenção especial são:

  • Referências a assemblies gerenciados externos com o mesmo nome, mas seu próprio número de bits específico (isso também se aplica a assemblies de interoperabilidade COM)
  • O pacote MSI (que, como já foi observado, precisará ter como alvo x86 ou x64)
  • Quaisquer ações baseadas em classes do instalador .NET personalizadas em seu pacote MSI

O problema de referência do assembly não pode ser resolvido inteiramente no VS.NET, pois ele só permitirá que você adicione uma referência com um determinado nome a um projeto uma vez. Para contornar isso, edite seu arquivo de projeto manualmente (no VS, clique com o botão direito do mouse no arquivo de projeto no Solution Explorer, selecione Unload Project, clique com o botão direito novamente e selecione Edit). Depois de adicionar uma referência a, digamos, a versão x86 de um assembly, seu arquivo de projeto conterá algo como:

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

Envolva essa tag de referência dentro de uma tag ItemGroup indicando a configuração da solução à qual se aplica, por exemplo:

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

Em seguida, copie e cole a tag ItemGroup inteira e edite-a para conter os detalhes de sua DLL de 64 bits, por exemplo:

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

Depois de recarregar seu projeto no VS.NET, a caixa de diálogo Referência de Montagem ficará um pouco confusa com essas mudanças e você pode encontrar alguns avisos sobre montagens com o processador de destino errado, mas todas as suas compilações funcionarão bem.

Resolver a questão MSI é o próximo, e infelizmente isso vai exigir uma ferramenta non-VS.NET: Eu prefiro de Caphyon Installer Avançada para o efeito, uma vez que retira o truque básico envolvido (criar um MSI comum, bem como de 32 bits e MSIs específicos de 64 bits e usar um iniciador de configuração .EXE para extrair a versão correta e fazer as correções necessárias no tempo de execução) muito, muito bem.

Você provavelmente pode obter os mesmos resultados usando outras ferramentas ou o conjunto de ferramentas XML do Windows Installer (WiX) , mas o Advanced Installer torna as coisas tão fáceis (e é bastante acessível) que eu nunca realmente olhei para alternativas.

Uma coisa para a qual você ainda pode exigir o WiX, mesmo ao usar o Advanced Installer, é para suas ações personalizadas da classe .NET Installer. Embora seja trivial especificar certas ações que devem ser executadas apenas em certas plataformas (usando as condições de execução VersionNT64 e NOT VersionNT64, respectivamente), as ações personalizadas de IA integradas serão executadas usando o Framework de 32 bits, mesmo em máquinas de 64 bits .

Isso pode ser corrigido em uma versão futura, mas por agora (ou ao usar uma ferramenta diferente para criar seus MSIs que tem o mesmo problema), você pode usar o suporte de ação personalizada gerenciada do WiX 3.0 para criar DLLs de ação com a quantidade adequada de bits que será executado usando o Framework correspondente.


Editar: a partir da versão 8.1.2, o Instalador Avançado suporta corretamente ações personalizadas de 64 bits. Desde a minha resposta original, seu preço aumentou um pouco, infelizmente, embora ainda seja um valor extremamente bom quando comparado ao InstallShield e seus semelhantes ...


Editar: Se suas DLLs estiverem registradas no GAC, você também pode usar as tags de referência padrão desta forma (SQLite como exemplo):

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

A condição também é reduzida a todos os tipos de compilação, liberação ou depuração, e apenas especifica a arquitetura do processador.

mdb
fonte
No Visual Studio 2008, descobri que <ItemGroup> s não podiam ser aninhados. Esta solução funcionou bem, uma vez que fiz com que os novos <ItemGroup> s abaixo do grupo o resto das <Referência> s. Eu também tive que mudar o x86 para AnyCPU, o que provavelmente está relacionado à história do meu projeto específico.
Oliver Bock,
Esse instalador avançado parece muito bom.
Pat
Esta pode ser uma pergunta idiota, mas como você pode obter o arquivo para editá-lo manualmente?
hora
1
Para editar o arquivo dentro do VS, clique com o botão direito no projeto no Solution Explorer e encontre "Unload Project". Assim que o projeto for descarregado, clique com o botão direito sobre ele novamente e clique em "Editar <nome do arquivo do projeto>". Depois de editar o arquivo do projeto, salve-o e clique com o botão direito do mouse no arquivo do projeto novamente e carregue-o. Se não houver erros de digitação ou erros, ele será carregado novamente. Caso contrário, o VS irá lhe dizer qual é o problema do arquivo. Espero que ajude!
John Baughman
Ótima resposta! Obrigado!
John Baughman
27

Digamos que você tenha as DLLs criadas para ambas as plataformas e elas estejam no seguinte local:

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

Você simplesmente precisa editar seu arquivo .csproj a partir deste:

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

Para isso:

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

Você deve então ser capaz de construir seu projeto visando ambas as plataformas, e o MSBuild irá procurar no diretório correto para a plataforma escolhida.

Tim Booker
fonte
Seria brilhante se funcionasse, mas não funciona. Pelo menos não para mim
John Sheehan,
10
Não era para ser: <HintPath> C: \ Whatever \ $ (Platform) \ Whatever.dll </HintPath>
Andreas
Funcionou bem no Visual Studio 2008 para mim, mas não copiou automaticamente a DLL para o diretório de destino da compilação, como uma <Referência> normal faz. A solução da mdb funcionou melhor para mim.
Oliver Bock,
1

Não tenho certeza da resposta total à sua pergunta - mas pensei em apontar um comentário na seção Informações adicionais da página de download do SQL Compact 3.5 SP1, visto que você está olhando para x64 - espero que ajude.

Devido às alterações no SQL Server Compact SP1 e ao suporte adicional à versão de 64 bits, os ambientes de modo misto e centralmente instalados da versão de 32 bits do SQL Server Compact 3.5 e da versão de 64 bits do SQL Server Compact 3.5 SP1 podem criar o que parece ser intermitente problemas. Para minimizar o potencial de conflitos e permitir a implantação de plataforma neutra de aplicativos clientes gerenciados, a instalação central da versão de 64 bits do SQL Server Compact 3.5 SP1 usando o arquivo Windows Installer (MSI) também requer a instalação da versão de 32 bits do SQL Server Arquivo MSI compacto 3.5 SP1. Para aplicativos que requerem apenas 64 bits nativos, a implantação privada da versão de 64 bits do SQL Server Compact 3.5 SP1 pode ser utilizada.

Eu li isso como "incluir os arquivos SQLCE de 32 bits , bem como os arquivos de 64 bits" se estiver distribuindo para clientes de 64 bits.

Acho que torna a vida interessante ... devo dizer que adoro a frase "o que parecem ser problemas intermitentes" ... soa um pouco como "você está imaginando coisas, mas por precaução, faça isso ..."

Gleng
fonte
1

Uma construção .Net com dependências x86 / x64

Enquanto todas as outras respostas fornecem uma solução para fazer diferentes Builds de acordo com a plataforma, eu ofereço a opção de ter apenas a configuração "AnyCPU" e fazer um build que funcione com suas dlls x86 e x64.

Você tem que escrever algum código de encanamento para isso.

Resolução de x86 / x64-dlls corretos em tempo de execução

Passos:

  1. Use AnyCPU em csproj
  2. Decida se você só faz referência a x86 ou x64 dlls em seus csprojs. Adapte as configurações de UnitTests às configurações de arquitetura que você escolheu. É importante para depurar / executar os testes dentro do VisualStudio.
  3. Em Propriedades de referência, defina Copiar versão local e específica para falso
  4. Livre-se dos avisos de arquitetura adicionando esta linha ao primeiro PropertyGroup em todos os seus arquivos csproj onde você faz referência a x86 / x64: <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
  5. Adicione este script postbuild ao seu projeto de inicialização, use e modifique os caminhos deste script para que ele copie todas as suas dlls x86 / x64 nas subpastas correspondentes de seu build 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

    -> Ao iniciar o aplicativo agora, você obtém uma exceção de que o assembly não foi encontrado.

  6. Registre o evento AssemblyResolve logo no início do ponto de entrada do seu aplicativo

    AppDomain.CurrentDomain.AssemblyResolve += TryResolveArchitectureDependency;

    com este método:

    /// <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. Se você tiver testes de unidade, faça um TestClass com um Método que tenha um AssemblyInitializeAttribute e também registre o TryResolveArchitectureDependency-Handler acima. (Isso não será executado às vezes se você executar testes únicos dentro do visual studio, as referências não serão resolvidas no bin UnitTest. Portanto, a decisão na etapa 2 é importante.)

Benefícios:

  • Uma instalação / construção para ambas as plataformas

Desvantagens: - Sem erros no tempo de compilação quando as dlls x86 / x64 não correspondem. - Você ainda deve executar o teste em ambos os modos!

Opcionalmente, crie um segundo executável exclusivo para a arquitetura x64 com Corflags.exe no script postbuild

Outras variantes para experimentar: - Você não precisa do manipulador de eventos AssemblyResolve se garantir que as dlls corretas sejam copiadas para sua pasta binária no início (Avalie a arquitetura do processo -> mova as dlls correspondentes de x64 / x86 para a pasta bin e de volta. ) - No Instalador, avalie a arquitetura e exclua os binários da arquitetura errada e mova os corretos para a pasta bin.

Felix Keil
fonte
0

Em relação à sua última pergunta. Provavelmente você não pode resolver isso dentro de um único MSI. Se você estiver usando pastas de registro / sistema ou qualquer coisa relacionada, o próprio MSI deve estar ciente disso e você deve preparar um MSI de 64 bits para instalar corretamente em uma máquina de 32 bits.

Existe a possibilidade de você fazer seu produto instalado como um aplicativo de 32 e ainda ser capaz de fazê-lo funcionar como um de 64 bits, mas acho que pode ser um pouco difícil de conseguir.

Dito isso, acho que você deve ser capaz de manter uma única base de código para tudo. No meu local de trabalho atual, conseguimos fazê-lo. (mas levou algum malabarismo para fazer tudo funcionar junto)

Espero que isto ajude. Aqui está um link para algumas informações relacionadas a problemas de 32/64 bits: http://blog.typemock.com/2008/07/registry-on-windows-64-bit-double-your.html

Lior Friedman
fonte
0

Se você usar ações personalizadas escritas em .NET como parte do instalador MSI, você terá outro problema.

O 'shim' que executa essas ações personalizadas é sempre de 32 bits, então sua ação personalizada também será executada em 32 bits, independentemente do alvo que você especificar.

Mais informações e alguns movimentos ninja para contornar (basicamente, altere o MSI para usar a versão de 64 bits deste calço)

Construindo um MSI no Visual Studio 2005/2008 para trabalhar em um SharePoint 64

Ações personalizadas gerenciadas de 64 bits com Visual Studio

Ryan
fonte
0

Você pode gerar duas soluções de forma diferente e mesclá-las depois! Eu fiz isso para o VS 2010. e funciona. Tive 2 soluções diferentes geradas pelo CMake e juntei-as

voidMainReturn
fonte
0

Você pode usar uma condição para um ItemGroup para as referências dll no arquivo de projeto.
Isso fará com que o visual studio verifique novamente a condição e as referências sempre que você alterar a configuração ativa.
Basta adicionar uma condição para cada configuração.

Exemplo:

 <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>
Yochai Timmer
fonte