2017-06-13 1 views
1

Ich muss einige alte Anwendungen (vb6) aktualisieren und ich habe COM-Interop verwendet, um den neuen Code in C# (Visual Studio 2010) zu schreiben. Es hat meistens gut funktioniert, aber ich bin auf ein Problem gestoßen und ich bin mir nicht sicher, was es verursacht.BinaryFormatter Deserialisierung löst Ausnahme bei Verwendung durch COM-Interop

Ich verwende die folgende Methode eine tiefe Kopie eines Objekts

public static T CloneObject<T>(T source) 
    { 
     T destination = default(T); 

     if (!typeof(T).IsSerializable) 
     { 
      throw new ArgumentException("The type must be serialisable.", "source"); 
     } 

     if (Object.ReferenceEquals(source, null)) 
     { 
      return default(T); 
     } 

     using (Stream ms = new MemoryStream()) 
     { 
      BinaryFormatter formatter = new BinaryFormatter(); 
      formatter.Serialize(ms, source); 
      ms.Position = 0; 

      destination = (T)formatter.Deserialize(ms); 
     } 

     return destination; 
    } 

die Codefehler Aus irgendeinem Grund durchzuführen, wenn die Deserialize Funktion aufgerufen wird. Die Ausnahme, die es auslöst, ist, dass die Assembly, in der sich der Code befindet, nicht gefunden werden kann.

Unable Montage finden 'AssemblyBeingUsed, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null'

Dieses leicht mich ist verwirrend, wenn die Anordnung bereits zugegriffen wird und dessen sagen, dass es kann nicht gefunden werden. Der Code, der die Fehlermeldung formatiert, ist Teil derselben Assembly!

Hier ist der Stack-Trace für die Ausnahme.

bei System.Runtime.Serialization.Formatters.Binary.BinaryAssemblyInfo.GetAssembly() bei System.Runtime.Serialization.Formatters.Binary.ObjectReader.GetType (BinaryAssemblyInfo Assembly, String name) bei System.Runtime. Serialization.Formatters.Binary.ObjectMap..ctor (String objectName, String [] memberNames, BinaryTypeEnum [] binaryTypeEnumA, Object [] typeInformationA, Int32 [] memberAssemIds, ObjectReader objectReader, Int32 objektId, BinaryAssemblyInfo assemblyInfo, SizedArray assemIdToAssemblyTable) bei System. Runtime.Serialization.Formatters.Binary .__ BinaryParser.ReadObjectWithMapTyped (BinaryObjectWithMapTyped record) bei System.Runtime.Serialization.Formatters.Binary .__ BinaryParser. ReadObjectWithMapTyped (BinaryHeaderEnum binaryHeaderEnum) bei System.Runtime.Serialization.Formatters.Binary .__ BinaryParser.Run() bei System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize (HeaderHandler Handler, __BinaryParser serParser, Boolean fcheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage) bei System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (Stream serializationStream, HeaderHandler Handler, Boolean fcheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)

bearbeiten: Weitere nützliche Informationen.

Diese Funktion wird niemals direkt von vb6 aufgerufen. Da es Generika verwendet, bin ich mir ziemlich sicher, dass dies sowieso unmöglich wäre. Diese Funktion wird von einem Formular aufgerufen, das von der vb6-Anwendung geladen wird. Wenn dieses Formular aus einer C# -Anwendung verwendet wird, gibt es kein Problem, obwohl es genau dasselbe tut.

Das Visual Studio-Projekt verwendet die Option "Für COM-Interop registrieren", und die Assembly wird als Referenz in das vb6-Projekt geladen.

bearbeiten: Ausgabe von Fuslogvw.exe

Die Ausgabe von Fuslogvw.exe zeigt 5 Einträge mit der Baugruppe zugeordnet I in (BarcodeAndOperatorDatabase) interessiert bin. Da es ziemlich lang ist, wenn sie kombiniert werden, habe ich alle Ausgaben auf this file hochgeladen.

