2011-01-05 2 views
7

Ich habe ein Programm, das sowohl in einer x86- als auch in einer x64-Umgebung funktionieren muss. Es verwendet ODBC-Treiber von Oracle. Ich habe einen Verweis auf Oracle.DataAccess.DLL. Diese DLL hängt davon ab, ob das System x64 oder x86 ist.Versucht, keine zwei separaten Lösungen für x86 und x64-Programm zu benötigen

Zur Zeit habe ich zwei separate Lösungen und ich behalte den Code auf beiden. Das ist grauenhaft. Ich habe mich gefragt, was die richtige Lösung ist?

Ich habe meine Plattform auf "Any CPU." und ich verstehe, dass VS die DLL zu einer Zwischensprache kompilieren sollte, so dass es keine Rolle spielt, ob ich die x86- oder x64-Version verwende. Wenn ich jedoch versuche, die x64-DLL zu verwenden, erhalte ich den Fehler "Konnte Datei oder Assembly nicht laden" Oracle.DataAccess, Version = 2.102.3.2, Culture = neutral, PublicKeyToken = 89b483f429c47342 'oder eine seiner Abhängigkeiten. Ein Versuch wurde unternommen um ein Programm mit einem falschen Format zu laden. "

Ich bin auf einer 32-Bit-Maschine laufen, so macht die Fehlermeldung Sinn, aber es lässt mich fragen, wie ich dieses Programm effizient entwickeln soll, wenn es auf x64 arbeiten muss.

Danke.

+2

Wenn Sie eine 32/64bit-App entwickeln, sollten Sie wirklich auf einem 64-Bit-Betriebssystem arbeiten. Zumindest unter Windows kann ein 32-Bit-Betriebssystem keine 64-Bit-Programme ausführen, aber ein 64-Bit-OS _kann_ 32-Bit-Programme ausführen. – rossipedia

+1

Eine andere Frage, über die Sie nachdenken sollten, ist: "Muss meine App wirklich in 64-Bit laufen?" WOW macht einen wunderbaren Job mit einer 32-Bit-App auf x64. – vcsjones

Antwort

0

Die Verwendung von AnyCPU mit nativen frühen Bindungen funktioniert einfach nicht, dafür benötigen Sie zwei separate Lösungen und Builds, wie Sie gesehen haben. Sie müssen sich ein 64-Bit-System besorgen, um x64-kompilierte DLLs zu entwickeln oder zumindest zu testen.

Bei der späten Bindung können Sie AnyCPU- und Systemeigenschaften verwenden, um herauszufinden, welche Architektur Sie ausführen, und mit der richtigen DLL verknüpfen, wenn Sie den Namen wie Oracle.DataAccess.x86.dll beibehalten. Wenn sie in den GAC installiert werden, ist es noch einfacher, Sie können binden, ohne sich erst einmal für die Architektur zu interessieren, aber ich glaube, dass Sie noch spät binden müssen.

Beachten Sie, dass VMware einen 64-Bit-Gast auf einem 32-Bit-Host ausführen kann, wenn Sie wirklich keine Mühe haben, Windows neu zu installieren.

2

Wenn Sie auf einem 32-Bit-Computer ausgeführt werden, müssen Sie die 32-Bit-Version von Oracle DLL laden. Ein 32-Bitprogramm kann nicht auf eine 64-Bit-DLL verweisen. Und ein 64-Bit-Programm kann nicht auf eine 32-Bit-DLL verweisen.

"Any CPU" ist das richtige Ziel, wenn Sie mehrere Versionen der externen DLL haben. Der Trick besteht darin, sicherzustellen, dass die richtige Oracle-DLL gefunden und geladen wird. Am besten finden Sie die 64-Bit-Version der DLL auf Ihrem 32-Bit-System und benennen sie um, damit sie von der Laufzeitumgebung nicht gefunden werden kann.

+0

Ich denke, wir sind im selben Stadion, aber meine Gedanken werden nicht richtig vermittelt. Ich habe manuell die richtige 32-Bit-DLL in meine 32-Bit-Lösung und die 64-Bit-DLL in die 64-Bit-Lösung eingefügt. Wenn ich die ausführbaren Dateien erzeugen muss, erstelle ich beide, separate Lösungen. Wenn ich mich entwickle, arbeite ich nur im 32-Bit, und wenn ich mit meinen Code-Änderungen fertig bin, muss ich diese Änderungen an der 64-Bit-Lösung replizieren. Ich möchte nur eine Lösung anstelle von zwei verwenden, habe aber die gleichen Ergebnisse. –

0

Sie sollten in der Lage sein, dieselbe Lösung zu konfigurieren, um x86/x64-Versionen separat zu erstellen. Sie müssen möglicherweise auch Post-Build-Schritte hinzufügen, um die korrekte DLL-Version in die entsprechenden Ausgabeordner zu kopieren ...

