Click here to Skip to main content
15,895,667 members
Articles / Programming Languages / C#

Pfz.Remoting - Stateful Remoting by interface

Rate me:
Please Sign up or sign in to vote.
4.56/5 (8 votes)
29 Oct 2009CPOL8 min read 27.6K   594   16  
This article explains the concept of a stateful remoting framework capable of doing the remoting of any object by interface, and how to use the classes.
using System;
using System.Collections.Generic;
using System.IO;
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.MonitorLockExtensions;
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:
		ThreadSafeDisposable
	{
		#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 StreamChanneller fChanneller;
			private string fHost;
			private int fPort;
			
			private volatile Dictionary<Thread, Channel> fChannelsByThread = new Dictionary<Thread, Channel>();
			private object fChannelsByThreadLock = new object();
			private long fNextInvokeId;
			
			private bool fIsRemoteUsingAsyncVoidCalls;

			private EventWrapper fEventWrapper;
			
			private EventHandler<GettingStreamEventArgs> fGettingStream;
			private EventHandler<RemotingHandshakingEventArgs> fHandshaking;
			
			internal ActionRunner<object> fRemoveDestroyedRemoteObjects = new ActionRunner<object>();
		#endregion
		
		#region Constructors
			#region Internal
				internal RemotingClient(RemotingServer owner, TcpClient connection, EventHandler<GettingStreamEventArgs> gettingStream)
				{
					try
					{
						fOwner = owner;
						MustUseAsyncVoidCalls = owner.MustUseAsyncVoidCalls;
						MustDisposeAllOnConnectionLost = owner.MustDisposeAllOnConnectionLost;
						
						fGettingStream = gettingStream;
						connection.NoDelay = true;
						Stream stream = connection.GetStream();
						if (gettingStream != null)
						{
							GettingStreamEventArgs args = new GettingStreamEventArgs();
							args.Stream = stream;
							gettingStream(this, args);
							stream = args.Stream;
							
							if (stream == null)
							{
								connection.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;

						connection.Client.SendTimeout = 60000;
						binaryFormatter.Serialize(stream, serverInitializationObject);
						connection.Client.SendTimeout = 0;
						
						connection.Client.ReceiveTimeout = 60000;
						var clientInitializationObject = (ClientRemotingInitialization)binaryFormatter.Deserialize(stream);
						connection.Client.ReceiveTimeout = 0;

						fIsRemoteUsingAsyncVoidCalls = clientInitializationObject.MustUseAsyncVoidCalls;
						
						var cryptographyType = clientInitializationObject.Cryptography;
						if (cryptographyType != null)
						{
							if (owner.CryptographyMode == CryptographyMode.Forbidden)
							{
								connection.Close();
								return;
							}
							
							var validCryptographies = owner.fAcceptedCryptographies;
							if (validCryptographies != null)
							{
								if (!owner.fAcceptedCryptographies.Contains(cryptographyType))
								{
									connection.Close();
									return;
								}
							}
							
							Cryptography = (SymmetricAlgorithm)cryptographyType.GetConstructor(Type.EmptyTypes).Invoke(null);
							stream = new SecureStream(stream, new RSACryptoServiceProvider(), Cryptography, true);
						}
						else
						{
							if (owner.CryptographyMode == CryptographyMode.Required)
							{
								connection.Close();
								return;
							}
						}

						using(owner.DisposeLock.LockWithTimeout())
						{
							// 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)
							{
								connection.Close();
								return;
							}

							fForcedSerializations = owner.fForcedSerializations;

							fEventWrapper = new EventWrapper(this);

							fChanneller = new StreamChanneller(stream, p_RemoteChannelCreated, Math.Max(connection.ReceiveBufferSize, connection.SendBufferSize), false, new SocketTimeOutParameters(connection.Client));
							fChanneller.Disposed += new EventHandler(p_Channeller_Disposed);

							fEventWrapper.OnInvoke += p_EventInvoke;

							GCUtils.Collected += p_Collected;

 							var clients = owner.fClients;
							using(clients.LockWithTimeout())
								clients.Add(this);

							owner.i_RegisterForEvents(this);
						}
					}
					catch
					{
						connection.Close();
						Dispose();
					}
				}
			#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.</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 Disposing;
			#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)
					{
						var removeDestroyedRemoteObject = fRemoveDestroyedRemoteObjects;
						if (removeDestroyedRemoteObject != null)
						{
							fRemoveDestroyedRemoteObjects = null;
							removeDestroyedRemoteObject.Dispose();
						}

						var disposingEvent = Disposing;
						if (disposingEvent != null)
							disposingEvent(this, EventArgs.Empty);
						
						GCUtils.Collected -= p_Collected;
						
						List<object> eventWrappers = fEventWrappers.ToList();
						using(fActiveObjectsLock.LockWithTimeout())
						{
							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();
						}
						
						if (fOwner != null)
						{
							var clients = fOwner.fClients;
							if (clients != null)
								using(clients.LockWithTimeout())
									clients.Remove(this);
						}
						
						if (MustDisposeAllOnConnectionLost)
						{
							using(fActiveObjectsLock.LockWithTimeout())
							{
								foreach(object obj in fActiveObjects.Values)
								{
									IDisposable disposable = obj as IDisposable;
									if (disposable != null)
										disposable.Dispose();
								}
								
								fActiveObjects.Clear();
							}
						}
					}
						
					base.Dispose(disposing);		
				}
			#endregion
			#region p_Channeller_Disposed
				private void p_Channeller_Disposed(object sender, EventArgs e)
				{
					Dispose();
				}
			#endregion
			#region p_Collected
				private void p_Collected()
				{
					try
					{
						if (WasDisposed)
						{
							GCUtils.Collected -= p_Collected;
							return;
						}

						lock(fActiveObjectsLock)
							fActiveObjects = new Dictionary<long, object>(fActiveObjects);

						lock(fActiveWrappersWithEventsLock)
							fActiveWrappersWithEvents = new Dictionary<long, RemotingInterfaceWrapper>(fActiveWrappersWithEvents);

						lock(fChannelsByThreadLock)
						{
							var oldChannelsByThread = fChannelsByThread;
							var newChannelsByThread = new Dictionary<Thread, Channel>();

							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
		#endregion
		#region Methods
			#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 Channel 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 Channel 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(Channel channel)
				{
					try
					{
						BinaryFormatter binaryFormatter = new BinaryFormatter();
						
						using(fChannelsByThreadLock.LockWithTimeout())
							fChannelsByThread.Add(Thread.CurrentThread, channel);
							
						while(true)
						{
							object deserialized;
							try
							{
								deserialized = binaryFormatter.Deserialize(channel);
							}
							catch(Exception)
							{
								using(fChannelsByThreadLock.LockWithTimeout())
									fChannelsByThread.Remove(Thread.CurrentThread);
									
								channel.Close();
								return;
							}
							
							RemotingInvoke invoke = (RemotingInvoke)deserialized;
							p_ProcessInvoke(invoke);
						}
					}
					catch
					{
						if (!WasDisposed)
						{
							if (!CanTryToReconnectAfterConnectionLost)
								Dispose();
							
							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;
					}

					Channel channel;
					using(fChannelsByThreadLock.LockWithTimeout())
						channel = fChannelsByThread[Thread.CurrentThread];
						
					try
					{
						BinaryFormatter binaryFormatter = new BinaryFormatter();
						using(channel.LockWithTimeout())
							binaryFormatter.Serialize(channel, result);
					}
					catch
					{
						using(fChannelsByThreadLock.LockWithTimeout())
							fChannelsByThread.Remove(Thread.CurrentThread);

						channel.Dispose();
					}
				}
			#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;
					using(fActiveWrappersWithEventsLock.LockWithTimeout())
						fActiveWrappersWithEvents.TryGetValue(eventInvoke.ObjectId, out wrapper);

					if (wrapper != null)
					{
						using(wrapper.fEventsLock.LockWithTimeout())
						{
							var events = wrapper.fEvents;
							if (events.Remove(eventInvoke.EventInfo))
								if (events.Count == 0)
									using(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;
						using(fActiveObjectsLock.LockWithTimeout())
							fActiveObjects.TryGetValue(destructorInvoke.ObjectId, out obj);
						
						if (obj != null)
						{
							IDisposable disposable = obj as IDisposable;
							if (disposable != null)
								disposable.Dispose();
						}
					}

					using(fActiveObjectsLock.LockWithTimeout())
						fActiveObjects.Remove(destructorInvoke.ObjectId);
				}
			#endregion
			#region p_InvokeEventCall
				private void p_InvokeEventCall(RemotingInvoke invoke)
				{
					var eventCall = (RemotingEventCallInvoke)invoke;

					RemotingInterfaceWrapper eventCallWrapper;

					using(fActiveWrappersWithEventsLock.LockWithTimeout())
						eventCallWrapper = fActiveWrappersWithEvents[eventCall.ObjectId];

					if (eventCallWrapper != null)
					{
						HashSet<Delegate> hashset;

						using(eventCallWrapper.fEventsLock.LockWithTimeout())
							eventCallWrapper.fEvents.TryGetValue(eventCall.EventInfo, out hashset);

						p_Unwrap(eventCall.Parameters);

						if (hashset != null)
							using(hashset.LockWithTimeout())
								foreach (var item in hashset)
									item.DynamicInvoke(eventCall.Parameters);
					}

				}
			#endregion
			#region p_InvokeEventAdd
				private WeakHashSet<object> fEventWrappers = new WeakHashSet<object>();
				private void p_InvokeEventAdd(RemotingInvoke invoke)
				{
					var eventAdd = (RemotingEventInvoke)invoke;

					object eventAddObj;
					using(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;
					using(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;
					using(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;
					using(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;
					using(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;
						
					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);
					
					using(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;
						
					object[] array = value as object[];
					if (array != null)
					{
						p_Unwrap(array);
						return array;
					}
					
					RestoreObjectFromId restorer = value as RestoreObjectFromId;
					if (restorer != null)
						using(fActiveObjectsLock.LockWithTimeout())
							return fActiveObjects[restorer.ObjectId];

					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)
					{
						if (WasDisposed)
							throw new ObjectDisposedException(GetType().FullName);
							
						if (!CanTryToReconnectAfterConnectionLost)
							throw new ObjectDisposedException(fChanneller.GetType().FullName, "Connection channeller was disposed.");
						
						using(fChannelsByThreadLock.LockWithTimeout())
							fChannelsByThread.Remove(Thread.CurrentThread);
							
						p_CreateConnection();
					}
					
					Channel threadChannel;
					using(fChannelsByThreadLock.LockWithTimeout())
						fChannelsByThread.TryGetValue(Thread.CurrentThread, out threadChannel);
						
					if (threadChannel == null || threadChannel.WasDisposed)
					{
						threadChannel = fChanneller.CreateChannel();
						using(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
					{
						threadChannel.Dispose();

						using(fChannelsByThreadLock.LockWithTimeout())
							fChannelsByThread.Remove(Thread.CurrentThread);

						throw;
					}
					
					RemotingResult result = (RemotingResult)deserialized;;
					Exception exception = result.Exception;
					if (exception != null)
						throw exception;
					
					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)
					{
						if (WasDisposed)
							throw new ObjectDisposedException(GetType().FullName);
							
						if (!CanTryToReconnectAfterConnectionLost)
							throw new ObjectDisposedException(fChanneller.GetType().FullName, "Connection channeller was disposed.");
						
						using(fChannelsByThreadLock.LockWithTimeout())
							fChannelsByThread.Remove(Thread.CurrentThread);
							
						p_CreateConnection();
					}
					
					Channel threadChannel;
					using(fChannelsByThreadLock.LockWithTimeout())
						fChannelsByThread.TryGetValue(Thread.CurrentThread, out threadChannel);
						
					if (threadChannel == null || threadChannel.WasDisposed)
					{
						threadChannel = fChanneller.CreateChannel();
						using(fChannelsByThreadLock.LockWithTimeout())
							fChannelsByThread[Thread.CurrentThread] = threadChannel; // adds or replaces the channel.
					}

					try
					{
						binaryFormatter.Serialize(threadChannel, invoke);
					}
					catch
					{
						threadChannel.Dispose();
						
						using(fChannelsByThreadLock.LockWithTimeout())
							fChannelsByThread.Remove(Thread.CurrentThread);
						
						throw;
					}
				}
			#endregion
			#region p_CreateConnection
				private void p_CreateConnection()
				{
					var connection = new TcpClient(fHost, fPort);
					try
					{
						connection.NoDelay = true;
						
						Stream stream = connection.GetStream();
						
						var gettingStream = fGettingStream;
						if (gettingStream != null)
						{
							GettingStreamEventArgs args = new GettingStreamEventArgs();
							args.Stream = stream;
							gettingStream(this, args);
							stream = args.Stream;
						}
						
						BinaryFormatter binaryFormatter = new BinaryFormatter();
						connection.Client.ReceiveTimeout = 60000;
						var serverInitializationObject = (ServerRemotingInitialization)binaryFormatter.Deserialize(stream);
						connection.Client.ReceiveTimeout = 0;
						
						if (fHandshaking != null)
						{
							RemotingHandshakingEventArgs args = new RemotingHandshakingEventArgs(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;
						connection.Client.SendTimeout = 60000;
						binaryFormatter.Serialize(stream, clientInitializationObject);
						connection.Client.SendTimeout = 0;
						
						if (Cryptography != null)
							stream = new SecureStream(stream, new RSACryptoServiceProvider(), Cryptography, false);
						
						fChanneller = new StreamChanneller(stream, p_RemoteChannelCreated, Math.Max(connection.ReceiveBufferSize, connection.SendBufferSize), new SocketTimeOutParameters(connection.Client));

						if (!CanTryToReconnectAfterConnectionLost)
							fChanneller.Disposed += new EventHandler(p_Channeller_Disposed);
					}
					catch
					{
						connection.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;
			}
		#endregion
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Microsoft
United States United States
I started to program computers when I was 11 years old, as a hobbyist, programming in AMOS Basic and Blitz Basic for Amiga.
At 12 I had my first try with assembler, but it was too difficult at the time. Then, in the same year, I learned C and, after learning C, I was finally able to learn assembler (for Motorola 680x0).
Not sure, but probably between 12 and 13, I started to learn C++. I always programmed "in an object oriented way", but using function pointers instead of virtual methods.

At 15 I started to learn Pascal at school and to use Delphi. At 16 I started my first internship (using Delphi). At 18 I started to work professionally using C++ and since then I've developed my programming skills as a professional developer in C++ and C#, generally creating libraries that help other developers do their work easier, faster and with less errors.

Want more info or simply want to contact me?
Take a look at: http://paulozemek.azurewebsites.net/
Or e-mail me at: paulozemek@outlook.com

Codeproject MVP 2012, 2015 & 2016
Microsoft MVP 2013-2014 (in October 2014 I started working at Microsoft, so I can't be a Microsoft MVP anymore).

Comments and Discussions