Um ehrlich zu sein, ich bin mir nicht sicher, was ich sehe. Es gibt drei Vorgänge, die meiner Meinung nach beim Binden der Assembly übereinstimmen:

15:29:06: VB6-Anwendung gestartet, Operation war erfolgreich.

15:29:14 (2 Einträge): Formular von Assembly geladen (denke ich), Operation fehlgeschlagen. Das ist verwirrend, da das Formular korrekt geladen wird und interagiert werden kann.

15:29:50 (2 Einträge): Button klicken, die fehlschlägt, wenn die CloneObject-Methode aufgerufen wird, Operation fehlgeschlagen.

+0

Etwas von einem wilden Schuss, aber haben Sie dlls in mehr als einem Verzeichnis? Wenn Sie diese Art von Twilight-Zone-Verhalten erhalten, wenn 2 Kopien der gleichen DLL von der exe gefunden werden können. –

+0

Es sieht so aus, als ob Sie Probleme beim Parsen einer Ganzzahl haben. Hat die Eingabe Nullen? Versuchen Sie Int? anstelle von int. Haben die ganzen Zahlen Dezimalstellen (Punkt oder Kommas)? Gibt es Leerzeichen zwischen der +/- und der Nummer? VB ist ein wenig besser im Umgang mit verschiedenen Formaten für Zahlen. – jdweng

+0

Klar, ich benutze den GAC nicht, ich rieche eine Assembly.LoadFile() Ratte. –

Antwort

1

TLDR Version:

Das System kann die Montage nicht gefunden werden. Die Lösung besteht darin, einen Handler für das Ereignis AppDomain.CurrentDomain.AssemblyResolve hinzuzufügen und einen Verweis auf die benötigte Assembly zurückzugeben.

Lange Version:

Der Stack-Trace endet mit System.Runtime.Serialization.Formatters.Binary.BinaryAssemblyInfo.GetAssembly(). Wenn Sie die Methodenaufrufe im Quellcode etwas weiter unten in der Kette verfolgen, werden Sie sehen, dass dieser folgende Code aufgerufen wird.

internal static Assembly LoadAssemblyFromString(String assemblyName) { 
     // 
     // Try using the stringized assembly name to load from the fusion cache. 
     // 
     BCLDebug.Trace("SER", "[LoadAssemblyFromString]Looking for assembly: ", assemblyName); 
     Assembly found = Assembly.Load(assemblyName); 
     return found; 
    } 

Das Fusionsprotokoll dieses Aufrufs ist:

*** Assembly Binder Log Entry (14/06/2017 @ 15:29:50) *** 

The operation failed. 
Bind result: hr = 0x80070002. The system cannot find the file specified. 

Assembly manager loaded from: C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll 
Running under executable C:\Program Files (x86)\Microsoft Visual Studio\VB98\vb6.exe 
--- A detailed error log follows. 

=== Pre-bind state information === 
LOG: DisplayName = BarcodeAndOperatorDatabase, Version=1.0.4.0, Culture=neutral, PublicKeyToken=null 
(Fully-specified) 
LOG: Appbase = file:///C:/Program Files (x86)/Microsoft Visual Studio/VB98/ 
LOG: Initial PrivatePath = NULL 
LOG: Dynamic Base = NULL 
LOG: Cache Base = NULL 
LOG: AppName = vb6.exe 
Calling assembly : (Unknown). 
=== 
LOG: This bind starts in default load context. 
LOG: No application configuration file found. 
LOG: Using host configuration file: 
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config. 
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind). 
LOG: The same bind was seen before, and was failed with hr = 0x80070002. 
ERR: Unrecoverable error occurred during pre-download check (hr = 0x80070002). 

