Utiliser conditionnellement une référence 32/64 bits lors de la création dans Visual Studio

124

J'ai un projet qui se construit en 32/64 bits et qui a des dépendances 32/64 bits correspondantes. Je veux pouvoir changer de configuration et utiliser la référence correcte, mais je ne sais pas comment dire à Visual Studio d'utiliser la dépendance appropriée à l'architecture.

Peut-être que je vais dans le mauvais sens, mais je veux pouvoir basculer entre x86 et x64 dans la liste déroulante de configuration, et faire en sorte que la DLL référencée soit le bon témoin.

Jonathan Yee
la source
Très peu clair, quelle langue est-ce? Le projet DLL est-il dans la solution?
Hans Passant
Désolé, c'est .NET, j'écris en C #.
Jonathan Yee
4
Ok, je l'ai résolu avec une solution stupide: j'ai créé un fichier csproj supplémentaire qui ne fait référence qu'à la DLL x64 (et j'ai supprimé la configuration x86 du csproj). Cela fonctionne, mais si quelqu'un avait une solution plus élégante qui n'impliquait pas de csproj supplémentaire, j'aimerais la voir.
Jonathan Yee

Réponses:

99

Voici ce que j'ai fait dans un projet précédent, qui nécessitera l'édition manuelle du (des) fichier (s) .csproj. Vous avez également besoin de répertoires séparés pour les différents binaires, idéalement frères les uns des autres, et portant le même nom que la plate-forme que vous ciblez.

Après avoir ajouté les références d'une seule plateforme au projet, ouvrez le .csproj dans un éditeur de texte. Avant le premier <ItemGroup>élément de l' <Project>élément, ajoutez le code suivant, qui vous aidera à déterminer sur quelle plate-forme vous exécutez (et construisez).

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

Ensuite, pour les références spécifiques à votre plate-forme, vous apportez des modifications telles que les suivantes:

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

Notez l'utilisation de la $(CurrentPlatform)propriété, que nous avons définie ci-dessus. Vous pouvez, à la place, utiliser des conditions pour les assemblys à inclure pour quelle plateforme. Vous pourriez également avoir besoin de:

  • Remplacez $(PROCESSOR_ARCHITEW6432)et $(PROCESSOR_ARCHITECTURE)par $(Platform)pour considérer UNIQUEMENT la plateforme cible des projets
  • Modifiez la logique de détermination de la plate-forme afin qu'elle soit appropriée à la machine actuelle, afin de ne pas créer / référencer un binaire 64 bits à exécuter sur une plate-forme 32 bits.

Je l'avais écrit à l'origine pour un Wiki interne au travail, cependant, je l'ai modifié et posté le processus complet sur mon blog , si vous êtes intéressé par les instructions détaillées étape par étape.

Hugo
la source
1
Agréable. J'ai choisi d'utiliser une condition sur le ItemGroup selon la suggestion ci-dessous, mais en utilisant $ (PROCESSOR_ARCHITEW6432) et $ (PROCESSOR_ARCHITECTURE) pour les conditions comme ici. Une note est que j'ai trouvé que $ (PROCESSOR_ARCHITECTURE) renvoie x86 sur les plates-formes 32 et 64 bits mais $ (PROCESSOR_ARCHITEW6432) renvoie AMD64 uniquement sur 64 bits. Quelque chose à noter si vous essayez de tester x86 (car AMD64 est un dérivé de x86 je suppose).
tjmoore
Merci pour cette information @tjmoore. Sur quel O / S avez-vous remarqué cela? Je viens de vérifier le mien à nouveau (Win7SP1) et dit AMD64 pour le $ (PROCESSOR_ARCHITECTURE), mais j'aimerais vraiment avoir des informations aussi complètes et approfondies que possible.
Hugo le
7
Drôle, ma recherche m'amène ici, et je n'en ai besoin que parce que j'utilise également LeadTools ... +1
Ed S.
La solution fonctionne pour la configuration par défaut, mais pas à partir de mes tests, pas si vous modifiez la configuration à partir de la configuration de la liste déroulante Configuration de la solution de Visual Studio (2012 dans mon cas).
Sarah Weinberger
Au lieu d'utiliser $ (PROCESSOR_ARCHITEW6432), j'ai utilisé $ (Platform) pour une raison quelconque, $ (PROCESSOR_ARCHITEW6432) ne fonctionnait pas.
Dzyann le
60

AFAIK, si votre projet nécessite des références spécifiques à 32 bits ou 64 bits (c'est-à-dire des assemblys d'interopérabilité COM) et que vous n'avez aucun intérêt à modifier manuellement le fichier .csproj, vous devrez créer des fichiers 32 bits et Projets 64 bits.

