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.