Das Problem ist, dass das System das Verzeichnis unter ‚Appbase` (C aufgeführt wird Sondieren: \ Program Files (x86) \ Microsoft Visual Studio \ VB98) und da die erforderliche Assembly nicht unter diesem Verzeichnis liegt, wird sie nicht gefunden.

Wenn COM zuerst die CLR lädt und die CLR die Assembly lädt, basiert sie auf dem Pfad, der in der Registrierung gespeichert ist. Dies ist aus diesem Protokolleintrag ersichtlich.

=== Pre-bind state information === 
LOG: Where-ref bind. Location = D:/Development/Library/C Sharp/BarcodeAndOperatorDatabase/BarcodeAndOperatorDatabase/bin/x86/ComInterop/BarcodeAndOperatorDatabase.dll 
LOG: Appbase = file:///C:/Program Files (x86)/Microsoft Visual Studio/VB98/ 
LOG: Initial PrivatePath = NULL 
LOG: Dynamic Base = NULL 
LOG: Cache Base = NULL 
LOG: AppName = vb6.exe 
Calling assembly : (Unknown). 
=== 
LOG: This bind starts in LoadFrom load context. 
WRN: Native image will not be probed in LoadFrom context. Native image will only be probed in default load context, like with Assembly.Load(). 
LOG: No application configuration file found. 
LOG: Using host configuration file: 
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config. 
LOG: Attempting download of new URL file:///D:/Development/Library/C Sharp/BarcodeAndOperatorDatabase/BarcodeAndOperatorDatabase/bin/x86/ComInterop/BarcodeAndOperatorDatabase.dll. 
LOG: Assembly download was successful. Attempting setup of file: D:\Development\Library\C Sharp\BarcodeAndOperatorDatabase\BarcodeAndOperatorDatabase\bin\x86\ComInterop\BarcodeAndOperatorDatabase.dll 
LOG: Entering run-from-source setup phase. 
LOG: Assembly Name is: BarcodeAndOperatorDatabase, Version=1.0.4.0, Culture=neutral, PublicKeyToken=null 
LOG: Re-apply policy for where-ref bind. 
LOG: Where-ref bind Codebase does not match what is found in default context. Keep the result in LoadFrom context. 
LOG: Binding succeeds. Returns assembly from D:\Development\Library\C Sharp\BarcodeAndOperatorDatabase\BarcodeAndOperatorDatabase\bin\x86\ComInterop\BarcodeAndOperatorDatabase.dll. 
LOG: Assembly is loaded in LoadFrom load context. 

Zunächst besteht die Baugruppe nur im LoadFrom load context und kann nicht gefunden werden, wenn die default load context Sondieren. Es ist daher notwendig, ein Mittel bereitzustellen, um die LoadFrom Baugruppe zu übergeben, während die geprüft wird. Dies kann durch Behandeln des AppDomain.AssemblyResolve-Ereignisses erfolgen.

Der folgende Auszug aus: Best Practices for Assembly Loading liefert eine Beschreibung der verschiedenen Kontexte.

Innerhalb einer Anwendungsdomäne können Baugruppen in einem der drei Kontexte geladen werden, oder sie können ohne Kontext geladen werden:

Der Kontext Standardlade enthält durch Sondieren der Global Assembly Cache, gefunden Baugruppen die Host-Assembly-Speicher, wenn die Laufzeit gehostet wird (z. B. in SQL Server) und die ApplicationBase und PrivateBinPath der Anwendungsdomäne. Die meisten Überladungen der Methode Load laden Baugruppen in diesen Kontext.

Der load-from-Kontext enthält Baugruppen, die aus Speicherorten geladen werden, die nicht vom Loader durchsucht werden. Beispielsweise können Add-Ins in einem Verzeichnis installiert werden, das sich nicht unter dem Pfad der Anwendung befindet. System.Reflection.Assembly.LoadFrom, System.AppDomain.CreateInstanceFrom und System.AppDomain.ExecuteAssembly sind Beispiele von Methoden, die von Pfad geladen werden.

Der reflection-only-Kontext enthält Baugruppen, die mit den ReflectionOnlyLoad- und ReflectionOnlyLoadFrom-Methoden geladen werden. Code in diesem Kontext kann nicht ausgeführt werden, so dass es hier nicht diskutiert wird. Weitere Informationen finden Sie unter Vorgehensweise: Laden von Baugruppen in den Nur-Reflektions-Kontext .

Verwandte Themen