目的:是在TCP通讯的的接受消息触发时利用AppDomain程序域通过反射动态调用本地动态编译生成的一个dll,执行其中的一个方法。
问题现象:
public interface IRemoteInterface
{
object Invoke(string lcMethod, object[] Parameters);
}
/// <summary>
/// Factory class to create objects exposing IRemoteInterface
/// </summary>
public class RemoteLoaderFactory : MarshalByRefObject
{
private const BindingFlags bfi = BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance;
public RemoteLoaderFactory() { }
public IRemoteInterface Create(string assemblyFile, string typeName, object[] constructArgs)
{
return (IRemoteInterface)Activator.CreateInstanceFrom(
assemblyFile, typeName, false, bfi, null, constructArgs,
null, null, null).Unwrap();
}
}
问题1:当我自定义的RemoteLoaderFactory 不继承MarshalByRefObject时,
item._server.DataReceived += new ViewModel.NetWorkServer.LeafEvent.DataReceivedHandler(_server_OnReceive);
触发通讯_server_OnReceive TCP响应函数可以执行反射调用我的本地动态生成好的dll。但是 不继承MarshalByRefObject时,会导致AppDomain程序域无法卸载。
问题2:当我自定义的RemoteLoaderFactory 继承MarshalByRefObject时,
则在_server_OnReceive TCP响应函数执行反射调用我的本地动态生成好的dll时报错。
报错很奇怪:
`捕捉到 System.Runtime.Serialization.SerializationException
HResult=-2146233076
Message=程序集“DiagramDesigner, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null”中的类型“MainWindow”未标记为可序列化。
Source=mscorlib
StackTrace:
Server stack trace:
在 System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType type)
在 System.Runtime.Serialization.FormatterServices.GetSerializableMembers(Type type, StreamingContext context)
在 System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo()
在 System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder)
提示是MainWindow未标记为可序列化,但window是不可序列化的啊。
说明下,问题2继承MarshalByRefObject,不在tcp通讯中调用而在主线程界面按钮函数中调用是不会报这个错误的。
请问我的问题在哪里,继承MarshalByRefObject的区别和在通讯响应函数中动态调用继承MarshalByRefObject的注意点。
1..net程序启动的时候会默认创建一个AppDomain,例如是App1。
2.你在App1里面去创建了新的AppDomain例如是App2。
3.RemoteLoaderFactory的实例会在App1和App2产生代码隔离。
4.例如你在App1里创建了RemoteLoaderFactory的实例:RemoteLoaderFactory1。
5.如果你的RemoteLoaderFactory继承了MarshalByRefObject说明对RemoteLoaderFactory实例在App1和App2之间是使用“按引用封送”,换句话说也就是说不管你在App1还是App2调用的都是RemoteLoaderFactory1本身.反之如果没有继承那么就是另外一种“按值封送”,这种情况下你其实实在App2创建了RemoteLoaderFactory1的副本RemoteLoaderFactory2。
6.那么从上面5得到结论,如果你最终使用的是RemoteLoaderFactory1反射创建MainWindow实例那么你的MainWindow就属于App1,反之则会用RemoteLoaderFactory2反射创建这个实例,那么你的MainWindow就属于App2。
7.同样的两个不同App1的MainWindow也会在App2产生代码隔离。
8.所以你在App2去调用RemoteLoaderFactory1创建的属于App1的MainWindow的势力实例的时候就会告诉你这个实例是跨应用程序域调用,如果想要调用的话就要打上[Serializable]特性。
所以你可以试试下面的方法在MainWindow上面打上Serializable标签:
[Serializable]
public partial class MainWindow : Window
但是请注意:这个Serializable是属于按值封送,也就是说你在App2里面对MainWindow 的任何操作都是对App1里面MainWindow的副本做操作,不会影响原先App1的实例。