Use condicionalmente referência de 32/64 bits ao criar no Visual Studio

124

Eu tenho um projeto que cria em 32/64 bits e tem dependências correspondentes em 32/64 bits. Quero poder alternar configurações e ter a referência correta usada, mas não sei como solicitar ao Visual Studio para usar a dependência apropriada da arquitetura.

Talvez eu esteja fazendo isso da maneira errada, mas quero poder alternar entre x86 e x64 na lista suspensa de configuração e fazer com que a DLL referenciada seja a testemunha certa.

Jonathan Yee
fonte
Muito claro, que idioma é esse? O projeto DLL está na solução?
Hans Passant
Desculpe, este é o .NET. Estou escrevendo em C #.
Jonathan Yee
4
Ok, resolvi-o com uma solução idiota: criei um arquivo csproj adicional que apenas faz referência à DLL x64 (e removeu a configuração x86 do csproj). Funciona, mas se alguém tiver uma solução mais elegante que não envolva um csproj adicional, eu adoraria vê-lo.
Jonathan Yee

Respostas:

99

Aqui está o que eu fiz em um projeto anterior, que exigirá a edição manual do (s) arquivo (s) .csproj. Você também precisa de diretórios separados para os binários diferentes, idealmente irmãos um do outro, e com o mesmo nome da plataforma que você está direcionando.

Depois de adicionar as referências de uma única plataforma ao projeto, abra o .csproj em um editor de texto. Antes do primeiro <ItemGroup>elemento dentro do <Project>elemento, adicione o código a seguir, que ajudará a determinar em qual plataforma você está executando (e construindo).

<!-- Properties group for Determining 64bit Architecture -->
<PropertyGroup>
  <CurrentPlatform>x86</CurrentPlatform>
  <CurrentPlatform Condition="'$(PROCESSOR_ARCHITECTURE)'=='AMD64' or '$(PROCESSOR_ARCHITEW6432)'=='AMD64'">AMD64</CurrentPlatform>
</PropertyGroup>

Em seguida, para referências específicas da sua plataforma, faça alterações como as seguintes:

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

Observe o uso da $(CurrentPlatform)propriedade, que definimos acima. Em vez disso, você poderia usar condicionais para quais assemblies incluir para qual plataforma. Você também pode precisar de:

  • Substitua o $(PROCESSOR_ARCHITEW6432)e $(PROCESSOR_ARCHITECTURE)por $(Platform)para considerar SOMENTE a plataforma de destino dos projetos
  • Altere a lógica de determinação da plataforma para se adequar à máquina atual, para que você não construa / faça referência a um binário de 64 bits para executar em uma plataforma de 32 bits.

Eu escrevi isso originalmente para um Wiki interno no trabalho, no entanto, eu o modifiquei e publiquei o processo completo no meu blog , se você estiver interessado nas instruções detalhadas, passo a passo.

Hugo
fonte
1
Agradável. Fui usando um condicional no ItemGroup conforme a sugestão abaixo, mas usando $ (PROCESSOR_ARCHITEW6432) e $ (PROCESSOR_ARCHITECTURE) para as condições como aqui. Uma observação é que descobri que $ (PROCESSOR_ARCHITECTURE) retorna x86 em plataformas de 32 e 64 bits, mas $ (PROCESSOR_ARCHITEW6432) retorna AMD64 apenas em 64 bits. Algo a observar se você tentar testar o x86 (porque o AMD64 é um derivado do x86, presumo).
tjmoore
Obrigado por essa informação @tjmoore. Em qual O / S você percebeu isso? Acabei de verificar o meu novamente (Win7SP1) e digitar AMD64 pelo $ (PROCESSOR_ARCHITECTURE), mas com certeza gostaria de ter informações o mais completas e completas possível.
234 Hugo Hugo
7
Engraçado, minha pesquisa me traz aqui, e eu só preciso disso porque também estou usando o LeadTools ... +1
Ed S.
A solução funciona para a configuração padrão, mas não do meu teste, se você alterar a configuração da lista de configuração da solução Visual Studio (2012 no meu caso).
22613 Sarah Weinberger
Em vez de usar $ (PROCESSOR_ARCHITEW6432), usei $ (Platform) por algum motivo $ (PROCESSOR_ARCHITEW6432) não estava funcionando.
Dzyann
60

