32-Bit- und 64-Bit-Targeting mit Visual Studio in derselben Lösung / demselben Projekt

111

Ich habe ein kleines Dilemma beim Einrichten meiner Visual Studio-Builds für Multi-Targeting.

Hintergrund: c # .NET v2.0 mit p / Aufruf in 32-Bit-DLLs von Drittanbietern, SQL compact v3.5 SP1, mit einem Setup-Projekt. Derzeit ist das Plattformziel auf x86 festgelegt, damit es unter Windows x64 ausgeführt werden kann.

Das Drittanbieterunternehmen hat gerade 64-Bit-Versionen seiner DLLs veröffentlicht, und ich möchte ein spezielles 64-Bit-Programm erstellen.

Dies wirft einige Fragen auf, auf die ich noch keine Antworten habe. Ich möchte genau die gleiche Codebasis haben. Ich muss mit Verweisen auf den 32-Bit-Satz von DLLs oder 64-Bit-DLLs bauen. (Sowohl Drittanbieter als auch SQL Server Compact)

Kann dies mit 2 neuen Konfigurationssätzen (Debug64 und Release64) gelöst werden?

Muss ich 2 separate Setup-Projekte erstellen (Standard-Visual Studio-Projekte, kein Wix oder ein anderes Dienstprogramm), oder kann dies innerhalb derselben MSI-Lösung gelöst werden?

Ideen und / oder Empfehlungen sind willkommen.

Magnus Johansson
quelle
@Magnus Johansson: Sie können zwei Konfigurationen verwenden, um die Hälfte Ihres Ziels zu erreichen. Das MSI ist etwas schwieriger.
user7116

Antworten:

83

Ja, Sie können sowohl x86 als auch x64 mit derselben Codebasis im selben Projekt als Ziel festlegen. Im Allgemeinen funktionieren die Dinge nur, wenn Sie die richtigen Lösungskonfigurationen in VS.NET erstellen (obwohl P / Invoke für vollständig nicht verwaltete DLLs höchstwahrscheinlich einen bedingten Code erfordert): Die Elemente, für die ich besondere Aufmerksamkeit benötigte, sind:

  • Verweise auf externe verwaltete Assemblys mit demselben Namen, aber ihrer eigenen spezifischen Bitness (dies gilt auch für COM-Interop-Assemblys)
  • Das MSI-Paket (das, wie bereits erwähnt, entweder auf x86 oder x64 abzielen muss)
  • Alle benutzerdefinierten, auf .NET Installer-Klassen basierenden Aktionen in Ihrem MSI-Paket

Das Assemblyreferenzproblem kann in VS.NET nicht vollständig gelöst werden, da Sie einem Projekt nur einmal eine Referenz mit einem bestimmten Namen hinzufügen können. Um dies zu umgehen, bearbeiten Sie Ihre Projektdatei manuell (klicken Sie in VS mit der rechten Maustaste auf Ihre Projektdatei im Projektmappen-Explorer, wählen Sie Projekt entladen, klicken Sie erneut mit der rechten Maustaste und wählen Sie Bearbeiten). Nachdem Sie beispielsweise einen Verweis auf die x86-Version einer Assembly hinzugefügt haben, enthält Ihre Projektdatei Folgendes:

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

Wickeln Sie dieses Referenz-Tag in ein ItemGroup-Tag ein und geben Sie die Lösungskonfiguration an, für die es gilt, z.

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

Kopieren Sie dann das gesamte ItemGroup-Tag, fügen Sie es ein und bearbeiten Sie es so, dass es die Details Ihrer 64-Bit-DLL enthält, z.

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

Nach dem erneuten Laden Ihres Projekts in VS.NET wird das Dialogfeld "Assembly-Referenz" durch diese Änderungen etwas verwirrt. Möglicherweise werden einige Warnungen zu Assemblys mit dem falschen Zielprozessor angezeigt, aber alle Builds funktionieren einwandfrei.

