我必须更新一些旧的应用程序(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;
}
由于某种原因,在调用Deserialize函数时代码出错。它抛出的异常是无法找到代码所在的程序集。
无法找到程序集'AssemblyBeingUsed,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null'
这有点令我感到困惑,因为已经访问了程序集,并且说它无法找到。格式化错误消息的代码是同一程序集的一部分!
以下是异常的堆栈跟踪。
在System.Runtime.Serialization.Formatters.Binary.BinaryAssemblyInfo.GetAssembly() at System.Runtime.Serialization.Formatters.Binary.ObjectReader.GetType(BinaryAssemblyInfo assemblyInfo,String name) at System.Runtime.Serialization.Formatters.Binary.ObjectMap..ctor(String objectName,String [] memberNames,BinaryTypeEnum [] binaryTypeEnumA,Object [] typeInformationA,Int32 [] memberAssemIds,ObjectReader objectReader,Int32 objectId,BinaryAssemblyInfo assemblyInfo,SizedArray assemIdToAssemblyTable ) 在System.Runtime.Serialization.Formatters.Binary .__ BinaryParser.ReadObjectWithMapTyped(BinaryObjectWithMapTyped record) 在System.Runtime.Serialization.Formatters.Binary .__ BinaryParser.ReadObjectWithMapTyped(BinaryHeaderEnum binaryHeaderEnum) 在System.Runtime.Serialization.Formatters.Binary .__ BinaryParser.Run() 在System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler,__BinaryParser serParser,Boolean fCheck,Boolean isCrossAppDomain,IMethodCallMessage methodCallMessage) 在System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream,HeaderHandler handler,Boolean fCheck,Boolean isCrossAppDomain,IMethodCallMessage methodCallMessage)
修改:更有用的信息。
永远不会直接从vb6调用此函数。因为它使用泛型,我很确定无论如何这都是不可能的。从从vb6应用程序加载的表单调用此函数。当从c#应用程序使用此表单时,即使它执行完全相同的操作也没有问题。
Visual Studio项目使用“Register for COM interop”选项,并将程序集作为参考加载到vb6项目中。
修改:来自fuslogvw.exe的输出
fuslogvw.exe的输出显示了与我感兴趣的程序集相关的5个条目(BarcodeAndOperatorDatabase)。由于它们合并很长时间我已将所有输出上传到 this file
说实话,我不确定我在看什么。当组装绑定发生时,我认为有3个操作对应:
15:29:06:VB6应用程序启动,操作成功。15:29:14(2个条目):表格从装配加载(我认为),操作失败。这有点令人困惑,因为表单正确加载并且可以与之交互。
15:29:50(2个条目):调用CloneObject方法时按钮单击失败,操作失败。
答案 0 :(得分:1)
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).
问题是系统正在探测&#39; 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。 Load的大多数重载 方法将程序集加载到此上下文中。
load-from上下文包含从中加载的程序集 加载程序未搜索的位置。例如,加载项 可能安装在不在应用程序下的目录中 路径。 System.Reflection.Assembly.LoadFrom, System.AppDomain.CreateInstanceFrom和 System.AppDomain.ExecuteAssembly是加载方法的示例 路径。
仅反射上下文包含使用 ReflectionOnlyLoad和ReflectionOnlyLoadFrom方法。代码在此 上下文无法执行,因此这里不再讨论。更多 信息,请参见如何:将装配加载到仅反射中 上下文。