AFAIK, se o seu projeto exigir referências específicas de 32 ou 64 bits (por exemplo, assemblies de interoperabilidade COM) e você não tiver interesse em editar manualmente o arquivo .csproj, será necessário criar 32 bits e Projetos de 64 bits.

Devo observar que a solução a seguir não foi testada, mas deve funcionar. Se você estiver disposto a editar manualmente o arquivo .csproj, poderá conseguir o resultado desejado com um único projeto. O arquivo .csproj é apenas um script do MSBuild, portanto, para uma referência completa, consulte aqui . Depois de abrir o arquivo .csproj em um editor, localize os <Reference>elementos. Você deve poder dividir esses elementos em três grupos de itens distintos : referências que não são específicas da plataforma, referências específicas do x86 e referências específicas do x64.

Aqui está um exemplo que assume que seu projeto está configurado com plataformas de destino denominadas "x86" e "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>

Agora, quando você define a configuração de compilação do projeto / solução para atingir a plataforma x86 ou x64, ela deve incluir as referências apropriadas em cada caso. Claro, você precisará brincar com os <Reference>elementos. Você pode até configurar projetos fictícios nos quais adiciona as referências x86 e x64 e apenas copiar os <Reference>elementos necessários desses arquivos de projetos fictícios para o arquivo de projeto "real".


Editar 1
Aqui está um link para os itens comuns do projeto MSBuild, que acidentalmente deixei de fora da postagem original: http://msdn.microsoft.com/en-us/library/bb629388.aspx

Justin Holzer
fonte
Excellent Answer !! Salvou o meu dia! Muito obrigado.
precisa
20

Você pode usar uma condição para um ItemGroup para as referências de 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
1
Isso é ótimo, obrigado! Definitivamente, essa deve ser a solução aceita!
ManicBlowfish 02/11
Sério, essa resposta é muito melhor e mais simples que a aceita.
Yandros
1
É normal ter entradas duplicadas nas referências depois de fazer isso?
Natenho 18/10/19
7

Estou referenciando as DLLs x86, localizadas em, por exemplo, \ component \ v3_NET4, no meu projeto. DLLs específicas para x86 / x64 estão localizadas nas subpastas denominadas "x86" e "x64" resp.

Então, eu estou usando um script de pré-compilação que copia DLLs apropriadas (x86 / x64) para a pasta referenciada, com base em $ (PlatformName).

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

Funciona para mim.

Micke
fonte
3

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

Embora todas as outras respostas ofereçam uma solução para criar diferentes compilações de acordo com a plataforma, eu lhe dou a opção de ter apenas a configuração "AnyCPU" e criar uma compilação que funcione com as DLLs x86 e x64.

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

Passos:

  1. Use AnyCPU no csproj
  2. Decida se você referencia apenas as DLLs x86 ou x64 em seus csprojs. Adapte as configurações de UnitTests às configurações de arquitetura que você escolheu. É importante para depurar / executar os testes no VisualStudio.
  3. Em Propriedades de Referência, defina Copiar Versão Local e Específica para false
  4. Livre-se dos avisos de arquitetura adicionando esta linha ao primeiro PropertyGroup em todos os seus arquivos csproj nos quais você faz referência a x86 / x64: <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
  5. Adicione esse script pós-construção ao seu projeto de inicialização, use e modifique os caminhos desse script para que ele copie todas as suas DLLs x86 / x64 nas subpastas correspondentes da sua bin de construção \ 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ê recebe uma exceção de que o assembly não pôde ser encontrado.

  6. Registre o evento AssemblyResolve logo no início do ponto de entrada do 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 Method que tenha um AssemblyInitializeAttribute e também registre o TryResolveArchitectureDependency-Handler acima. (Às vezes, isso não será executado se você executar testes únicos no visual studio, as referências serão resolvidas e não da lixeira UnitTest. Portanto, a decisão na etapa 2 é importante.)

