Verwenden Sie beim Erstellen in Visual Studio unter bestimmten Bedingungen eine 32/64-Bit-Referenz

124

Ich habe ein Projekt, das 32/64-Bit erstellt und entsprechende 32/64-Bit-Abhängigkeiten aufweist. Ich möchte in der Lage sein, Konfigurationen zu wechseln und die richtige Referenz zu verwenden, weiß jedoch nicht, wie ich Visual Studio anweisen soll, die architekturgerechte Abhängigkeit zu verwenden.

Vielleicht gehe ich falsch vor, aber ich möchte in der Konfigurations-Dropdown-Liste zwischen x86 und x64 wechseln können und die referenzierte DLL muss die richtige Bitigkeit haben.

Jonathan Yee
quelle
Sehr unklar, welche Sprache ist das? Ist das DLL-Projekt in der Lösung?
Hans Passant
Entschuldigung, das ist .NET, ich schreibe in C #.
Jonathan Yee
4
Ok, ich habe es mit einer dummen Lösung gelöst: Es wurde eine zusätzliche csproj-Datei erstellt, die nur auf die x64-DLL verweist (und die x86-Konfiguration aus dem csproj entfernt). Es funktioniert, aber wenn jemand eine elegantere Lösung hätte, die kein zusätzliches csproj beinhaltet, würde ich es gerne sehen.
Jonathan Yee

Antworten:

99

Folgendes habe ich in einem früheren Projekt getan, für das die manuelle Ausgabe der .csproj-Datei (en) erforderlich ist. Sie benötigen außerdem separate Verzeichnisse für die verschiedenen Binärdateien, idealerweise Geschwister voneinander, und mit demselben Namen wie die Plattform, auf die Sie abzielen.

Öffnen Sie nach dem Hinzufügen der Referenzen einer einzelnen Plattform zum Projekt die .csproj in einem Texteditor. Fügen Sie vor dem ersten <ItemGroup>Element innerhalb des <Project>Elements den folgenden Code hinzu, um festzustellen, auf welcher Plattform Sie ausgeführt werden (und auf welcher Sie aufbauen).

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

Anschließend nehmen Sie für Ihre plattformspezifischen Referenzen Änderungen vor, z. B. die folgenden:

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

Beachten Sie die Verwendung der $(CurrentPlatform)Eigenschaft, die wir oben definiert haben. Sie können stattdessen Bedingungen verwenden, für die Assemblys für welche Plattform eingeschlossen werden sollen. Möglicherweise müssen Sie auch:

  • Ersetzen Sie das $(PROCESSOR_ARCHITEW6432)und $(PROCESSOR_ARCHITECTURE)durch $(Platform), um NUR die Zielplattform der Projekte zu berücksichtigen
  • Ändern Sie die Plattformbestimmungslogik so, dass sie dem aktuellen Computer entspricht, sodass Sie keine 64-Bit-Binärdatei erstellen / referenzieren, die auf einer 32-Bit-Plattform ausgeführt werden soll.

Ich hatte dies ursprünglich für ein internes Wiki bei der Arbeit geschrieben, habe es jedoch geändert und den gesamten Prozess in meinem Blog veröffentlicht , wenn Sie an den detaillierten schrittweisen Anweisungen interessiert sind.

Hugo
quelle
1
Nett. Ich habe eine Bedingung für die ItemGroup gemäß dem folgenden Vorschlag verwendet, aber $ (PROCESSOR_ARCHITEW6432) und $ (PROCESSOR_ARCHITECTURE) für die Bedingungen wie hier verwendet. Ich habe festgestellt, dass $ (PROCESSOR_ARCHITECTURE) auf 32- und 64-Bit-Plattformen x86 zurückgibt, $ (PROCESSOR_ARCHITEW6432) AMD64 jedoch nur auf 64-Bit zurückgibt. Etwas zu beachten, wenn Sie versuchen, auf x86 zu testen (da AMD64 eine Ableitung von x86 ist, nehme ich an).
tjmoore
Vielen Dank für diese Information @tjmoore. Auf welcher O / S haben Sie das bemerkt? Ich habe gerade meine erneut überprüft (Win7SP1) und AMD64 für das $ (PROCESSOR_ARCHITECTURE) angegeben, möchte aber auf jeden Fall so vollständige und gründliche Informationen wie möglich haben.
Hugo
7
Komisch, meine Suche bringt mich hierher, und ich brauche das nur, weil ich auch LeadTools benutze ... +1
Ed S.
Die Lösung funktioniert für die Standardkonfiguration, jedoch nicht aus meinen Tests, nicht, wenn Sie die Konfiguration aus der Konfiguration in der Dropdown-Liste Lösungskonfiguration von Visual Studio (in meinem Fall 2012) ändern.
Sarah Weinberger
Anstatt $ (PROCESSOR_ARCHITEW6432) zu verwenden, habe ich aus irgendeinem Grund $ (Platform) verwendet. $ (PROCESSOR_ARCHITEW6432) funktionierte nicht.
Dzyann
60

