Click here to Skip to main content
15,886,110 members
Articles / Programming Languages / C#

Web-Cam SecureChat

Rate me:
Please Sign up or sign in to vote.
4.94/5 (16 votes)
12 Mar 2010CPOL6 min read 93.6K   7.4K   70  
This article will explain how to create a simple chat program using this remoting technology, which supports web-cam and sending files.
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
	}
}

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