我必须更新一些旧应用程序(vb6),并且一直在使用COM interop在c#(Visual Studio 2010)中编写新代码。它通常可以正常工作,但是我遇到了一个问题,我不确定是什么原因引起的。

我使用以下方法执行对象的深层复制

    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;
    }


由于某种原因,在调用反序列化函数时,代码会出错。它引发的异常是找不到代码所在的程序集。


  找不到程序集'AssemblyBeingUsed,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null'


这使我有些困惑,因为该程序集已经被访问,并且它说找不到该程序集。格式化错误消息的代码是同一程序集的一部分!

这是异常的堆栈跟踪。


  在System.Runtime.Serialization.Formatters.Binary.BinaryAssemblyInfo.GetAssembly()处
     在System.Runtime.Serialization.Formatters.Binary.ObjectReader.GetType(BinaryAssemblyInfo assemblyInfo,字符串名称)
     在System.Runtime.Serialization.Formatters.Binary.ObjectMap..ctor处(String objectName,String [] memberNames,BinaryTypeEnum [] binaryTypeEnumA,Object [] typeInformationA,Int32 [] memberAssemIds,ObjectReader objectReader,Int32 objectId,BinaryAssemblyInfo AssemblyInfo,TableizedToTable )
     在System.Runtime.Serialization.Formatters.Binary .__ BinaryParser.ReadObjectWithMapTyped(BinaryObjectWithMapTyped记录)处
     在System.Runtime.Serialization.Formatters.Binary .__ BinaryParser.ReadObjectWithMapTyped(BinaryHeaderEnum binaryHeaderEnum)中
     在System.Runtime.Serialization.Formatters.Binary .__ BinaryParser.Run()处
     在System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize处(HeaderHandler处理程序,__ BinaryParser serParser,布尔值fCheck,布尔值isCrossAppDomain,IMethodCallMessage methodCallMessage)
     在System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize处(流serializationStream,HeaderHandler处理程序,布尔fCheck,布尔isCrossAppDomain,IMethodCallMessage methodCallMessage)


编辑:更多有用的信息。

从不直接从vb6调用此函数。因为它使用泛型,所以我非常确定这将是不可能的。从从vb6应用程序加载的表单中调用此函数。当从C#应用程序使用此表单时,即使它做的完全相同,也没有问题。

Visual Studio项目使用“注册COM互操作”选项,并且程序集作为参考加载到vb6项目中。

编辑:从fuslogvw.exe输出

fuslogvw.exe的输出显示5个与我感兴趣的程序集(BarcodeAndOperatorDatabase)相关的条目。由于合并在一起的时间很长,我已将所有输出上传到
this file

老实说,我不确定我在看什么。我认为与程序集绑定发生时对应的3种操作:

15:29:06:VB6应用程序启动,操作成功。

15:29:14(2个条目):从程序集加载的表单(我认为),操作失败。表单正确加载并可以交互时,这有点令人困惑。

15:29:50(2个条目):调用CloneObject方法时,单击按钮失败,操作失败。

最佳答案

TLDR版本:

系统找不到组件。解决方案是为AppDomain.CurrentDomain.AssemblyResolve事件添加处理程序,并返回对所需程序集的引用。

长版:

堆栈跟踪以System.Runtime.Serialization.Formatters.Binary.BinaryAssemblyInfo.GetAssembly()结尾。如果您在源代码中更进一步地遵循方法调用,那么您将看到以下代码被调用。

    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;
    }


该调用的融合日志为:

*** 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).


问题在于系统正在探测“ Appbase”下列出的目录(C:\ Program Files(x86)\ Microsoft Visual Studio \ VB98),并且由于所需的程序集不在该目录下,因此找不到该程序集。

最初,当COM加载CLR和CLR加载程序集时,它基于存储在注册表中的路径。从此日志条目中可以看出。

=== 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.


最初,程序集仅存在于LoadFrom load context中,而在探测default load context时找不到。因此,有必要提供一种在探测LoadFrom时通过default load context组件的方法。这可以通过处理AppDomain.AssemblyResolve事件来完成。

以下摘录自以下内容:Best Practices for Assembly Loading提供了各种上下文的描述。


  在应用程序域内,程序集可以加载到以下其中一个
  三个上下文,或者可以在没有上下文的情况下加载它们:
  
  默认的加载上下文包含通过探测
  全局程序集缓存,如果运行时为
  托管(例如,在SQL Server中),以及ApplicationBase和
  应用程序域的PrivateBinPath。负载的大多数过载
  方法将程序集加载到此上下文中。
  
  加载源上下文包含从中加载的程序集
  加载程序未搜索的位置。例如,加载项
  可能安装在不在应用程序下的目录中
  路径。 System.Reflection.Assembly.LoadFrom,
  System.AppDomain.CreateInstanceFrom和
  System.AppDomain.ExecuteAssembly是通过以下方式加载的方法的示例
  路径。
  
  仅反射上下文包含使用
  ReflectionOnlyLoad和ReflectionOnlyLoadFrom方法。此代码
  上下文无法执行,因此这里不讨论。欲了解更多
  信息,请参见如何:仅将组件加载到反射中
  上下文。

关于c# - 通过COM互操作使用BinaryFormatter反序列化时会引发异常,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/44526617/

10-13 07:47
查看更多