Schlagwort-Archive: Interop

System.IO.FileLoadException: Verwenden von .NET 2.0 Assemblies innerhalb eines .NET 4.0 Prozesses

Verwendet eine .NET 4.0 Assembly .NET 2.0 Komponenten, kommt es zu einer System.IO.FileLoadException mit der detaillierten Meldung „Die Assembly im gemischten Modus wurde während Version v2.0.50727 der Laufzeit erstellt und kann nicht während der 4.0-Laufzeit ohne zusätzliche Konfigurationsinformationen geladen werden.“:

Bei mir lag es einfach daran, dass ich eine UI-Bibliothek verwendete, die für die CLR 2.0 entwickelt wurde, während mein Hauptprojekt in .NET 4.0 entwickelt wurde.

Es gibt mehrere Möglichkeiten, das Problem zu lösen:

–          Über eine Konfigurationsdatei

–          Über das Konfigurierung der Aktivierung via Code

Lösungsmöglichkeit 1: via Konfigurationsdatei

<startup useLegacyV2RuntimeActivationPolicy=“true“>
<supportedRuntime version=“v4.0″/>
</startup>

Die app.config muss natürlich neben der ausführbaren Datei stehen.

Beispiel:

Ausführbare Datei: example.exe
Konfigurationsdatei: example.exe.config

Mark Miller hat in einem Blog-Beitrag (als PDF: marklio – What is useLegacyV2RuntimeActivationPolicy for_) beschrieben, wozu die useLegacyV2RuntimeActivationPolicy nun wirklich gut ist und was sie tut.

Es gibt jedoch Anwendungsfälle, für die man nicht einfach eine Konfigurationsdatei neben die ausführbare Datei legen kann. Ein Beispiel dafür könnte sein, dass eine C-API oder eine COM-API zur Verfügung gestellt wird. Dann kann nicht einfach der ausführbaren Datei, die die C-API verwendet, eine Konfigurationsdatei beigelegt werden.

Lösungsmöglichkeit 2: via Code

Im Blog von Reed Copsey bin ich auf einen interessanten Artikel gestoßen, der genau mein Problem löst: http://reedcopsey.com/2011/09/15/setting-uselegacyv2runtimeactivationpolicy-at-runtime/

Nachfolgend der Code, der für meine Belange (C-API) funktionierte. Jedoch ist die Lösung in Reed Copseys Blog explizit als inoffiziell gekennzeichnet.

public static bool LegacyV2RuntimeEnabledSuccessfully { get; private set; }

staticRuntimePolicyHelper()

{

ICLRRuntimeInfoclrRuntimeInfo =   (ICLRRuntimeInfo)RuntimeEnvironment.GetRuntimeInterfaceAsObject(Guid.Empty,  typeof(ICLRRuntimeInfo).GUID);

try

{

clrRuntimeInfo.BindAsLegacyV2Runtime();

LegacyV2RuntimeEnabledSuccessfully = true;

}

catch (COMException)

{

// This occurs with an HRESULT meaning

// „A different runtime was already bound to the legacy CLR version 2 activation policy.“

LegacyV2RuntimeEnabledSuccessfully = false;

}

}

[ComImport]

[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]

[Guid(„BD39D1D2-BA2F-486A-89B0-B4B0CB466891“)]

private interface ICLRRuntimeInfo

{

void xGetVersionString();

void xGetRuntimeDirectory();

void xIsLoaded();

void xIsLoadable();

void xLoadErrorString();

void xLoadLibrary();

void xGetProcAddress();

void xGetInterface();

void xSetDefaultStartupFlags();

void xGetDefaultStartupFlags();

[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]

void BindAsLegacyV2Runtime();

}

Interessanterweise wird ein statischer Konstruktor verwendet, der unmittelbar nach dem Laden der assembly ausgeführt wird. Dadurch wird der Code implizit von der Runtime ausgeführt und nicht explizit vom Programm selbst. Das sollte ausreichend gut dokumentiert werden, da potentiell nicht jeder im Team den Code sofort versteht und potentiell den Code als „unnütz“ löschen könnte 😉

Die Eigenschaft „LegacyV2RuntimeEnabledSuccessfully“ gibt an, ob das Umschalten der Runtime-Aktivierung funktioniert hatte. In meinem Fall habe ich eine Ausgabe für die API erstellt, die dem Benutzer angibt, wie er das Problem mithilfe der Konfigurationsdatei lösen kann.

Debuggen von Problemen beim Laden von Assemblies

Ich entwickle beruflich u.a. Assemblies mit C++/CLI, die eine Interop-Brücke zwischen den vorhandenen C++-Bibliotheken und unseren .NET-Anwendungen (in C#) darstellen. Hin und wieder (beispielsweise wenn sich eine native C++-DLL geändert hat), bekomme ich Loader Exceptions in Form von System.IO.FileNotFoundException.

Bisher habe ich mir immer Hilfe bei dem Assembly Binding Log Viewer geholt, der mich angezeigt hat, welche Assemblies Probleme machen. Der Assembly Binding Fusion Log Viewer liegt üblicherweise im SDK-Ordner vom .NET Framework (in meinem Fall C:Program FilesMicrosoft Visual Studio 8SDKv2.0Bin) und heißt FUSLOGVW.exe.

Assembly Binding Log Viewer

Danach habe ich mir die Assemblies angeschaut, die Probleme machen und habe somit schnell die fehlenden Abhängigkeiten gefunden.

Weiter Tipps in dieser Richtung habe ich im Blog von Suzsanne Cook gefunden, wie man Probleme beim Laden von Assemblies in den Griff bekommt:

http://blogs.msdn.com/suzcook/archive/2003/05/29/57120.aspx

Managed-Unmanaged InterOp: unmanaged DLLs in Assemblies speichern

Ich habe heute im Blog von Ralf Westphal einen etwas älteren, aber dennoch sehr interessanten Artikel gelesen, der sich damit beschäftigt wie man native C/C++-DLLs in Assemblies unterbringen kann.

Dabei wird zur Compile-Zeit einfach die unmanaged DLL als Ressource in die .NET Assembly eingebettet und dann zur Laufzeit wieder extrahiert und im Dateisystem gespeichert.

Ein einfacher, aber dennoch cooler Trick, der das Deployment (vorzugsweise XCOPY-Deployment) für Interop-Applikationen vereinfacht.

Eine ähnliche Vorgehensweise beschreibt auch Suzanne Cook in ihrem Blog: Sie schlägt vor, die unmanaged DLL als Teil einer Multi-File-Assembly abzulegen. Somit nimmt man den Vorteil der Versionierung von .NET Assemblies mit.

Hier nochmal die Links zu den beiden Beiträgen:

Ralf Westphals Blogeintrag „Single Assembly Deployment of Managed and Unmanaged Code“:
http://weblogs.asp.net/ralfw/archive/2007/02/04/single-assembly-deployment-of-managed-and-unmanaged-code.aspx

Suzanne Cock’s Blogeintrag „Versioning/Deploying Unmanaged Files“:
http://blogs.msdn.com/suzcook/archive/2004/10/28/249280.aspx