Zumindest, wenn Sie 2 Lösungen erstellen müssen - verwenden Sie die gleiche Quelle (Dateien als Verweis auf zweite Lösung hinzufügen, nicht Kopie in zweite Lösung).

3

Dies ist ein reines Bereitstellungsproblem, Sie sollten nie verschiedene Projekte verwalten müssen. Es ist jedoch eine peinliche Sache, und buh auf Oracle dafür, sich nicht selbst darum zu kümmern. Eine weitere Überlegung ist, dass diese Assembly wirklich auf dem Zielcomputer erstellt werden sollte. Einige Optionen

  • Erstellen Sie zwei Installer, einen für x64 und einen für x86. Der Kunde wählt die richtige aus, je nachdem, welches Betriebssystem er verwendet. Einfach genug, kopieren Sie einfach die richtige Datei.
  • Stellen Sie beide Assemblys auf dem GAC bereit. Jetzt ist es automatisch, .NET wählt den richtigen auf jedem Maschinentyp aus.Große Unternehmen sollten fast immer den GAC verwenden, damit sie Sicherheitsupdates bereitstellen können, ohne sicher zu sein, warum Oracle dies nicht tut.
  • Stellen Sie die Assemblys in einem x86- und x64-Unterverzeichnis des Installationsverzeichnisses bereit. Sie müssen einen AppDomain.AssemblyResolve-Ereignishandler schreiben, der basierend auf dem Wert von IntPtr.Size das richtige Verzeichnis auswählt.
  • Ändern Sie die Zielplattform in Ihrem EXE-Projekt auf x86. Da Ihr Code sowohl auf einem 32-Bit-Computer als auch auf einem 64-Bit-Computer ausgeführt werden muss, sollte/sollte kein Grund für AnyCPU bestehen.
3

Dies ist eine funktionierende Lösung für Ihr Problem:

Fügen Sie die 2 DLL (x86 und x64), um Ihre Lösung in einem Unterordner. Machen Sie sie "Copy if neuher"

Referenzieren Sie die richtige DLL, die Sie für die Fehlersuche aus den 2 DLLs verwenden, die Sie hinzugefügt haben. Machen Sie es Kopie lokal = falsch.

Dies bedeutet, dass beim Start der Anwendung die DLL nicht automatisch geladen wird. Es wird erst geladen, wenn Sie einen Type von dieser Assembly verwenden. Sobald dies passiert, wird in .Net ein Ereignis ausgelöst, das fragt, wo es Ihre Assembly finden kann.

Also irgendwann vor der ersten Verwendung dieser Baugruppe sicherstellen, dass Sie sich an dieses Ereignis anschließen.

AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; 

Im Inhalt des Handlers sicherstellen, dass Sie die DLL (x86 oder x64) laden, wenn sie danach fragt.

static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { 
     if (args.Name.Equals("MyFullAssemblyName")) { 
      var path = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); 
      if (IntPtr.Size > 4) { 
       var dll = System.IO.Path.Combine(path, @"MySubDir\MyDLL_x64.dll"); 
       return System.Reflection.Assembly.LoadFile(dll); 
      } 
      else { 
       var dll = System.IO.Path.Combine(path, @"MySubDir\MyDLL.dll"); 
       return System.Reflection.Assembly.LoadFile(dll); 
      } 
     } 
     return null; 
    } 

Voila. Sie können Ihre App jetzt sowohl als 32-Bit- als auch als 64-Bit-Version ausführen.

Alternativ können die DLLs in einem Unterordner hinzufügen, können Sie sie als eingebettete Ressourcen machen und sie dann wie folgt laden:

static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { 
     if (args.Name.Equals("MyFullAssemblyName")) { 
      var ass = Assembly.GetExecutingAssembly(); 

      if (IntPtr.Size > 4) { 
       var strm = ass.GetManifestResourceStream("the.resource.name.for.MyDLL_x64.dll"); 
       var data = new byte[strm.Length]; 
       strm.Read(data, 0, data.Length); 
       return Assembly.Load(data); 
      } 
      else { 
       var strm = ass.GetManifestResourceStream("the.resource.name.for.MyDLL.dll"); 
       var data = new byte[strm.Length]; 
       strm.Read(data, 0, data.Length); 
       return Assembly.Load(data); 
      } 
     } 
     return null; 
    } 

Dies funktioniert nicht für alle Baugruppen. Einige "hybride" Assemblies neigen dazu, zu versagen, wenn sie nicht von der Festplatte geladen werden (können gelöst werden, indem sie kurz vor dem Laden auf die Festplatte geschrieben werden).