Das MSI Problem zu lösen , ist als nächstes, und leider wird ein non-VS.NET Werkzeug benötigen: Ich ziehe es Caphyon des Advanced Installer für diesen Zweck, da es den grundlegenden Trick beteiligt (Schaffung eines gemeinsamen MSI, sowie 32-Bit zieht ab und 64-Bit-spezifische MSIs und verwenden Sie einen EXE-Setup-Launcher, um die richtige Version zu extrahieren und die erforderlichen Korrekturen zur Laufzeit durchzuführen.

Sie können wahrscheinlich die gleichen Ergebnisse mit anderen Tools oder dem Windows Installer XML (WiX) -Toolset erzielen , aber Advanced Installer macht die Dinge so einfach (und ist dabei recht erschwinglich), dass ich nie wirklich nach Alternativen gesucht habe.

Möglicherweise benötigen Sie dennoch WiX, auch wenn Sie Advanced Installer verwenden, die benutzerdefinierten Aktionen der .NET Installer-Klasse. Obwohl es trivial ist, bestimmte Aktionen anzugeben, die nur auf bestimmten Plattformen ausgeführt werden sollen (unter Verwendung der Ausführungsbedingungen VersionNT64 bzw. NOT VersionNT64), werden die integrierten benutzerdefinierten AI-Aktionen mithilfe des 32-Bit-Frameworks ausgeführt, selbst auf 64-Bit-Computern .

Dies wird möglicherweise in einer zukünftigen Version behoben, aber für den Moment (oder wenn Sie ein anderes Tool zum Erstellen Ihrer MSIs mit demselben Problem verwenden) können Sie die verwaltete benutzerdefinierte Aktionsunterstützung von WiX 3.0 verwenden, um Aktions-DLLs mit der richtigen Bitness zu erstellen wird mit dem entsprechenden Framework ausgeführt.


Bearbeiten: Ab Version 8.1.2 unterstützt Advanced Installer benutzerdefinierte 64-Bit-Aktionen korrekt. Seit meiner ursprünglichen Antwort ist der Preis leider ziemlich gestiegen, obwohl er im Vergleich zu InstallShield und seiner Art immer noch ein extrem gutes Preis-Leistungs-Verhältnis bietet ...


Bearbeiten: Wenn Ihre DLLs im GAC registriert sind, können Sie auch die Standardreferenz-Tags auf diese Weise verwenden (Beispiel 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>

Die Bedingung wird auch auf alle Build-Typen (Release oder Debug) reduziert und gibt lediglich die Prozessorarchitektur an.

mdb
quelle
In Visual Studio 2008 stellte ich fest, dass <ItemGroup> s nicht verschachtelt werden konnten. Diese Lösung hat gut funktioniert, nachdem ich die neuen <ItemGroup> s unterhalb der Gruppe zum Rest der <Reference> s gemacht habe. Ich musste auch x86 in AnyCPU ändern, was wahrscheinlich mit der Geschichte meines speziellen Projekts zusammenhängt.
Oliver Bock
Das Advanced Installer sieht ziemlich gut aus.
Pat
Dies mag eine dumme Frage sein, aber wie gelangen Sie zu der Datei, um sie manuell zu bearbeiten?
Stunde
1
Um die Datei in VS zu bearbeiten, klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt und suchen Sie "Projekt entladen". Sobald das Projekt entladen ist, klicken Sie erneut mit der rechten Maustaste darauf und klicken Sie auf "Bearbeiten <Name der Projektdatei>". Nachdem Sie die Projektdatei bearbeitet haben, speichern Sie sie und klicken Sie erneut mit der rechten Maustaste auf die Projektdatei und laden Sie sie. Wenn keine Tippfehler oder Fehler vorliegen, wird es erneut geladen. Wenn nicht, wird VS Ihnen ziemlich genau sagen, wo das Problem mit der Datei liegt. Hoffentlich hilft das!
John Baughman
Gute Antwort! Danke dir!
John Baughman
27

Angenommen, Sie haben die DLLs für beide Plattformen erstellt und sie befinden sich am folgenden Speicherort:

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

Sie müssen lediglich Ihre .csproj-Datei wie folgt bearbeiten:

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

Dazu:

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

Sie sollten dann in der Lage sein, Ihr Projekt für beide Plattformen zu erstellen, und MSBuild sucht im richtigen Verzeichnis für die ausgewählte Plattform.

Tim Booker
quelle
Das wäre großartig, wenn es funktionieren würde, aber es funktioniert nicht. Zumindest nicht für mich.
John Sheehan
10
Soll das nicht sein: <HintPath> C: \ Whatever \ $ (Platform) \ Whatever.dll </ HintPath>
Andreas
Funktionierte in Visual Studio 2008 für mich in Ordnung, kopierte die DLL jedoch nicht automatisch in das Build-Zielverzeichnis, wie dies bei einer normalen <Referenz> der Fall ist. Die Lösung von mdb hat bei mir besser funktioniert.
Oliver Bock
1

Ich bin mir nicht sicher, ob Ihre Frage vollständig beantwortet wurde. Ich dachte jedoch, ich würde auf einen Kommentar im Abschnitt "Zusätzliche Informationen" der Download-Seite von SQL Compact 3.5 SP1 hinweisen, wenn Sie x64 betrachten. Ich hoffe, es hilft.

Aufgrund von Änderungen in SQL Server Compact SP1 und zusätzlicher Unterstützung für 64-Bit-Versionen können zentral installierte und gemischte Umgebungen der 32-Bit-Version von SQL Server Compact 3.5 und der 64-Bit-Version von SQL Server Compact 3.5 SP1 scheinbar zeitweise auftreten Probleme. Um das Konfliktpotential zu minimieren und die plattformneutrale Bereitstellung verwalteter Clientanwendungen zu ermöglichen, muss für die zentrale Installation der 64-Bit-Version von SQL Server Compact 3.5 SP1 mithilfe der Windows Installer-Datei (MSI) auch die 32-Bit-Version von SQL Server installiert werden Kompakte 3.5 SP1 MSI-Datei. Für Anwendungen, die nur natives 64-Bit erfordern, kann die private Bereitstellung der 64-Bit-Version von SQL Server Compact 3.5 SP1 verwendet werden.

Ich habe dies als "32-Bit-SQLCE-Dateien sowie 64-Bit-Dateien einschließen" gelesen, wenn es für 64-Bit-Clients verteilt wird.

Das macht das Leben interessant, denke ich. Ich muss sagen, dass ich die Zeile "Was scheint, sind zeitweise auftretende Probleme" liebe. Klingt ein bisschen wie "Sie stellen sich Dinge vor, aber nur für den Fall, tun Sie dies ...".

gleng
quelle
1

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 haben und einen Build zu erstellen, der mit Ihren x86- und x64-DLLs funktioniert.

Sie müssen dafür einen Installationscode schreiben.

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, damit 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 benötigen den AssemblyResolve-Ereignishandler nicht, wenn Sie sicherstellen, dass die richtigen 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
0

In Bezug auf Ihre letzte Frage. Höchstwahrscheinlich können Sie dies nicht in einem einzigen MSI lösen. Wenn Sie Registrierungs- / Systemordner oder ähnliches verwenden, muss dies dem MSI selbst bekannt sein und Sie müssen ein 64-Bit-MSI für die ordnungsgemäße Installation auf einem 32-Bit-Computer vorbereiten.

Es besteht die Möglichkeit, dass Sie Ihr Produkt als 32-it-Anwendung installieren und dennoch als 64-Bit-Anwendung ausführen können, aber ich denke, dass dies etwas schwierig zu erreichen ist.

Davon abgesehen denke ich, dass Sie in der Lage sein sollten, eine einzige Codebasis für alles zu behalten. An meinem derzeitigen Arbeitsplatz haben wir das geschafft. (aber es brauchte etwas Jonglieren, um alles zusammen zu spielen)

Hoffe das hilft. Hier ist ein Link zu einigen Informationen zu 32/64-Bit-Problemen: http://blog.typemock.com/2008/07/registry-on-windows-64-bit-double-your.html

Lior Friedman
quelle
0

Wenn Sie in .NET geschriebene benutzerdefinierte Aktionen als Teil Ihres MSI-Installationsprogramms verwenden, liegt ein anderes Problem vor.

Der Shim, der diese benutzerdefinierten Aktionen ausführt, ist immer 32 Bit, dann wird Ihre benutzerdefinierte Aktion auch 32 Bit ausführen, unabhängig davon, welches Ziel Sie angeben.

Weitere Informationen und einige Ninja-Bewegungen, um herumzukommen (ändern Sie im Grunde das MSI, um die 64-Bit-Version dieses Shims zu verwenden).

Erstellen einer MSI in Visual Studio 2005/2008 für die Arbeit mit SharePoint 64

Verwaltete benutzerdefinierte 64-Bit-Aktionen mit Visual Studio

Ryan
quelle
0

Sie können zwei Lösungen unterschiedlich generieren und anschließend zusammenführen! Ich habe das für VS 2010 gemacht und es funktioniert. Ich hatte 2 verschiedene Lösungen von CMake generiert und sie zusammengeführt

voidMainReturn
quelle
0

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