Benefícios:

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

Desvantagens: - Não há erros no tempo de compilação quando as DLLs x86 / x64 não coincidem. - Você ainda deve executar o teste nos dois modos!

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

Outras variantes a serem testadas: - Você não precisaria do manipulador de eventos AssemblyResolve se garantir que as dlls serão copiadas na sua pasta binária no início (Avaliar arquitetura do processo -> mova as dlls correspondentes de x64 / x86 para a pasta bin e vice-versa). - No Installer, avalie a arquitetura e exclua os binários da arquitetura incorreta e mova os corretos para a pasta bin.

Felix Keil
fonte
2

Enfrentei o mesmo problema e passei um bom tempo procurando uma solução decente. A maioria das pessoas oferece edição manual de arquivos de solução do Visual Studio, o que é bastante tedioso, propenso a erros e confuso ao explorar esses arquivos editados na GUI do Visual Studio posteriormente. Quando eu já desisti, a solução surgiu. É muito semelhante ao que Micke recomenda em sua resposta acima.

No gerente de contas, criei dois destinos de compilação separados para plataformas x86 e x64, como de costume. Em seguida, adicionei uma referência ao assembly x86 ao meu projeto. Nesse ponto, eu acreditava que o projeto está configurado apenas para a compilação x86 e nunca será compilado para a configuração x64, a menos que eu faça uma edição manual conforme sugerido por Hugo acima.

Depois de um tempo, finalmente esqueci a limitação e acidentalmente iniciei a compilação x64. Obviamente, a construção falhou. Mas importante foi a mensagem de erro que recebi. A mensagem de erro informava que o assembly nomeado exatamente como meu assembly x86 mencionado está ausente na pasta destinada como destino de compilação x64 para a minha solução.

Tendo notado isso, copiei manualmente o assembly x64 adequado para esse diretório. Glória! Minha compilação x64 foi milagrosamente bem-sucedida com a montagem adequada encontrada e vinculada implicitamente. Foi questão de minutos para modificar minha solução para definir um diretório de destino de compilação para o assembly x64 para esta pasta. Após essas etapas, a solução cria automaticamente para x86 e x64 sem nenhuma edição manual dos arquivos do MSBuild.

Resumindo:

  1. Crie destinos x86 e x64 em um único projeto
  2. Adicione todas as referências de projeto adequadas aos assemblies x86
  3. Defina um diretório de destino de construção comum para todos os assemblies x64
  4. Caso você tenha montagens x64 prontas, basta copiá-las uma vez no diretório de destino da construção x64

Após a conclusão dessas etapas, sua solução será criada corretamente para as configurações x86 e x64.

Isso funcionou para mim no projeto C # do Visual Studio 2010 .NET 4.0. Evidentemente, esse é um tipo de comportamento interno não documentado do Visual Studio, que pode estar sujeito a alterações nas versões 2012, 2013 e 2015. Se alguém tentar outras versões, compartilhe sua experiência.

Boris Zinchenko
fonte
-1

Acabei usando o que considero uma solução mais fácil que é uma inversão da de Micke. O projeto é um aplicativo de formulários C #, Visual Studio 2015, com destinos x86 e x64. Mencionei um dos assemblies .NET, usei o de 32 bits. Nas propriedades de referência, defino "Copy Local" como false. Depois, coloquei manualmente o assembly .Net apropriado (32 ou 64 bits) em cada diretório de destino. O número real de referência é irrelevante, supondo que eles tenham os mesmos recursos, pois está apenas definindo a interface externa. Você também pode colocar uma etapa de cópia de pós-compilação, se quiser ser sofisticado. Observe que este projeto também tinha uma referência COM, o mesmo funciona. A referência define os objetos / interfaces para que o número de bits da DLL de referência seja irrelevante. Se as DLLs COM de 32 e 64 bits estiverem registradas, o aplicativo procurará no local apropriado no registro e criará o objeto COM correto de 32 ou 64 bits. Funciona para mim!

Jeff H
fonte