using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using Pfz.Caching;
using Pfz.Extensions.DictionaryExtensions;
using Pfz.Extensions.DisposeExtensions;
using Pfz.Extensions.MonitorLockExtensions;
using Pfz.Extensions.StreamExtensions;
using Pfz.InterfaceWrapping.EventArguments;
using Pfz.Remoting.Internal;
using Pfz.Threading;
namespace Pfz.Remoting
{
/// <summary>
/// The main class for clients that depends on remote objects.
/// This class is able to connect to a remoting server, and accepts
/// bidirectional calls to the remote objects. The bidirectional calls
/// occur when the client pass a local object by interface to the server,
/// or when it registers itself into an event in a server object.
///
/// Remember that the remoting is only done to the interfaces.
/// </summary>
public sealed class RemotingClient:
ThreadSafeExceptionAwareDisposable
{
#region Private and internal fields
private static RemotingInterfaceWrapperGenerator fGenerator = new RemotingInterfaceWrapperGenerator();
private RemotingServer fOwner;
private object fActiveObjectsLock = new object();
private volatile Dictionary<long, object> fActiveObjects = new Dictionary<long, object>();
internal object fActiveWrappersWithEventsLock = new object();
internal volatile Dictionary<long, RemotingInterfaceWrapper> fActiveWrappersWithEvents = new Dictionary<long, RemotingInterfaceWrapper>();
private long fNextObjectId;
private bool fCanThrow;
private HashSet<Type> fForcedSerializations = new HashSet<Type>();
private IChanneller fChanneller;
private string fHost;
private int fPort;
private volatile Dictionary<Thread, ExceptionAwareStream> fChannelsByThread = new Dictionary<Thread, ExceptionAwareStream>();
private object fChannelsByThreadLock = new object();
private long fNextInvokeId;
private bool fIsRemoteUsingAsyncVoidCalls;
private EventWrapper fEventWrapper;
private EventHandler<GettingStreamEventArgs> fGettingStream;
private EventHandler<HandshakingEventArgs> fHandshaking;
internal ActionRunner<object> fRemoveDestroyedRemoteObjects = new ActionRunner<object>();
#endregion
#region CreateFromNamedPipeListener
/// <summary>
/// Creates a new RemotingClient connecting to the given NamedPipeListener.
/// Note that a NamedPipeServerStream is not a NamedPipeListener. Listener accepts
/// many connections.
/// </summary>
public static RemotingClient ConnectToNamedPipeListener(string pipeName)
{
if (pipeName == null)
throw new ArgumentNullException("pipeName");
Mutex mutex = null;
try
{
AbortSafe.Run(() => mutex = new Mutex(false, "_Pfz.Remoting.IpcClient" + pipeName.Replace('\\', '_')));
mutex.WaitOne();
try
{
string name = pipeName + "_Listener";
NamedPipeClientStream client = null;
try
{
AbortSafe.Run(() => client = new NamedPipeClientStream(".", name, PipeDirection.In, PipeOptions.WriteThrough));
client.Connect(60000);
var bytes = new byte[4];
client.FullRead(bytes);
int id = BitConverter.ToInt32(bytes, 0);
return new RemotingClient(pipeName + '_' + id, 0);
}
finally
{
client.CheckedDispose();
}
}
finally
{
mutex.ReleaseMutex();
}
}
finally
{
mutex.CheckedDispose();
}
}
#endregion
#region Constructors
#region Internal
internal RemotingClient(RemotingServer owner, object baseConnection, Stream baseStream, int buffersLength, EventHandler<GettingStreamEventArgs> gettingStream, Func<Stream, EventHandler<ChannelCreatedEventArgs>, IChanneller> newChanneller)
{
BaseConnection = baseConnection;
try
{
fOwner = owner;
MustUseAsyncVoidCalls = owner.MustUseAsyncVoidCalls;
MustDisposeAllOnConnectionLost = owner.MustDisposeAllOnConnectionLost;
fGettingStream = gettingStream;
Stream stream = baseStream;
if (gettingStream != null)
{
GettingStreamEventArgs args = new GettingStreamEventArgs();
args.Stream = stream;
gettingStream(this, args);
stream = args.Stream;
if (stream == null)
{
baseStream.Close();
return;
}
}
BinaryFormatter binaryFormatter = new BinaryFormatter();
ServerRemotingInitialization serverInitializationObject = new ServerRemotingInitialization();
serverInitializationObject.MustUseAsyncVoidCalls = MustUseAsyncVoidCalls;
serverInitializationObject.CryptographyMode = owner.CryptographyMode;
serverInitializationObject.AcceptedCryptographies = owner.fAcceptedCryptographiesDictionary;
serverInitializationObject.ForcedSerializations = owner.fForcedSerializationsDictionary;
if (baseStream.CanTimeout)
baseStream.WriteTimeout = 60000;
binaryFormatter.Serialize(stream, serverInitializationObject);
if (baseStream.CanTimeout)
{
baseStream.WriteTimeout = Timeout.Infinite;
baseStream.ReadTimeout = 60000;
}
var clientInitializationObject = (ClientRemotingInitialization)binaryFormatter.Deserialize(stream);
if (baseStream.CanTimeout)
baseStream.ReadTimeout = Timeout.Infinite;
fIsRemoteUsingAsyncVoidCalls = clientInitializationObject.MustUseAsyncVoidCalls;
var cryptographyType = clientInitializationObject.Cryptography;
if (cryptographyType != null)
{
if (owner.CryptographyMode == CryptographyMode.Forbidden)
{
baseStream.Close();
return;
}
var validCryptographies = owner.fAcceptedCryptographies;
if (validCryptographies != null)
{
if (!owner.fAcceptedCryptographies.Contains(cryptographyType))
{
baseStream.Close();
return;
}
}
Cryptography = (SymmetricAlgorithm)cryptographyType.GetConstructor(Type.EmptyTypes).Invoke(null);
stream = new SecureStream(stream, new RSACryptoServiceProvider(), Cryptography, true);
}
else
{
if (owner.CryptographyMode == CryptographyMode.Required)
{
baseStream.Close();
return;
}
}
owner.DisposeLock.LockWithTimeout
(
delegate
{
// if the server was disposed just between the accept and this
// line, we simple close the connection and ignore the
// construction of this object.
// So, this will be collected when possible and will not throw
// an unnecessary exception (especially if this happens
// while stopping a service, an exception must be avoided).
if (owner.WasDisposed)
{
baseStream.Close();
return;
}
fForcedSerializations = owner.fForcedSerializations;
fEventWrapper = new EventWrapper(this);
fChanneller = newChanneller(stream, p_RemoteChannelCreated);
fChanneller.Disposed += new EventHandler(p_Channeller_Disposed);
fEventWrapper.OnInvoke += p_EventInvoke;
GCUtils.Collected += p_Collected;
var clients = owner.fClients;
clients.LockWithTimeout
(
() => clients.Add(this)
);
owner.i_RegisterForEvents(this);
}
);
}
catch(Exception exception)
{
baseStream.Close();
Dispose(exception);
}
}
#endregion
#region Public
/// <summary>
/// Creates the remoting client, connecting to the specified host and port.
/// </summary>
/// <param name="host">The server host.</param>
/// <param name="port">The server port. If 0 is used, the server host will be treated as a named-pipe.</param>
public RemotingClient(string host, int port):
this(new RemotingParameters(host, port))
{
}
/// <summary>
/// Creates the remoting client, connecting to the specified host and port,
/// and allows you to tell if the reconnections will be automatic.
/// </summary>
/// <param name="parameters">The parameters used to initialize this remoting client.</param>
public RemotingClient(RemotingParameters parameters)
{
if (parameters == null)
throw new ArgumentNullException("parameters");
CanTryToReconnectAfterConnectionLost = parameters.CanTryToReconnectAfterConnectionLost;
fGettingStream = parameters.fGettingStream;
Cryptography = parameters.Cryptography;
fHandshaking = parameters.fHandshaking;
fHost = parameters.Host;
fPort = parameters.Port;
fCanThrow = true;
fEventWrapper = new EventWrapper(this);
fEventWrapper.OnInvoke += p_EventInvoke;
try
{
p_CreateConnection();
}
catch
{
if (!CanTryToReconnectAfterConnectionLost)
throw;
}
GCUtils.Collected += p_Collected;
}
#endregion
#endregion
#region Destructors
/// <summary>
/// Event invoked when the object is being disposed.
/// </summary>
public event EventHandler Disposed;
#region Dispose
/// <summary>
/// Closes the tcp/connection required by this remoting object and makes
/// the object unusable.
/// </summary>
/// <param name="disposing">True if invoked from Dispose() and false if invoked from destructor.</param>
protected override void Dispose(bool disposing)
{
if (disposing)
{
GCUtils.Collected -= p_Collected;
var baseConnection = BaseConnection as IDisposable;
if (baseConnection != null)
{
BaseConnection = null;
baseConnection.Dispose();
}
var removeDestroyedRemoteObject = fRemoveDestroyedRemoteObjects;
if (removeDestroyedRemoteObject != null)
{
fRemoveDestroyedRemoteObjects = null;
removeDestroyedRemoteObject.Dispose();
}
List<object> eventWrappers = fEventWrappers.ToList();
fActiveObjectsLock.LockWithTimeout
(
delegate
{
var activeObjects = fActiveObjects;
foreach(object wrapperObj in eventWrappers)
{
Type type = wrapperObj.GetType();
long objectId = (long)type.GetField("__Object__Id").GetValue(wrapperObj);
object obj;
if (activeObjects.TryGetValue(objectId, out obj))
{
EventInfo eventInfo = (EventInfo)type.GetField("__SourceEventInfo").GetValue(wrapperObj);
Delegate delegat = Delegate.CreateDelegate(eventInfo.EventHandlerType, wrapperObj, type.GetMethod("ImplementedMethod"));
eventInfo.RemoveEventHandler(obj, delegat);
}
}
}
);
var channeller = fChanneller;
if (channeller != null)
{
fChanneller = null;
channeller.Dispose(DisposeException);
}
if (fOwner != null)
{
var clients = fOwner.fClients;
if (clients != null)
clients.LockWithTimeout
(
() => clients.Remove(this)
);
}
if (MustDisposeAllOnConnectionLost)
{
fActiveObjectsLock.LockWithTimeout
(
delegate
{
foreach(object obj in fActiveObjects.Values)
{
IDisposable disposable = obj as IDisposable;
if (disposable != null)
disposable.Dispose();
}
fActiveObjects.Clear();
}
);
}
}
base.Dispose(disposing);
if (disposing)
{
var disposedEvent = Disposed;
if (disposedEvent != null)
disposedEvent(this, EventArgs.Empty);
}
}
#endregion
#region p_Channeller_Disposed
private void p_Channeller_Disposed(object sender, EventArgs e)
{
var channeller = fChanneller;
if (channeller != null)
Dispose(channeller.DisposeException);
}
#endregion
#region p_Collected
private void p_Collected()
{
try
{
if (WasDisposed)
{
GCUtils.Collected -= p_Collected;
return;
}
AbortSafe.Lock
(
fActiveObjectsLock,
() => fActiveObjects = new Dictionary<long, object>(fActiveObjects)
);
AbortSafe.Lock
(
fActiveWrappersWithEventsLock,
() => fActiveWrappersWithEvents = new Dictionary<long, RemotingInterfaceWrapper>(fActiveWrappersWithEvents)
);
AbortSafe.Lock
(
fChannelsByThreadLock,
delegate
{
var oldChannelsByThread = fChannelsByThread;
var newChannelsByThread = new Dictionary<Thread, ExceptionAwareStream>();
foreach (var pair in oldChannelsByThread)
if (pair.Key.IsAlive)
newChannelsByThread.Add(pair.Key, pair.Value);
fChannelsByThread = newChannelsByThread;
}
);
}
catch
{
}
}
#endregion
#endregion
#region Properties
#region MustUseAsyncVoidCalls
/// <summary>
/// Set this property to true if you want to call voids asynchronously.
/// This also avoids receiving exceptions from such calls.
/// </summary>
public bool MustUseAsyncVoidCalls { get; set; }
#endregion
#region MustDisposeAllOnConnectionLost
/// <summary>
/// Gets or sets a property indicating that objects that were remoted
/// must be disposed when the connection is lost.
/// </summary>
public bool MustDisposeAllOnConnectionLost { get; set; }
#endregion
#region CanTryToReconnectAfterConnectionLost
/// <summary>
/// If true, the object will automatically try to reconnect when the connection
/// is lost. If false, the object will become unusable when the connection is lost.
/// </summary>
public bool CanTryToReconnectAfterConnectionLost { get; internal set; }
#endregion
#region RemotingCryptography
/// <summary>
/// Gets the remoting cryptography being used.
/// </summary>
public SymmetricAlgorithm Cryptography { get; private set; }
#endregion
#region BaseConnection
/// <summary>
/// Gets the object that represents the real connection of this
/// remoting client. This can be a DuplexStream of NamedPipes
/// or a TcpClient.
/// </summary>
public object BaseConnection { get; private set; }
#endregion
#endregion
#region Methods
#region GetFromRemoteObject
/// <summary>
/// Gets the RemotingClient that created the actual remote-object
/// wrapper, or null if this object is not remote.
/// </summary>
public static RemotingClient GetFromRemoteObject(object remoteObject)
{
RemotingInterfaceWrapper wrapper = remoteObject as RemotingInterfaceWrapper;
if (wrapper == null)
return null;
return wrapper.fClient;
}
#endregion
#region Create<T>
/// <summary>
/// Creates an object on the server, registered as the default implementor
/// of the specified T interface.
/// </summary>
/// <typeparam name="T">The interface to create a remote object for.</typeparam>
/// <param name="parameters">The constructor parameters</param>
/// <returns>An object that implements the specified interface.</returns>
public T Create<T>(params object[] parameters)
{
return (T)Create(typeof(T).FullName, parameters);
}
#endregion
#region Create
/// <summary>
/// Creates a remote object by the specified name and parameters.
/// </summary>
/// <param name="serverObjectName">The name of the registered server type.</param>
/// <param name="parameters">The constructor parameters to create the object.</param>
/// <returns>An object with all the interfaces implemented by the remote object, as redirectors to that real object.</returns>
// [DebuggerHidden]
public object Create(string serverObjectName, params object[] parameters)
{
int count = parameters.Length;
for (int i=0; i<count; i++)
parameters[i] = i_GenerateWrapperReference(typeof(object), parameters[i]);
RemotingStaticInvoke invoke = new RemotingStaticInvoke();
invoke.Name = serverObjectName;
invoke.Parameters = parameters;
return i_Invoke(invoke, InvokeType.Constructor);
}
#endregion
#region InvokeStaticMethod
/// <summary>
/// Invokes an static method registered on the server.
/// </summary>
/// <param name="methodName">The registered method name.</param>
/// <param name="parameters">The parameters needed by the method.</param>
/// <returns>The possible result of such method.</returns>
// [DebuggerHidden]
public object InvokeStaticMethod(string methodName, params object[] parameters)
{
int count = parameters.Length;
for (int i=0; i<count; i++)
parameters[i] = i_GenerateWrapperReference(typeof(object), parameters[i]);
RemotingStaticInvoke invoke = new RemotingStaticInvoke();
invoke.Name = methodName;
invoke.Parameters = parameters;
return i_Invoke(invoke, InvokeType.StaticMethod);
}
#endregion
#region CreateUserChannel
#region UserChannelNullData - Nested Type
[Serializable]
private sealed class UserChannelNullData
{
}
#endregion
/// <summary>
/// Creates a new channel inside the tcp/ip connection used by this
/// client, but does not controls it. It's free to be used by the user.
/// </summary>
/// <returns>A new channel that must be used by the user.</returns>
public ExceptionAwareStream CreateUserChannel()
{
return CreateUserChannel(null);
}
/// <summary>
/// Creates a new channel inside the tcp/ip connection used by this
/// client, but does not controls it. It's free to be used by the user.
/// </summary>
/// <param name="serializableData">Data to send during channel creation. Must be serializable.</param>
/// <returns>A new channel that must be used by the user.</returns>
public ExceptionAwareStream CreateUserChannel(object serializableData)
{
if (serializableData == null)
serializableData = new UserChannelNullData();
return fChanneller.CreateChannel(serializableData);
}
#endregion
#region p_RemoteChannelCreated
private void p_RemoteChannelCreated(object sender, ChannelCreatedEventArgs args)
{
if (args.Data != null)
{
if (args.Data is UserChannelNullData)
args.Data = null;
var userChannelCreated = UserChannelCreated;
if (userChannelCreated != null)
userChannelCreated(this, args);
return;
}
p_RunAsServer(args.Channel);
}
#endregion
#region p_RunAsServer
private void p_RunAsServer(ExceptionAwareStream channel)
{
try
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
fChannelsByThreadLock.LockWithTimeout
(
() => fChannelsByThread[Thread.CurrentThread] = channel
);
while(true)
{
object deserialized;
try
{
deserialized = binaryFormatter.Deserialize(channel);
}
catch(Exception exception)
{
fChannelsByThreadLock.LockWithTimeout
(
() => fChannelsByThread.Remove(Thread.CurrentThread)
);
channel.Dispose(exception);
return;
}
RemotingInvoke invoke = (RemotingInvoke)deserialized;
p_ProcessInvoke(invoke);
}
}
catch(Exception exception)
{
if (!WasDisposed)
{
if (!CanTryToReconnectAfterConnectionLost)
Dispose(exception);
if (fCanThrow)
throw;
}
}
}
#endregion
#region p_ProcessInvoke
private void p_ProcessInvoke(RemotingInvoke invoke)
{
RemotingResult result = new RemotingResult();
result.InvokeId = invoke.InvokeId;
Type resultType = null;
try
{
switch (invoke.Type)
{
case InvokeType.Constructor:
resultType = p_InvokeConstructor(invoke, result);
break;
case InvokeType.Destructor:
p_InvokeDestructor(invoke);
break;
case InvokeType.Method:
if (p_InvokeMethod(invoke, result))
return;
break;
case InvokeType.StaticMethod:
p_InvokeStaticMethod(invoke, result);
resultType = typeof(object);
break;
case InvokeType.PropertyGet:
resultType = p_InvokePropertyGet(invoke, result, resultType);
break;
case InvokeType.PropertySet:
p_InvokePropertySet(invoke);
break;
case InvokeType.EventAdd:
p_InvokeEventAdd(invoke);
break;
case InvokeType.EventRemove:
p_InvokeEventRemove(invoke);
break;
case InvokeType.EventCall:
p_InvokeEventCall(invoke);
break;
case InvokeType.EventLost:
p_InvokeEventLost(invoke);
break;
default:
throw new RemotingException("Unknown invoke type.");
}
if (resultType != null)
result.Result = i_GenerateWrapperReference(resultType, result.Result);
}
catch (Exception caughtException)
{
Exception exception = caughtException;
if (exception is TargetInvocationException)
exception = exception.InnerException;
result.Exception = exception;
result.Result = null;
}
ExceptionAwareStream channel = null;
fChannelsByThreadLock.LockWithTimeout
(
() => channel = fChannelsByThread[Thread.CurrentThread]
);
try
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
channel.LockWithTimeout
(
() => binaryFormatter.Serialize(channel, result)
);
}
catch(Exception exception)
{
fChannelsByThreadLock.LockWithTimeout
(
() => fChannelsByThread.Remove(Thread.CurrentThread)
);
channel.Dispose(exception);
}
}
#endregion
#region p_EventInvoke
// [DebuggerHidden]
private void p_EventInvoke(EventInfo sourceEventInfo, long objectId, object[] parameters)
{
RemotingEventCallInvoke invoke = new RemotingEventCallInvoke();
invoke.ObjectId = objectId;
invoke.EventInfo = sourceEventInfo;
int count = parameters.Length;
ParameterInfo[] parameterInfos = sourceEventInfo.EventHandlerType.GetMethod("Invoke").GetParameters();
for (int i=0; i<count; i++)
parameters[i] = i_GenerateWrapperReference(parameterInfos[i].ParameterType, parameters[i]);
invoke.Parameters = parameters;
i_Invoke(invoke, InvokeType.EventCall);
}
#endregion
#region p_InvokeEventLost
private void p_InvokeEventLost(RemotingInvoke invoke)
{
RemotingEventInvoke eventInvoke = (RemotingEventInvoke)invoke;
RemotingInterfaceWrapper wrapper = null;
fActiveWrappersWithEventsLock.LockWithTimeout
(
() => fActiveWrappersWithEvents.TryGetValue(eventInvoke.ObjectId, out wrapper)
);
if (wrapper != null)
{
wrapper.fEventsLock.LockWithTimeout
(
delegate
{
var events = wrapper.fEvents;
if (events.Remove(eventInvoke.EventInfo))
{
if (events.Count == 0)
{
fActiveWrappersWithEventsLock.LockWithTimeout
(
() => fActiveWrappersWithEvents.Remove(eventInvoke.ObjectId)
);
}
}
}
);
}
}
#endregion
#region p_InvokeConstructor
private Type p_InvokeConstructor(RemotingInvoke invoke, RemotingResult result)
{
var constructorInvoke = (RemotingStaticInvoke)invoke;
p_Unwrap(constructorInvoke.Parameters);
Type type = fOwner.fRegisteredTypes[constructorInvoke.Name];
result.Result = Activator.CreateInstance(type, constructorInvoke.Parameters);
Type resultType = result.Result.GetType().GetInterfaces()[0];
return resultType;
}
#endregion
#region p_InvokeStaticMethod
private void p_InvokeStaticMethod(RemotingInvoke invoke, RemotingResult result)
{
var staticInvoke = (RemotingStaticInvoke)invoke;
p_Unwrap(staticInvoke.Parameters);
MethodInfo method = fOwner.fStaticMethods[staticInvoke.Name];
InvokeMethodEventArgs args = new InvokeMethodEventArgs();
args.MethodInfo = method;
args.Parameters = staticInvoke.Parameters;
try
{
if (BeforeInvokeLocalMethod != null)
BeforeInvokeLocalMethod(this, args);
if (args.CanInvoke)
args.Result = method.Invoke(null, args.Parameters);
}
catch (Exception exception)
{
args.Exception = exception;
}
finally
{
if (args.CanInvoke)
if (AfterInvokeLocalMethod != null)
AfterInvokeLocalMethod(this, args);
if (args.Exception != null)
throw args.Exception;
}
result.Result = args.Result;
}
#endregion
#region p_InvokeDestructor
private void p_InvokeDestructor(RemotingInvoke invoke)
{
var destructorInvoke = (RemotingDestructorInvoke)invoke;
if (destructorInvoke.Disposing)
{
object obj = null;
fActiveObjectsLock.LockWithTimeout
(
() => fActiveObjects.TryGetValue(destructorInvoke.ObjectId, out obj)
);
if (obj != null)
{
IDisposable disposable = obj as IDisposable;
if (disposable != null)
disposable.Dispose();
}
}
fActiveObjectsLock.LockWithTimeout
(
() => fActiveObjects.Remove(destructorInvoke.ObjectId)
);
}
#endregion
#region p_InvokeEventCall
private void p_InvokeEventCall(RemotingInvoke invoke)
{
var eventCall = (RemotingEventCallInvoke)invoke;
RemotingInterfaceWrapper eventCallWrapper = null;
fActiveWrappersWithEventsLock.LockWithTimeout
(
() => eventCallWrapper = fActiveWrappersWithEvents[eventCall.ObjectId]
);
if (eventCallWrapper != null)
{
HashSet<Delegate> hashset = null;
eventCallWrapper.fEventsLock.LockWithTimeout
(
() => eventCallWrapper.fEvents.TryGetValue(eventCall.EventInfo, out hashset)
);
p_Unwrap(eventCall.Parameters);
if (hashset != null)
{
hashset.LockWithTimeout
(
delegate
{
foreach (var item in hashset)
item.DynamicInvoke(eventCall.Parameters);
}
);
}
}
}
#endregion
#region p_InvokeEventAdd
private WeakList<object> fEventWrappers = new WeakList<object>();
private void p_InvokeEventAdd(RemotingInvoke invoke)
{
var eventAdd = (RemotingEventInvoke)invoke;
object eventAddObj = null;
fActiveObjectsLock.LockWithTimeout
(
() => eventAddObj = fActiveObjects[eventAdd.ObjectId]
);
InvokeEventEventArgs args = new InvokeEventEventArgs();
args.Target = eventAddObj;
args.EventInfo = eventAdd.EventInfo;
args.Handler = fEventWrapper.GetWrapper(eventAdd.EventInfo, eventAdd.ObjectId, fEventWrappers);
try
{
if (BeforeInvokeLocalEventAdd != null)
BeforeInvokeLocalEventAdd(this, args);
if (args.CanInvoke)
eventAdd.EventInfo.AddEventHandler(eventAddObj, args.Handler);
}
catch(Exception exception)
{
args.Exception = exception;
}
finally
{
if (args.CanInvoke)
if (AfterInvokeLocalEventAdd != null)
AfterInvokeLocalEventAdd(this, args);
if (args.Exception != null)
throw args.Exception;
}
}
#endregion
#region p_InvokeEventRemove
private void p_InvokeEventRemove(RemotingInvoke invoke)
{
var eventRemove = (RemotingEventInvoke)invoke;
object eventRemoveObj = null;
fActiveObjectsLock.LockWithTimeout
(
() => eventRemoveObj = fActiveObjects[eventRemove.ObjectId]
);
InvokeEventEventArgs args = new InvokeEventEventArgs();
args.Target = eventRemoveObj;
args.EventInfo = eventRemove.EventInfo;
args.Handler = fEventWrapper.GetWrapper(eventRemove.EventInfo, eventRemove.ObjectId, fEventWrappers);
try
{
if (BeforeInvokeLocalEventRemove != null)
BeforeInvokeLocalEventRemove(this, args);
if (args.CanInvoke)
{
eventRemove.EventInfo.RemoveEventHandler(eventRemoveObj, args.Handler);
IDisposable disposable = (IDisposable)args.Handler.Target as IDisposable;
disposable.Dispose();
}
}
catch(Exception exception)
{
args.Exception = exception;
}
finally
{
if (args.CanInvoke)
if (AfterInvokeLocalEventRemove != null)
AfterInvokeLocalEventRemove(this, args);
if (args.Exception != null)
throw args.Exception;
}
}
#endregion
#region p_InvokePropertyGet
private Type p_InvokePropertyGet(RemotingInvoke invoke, RemotingResult result, Type resultType)
{
var propertyGetInvoke = (RemotingPropertyGetInvoke)invoke;
object propertyGetObj = null;
fActiveObjectsLock.LockWithTimeout
(
() => propertyGetObj = fActiveObjects[propertyGetInvoke.ObjectId]
);
p_Unwrap(propertyGetInvoke.Index);
InvokePropertyEventArgs args = new InvokePropertyEventArgs();
args.Target = propertyGetObj;
args.PropertyInfo = propertyGetInvoke.PropertyInfo;
args.Index = propertyGetInvoke.Index;
try
{
if (BeforeInvokeLocalPropertyGet != null)
BeforeInvokeLocalPropertyGet(this, args);
if (args.CanInvoke)
args.Value = propertyGetInvoke.PropertyInfo.GetValue(propertyGetObj, propertyGetInvoke.Index);
}
catch(Exception exception)
{
args.Exception = exception;
}
finally
{
if (args.CanInvoke)
if (AfterInvokeLocalPropertyGet != null)
AfterInvokeLocalPropertyGet(this, args);
if (args.Exception != null)
throw args.Exception;
}
result.Result = args.Value;
resultType = propertyGetInvoke.PropertyInfo.PropertyType;
return resultType;
}
#endregion
#region p_InvokePropertySet
private void p_InvokePropertySet(RemotingInvoke invoke)
{
var propertySetInvoke = (RemotingPropertySetInvoke)invoke;
object propertySetObj = null;
fActiveObjectsLock.LockWithTimeout
(
() => propertySetObj = fActiveObjects[propertySetInvoke.ObjectId]
);
p_Unwrap(propertySetInvoke.Index);
InvokePropertyEventArgs args = new InvokePropertyEventArgs();
args.Target = propertySetObj;
args.PropertyInfo = propertySetInvoke.PropertyInfo;
args.Index = propertySetInvoke.Index;
args.Value = p_Unwrap(propertySetInvoke.Value);
try
{
if (BeforeInvokeLocalPropertySet != null)
BeforeInvokeLocalPropertySet(this, args);
if (args.CanInvoke)
propertySetInvoke.PropertyInfo.SetValue(propertySetObj, args.Value, args.Index);
}
catch(Exception exception)
{
args.Exception = exception;
}
finally
{
if (args.CanInvoke)
if (AfterInvokeLocalPropertySet != null)
AfterInvokeLocalPropertySet(this, args);
if (args.Exception != null)
throw args.Exception;
}
}
#endregion
#region p_InvokeMethod
private bool p_InvokeMethod(RemotingInvoke invoke, RemotingResult result)
{
var methodInvoke = (RemotingMethodInvoke)invoke;
if (methodInvoke.GenericArguments != null)
methodInvoke.MethodInfo = methodInvoke.MethodInfo.MakeGenericMethod(methodInvoke.GenericArguments);
object methodObj = null;
fActiveObjectsLock.LockWithTimeout
(
() => methodObj = fActiveObjects[methodInvoke.ObjectId]
);
List<object> refsAndOuts = new List<object>();
p_Unwrap(methodInvoke.Parameters);
InvokeMethodEventArgs args = new InvokeMethodEventArgs();
args.Target = methodObj;
args.MethodInfo = methodInvoke.MethodInfo;
args.Parameters = methodInvoke.Parameters;
try
{
if (BeforeInvokeLocalMethod != null)
BeforeInvokeLocalMethod(this, args);
if (args.CanInvoke)
args.Result = methodInvoke.MethodInfo.Invoke(methodObj, methodInvoke.Parameters);
}
catch (Exception exception)
{
args.Exception = exception;
}
finally
{
if (args.CanInvoke)
if (AfterInvokeLocalMethod != null)
AfterInvokeLocalMethod(this, args);
if (args.Exception != null)
throw args.Exception;
}
if (fIsRemoteUsingAsyncVoidCalls && i_CanBeAsync(methodInvoke.MethodInfo))
return true;
args.Result = i_GenerateWrapperReference(methodInvoke.MethodInfo.ReturnType, args.Result);
refsAndOuts.Add(args.Result);
p_GetRefAndOutResults(refsAndOuts, methodInvoke.MethodInfo.GetParameters(), methodInvoke.Parameters);
result.Result = refsAndOuts.ToArray();
return false;
}
#endregion
#region i_CanBeAsync
internal static bool i_CanBeAsync(MethodInfo method)
{
if (method.ReturnType != typeof(void))
return false;
foreach(ParameterInfo parameter in method.GetParameters())
if (parameter.IsOut || parameter.ParameterType.IsByRef)
return false;
return true;
}
#endregion
#region p_GetRefAndOutResults
private void p_GetRefAndOutResults(List<object> refsAndOuts, ParameterInfo[] parameterInfos, object[] sourceParameters)
{
int count = parameterInfos.Length;
for (int i=0; i<count; i++)
{
ParameterInfo parameterInfo = parameterInfos[i];
if (parameterInfo.ParameterType.IsByRef)
refsAndOuts.Add(i_GenerateWrapperReference(parameterInfo.ParameterType, sourceParameters[i]));
}
}
#endregion
#region i_GenerateWrapperReference
internal object i_GenerateWrapperReference(Type parameterType, object value)
{
if (value == null)
return null;
var array = value as object[];
if (array != null)
{
var type = array.GetType().GetElementType();
int count = array.Length;
ArrayReference ar = new ArrayReference();
ar.ElementType = type;
ar.Array = new object[count];
for(int i=0; i<count; i++)
ar.Array[i] = i_GenerateWrapperReference(type, array[i]);
return ar;
}
RemotingInterfaceWrapper riw = value as RemotingInterfaceWrapper;
if (riw != null && riw.fClient == this)
{
var restoreObjectFromId = new RestoreObjectFromId();
restoreObjectFromId.ObjectId = riw.fObjectId;
return restoreObjectFromId;
}
if (!parameterType.IsAssignableFrom(value.GetType()))
return value;
Type valueType = value.GetType();
if (!parameterType.IsInterface && parameterType.IsSerializable && valueType.IsSerializable)
return value;
foreach(Type forcedSerializationType in fForcedSerializations)
if (forcedSerializationType.IsAssignableFrom(valueType))
return value;
if (!parameterType.IsInterface && parameterType != typeof(object))
{
throw new RemotingException
(
"An object of type " + value.GetType().FullName + " cannot be " +
"serialized to the client because the object is not serializable " +
"and also cannot be sent by reference as it is expected as an " +
"specific type (" + parameterType.FullName + ").\r\n" +
"Try to use an interface as the expected type or making the " +
"object serializable."
);
}
long id = Interlocked.Increment(ref fNextObjectId);
fActiveObjectsLock.LockWithTimeout
(
() => fActiveObjects.Add(id, value)
);
WrapperReference wrapperReference = new WrapperReference();
wrapperReference.ObjectId = id;
// gets only the main interfaces (the base interfaces that are already part of those
// interfaces are removed).
Type[] interfaces = value.GetType().GetInterfaces();
HashSet<Type> interfaceTypes = new HashSet<Type>(interfaces);
foreach(Type interfaceType in interfaces)
{
if (interfaceType.IsNotPublic)
interfaceTypes.Remove(interfaceType);
Type[] baseInterfaces = interfaceType.GetInterfaces();
foreach(Type baseInterface in baseInterfaces)
interfaceTypes.Remove(baseInterface);
}
interfaceTypes.Remove(typeof(IDisposable));
interfaceTypes.Remove(typeof(IAdvancedDisposable));
wrapperReference.InterfaceTypes = interfaceTypes.ToArray();
return wrapperReference;
}
#endregion
#region p_Unwrap
private void p_Unwrap(object[] parameters)
{
if (parameters == null)
return;
int count = parameters.Length;
for (int i=0; i<count; i++)
parameters[i] = p_Unwrap(parameters[i]);
}
private object p_Unwrap(object value)
{
if (value == null)
return null;
var array = value as object[];
if (array != null)
{
p_Unwrap(array);
return array;
}
var arrayReference = value as ArrayReference;
if (arrayReference != null)
{
p_Unwrap(arrayReference.Array);
if (arrayReference.ElementType.Equals(typeof(object)))
return arrayReference.Array;
var result = Array.CreateInstance(arrayReference.ElementType, arrayReference.Array.Length);
arrayReference.Array.CopyTo(result, 0);
return result;
}
RestoreObjectFromId restorer = value as RestoreObjectFromId;
if (restorer != null)
{
object resultObject = null;
fActiveObjectsLock.LockWithTimeout
(
() => resultObject = fActiveObjects[restorer.ObjectId]
);
return resultObject;
}
WrapperReference wr = value as WrapperReference;
if (wr != null)
{
Type generatedType = fGenerator.Generate(wr.InterfaceTypes);
object created = generatedType.GetConstructor(Type.EmptyTypes).Invoke(null);
RemotingInterfaceWrapper wrapper = (RemotingInterfaceWrapper)created;
wrapper.fObjectId = wr.ObjectId;
wrapper.fClient = this;
return wrapper;
}
return value;
}
#endregion
#region i_DestroyingImplementedEvent
internal void i_DestroyingImplementedEvent(object sender, EventArgs e)
{
if (WasDisposed)
return;
fRemoveDestroyedRemoteObjects.Run(p_ImplementedEventDestroyed, sender);
}
#endregion
#region p_ImplementedEventDestroyed
private void p_ImplementedEventDestroyed(object state)
{
if (WasDisposed)
return;
try
{
Type type = state.GetType();
EventInfo eventInfo = (EventInfo)type.GetField("__SourceEventInfo").GetValue(state);
long objectId = (long)type.GetField("__Object__Id").GetValue(state);
RemotingEventInvoke invoke = new RemotingEventInvoke();
invoke.EventInfo = eventInfo;
invoke.ObjectId = objectId;
i_Invoke(invoke, InvokeType.EventLost);
}
catch
{
if (!WasDisposed && fCanThrow)
throw;
}
}
#endregion
#region i_Invoke
// [DebuggerHidden]
internal object i_Invoke(RemotingInvoke invoke, InvokeType type)
{
long invokeId = Interlocked.Increment(ref fNextInvokeId);
invoke.InvokeId = invokeId;
invoke.Type = type;
BinaryFormatter binaryFormatter = new BinaryFormatter();
var channeller = fChanneller;
if (channeller == null || channeller.WasDisposed)
{
CheckUndisposed();
if (!CanTryToReconnectAfterConnectionLost)
{
if (channeller != null)
channeller.CheckUndisposed();
throw new ObjectDisposedException(fChanneller.GetType().FullName, "Connection channeller was disposed.");
}
fChannelsByThreadLock.LockWithTimeout
(
() => fChannelsByThread.Remove(Thread.CurrentThread)
);
p_CreateConnection();
}
ExceptionAwareStream threadChannel = null;
fChannelsByThreadLock.LockWithTimeout
(
() => fChannelsByThread.TryGetValue(Thread.CurrentThread, out threadChannel)
);
if (threadChannel == null || threadChannel.WasDisposed)
{
threadChannel = fChanneller.CreateChannel();
fChannelsByThreadLock.LockWithTimeout
(
() => fChannelsByThread[Thread.CurrentThread] = threadChannel // adds or replaces the channel.
);
}
object deserialized;
try
{
binaryFormatter.Serialize(threadChannel, invoke);
while(true)
{
deserialized = binaryFormatter.Deserialize(threadChannel);
invoke = deserialized as RemotingInvoke;
if (invoke == null)
break;
p_ProcessInvoke(invoke);
}
}
catch(Exception exception)
{
threadChannel.Dispose(exception);
fChannelsByThreadLock.LockWithTimeout
(
() => fChannelsByThread.Remove(Thread.CurrentThread)
);
throw;
}
RemotingResult result = (RemotingResult)deserialized;;
Exception resultException = result.Exception;
if (resultException != null)
throw resultException;
return p_Unwrap(result.Result);
}
#endregion
#region i_InvokeAsync
// [DebuggerHidden]
internal void i_InvokeAsync(RemotingInvoke invoke)
{
long invokeId = Interlocked.Increment(ref fNextInvokeId);
invoke.InvokeId = invokeId;
invoke.Type = InvokeType.Method;
BinaryFormatter binaryFormatter = new BinaryFormatter();
var channeller = fChanneller;
if (channeller == null || channeller.WasDisposed)
{
CheckUndisposed();
if (!CanTryToReconnectAfterConnectionLost)
{
if (channeller != null)
channeller.CheckUndisposed();
throw new ObjectDisposedException(fChanneller.GetType().FullName, "Connection channeller was disposed.");
}
fChannelsByThreadLock.LockWithTimeout
(
() => fChannelsByThread.Remove(Thread.CurrentThread)
);
p_CreateConnection();
}
ExceptionAwareStream threadChannel = null;
fChannelsByThreadLock.LockWithTimeout
(
() => fChannelsByThread.TryGetValue(Thread.CurrentThread, out threadChannel)
);
if (threadChannel == null || threadChannel.WasDisposed)
{
threadChannel = fChanneller.CreateChannel();
fChannelsByThreadLock.LockWithTimeout
(
() => fChannelsByThread[Thread.CurrentThread] = threadChannel // adds or replaces the channel.
);
}
try
{
binaryFormatter.Serialize(threadChannel, invoke);
}
catch(Exception exception)
{
threadChannel.Dispose(exception);
fChannelsByThreadLock.LockWithTimeout
(
() => fChannelsByThread.Remove(Thread.CurrentThread)
);
throw;
}
}
#endregion
#region p_CreateConnection
private void p_CreateConnection()
{
Stream stream = null;
try
{
int buffersLength;
if (fPort != 0)
{
TcpClient connection = null;
try
{
connection = new TcpClient(fHost, fPort);
connection.NoDelay = true;
stream = connection.GetStream();
BaseConnection = connection;
}
catch
{
if (connection != null)
connection.Close();
throw;
}
buffersLength = Math.Max(connection.ReceiveBufferSize, connection.SendBufferSize);
}
else
{
string serverName;
string pipeName;
int pos = fHost.IndexOf('\\');
if (pos == -1)
{
serverName = ".";
pipeName = fHost;
}
else
{
serverName = fHost.Substring(0, pos);
pipeName = fHost.Substring(pos + 1);
}
stream = DuplexStream.CreateNamedPipeClient(serverName, pipeName, true);
BaseConnection = stream;
buffersLength = 8 * 1024;
}
var gettingStream = fGettingStream;
if (gettingStream != null)
{
GettingStreamEventArgs args = new GettingStreamEventArgs();
args.Stream = stream;
gettingStream(this, args);
stream = args.Stream;
}
BinaryFormatter binaryFormatter = new BinaryFormatter();
if (stream.CanTimeout)
stream.ReadTimeout = 60000;
var serverInitializationObject = (ServerRemotingInitialization)binaryFormatter.Deserialize(stream);
if (stream.CanTimeout)
stream.ReadTimeout = Timeout.Infinite;
if (fHandshaking != null)
{
HandshakingEventArgs args = new HandshakingEventArgs(serverInitializationObject);
args.Cryptography = Cryptography;
fHandshaking(this, args);
Cryptography = args.Cryptography;
}
fIsRemoteUsingAsyncVoidCalls = serverInitializationObject.MustUseAsyncVoidCalls;
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach(var pair in serverInitializationObject.ForcedSerializations)
{
string assemblyName = pair.Key;
foreach(var assembly in assemblies)
{
if (assembly.FullName == assemblyName)
{
foreach(var typeName in pair.Value)
{
Type type = assembly.GetType(typeName);
if (type != null)
fForcedSerializations.Add(type);
}
break;
}
}
}
var clientInitializationObject = new ClientRemotingInitialization();
if (Cryptography != null)
{
if (serverInitializationObject.CryptographyMode == CryptographyMode.Forbidden)
throw new RemotingException("Server is forbidding cryptography.");
Type cryptographyType = Cryptography.GetType();
clientInitializationObject.Cryptography = cryptographyType;
var acceptedCryptographies = serverInitializationObject.AcceptedCryptographies;
if (acceptedCryptographies != null)
{
var acceptedTypes = acceptedCryptographies.GetValueOrDefault(cryptographyType.Assembly.FullName);
if (acceptedTypes == null || !acceptedTypes.Contains(cryptographyType.FullName))
{
StringBuilder message = new StringBuilder();
message.Append("Server does not accept cryptography of type:\r\n ");
message.Append(cryptographyType.FullName);
message.Append(" from ");
message.Append(cryptographyType.Assembly.FullName);
message.Append("\r\n\r\nValid cryptographies:");
foreach(var pair in acceptedCryptographies)
{
message.Append("\r\n\r\nFrom ");
message.Append(pair.Key);
message.Append(":");
foreach(var name in pair.Value)
{
message.Append("\r\n ");
message.Append(name);
}
}
throw new RemotingException(message.ToString());
}
}
}
else
{
if (serverInitializationObject.CryptographyMode == CryptographyMode.Required)
throw new RemotingException("Server requires cryptography.");
}
clientInitializationObject.MustUseAsyncVoidCalls = MustUseAsyncVoidCalls;
if (stream.CanTimeout)
stream.WriteTimeout = 60000;
binaryFormatter.Serialize(stream, clientInitializationObject);
if (stream.CanTimeout)
stream.WriteTimeout = Timeout.Infinite;
if (Cryptography != null)
stream = new SecureStream(stream, new RSACryptoServiceProvider(), Cryptography, false);
if (fPort != 0)
fChanneller = new StreamChanneller(stream, p_RemoteChannelCreated, buffersLength);
else
fChanneller = new NamedPipeChanneller(stream, p_RemoteChannelCreated);
if (!CanTryToReconnectAfterConnectionLost)
fChanneller.Disposed += new EventHandler(p_Channeller_Disposed);
}
catch
{
if (stream != null)
stream.Close();
throw;
}
}
#endregion
#region i_Do... events
internal void i_DoBeforeInvokeRemoteMethod(InvokeMethodEventArgs args)
{
if (BeforeInvokeRemoteMethod != null)
BeforeInvokeRemoteMethod(this, args);
}
internal void i_DoAfterInvokeRemoteMethod(InvokeMethodEventArgs args)
{
if (AfterInvokeRemoteMethod != null)
AfterInvokeRemoteMethod(this, args);
}
internal void i_DoBeforeInvokeRemotePropertyGet(InvokePropertyEventArgs args)
{
if (BeforeInvokeRemotePropertyGet != null)
BeforeInvokeRemotePropertyGet(this, args);
}
internal void i_DoAfterInvokeRemotePropertyGet(InvokePropertyEventArgs args)
{
if (AfterInvokeRemotePropertyGet != null)
AfterInvokeRemotePropertyGet(this, args);
}
internal void i_DoBeforeInvokeRemotePropertySet(InvokePropertyEventArgs args)
{
if (BeforeInvokeRemotePropertySet != null)
BeforeInvokeRemotePropertySet(this, args);
}
internal void i_DoAfterInvokeRemotePropertySet(InvokePropertyEventArgs args)
{
if (AfterInvokeRemotePropertySet != null)
AfterInvokeRemotePropertySet(this, args);
}
internal void i_DoBeforeInvokeRemoteEventAdd(InvokeEventEventArgs args)
{
if (BeforeInvokeRemoteEventAdd != null)
BeforeInvokeRemoteEventAdd(this, args);
}
internal void i_DoAfterInvokeRemoteEventAdd(InvokeEventEventArgs args)
{
if (AfterInvokeRemoteEventAdd != null)
AfterInvokeRemoteEventAdd(this, args);
}
internal void i_DoBeforeInvokeRemoteEventRemove(InvokeEventEventArgs args)
{
if (BeforeInvokeRemoteEventRemove != null)
BeforeInvokeRemoteEventRemove(this, args);
}
internal void i_DoAfterInvokeRemoteEventRemove(InvokeEventEventArgs args)
{
if (AfterInvokeRemoteEventRemove != null)
AfterInvokeRemoteEventRemove(this, args);
}
#endregion
#endregion
#region Events
/// <summary>
/// Event called in a new thread when a client channel is created by a
/// request on the other side.
/// </summary>
public event EventHandler<ChannelCreatedEventArgs> UserChannelCreated;
/// <summary>
/// Event called just before a remote call invokes a local method.
/// </summary>
public event EventHandler<InvokeMethodEventArgs> BeforeInvokeLocalMethod;
/// <summary>
/// Event called just after invoking a local method from a remote call.
/// </summary>
public event EventHandler<InvokeMethodEventArgs> AfterInvokeLocalMethod;
/// <summary>
/// Event called just before a local method is redirected to a remote invoke.
/// </summary>
public event EventHandler<InvokeMethodEventArgs> BeforeInvokeRemoteMethod;
/// <summary>
/// Event called just after the remote method was executed.
/// </summary>
public event EventHandler<InvokeMethodEventArgs> AfterInvokeRemoteMethod;
/// <summary>
/// Event called just before a remote call invokes a local property get.
/// </summary>
public event EventHandler<InvokePropertyEventArgs> BeforeInvokeLocalPropertyGet;
/// <summary>
/// Event called just after a remote call invokes a local property get.
/// </summary>
public event EventHandler<InvokePropertyEventArgs> AfterInvokeLocalPropertyGet;
/// <summary>
/// Event called just before redirecting a property get to a remote object.
/// </summary>
public event EventHandler<InvokePropertyEventArgs> BeforeInvokeRemotePropertyGet;
/// <summary>
/// Event called just after redirecting a property get to a remote object.
/// </summary>
public event EventHandler<InvokePropertyEventArgs> AfterInvokeRemotePropertyGet;
/// <summary>
/// Event called just before a remote call invokes a local property set.
/// </summary>
public event EventHandler<InvokePropertyEventArgs> BeforeInvokeLocalPropertySet;
/// <summary>
/// Event called just after a remote call invokes a local property set.
/// </summary>
public event EventHandler<InvokePropertyEventArgs> AfterInvokeLocalPropertySet;
/// <summary>
/// Event called just before redirecting a property set to a remote object.
/// </summary>
public event EventHandler<InvokePropertyEventArgs> BeforeInvokeRemotePropertySet;
/// <summary>
/// Event called just after redirecting a property set to a remote object.
/// </summary>
public event EventHandler<InvokePropertyEventArgs> AfterInvokeRemotePropertySet;
/// <summary>
/// Event called just before a remote call adds an event handler to a local object.
/// </summary>
public event EventHandler<InvokeEventEventArgs> BeforeInvokeLocalEventAdd;
/// <summary>
/// Event called just after a remote call adds an event handler to a local object.
/// </summary>
public event EventHandler<InvokeEventEventArgs> AfterInvokeLocalEventAdd;
/// <summary>
/// Event called just before redirecting an event add to a remote object.
/// </summary>
public event EventHandler<InvokeEventEventArgs> BeforeInvokeRemoteEventAdd;
/// <summary>
/// Event called just after redirecting an event add to a remote object.
/// </summary>
public event EventHandler<InvokeEventEventArgs> AfterInvokeRemoteEventAdd;
/// <summary>
/// Event called just before a remote call removes an event handler from a local object.
/// </summary>
public event EventHandler<InvokeEventEventArgs> BeforeInvokeLocalEventRemove;
/// <summary>
/// Event called just after a remote call removes an event handler from a local object.
/// </summary>
public event EventHandler<InvokeEventEventArgs> AfterInvokeLocalEventRemove;
/// <summary>
/// Event called just before an event remove is redirected to a remote object.
/// </summary>
public event EventHandler<InvokeEventEventArgs> BeforeInvokeRemoteEventRemove;
/// <summary>
/// Event called just after an event remove is redirected to a remote object.
/// </summary>
public event EventHandler<InvokeEventEventArgs> AfterInvokeRemoteEventRemove;
#endregion
#region Nested types
[Serializable]
private sealed class RestoreObjectFromId
{
internal long ObjectId;
}
[Serializable]
private sealed class RemotingResult
{
internal long InvokeId;
internal object Result;
internal Exception Exception;
}
internal enum InvokeType
{
Constructor,
Destructor,
Method,
StaticMethod,
PropertyGet,
PropertySet,
EventAdd,
EventRemove,
EventCall,
/// <summary>
/// Happens when the component for which the event was registered removed the event,
/// like when it is disposed and clears the events list.
/// </summary>
EventLost
}
[Serializable]
internal class RemotingInvoke
{
internal long InvokeId;
internal InvokeType Type;
}
[Serializable]
private class RemotingStaticInvoke:
RemotingInvoke
{
internal string Name;
internal object[] Parameters;
}
[Serializable]
internal class RemotingInstanceInvoke:
RemotingInvoke
{
internal long ObjectId;
}
[Serializable]
internal class RemotingDestructorInvoke:
RemotingInstanceInvoke
{
internal bool Disposing;
}
[Serializable]
internal sealed class RemotingMethodInvoke:
RemotingInstanceInvoke
{
internal Type[] GenericArguments;
internal MethodInfo MethodInfo;
internal object[] Parameters;
}
[Serializable]
internal class RemotingPropertyGetInvoke:
RemotingInstanceInvoke
{
internal PropertyInfo PropertyInfo;
internal object[] Index;
}
[Serializable]
internal sealed class RemotingPropertySetInvoke:
RemotingPropertyGetInvoke
{
internal object Value;
}
[Serializable]
internal class RemotingEventInvoke:
RemotingInstanceInvoke
{
public EventInfo EventInfo;
}
[Serializable]
private sealed class RemotingEventCallInvoke:
RemotingEventInvoke
{
public object[] Parameters;
}
[Serializable]
private sealed class WrapperReference
{
internal long ObjectId;
internal Type[] InterfaceTypes;
}
[Serializable]
internal sealed class ServerRemotingInitialization
{
internal bool MustUseAsyncVoidCalls;
internal CryptographyMode CryptographyMode;
internal Dictionary<string, HashSet<string>> AcceptedCryptographies;
internal Dictionary<string, HashSet<string>> ForcedSerializations;
}
[Serializable]
private sealed class ClientRemotingInitialization
{
internal bool MustUseAsyncVoidCalls;
internal Type Cryptography;
}
[Serializable]
private sealed class ArrayReference
{
internal Type ElementType;
internal object[] Array;
}
#endregion
}
}