Je dois noter que la solution suivante n'a pas été testée, mais devrait fonctionner. Si vous souhaitez modifier manuellement le fichier .csproj, vous devriez pouvoir obtenir le résultat souhaité avec un seul projet. Le fichier .csproj est juste un script MSBuild, donc pour une référence complète, regardez ici . Une fois que vous avez ouvert le fichier .csproj dans un éditeur, recherchez les <Reference>éléments. Vous devriez être capable de diviser ces éléments en 3 groupes d'éléments distincts : des références qui ne sont pas spécifiques à la plate-forme, des références spécifiques à x86 et des références spécifiques à x64.

Voici un exemple qui suppose que votre projet est configuré avec des plates-formes cibles nommées «x86» et «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>

Désormais, lorsque vous définissez la configuration de construction de votre projet / solution pour cibler la plate-forme x86 ou x64, elle doit inclure les références appropriées dans chaque cas. Bien sûr, vous devrez jouer avec les <Reference>éléments. Vous pouvez même configurer des projets factices dans lesquels vous ajoutez les références x86 et x64, puis copiez simplement les <Reference>éléments nécessaires de ces fichiers de projet factices dans votre "vrai" fichier de projet.


Edit 1
Voici un lien vers les éléments communs du projet MSBuild, que j'ai accidentellement omis du message d'origine: http://msdn.microsoft.com/en-us/library/bb629388.aspx

Justin Holzer
la source
Excellente réponse !! J'ai sauvé ma journée! Merci beaucoup.
hellodear
20

Vous pouvez utiliser une condition à un ItemGroup pour les références dll dans le fichier projet.
Cela obligera Visual Studio à revérifier la condition et les références chaque fois que vous modifiez la configuration active.
Ajoutez simplement une condition pour chaque configuration.

Exemple:

 <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
la source
1
C'est super merci! Cela devrait certainement être la solution acceptée!
ManicBlowfish
Sérieusement, cette réponse est bien meilleure et plus simple que celle acceptée.
Yandros
1
Est-il normal d'avoir des entrées dupliquées dans les références après avoir fait cela?
natenho
7

Je fais référence aux DLL x86, situées par exemple dans \ component \ v3_NET4, dans mon projet. Les DLL spécifiques pour x86 / x64 sont situées dans des sous-dossiers nommés "x86" et "x64" resp.

Ensuite, j'utilise un script de pré-construction qui copie les DLL appropriées (x86 / x64) dans le dossier référencé, basé sur $ (PlatformName).

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

Travaille pour moi.

Micke
la source
3

Une version .Net avec des dépendances x86 / x64

Alors que toutes les autres réponses vous donnent une solution pour créer des builds différents en fonction de la plate-forme, je vous donne la possibilité de n'avoir que la configuration "AnyCPU" et de créer une build qui fonctionne avec vos dll x86 et x64.

Résolution des dll x86 / x64 correctes au moment de l'exécution

Pas:

  1. Utilisez AnyCPU dans csproj
  2. Décidez si vous ne référencez que les DLL x86 ou x64 dans vos csprojs. Adaptez les paramètres UnitTests aux paramètres d'architecture que vous avez choisis. C'est important pour le débogage / l'exécution des tests dans VisualStudio.
  3. Sur Reference-Properties, définissez Copier la version locale et spécifique sur false
  4. Débarrassez-vous des avertissements d'architecture en ajoutant cette ligne au premier PropertyGroup dans tous vos fichiers csproj où vous faites référence à x86 / x64: <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
  5. Ajoutez ce script postbuild à votre projet de démarrage, utilisez et modifiez les chemins de ce script afin qu'il copie toutes vos dll x86 / x64 dans les sous-dossiers correspondants de votre 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

    -> Lorsque vous démarrez l'application maintenant, vous obtenez une exception indiquant que l'assembly n'a pas pu être trouvé.

  6. Enregistrez l'événement AssemblyResolve dès le début du point d'entrée de votre application

    AppDomain.CurrentDomain.AssemblyResolve += TryResolveArchitectureDependency;

    avec cette méthode:

    /// <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. Si vous avez des tests unitaires, créez une TestClass avec une méthode qui a un AssemblyInitializeAttribute et enregistrez également le TryResolveArchitectureDependency-Handler ci-dessus. (Cela ne sera parfois pas exécuté si vous exécutez des tests uniques dans Visual Studio, les références ne seront pas résolues à partir du bac UnitTest. Par conséquent, la décision de l'étape 2 est importante.)

