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
}
}