AFAIK: Wenn für Ihr Projekt 32-Bit- oder 64-Bit-spezifische Referenzen erforderlich sind (z. B. COM-Interop-Assemblys) und Sie kein Interesse daran haben, die .csproj-Datei manuell zu bearbeiten, müssen Sie separate 32-Bit- und 32-Bit-Dateien erstellen 64-Bit-Projekte.

Ich sollte beachten, dass die folgende Lösung nicht getestet wurde, aber funktionieren sollte. Wenn Sie bereit sind, die .csproj-Datei manuell zu bearbeiten, sollten Sie in der Lage sein, das gewünschte Ergebnis mit einem einzigen Projekt zu erzielen. Die .csproj-Datei ist nur ein MSBuild-Skript. Eine vollständige Referenz finden Sie hier . Suchen Sie die <Reference>Elemente, nachdem Sie die .csproj-Datei in einem Editor geöffnet haben . Sie sollten in der Lage sein, diese Elemente in drei verschiedene Elementgruppen aufzuteilen : Referenzen, die nicht plattformspezifisch sind, x86-spezifische Referenzen und x64-spezifische Referenzen.

In diesem Beispiel wird davon ausgegangen, dass Ihr Projekt mit Zielplattformen mit den Namen "x86" und "x64" konfiguriert ist.

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

Wenn Sie nun Ihre Projekt- / Lösungserstellungskonfiguration so einstellen, dass sie auf die x86- oder x64-Plattform abzielt, sollte sie jeweils die richtigen Referenzen enthalten. Natürlich müssen Sie mit den <Reference>Elementen herumspielen. Sie können sogar Dummy-Projekte einrichten, in denen Sie die x86- und x64-Referenzen hinzufügen, und dann einfach die erforderlichen <Reference>Elemente aus diesen Dummy-Projektdateien in Ihre "echte" Projektdatei kopieren .


Bearbeiten 1
Hier ist ein Link zu den allgemeinen MSBuild-Projektelementen, die ich versehentlich aus dem ursprünglichen Beitrag herausgelassen habe: http://msdn.microsoft.com/en-us/library/bb629388.aspx

Justin Holzer
quelle
Ausgezeichnete Antwort !! Rettete meinen Tag! Vielen Dank.
hellodear
20

Sie können eine Bedingung für eine ItemGroup für die DLL-Referenzen in der Projektdatei verwenden.
Dadurch überprüft Visual Studio den Zustand und die Referenzen erneut, wenn Sie die aktive Konfiguration ändern.
Fügen Sie einfach eine Bedingung für jede Konfiguration hinzu.

Beispiel:

 <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
quelle
1
Das ist toll, danke! Dies sollte definitiv die akzeptierte Lösung sein!
ManicBlowfish
Im Ernst, diese Antwort ist viel besser und einfacher als die akzeptierte.
Yandros
1
Ist es normal, danach doppelte Einträge in Referenzen zu haben?
Natenho
7

Ich verweise in meinem Projekt auf die x86-DLLs, die sich beispielsweise in \ component \ v3_NET4 befinden. Bestimmte DLLs für x86 / x64 befinden sich in Unterordnern mit den Namen "x86" bzw. "x64".

