.NET AppDomain无法附加AssemblyResolve事件的问题求助
问题:跨AppDomain绑定AssemblyResolve事件时类型解析失败
程序结构
- VB6 可执行程序(主调用端,通过COM调用testpi.dll)
- VB.NET 编写的
testpi.dll(COM兼容,编译为x86架构) - C# 编写的
PlugIn1.dll(被testpi.dll引用)
PlugIn1.dll(C#代码)
using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Web; using CommonInterface; namespace PlugIn1 { [Serializable] public class PlugIn : MarshalByRefObject, IPlugIn { private Dictionary<string, RuleHandle> dHandleRules = new Dictionary<string, RuleHandle>(); private string name; public string Name { get { return name; } } public void Initialize() { name = "PlugIn 1"; } public object GetHandle(string Name, ref string msgErr) { msgErr = ""; try { if (String.IsNullOrEmpty(Name)) return null; if (dHandleRules == null) return null; dHandleRules.TryGetValue(Name, out var handle); return handle; } catch (Exception ex) { msgErr = Error(ex, "PlugIn.LoadAssembly"); } return null; } public object LoadAssembly(string sInput, string RuleName, string sPrefixClassName, ref string msgErr) { RuleHandle hR = null; msgErr = ""; try { if (System.IO.File.Exists(sInput)) { Byte[] rawAssembly = LoadFile(sInput); Assembly assemblyr = Assembly.Load(rawAssembly); Type TypeR = assemblyr.GetType("MyScript." + sPrefixClassName + RuleName); Object instance = Activator.CreateInstance(TypeR); hR = new RuleHandle(TypeR, instance); if (dHandleRules == null) dHandleRules = new Dictionary<string, RuleHandle>(); if (dHandleRules.ContainsKey(RuleName)) dHandleRules.Remove(RuleName); dHandleRules.Add(RuleName, hR); } else { msgErr = "FILE NON TROVATO:" + sInput; } } catch (Exception ex) { msgErr=Error(ex, "PlugIn.LoadAssembly"); } return hR; } private byte[] LoadFile(String filename) { Byte[] buffer = File.ReadAllBytes(filename); return buffer; } private string Error(Exception ex,string procedure) { return procedure + "\r\n" + ex.Message + "\r\n" + ex.StackTrace.ToString(); } public void FinalizeInterface() { if (dHandleRules != null) dHandleRules.Clear(); dHandleRules = null; } ~PlugIn() { FinalizeInterface(); } } }
VB6 按钮点击事件代码
Private Sub Command1_Click() Dim obj As Object Set obj = New testpi.tClass1 obj.TESTClick End Sub
testpi.dll(VB.NET代码)
Imports System.Reflection <Serializable> Public Class tClass1 Public Sub New() End Sub Protected Overrides Sub Finalize() MyBase.Finalize() End Sub Public sub TESTClick() Dim setup As AppDomainSetup = New AppDomainSetup() With { .ApplicationName = "PlugIn1", .ConfigurationFile = "PlugIn1" & ".dll.config", .ApplicationBase = AppDomain.CurrentDomain.BaseDirectory } Dim appDm As AppDomain = AppDomain.CreateDomain(setup.ApplicationName, AppDomain.CurrentDomain.Evidence, setup) AddHandler appDm.AssemblyResolve, AddressOf AssemblyResolve End sub End Class
触发的错误
unresolved type for member testpi.tclass1, testpi, version 1.0.0.0, Culture neutral PublicKeyToken = null
解决方案
问题根源
AssemblyResolve事件的处理方法属于testpi.tClass1类型,绑定事件时需要将该方法的委托跨AppDomain传递。当前tClass1仅标记了Serializable但未继承MarshalByRefObject,.NET会尝试将整个tClass1实例序列化复制到新AppDomain,而新域无法正确解析tClass1的类型信息,导致错误。
具体修复步骤
修改
tClass1继承MarshalByRefObject
让tClass1继承MarshalByRefObject,这样跨AppDomain传递的是对象代理而非完整副本,避免序列化问题:Imports System.Reflection Imports System.IO ' 需添加此引用用于文件路径处理 <Serializable> Public Class tClass1 Inherits MarshalByRefObject ' 新增继承 Public Sub New() End Sub Protected Overrides Sub Finalize() MyBase.Finalize() End Sub Public Sub TESTClick() Dim setup As AppDomainSetup = New AppDomainSetup() With { .ApplicationName = "PlugIn1", .ConfigurationFile = "PlugIn1" & ".dll.config", .ApplicationBase = AppDomain.CurrentDomain.BaseDirectory } Dim appDm As AppDomain = AppDomain.CreateDomain(setup.ApplicationName, AppDomain.CurrentDomain.Evidence, setup) AddHandler appDm.AssemblyResolve, AddressOf AssemblyResolve End sub ' 必须补全AssemblyResolve的实现,否则绑定事件无意义 Private Function AssemblyResolve(sender As Object, args As ResolveEventArgs) As Assembly ' 示例:从当前目录加载目标程序集 Dim assemblyFileName = $"{args.Name.Split(",")(0)}.dll" Dim assemblyPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, assemblyFileName) If File.Exists(assemblyPath) Then Return Assembly.LoadFrom(assemblyPath) End If Return Nothing End Function End Class确保新AppDomain的程序集访问路径
确认AppDomainSetup.ApplicationBase指向包含testpi.dll和PlugIn1.dll的目录,当前代码设置为当前域的BaseDirectory,一般无需修改;若DLL在其他路径,需调整该参数。重新注册COM组件
修改testpi.dll后,以管理员身份运行命令行,执行regasm.exe /codebase testpi.dll重新注册组件,确保VB6能获取到最新版本的组件。
额外提示
- 跨AppDomain的事件绑定、回调场景,优先使用
MarshalByRefObject而非仅依赖Serializable,避免复杂类型的序列化问题。 AssemblyResolve方法必须有实际逻辑,否则遇到程序集加载失败时会抛出未处理异常。
内容的提问来源于stack exchange,提问作者pi314
相关产品推荐
相关产品推荐