Avantages:

  • Une installation / construction pour les deux plates-formes

Inconvénients: - Aucune erreur au moment de la compilation lorsque les dll x86 / x64 ne correspondent pas. - Vous devez toujours exécuter le test dans les deux modes!

Créez éventuellement un deuxième exécutable exclusif pour l'architecture x64 avec Corflags.exe dans le script postbuild

Autres variantes à essayer: - Vous n'auriez pas besoin du gestionnaire d'événements AssemblyResolve si vous vous assurez autrement que les dll sont copiées dans votre dossier binaire au démarrage (Évaluer l'architecture du processus -> déplacer les dll correspondantes de x64 / x86 vers le dossier bin et vice versa.) - Dans le programme d'installation, évaluez l'architecture et supprimez les binaires pour la mauvaise architecture et déplacez les bons dans le dossier bin.

Felix Keil
la source
2

J'ai été confronté au même problème et j'ai passé pas mal de temps à chercher une solution décente. La plupart des gens proposent l'édition manuelle des fichiers de la solution Visual Studio, ce qui est assez fastidieux, sujet aux erreurs et déroutant lors de l'exploration de ces fichiers modifiés dans l'interface graphique de Visual Studio par la suite. Quand j'ai déjà abandonné, la solution est venue d'elle-même. C'est très similaire à ce que Micke recommande dans sa réponse ci-dessus.

Dans le gestionnaire de compte, j'ai créé deux cibles de construction distinctes pour les plates-formes x86 et x64, comme d'habitude. Ensuite, j'ai ajouté une référence à l'assembly x86 à mon projet. Sur ce point, je pensais que le projet est configuré pour la version x86 uniquement et ne sera jamais construit pour la configuration x64, à moins que je ne fasse une édition manuelle de celui-ci comme suggéré par Hugo ci-dessus.

Après un certain temps, j'ai finalement oublié la limitation et j'ai accidentellement démarré la construction x64. Bien sûr, la construction a échoué. Mais le message d'erreur que j'ai reçu était important. Le message d'erreur indique que l'assembly nommé exactement comme mon assembly x86 référencé est manquant dans le dossier destiné comme cible de build x64 pour ma solution.

Ayant remarqué cela, j'ai copié manuellement l'assemblage x64 approprié dans ce répertoire. Gloire! Ma version x64 a miraculeusement réussi avec un assemblage approprié trouvé et lié implicitement. Il était question de quelques minutes pour modifier ma solution pour définir un répertoire cible de construction pour l'assembly x64 dans ce dossier. Après ces étapes, la solution se construit automatiquement pour x86 et x64 sans aucune modification manuelle des fichiers MSBuild.

Pour résumer:

  1. Créez des cibles x86 et x64 dans un seul projet
  2. Ajouter toutes les références de projet appropriées aux assemblys x86
  3. Définir un répertoire cible de construction commun pour tous les assemblys x64
  4. Si vous avez des assemblys x64 prêts, copiez-les simplement une fois dans votre répertoire cible de compilation x64

Une fois ces étapes terminées, votre solution sera correctement compilée pour les configurations x86 et x64.

Cela a fonctionné pour moi sur le projet Visual Studio 2010 .NET 4.0 C #. Évidemment, il s'agit d'une sorte de comportement interne non documenté de Visual Studio, qui pourrait être sujet à changement dans les versions 2012, 2013 et 2015. Si quelqu'un essaie d'autres versions, partagez votre expérience.

Boris Zinchenko
la source
-1

J'ai fini par utiliser ce que je considère comme une solution plus simple qui est une sorte d'inversion de Micke. Le projet est une application de formulaires C #, Visual Studio 2015, avec des cibles x86 et x64. J'ai référencé l'un des assemblys .NET, j'ai utilisé celui de 32 bits. Dans les propriétés de référence, j'ai défini "Copier Local" sur false. Ensuite, je mets simplement manuellement l'assembly .Net approprié (32 ou 64 bits) dans chaque répertoire cible. Le bitness de référence réel n'est pas pertinent, en supposant qu'ils ont les mêmes capacités, car il ne fait que définir l'interface externe. Vous pouvez également mettre une étape de copie post build si vous voulez avoir de la fantaisie. Notez que ce projet avait également une référence COM, la même chose fonctionne. La référence définit les objets / interfaces de sorte que le bitness de la DLL de référence n'est pas pertinent. Si les DLL COM 32 bits et 64 bits sont enregistrées, l'application cherchera à l'endroit approprié dans le registre et créera l'objet COM 32 ou 64 bits correct. Travaille pour moi!

Jeff H
la source