Dann verwende ich ein vorgefertigtes Skript, das geeignete DLLs (x86 / x64) basierend auf $ (PlatformName) in den Ordner kopiert, auf den verwiesen wird.

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

Funktioniert bei mir.

Micke
quelle
3

Ein .NET-Build mit x86 / x64-Abhängigkeiten

Während alle anderen Antworten Ihnen eine Lösung bieten, um je nach Plattform unterschiedliche Builds zu erstellen, gebe ich Ihnen die Option, nur die "AnyCPU" -Konfiguration zu verwenden und einen Build zu erstellen, der mit Ihren x86- und x64-DLLs funktioniert.

Auflösung der richtigen x86 / x64-DLLs zur Laufzeit

Schritte:

  1. Verwenden Sie AnyCPU in csproj
  2. Entscheiden Sie, ob Sie nur auf die x86- oder x64-DLLs in Ihren csprojs verweisen. Passen Sie die UnitTests-Einstellungen an die von Ihnen ausgewählten Architektureinstellungen an. Dies ist wichtig für das Debuggen / Ausführen der Tests in VisualStudio.
  3. Setzen Sie in den Referenzeigenschaften Copy Local & Specific Version auf false
  4. Entfernen Sie die Architekturwarnungen, indem Sie diese Zeile zur ersten PropertyGroup in all Ihren csproj-Dateien hinzufügen, in denen Sie auf x86 / x64 verweisen: <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
  5. Fügen Sie dieses Postbuild-Skript zu Ihrem Startprojekt hinzu, verwenden und ändern Sie die Pfade dieses Skripts, sodass alle Ihre x86 / x64-DLLs in die entsprechenden Unterordner Ihres Build-Bin \ x86 \ bin \ x64 \ kopiert werden

    xcopy /E /H /R /Y /I /D $(SolutionDir)\YourPathToX86Dlls $(TargetDir)\x86 xcopy /E /H /R /Y /I /D $(SolutionDir)\YourPathToX64Dlls $(TargetDir)\x64

    -> Wenn Sie die Anwendung jetzt starten, erhalten Sie eine Ausnahme, dass die Assembly nicht gefunden wurde.

  6. Registrieren Sie das AssemblyResolve-Ereignis direkt am Anfang Ihres Anwendungseinstiegspunkts

    AppDomain.CurrentDomain.AssemblyResolve += TryResolveArchitectureDependency;

    mit dieser Methode:

    /// <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. Wenn Sie Unit-Tests haben, erstellen Sie eine Testklasse mit einer Methode, die ein AssemblyInitializeAttribute hat, und registrieren Sie dort auch den obigen TryResolveArchitectureDependency-Handler. (Dies wird manchmal nicht ausgeführt, wenn Sie einzelne Tests in Visual Studio ausführen. Die Referenzen werden nicht aus dem UnitTest-Bin aufgelöst. Daher ist die Entscheidung in Schritt 2 wichtig.)

Leistungen:

  • Eine Installation / Build für beide Plattformen

Nachteile: - Keine Fehler beim Kompilieren, wenn x86 / x64-DLLs nicht übereinstimmen. - Sie sollten trotzdem in beiden Modi testen!

Erstellen Sie optional eine zweite ausführbare Datei, die exklusiv für die x64-Architektur ist, mit Corflags.exe im Postbuild-Skript

Andere Varianten zum Ausprobieren: - Sie würden den AssemblyResolve-Ereignishandler nicht benötigen, wenn Sie andernfalls sicherstellen würden, dass die DLLs beim Start in Ihren Binärordner kopiert werden (Prozessarchitektur auswerten -> entsprechende DLLs von x64 / x86 in den Bin-Ordner und zurück verschieben.) - Bewerten Sie im Installationsprogramm die Architektur und löschen Sie Binärdateien auf falsche Architektur und verschieben Sie die richtigen in den Ordner bin.

Felix Keil
quelle
2

Ich hatte das gleiche Problem und suchte eine ganze Weile nach einer anständigen Lösung. Die meisten Benutzer bieten die manuelle Bearbeitung von Visual Studio-Lösungsdateien an, was ziemlich mühsam, fehleranfällig und verwirrend ist, wenn diese bearbeiteten Dateien anschließend in der Visual Studio-Benutzeroberfläche untersucht werden. Als ich bereits aufgab, kam die Lösung von selbst. Es ist sehr ähnlich zu dem, was Micke in seiner obigen Antwort empfiehlt.

