using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using Pfz.Extensions.MonitorLockExtensions;
using Pfz.InterfaceWrapping;
using Pfz.InterfaceWrapping.EventArguments;
namespace Pfz.Remoting.Internal
{
/// <summary>
/// Each time a remote object is created, a client wrapper is also created,
/// and they are all inheritors of this class. This class is only public to
/// be acessible by CodeDOM, but is not intended for users to implement.
/// </summary>
public abstract class RemotingInterfaceWrapper:
InvokeWrapper,
IAdvancedDisposable
{
internal long fObjectId;
internal RemotingClient fClient;
internal object fEventsLock = new object();
internal volatile Dictionary<EventInfo, HashSet<Delegate>> fEvents = new Dictionary<EventInfo, HashSet<Delegate>>();
/// <summary>
/// Informs the remote side that the real object can now be collected.
/// </summary>
~RemotingInterfaceWrapper()
{
var client = fClient;
if (client == null || client.WasDisposed)
return;
try
{
client.fRemoveDestroyedRemoteObjects.Run(p_InvokeDestructor, new object[]{client, fObjectId});
}
catch
{
GC.ReRegisterForFinalize(this);
}
}
private volatile bool fDisposed;
private object fDisposeLock = new object();
void IDisposable.Dispose()
{
bool mustReturn = true;
fDisposeLock.LockWithTimeout
(
delegate
{
if (fDisposed)
return;
fDisposed = true;
mustReturn = false;
}
);
if (mustReturn)
return;
p_InvokeDestructor(fClient, fObjectId, true);
GC.SuppressFinalize(this);
}
bool IAdvancedDisposable.WasDisposed
{
get
{
return fDisposed;
}
}
private static void p_InvokeDestructor(object state)
{
object[] parameters = (object[])state;
RemotingClient client = (RemotingClient)parameters[0];
long objectId = (long)parameters[1];
p_InvokeDestructor(client, objectId, false);
}
private static void p_InvokeDestructor(RemotingClient client, long objectId, bool disposing)
{
if (client == null || client.WasDisposed)
return;
var invoke = new RemotingClient.RemotingDestructorInvoke();
invoke.ObjectId = objectId;
invoke.Disposing = disposing;
try
{
client.i_Invoke(invoke, RemotingClient.InvokeType.Destructor);
}
catch
{
}
finally
{
client.fActiveWrappersWithEventsLock.LockWithTimeout
(
() => client.fActiveWrappersWithEvents.Remove(objectId)
);
}
}
/// <summary>
/// Redirects a method call to the remote object.
/// </summary>
[DebuggerHidden]
protected override object InvokeMethod(MethodInfo methodInfo, object[] parameters)
{
return p_InvokeMethod(methodInfo, parameters);
}
[DebuggerHidden]
private object p_InvokeMethod(MethodInfo methodInfo, object[] parameters)
{
var invoke = new RemotingClient.RemotingMethodInvoke();
if (methodInfo.IsGenericMethod)
{
invoke.MethodInfo = methodInfo.GetGenericMethodDefinition();
invoke.GenericArguments = methodInfo.GetGenericArguments();
}
else
invoke.MethodInfo = methodInfo;
ParameterInfo[] parameterInfos = methodInfo.GetParameters();
int count = parameterInfos.Length;
InvokeMethodEventArgs args = new InvokeMethodEventArgs();
args.Target = this;
args.MethodInfo = methodInfo;
args.Parameters = parameters;
try
{
fClient.i_DoBeforeInvokeRemoteMethod(args);
if (args.CanInvoke)
{
for (int i=0; i<count; i++)
parameters[i] = fClient.i_GenerateWrapperReference(parameterInfos[i].ParameterType, parameters[i]);
invoke.Parameters = parameters;
if (fClient.MustUseAsyncVoidCalls && RemotingClient.i_CanBeAsync(methodInfo))
{
p_InvokeAsync(invoke);
return null;
}
args.Result = p_Invoke(invoke, RemotingClient.InvokeType.Method);
}
}
catch(Exception exception)
{
args.Exception = exception;
}
finally
{
if(args.CanInvoke)
fClient.i_DoAfterInvokeRemoteMethod(args);
if (args.Exception != null)
throw args.Exception;
}
object[] result = args.Result as object[];
if (result == null)
return args.Result;
int resultIndex = 1;
for (int i=0; i<count; i++)
{
if (parameterInfos[i].ParameterType.IsByRef)
{
parameters[i] = result[resultIndex];
resultIndex++;
}
}
return result[0];
}
[DebuggerHidden]
private object p_Invoke(RemotingClient.RemotingInstanceInvoke invoke, RemotingClient.InvokeType invokeType)
{
if (fDisposed)
throw new ObjectDisposedException(typeof(RemotingInterfaceWrapper).FullName);
invoke.ObjectId = fObjectId;
return fClient.i_Invoke(invoke, invokeType);
}
[DebuggerHidden]
private void p_InvokeAsync(RemotingClient.RemotingInstanceInvoke invoke)
{
if (fDisposed)
throw new ObjectDisposedException(typeof(RemotingInterfaceWrapper).FullName);
invoke.ObjectId = fObjectId;
fClient.i_InvokeAsync(invoke);
}
/// <summary>
/// Redirects a property get call to the remote object.
/// </summary>
[DebuggerHidden]
protected override object InvokePropertyGet(PropertyInfo propertyInfo, object[] index)
{
var invoke = new RemotingClient.RemotingPropertyGetInvoke();
invoke.PropertyInfo = propertyInfo;
InvokePropertyEventArgs args = new InvokePropertyEventArgs();
args.Target = this;
args.PropertyInfo = propertyInfo;
args.Index = index;
try
{
fClient.i_DoBeforeInvokeRemotePropertyGet(args);
if (args.CanInvoke)
{
ParameterInfo[] parameterInfos = propertyInfo.GetIndexParameters();
int count = parameterInfos.Length;
for (int i=0; i<count; i++)
index[i] = fClient.i_GenerateWrapperReference(parameterInfos[i].ParameterType, index[i]);
invoke.Index = index;
args.Value = p_Invoke(invoke, RemotingClient.InvokeType.PropertyGet);
}
}
catch(Exception exception)
{
args.Exception = exception;
}
finally
{
if (args.CanInvoke)
fClient.i_DoAfterInvokeRemotePropertyGet(args);
if (args.Exception != null)
throw args.Exception;
}
return args.Value;
}
/// <summary>
/// Redirects a property set call to the remote object.
/// </summary>
[DebuggerHidden]
protected override void InvokePropertySet(PropertyInfo propertyInfo, object[] index, object value)
{
ParameterInfo[] parameterInfos = propertyInfo.GetIndexParameters();
int count = parameterInfos.Length;
InvokePropertyEventArgs args = new InvokePropertyEventArgs();
args.Target = this;
args.PropertyInfo = propertyInfo;
args.Index = index;
args.Value = value;
try
{
fClient.i_DoBeforeInvokeRemotePropertySet(args);
if (args.CanInvoke)
{
for (int i=0; i<count; i++)
index[i] = fClient.i_GenerateWrapperReference(parameterInfos[i].ParameterType, index[i]);
args.Value = fClient.i_GenerateWrapperReference(propertyInfo.PropertyType, args.Value);
var invoke = new RemotingClient.RemotingPropertySetInvoke();
invoke.PropertyInfo = args.PropertyInfo;
invoke.Index = args.Index;
invoke.Value = args.Value;
p_Invoke(invoke, RemotingClient.InvokeType.PropertySet);
}
}
catch(Exception exception)
{
args.Exception = exception;
}
finally
{
if (args.CanInvoke)
fClient.i_DoAfterInvokeRemotePropertySet(args);
if (args.Exception != null)
throw args.Exception;
}
}
/// <summary>
/// Redirects an event add call to the remote object.
/// </summary>
[DebuggerHidden]
protected override void InvokeEventAdd(EventInfo eventInfo, Delegate handler)
{
InvokeEventEventArgs args = new InvokeEventEventArgs();
args.Target = this;
args.EventInfo = eventInfo;
args.Handler = handler;
try
{
fClient.i_DoBeforeInvokeRemoteEventAdd(args);
if (args.CanInvoke)
{
HashSet<Delegate> hashset = null;
bool mustAddAsActiveWrapper = false;
fEventsLock.LockWithTimeout
(
delegate
{
var events = fEvents;
if (!events.TryGetValue(args.EventInfo, out hashset))
{
mustAddAsActiveWrapper = (fEvents.Count == 0);
hashset = new HashSet<Delegate>();
events.Add(args.EventInfo, hashset);
}
}
);
if (mustAddAsActiveWrapper)
{
fClient.fActiveWrappersWithEventsLock.LockWithTimeout
(
() => fClient.fActiveWrappersWithEvents.Add(fObjectId, this)
);
}
bool added = false;
hashset.LockWithTimeout
(
() => added = hashset.Add(args.Handler)
);
if (added)
{
var invoke = new RemotingClient.RemotingEventInvoke();
invoke.EventInfo = args.EventInfo;
p_Invoke(invoke, RemotingClient.InvokeType.EventAdd);
}
}
}
catch(Exception exception)
{
args.Exception = exception;
}
finally
{
if (args.CanInvoke)
fClient.i_DoAfterInvokeRemoteEventAdd(args);
if (args.Exception != null)
throw args.Exception;
}
}
/// <summary>
/// Redirects an event remove call to the remote object.
/// </summary>
[DebuggerHidden]
protected override void InvokeEventRemove(EventInfo eventInfo, Delegate handler)
{
InvokeEventEventArgs args = new InvokeEventEventArgs();
args.Target = this;
args.EventInfo = eventInfo;
args.Handler = handler;
try
{
fClient.i_DoBeforeInvokeRemoteEventRemove(args);
if (args.CanInvoke)
{
HashSet<Delegate> hashset = null;
fEventsLock.LockWithTimeout
(
() => fEvents.TryGetValue(args.EventInfo, out hashset)
);
if (hashset == null)
return;
bool removed = false;
bool mustRemoveAsActiveWrapper = false;
hashset.LockWithTimeout
(
delegate
{
removed = hashset.Remove(args.Handler);
if (removed && hashset.Count == 0)
{
fEventsLock.LockWithTimeout
(
delegate
{
var events = fEvents;
events.Remove(args.EventInfo);
mustRemoveAsActiveWrapper = events.Count == 0;
}
);
}
}
);
if (removed)
{
var invoke = new RemotingClient.RemotingEventInvoke();
invoke.EventInfo = args.EventInfo;
p_Invoke(invoke, RemotingClient.InvokeType.EventRemove);
}
if (mustRemoveAsActiveWrapper)
{
fClient.fActiveWrappersWithEventsLock.LockWithTimeout
(
() => fClient.fActiveWrappersWithEvents.Remove(fObjectId)
);
}
}
}
catch(Exception exception)
{
args.Exception = exception;
}
finally
{
if (args.CanInvoke)
fClient.i_DoAfterInvokeRemoteEventRemove(args);
if (args.Exception != null)
throw args.Exception;
}
}
/// <summary>
/// Throws an exception if the object was disposed.
/// </summary>
public void CheckUndisposed()
{
if (fDisposed)
throw new ObjectDisposedException(GetType().FullName);
}
}
}