Benutzer mit den meisten Antworten
Klassen-Lib kompilieren für AnyCPU/x86/x64

Frage
-
Hi,
angenommen, ich habe in VS2010 eine Projektmappe. In dieser befinden sich z.B. 3 Klassenbibliotheken und eine "Windows-Forms-Anwendung". Die "Windows-Forms-Anwendung" hat einen Verweis auf alle 3 Klassenbibliotheken.
Bei der Projekterstellung steht die Einstellung auf "Debug/AnyCPU". So weit, so gut. Es gibt aber eine Klassenbibliothek welche eine unmanged-DLL enthält. Bzw. es sind zwei. Eine für x86 und eine für x64.
Wenn ich die Klassenbibliothek erstelle, und vor dem erstellen zwischen x86 und x64 hin- und herschalte. Wird auch die richtige DLL in das Ausgabeverzeichnis (mittels Buildereignisse/Makros) kopiert.
Wenn ich aber meine Solution wiederum kompiliere (also die eigentliche Windows-Forms-Anwendung), gibt es einen Fehler, und zwar deshalb, weil die eigentliche Klassenbibliothek ja nicht mitbekommt, für welche Konfiguration das EIGENTLICHE Programm kompiliert wird.
Jetzt könnte man ja sagen "stell doch Klassenbibliothek" entsprechend um. Das würde aber andere Projekte wiederum "kaputtmachen", weil auch diese von der jeweiligen Klassenbibliothek abhängig sind. Bzw. ich irgendwie selbst dann durcheinander komme.
Jetzt meine Frage:
Besteht irgendwie die Möglichkeit, dass ich (z.B. mittels Makros in der Klassenbibliothek) feststellen kann, für welche CPU das HAUPTPROGRAMM kompiliert wird bzw. wurde? AnyCPU, wenn ich das richtig verstanden habe, bedeutet doch, dass Programm wird "entweder...oder..." x86/x64 kompiliert, je nachdem worauf gerade VisualStudio bzw. der Compiler läuft.
Und wie gesagt, wenn ich in meinem Hauptprojekt für x86 kompiliere, und in der Klassenbibliothek steht AnyCPU oder x64, wird das auch entsprechend kompiliert, leider. Habe bis jetzt noch keine Möglichkeit gefunden der Klassenbibliothek zu sagen "Nene mein Freund, das Anwendungsprogramm ist der Chef, wenn er sagt x86, dann machst du das bitte auch".
Gruß
Andy
Antworten
-
Hallo Andreas,
grundsätzlich geht es schon, eine Bibliothek für AnyCPU zu kompilieren und die unmanaged DLLs entsprechend nachzuladen. Dazu muss man dafür sorgen, dass im richtigen Verzeichnis nachgeschaut wird.
SQLite macht das z. B. so, siehe http://stackoverflow.com/questions/3787428/loading-x86-or-x64-assembly
In getting SQLite App working when compiled on a 64 bit pc to run on a 32 bit pc hatte ich das mal (in englisch) versucht zu erklären, der Fragende hatte aber Schwierigkeiten es zu verstehen bzw. umzusetzen.
Wichtig ist dabei, dass die unmanaged DLLs gleich benannt sind und in unterschiedlichen (Unter-)Verzeichnissen liegen (bei SQLite x86 und x84 für die SQLite.InterOp.dll).
Zudem muss man tunlichst darauf achten, dass der Aufruf von
SetDllDirectory
vor jeglichem Zugriff auf eine unmanaged Dll passiert - üblicherweise als erstes in Main.
Gruß Elmar
- Als Antwort markiert AndreasMahub Mittwoch, 28. Mai 2014 15:31
-
Hallo Andreas,
ich weiß nicht, welche Klasse das Sub New() enthält. Aber Du solltest vorsichtig sein, denn wenn die Klasse nur einen Ticken später geladen wird, könnte bereits eine Dll geladen werden (und einmal geladen, ist es fest).
Besser verpackt man den Code direkt in Main
Du solltest die x86 Dateien gleichfalls in ein Unterverzeichnis legen.
SetDllDirectory sollte immer vollständige Pfade enthalten, sonst wird das aktuelle Verzeichnis davor gesetzt, was zum einen wechselt, zum anderen ein Sicherheitsrisiko darstellt.
Fürs Startverzeichnis würde ich Application.StartupDirectory verwenden oder alternativ (auch bei WPF): http://stackoverflow.com/questions/938421/getting-the-applications-directory-from-a-wpf-application
Gruß Elmar
- Als Antwort markiert AndreasMahub Freitag, 30. Mai 2014 04:44
Alle Antworten
-
Hi,
AnyCPU bedeutet nicht, dass das Programm entsprechend der CPU des Compiler-Rechners erstellt wird, sondern, dass das Compilat (der IL-Code) so erstellt wird, dass der JIT-Compiler (der ja beim Programmstart das eigentliche Executable erstellt) dies passend zum ZIEL-System machen kann. Aus einem AnyCPU-Compilat wird also auf einem 32bit-Rechner ein 32bit-Programm und auf einem 64bit-Rechner ein 64bit-Programm (dann sind aber möglicherweise du und VisualStudio schon gar nicht mehr dabei). (vgl. Stack Overflow).
Ich weiß nicht, ob es funktionieren kann, beide DLLs mitzugeben (in eigenen Verzeichnissen) und beim Start deines Programmes zu schauen, welche von beiden zum Zielsystem passt und diese dann in das Programmverzeichnis zu kopieren. Außerdem werden dafür i. d. R. mit Admin-Rechten benötigt und insofern ist das eh eine eher ?fragwürdige? Idee.
Es scheint also nur die Option zu bleiben, zwei Hauptprogramme zu erstellen.
Gruß
Jürgen -
Hallo Andreas,
grundsätzlich geht es schon, eine Bibliothek für AnyCPU zu kompilieren und die unmanaged DLLs entsprechend nachzuladen. Dazu muss man dafür sorgen, dass im richtigen Verzeichnis nachgeschaut wird.
SQLite macht das z. B. so, siehe http://stackoverflow.com/questions/3787428/loading-x86-or-x64-assembly
In getting SQLite App working when compiled on a 64 bit pc to run on a 32 bit pc hatte ich das mal (in englisch) versucht zu erklären, der Fragende hatte aber Schwierigkeiten es zu verstehen bzw. umzusetzen.
Wichtig ist dabei, dass die unmanaged DLLs gleich benannt sind und in unterschiedlichen (Unter-)Verzeichnissen liegen (bei SQLite x86 und x84 für die SQLite.InterOp.dll).
Zudem muss man tunlichst darauf achten, dass der Aufruf von
SetDllDirectory
vor jeglichem Zugriff auf eine unmanaged Dll passiert - üblicherweise als erstes in Main.
Gruß Elmar
- Als Antwort markiert AndreasMahub Mittwoch, 28. Mai 2014 15:31
-
Hallo Elmar,
meinst du in etwa so?
Public Declare Function SetDllDirectoryA Lib "kernel32" (ByVal lpPathName As String) As Long Public Sub New() If Environment.Is64BitProcess Then SetDllDirectoryA(My.Application.Info.DirectoryPath & "\x64\") End If End Sub
Wenn ja: FUNKTIONIERT :-)
Wichtig ist: My.Application.Info.DirectoryPath MUSS drin sein. Wenn man sein Programm mit einem Installer installiert und vom Desktop aus startet, wird nicht im Programmverzeichnis gesucht, sondern auf dem "Desktop\x64" ;-)
Danke schön!!!!!!!!!! :-)
Gruß
Andy -
Hallo Andreas,
ich weiß nicht, welche Klasse das Sub New() enthält. Aber Du solltest vorsichtig sein, denn wenn die Klasse nur einen Ticken später geladen wird, könnte bereits eine Dll geladen werden (und einmal geladen, ist es fest).
Besser verpackt man den Code direkt in Main
Du solltest die x86 Dateien gleichfalls in ein Unterverzeichnis legen.
SetDllDirectory sollte immer vollständige Pfade enthalten, sonst wird das aktuelle Verzeichnis davor gesetzt, was zum einen wechselt, zum anderen ein Sicherheitsrisiko darstellt.
Fürs Startverzeichnis würde ich Application.StartupDirectory verwenden oder alternativ (auch bei WPF): http://stackoverflow.com/questions/938421/getting-the-applications-directory-from-a-wpf-application
Gruß Elmar
- Als Antwort markiert AndreasMahub Freitag, 30. Mai 2014 04:44
-
Guten Morgen,
klingt einleuchtend mit der Main und den Unterverzeichnissen.
Bei SetDllDirectory hatte ich doch mit My.Application.Info.DirectoryPath einen vollständige Pfadangabe, oder? Oder kann es vorkommen das da mal was anderes stehen könnte? Bei mir heißt es übrigens nicht Application.StartupDirectory sondern Application.StartupPath ;-)
Gruß
Andy- Bearbeitet AndreasMahub Freitag, 30. Mai 2014 04:52 Edit: Hoppla, hier wurde etwas verschluckt
-
Hi,
noch eine kurze Frage: Gibt es ein Tool, Trick, Funktion in VS, wo ich feststellen kann, dass eine DLL von einem Drittanbieter x86, x64 oder AnyCPU ist? Bin gerade dabei den Vorschlag mit den Directorys umzusetzen. Ich würde dann alle DLLs in ein Unterverzeichnis legen.
\Extensions\ für DLLs mit "AnyCPU"
\Extensions\x64\ für DLLs die explizit für x64 kompilierte Programme sind.
\Extensions\x86\ für DLLs die explizit für x86 kompilierte Programme.Gruß
Andy -
Hallo Andreas,
zur ersten Ergänzung: My.Application.Info.DirectoryPath ist implementiert über System.Reflection.Assembly.GetExecutingAssembly().Location - was man erst via Reflector klären muss - weswegen ich My-Zeugs nicht mag; versteckt was bereits implementiert und dokumentiert ist. Potentiell fällt es auf die Nase, wenn es sich um eine Schattenkopie handelt - siehe Link in meiner Antwort.
Zum zweiten Teil: Via dumpbin kann man die PE-Header von Programmen auswerten, siehe How to find if a native DLL file is compiled as x64 or x86?
Will man es im Programm tun müsste man selbst an den PE-Header ran, z. B. (ungeprüft): How to determine whether a file is a .NET Assembly or not?
Zu beachten SetDllDirectory gilt nur für den unmanaged Teil. Bei .NET gilt weiterhin: So sucht Common Language Runtime nach Assemblys
Gruß Elmar