Im Account Manager habe ich wie gewohnt zwei separate Build-Ziele für x86- und x64-Plattformen erstellt. Als Nächstes habe ich meinem Projekt einen Verweis auf die x86-Assembly hinzugefügt. In diesem Punkt habe ich geglaubt, dass das Projekt nur für die x86-Erstellung konfiguriert ist und niemals für die x64-Konfiguration erstellt wird, es sei denn, ich werde es manuell bearbeiten, wie von Hugo oben vorgeschlagen.

Nach einer Weile vergaß ich schließlich die Einschränkung und startete versehentlich den x64-Build. Natürlich ist der Build fehlgeschlagen. Aber wichtig war die Fehlermeldung, die ich erhalten habe. Die Fehlermeldung besagt, dass die Assembly, die genau so benannt ist, wie meine referenzierte x86-Assembly, in dem Ordner fehlt, der als x64-Build-Ziel für meine Lösung vorgesehen ist.

Nachdem ich dies bemerkt habe, habe ich die richtige x64-Assembly manuell in dieses Verzeichnis kopiert. Ruhm! Mein x64-Build war auf wundersame Weise erfolgreich, da die richtige Assembly implizit gefunden und verknüpft wurde. Es war nur eine Frage von Minuten, meine Lösung zu ändern, um ein Build-Zielverzeichnis für die x64-Assembly in diesem Ordner festzulegen. Nach diesen Schritten wird die Lösung automatisch für x86 und x64 erstellt, ohne dass MSBuild-Dateien manuell bearbeitet werden müssen.

Um zusammenzufassen:

  1. Erstellen Sie x86- und x64-Ziele in einem einzigen Projekt
  2. Fügen Sie x86-Assemblys alle richtigen Projektreferenzen hinzu
  3. Legen Sie ein gemeinsames Build-Zielverzeichnis für alle x64-Assemblys fest
  4. Wenn Sie über x64-Assemblys verfügen, kopieren Sie diese einfach einmal in Ihr x64-Build-Zielverzeichnis

Nach Abschluss dieser Schritte wird Ihre Lösung ordnungsgemäß für x86- und x64-Konfigurationen erstellt.

Dies funktionierte für mich im Visual Studio 2010 .NET 4.0 C # -Projekt. Offensichtlich handelt es sich hierbei um eine Art undokumentiertes internes Verhalten von Visual Studio, das in den Versionen 2012, 2013 und 2015 möglicherweise geändert wird. Wenn jemand andere Versionen anprobiert, teilen Sie uns bitte Ihre Erfahrungen mit.

Boris Zinchenko
quelle
-1

Am Ende habe ich eine meiner Meinung nach einfachere Lösung verwendet, die eine Art Umkehrung von Mickes ist. Das Projekt ist eine C # -Formular-App, Visual Studio 2015, mit x86- und x64-Zielen. Ich habe auf eine der .NET-Assemblys verwiesen und die 32-Bit-Assembly verwendet. In den Referenzeigenschaften habe ich "Copy Local" auf false gesetzt. Dann lege ich einfach manuell die entsprechende (32 oder 64 Bit) .Net-Assembly in jedes Zielverzeichnis. Die tatsächliche Referenzbitität ist irrelevant, vorausgesetzt, sie verfügen über dieselben Funktionen, da sie nur die externe Schnittstelle definiert. Sie können auch einen Post-Build-Kopierschritt ausführen, wenn Sie Lust haben. Beachten Sie, dass dieses Projekt auch eine COM-Referenz hatte, dasselbe funktioniert. Die Referenz definiert die Objekte / Schnittstellen, sodass die Bitigkeit der Referenz-DLL irrelevant ist. Wenn sowohl 32-Bit- als auch 64-Bit-COM-DLLs registriert sind, Die App sucht an der entsprechenden Stelle in der Registrierung und erstellt das richtige 32- oder 64-Bit-COM-Objekt. Funktioniert bei mir!

Jeff H.
quelle