using System;
using System.Reflection;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Activation;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;
using Developpez.Dotnet.Reflection;
namespace Developpez.Dotnet.Diagnostics
{
///
/// Moniteur d'appels à des méthodes d'un objet
///
/// Type de l'objet à surveiller
public class MethodCallMonitor : RealProxy where T : MarshalByRefObject
{
///
/// Uri de l'objet
///
protected virtual string ObjUri { get; set; }
///
/// Liste des méthodes surveillées. null ou un tableau vide
/// signifie de surveiller toutes les méthodes sans exception
///
public virtual string[] MonitoredMethods { get; set; }
MethodCallMonitor(MarshalByRefObject target)
: base(typeof(T))
{
if (target == null)
throw new ArgumentNullException("target");
MonitoredMethods = null;
ObjUri = RemotingServices.Marshal(target).URI;
}
///
/// Transmet l'appel d'une méthode au proxy sous-jacent
///
/// Le message contenant les informations d'appel de méthode
/// Le message renvoyé par la méthode appelée, contenant la valeur de retour et les paramètres ref ou out
public override IMessage Invoke(IMessage invokeMessage)
{
if (invokeMessage is IConstructionCallMessage)
{
IConstructionReturnMessage myIConstructionReturnMessage =
this.InitializeServerObject((IConstructionCallMessage)invokeMessage);
ConstructionResponse constructionResponse = new
ConstructionResponse(null, (IMethodCallMessage)invokeMessage);
return constructionResponse;
}
else
{
IMethodCallMessage methodCall_base = invokeMessage as IMethodCallMessage;
if (methodCall_base != null)
{
MethodCallMessageWrapper methodCall =
new MethodCallMessageWrapper(methodCall_base);
methodCall.Properties["__Uri"] = ObjUri;
bool ignoreMonitoring = false;
/* on regarde s'il faut surveiller cette méthode */
if (MonitoredMethods != null && MonitoredMethods.Length > 0)
{
ignoreMonitoring = !methodCall.MethodName.In(MonitoredMethods);
}
if (!ignoreMonitoring)
{
// Build default return message
object retVal = ((MethodInfo)methodCall.MethodBase).ReturnType.GetDefaultValue();
var outArgs = methodCall.Args;
var retMsg = new ReturnMessage(retVal, outArgs, outArgs.Length, methodCall.LogicalCallContext, methodCall);
BeforeMethodCallEventArgs arg = new BeforeMethodCallEventArgs(methodCall) { Cancel = false, ReturnMessage = retMsg };
OnBeforeMethodCall(arg);
if (arg.Cancel)
{
AfterMethodCallEventArgs r_arg = new AfterMethodCallEventArgs(methodCall, true) { ReturnMessage = arg.ReturnMessage };
OnAfterMethodCall(r_arg);
return r_arg.ReturnMessage;
}
}
IMessage returnMessage = null;
try
{
returnMessage = ChannelServices.SyncDispatchMessage(methodCall);
IMethodReturnMessage methodReturnMessage = (IMethodReturnMessage)returnMessage;
if (!ignoreMonitoring)
{
AfterMethodCallEventArgs r_arg = new AfterMethodCallEventArgs(methodCall, true) { ReturnMessage = returnMessage };
OnAfterMethodCall(r_arg);
returnMessage = r_arg.ReturnMessage;
}
return returnMessage;
}
catch (Exception ex)
{
returnMessage = null;
if (!ignoreMonitoring)
{
MethodCallExceptionEventArgs ex_args = new MethodCallExceptionEventArgs(methodCall, ex) { Rethrow = true };
OnMethodCallException(ex_args);
if (ex_args.Rethrow)
{
throw new TargetInvocationException(ex_args.Exception);
}
else
return ex_args.ReturnMessage;
}
else
{
/* en l'absence de monitor on génère une exception */
throw new TargetInvocationException(ex);
}
}
}
}
return ChannelServices.SyncDispatchMessage(invokeMessage);
}
///
/// Crée un nouveau proxy dynamique crée à partir de l'objet target passé en paramètre
///
/// Objet pour lequel créer un proxy
/// Paramètre de sortie qui reçoit le MethodCallMonitor créé
/// Le proxy dynamique créé
public static T Create(T target, out MethodCallMonitor proxy)
{
proxy = new MethodCallMonitor(target);
return (T)proxy.GetTransparentProxy();
}
#region Events
///
/// Appelé avant l'exécution d'une méthode au sein d'un objet surveillé
///
public event BeforeMethodCallEventHandler BeforeMethodCall;
///
/// Appelé après l'exécution d'une méthode au sein d'un objet surveillé, si l'exécution n'entraine pas d'exception
///
public event AfterMethodCallEventHandler AfterMethodCall;
///
/// Appelé si une exception survient dans une des méthodes surveillées
///
public event MethodCallExceptionEventHandler MethodCallException;
///
/// Déclanche l'évènement
///
/// Paramètre de l'évènement
protected virtual void OnBeforeMethodCall(BeforeMethodCallEventArgs e)
{
BeforeMethodCallEventHandler evt = BeforeMethodCall;
if (evt != null)
evt(this, e);
}
///
/// Déclanche l'évènement
///
/// Paramètre de l'évènement
protected virtual void OnAfterMethodCall(AfterMethodCallEventArgs e)
{
AfterMethodCallEventHandler evt = AfterMethodCall;
if (evt != null)
evt(this, e);
}
///
/// Déclanche l'évènement
///
/// Paramètre de l'évènement
protected virtual void OnMethodCallException(MethodCallExceptionEventArgs e)
{
MethodCallExceptionEventHandler evt = MethodCallException;
if (evt != null)
evt(this, e);
}
#endregion
}
#region Events Objects
///
/// Représente le délégué utilisé pour un évènement précédent l'appel d'une méthode au sein d'un objet surveillé
///
/// La source de l'évènement
/// Un qui contient les arguments de l'évènement
public delegate void BeforeMethodCallEventHandler(object sender, BeforeMethodCallEventArgs e);
///
/// Représente le délégué utilisé pour un évènement suivant l'appel d'une méthode au sein d'un objet surveillé
///
/// La source de l'évènement
/// Un qui contient les arguments de l'évènement
public delegate void AfterMethodCallEventHandler(object sender, AfterMethodCallEventArgs e);
///
/// Représente le délégué utilisé pour un évènement résultant d'une exception dans une des méthodes surveillées
///
/// La source de l'évènement
/// Un qui contient les arguments de l'évènement
public delegate void MethodCallExceptionEventHandler(object sender, MethodCallExceptionEventArgs e);
///
/// Classe de base pour les évènements de
///
public abstract class MethodCallMonitorBaseEventArgs : EventArgs
{
///
/// Initialise une nouvelle instance de MethodCallMonitorBaseEventArgs pour le message spécifié
///
/// Message d'appel de méthode
protected MethodCallMonitorBaseEventArgs(IMethodCallMessage message)
{
this.MethodName = message.MethodName;
this.Msg = message;
}
///
/// Nom de la méthode
///
public virtual string MethodName { get; private set; }
///
/// Message (remoting) utilisé par le Framework pour tracer l'appel à la méthode
///
public virtual IMethodCallMessage Msg { get; private set; }
}
///
/// Classe contenant les informations pour l'évènement
///
public class MethodCallExceptionEventArgs : MethodCallMonitorBaseEventArgs
{
///
/// Initialise une nouvelle instance de MethodCallExceptionEventArgs pour le message et l'exception spécifiés
///
/// Message d'appel de méthode
/// Exception levée pendant l'exécution de la méthode
public MethodCallExceptionEventArgs(IMethodCallMessage message, Exception exception)
: base(message)
{
this.Exception = exception;
}
///
/// Exception détectée au sein de la méthode (résultant d'un "throw")
///
public Exception Exception { get; private set; }
///
/// Indique si, une fois cet évènement traité, l'exception doit être à nouveau lancée
/// pour être interceptée par un niveau supérieur.
/// Par défaut, vaut true (dans le cas d'une non gestion de cet évènement, l'exception est remontée)
///
public bool Rethrow { get; set; }
///
/// Message de retour à passer à l'appelant, contenant la valeur renvoyée par la méthode
///
public IMessage ReturnMessage { get; set; }
}
///
/// Classe contenant les informations pour l'évènement
///
public class BeforeMethodCallEventArgs : MethodCallMonitorBaseEventArgs
{
///
/// Initialise une nouvelle instance de BeforeMethodCallEventArgs pour le message spécifié
///
/// Message d'appel de méthode
public BeforeMethodCallEventArgs(IMethodCallMessage message)
: base(message)
{
}
///
/// Indique si l'appel doit être annulé. Dans ce cas, il faut placer un message dans ReturnMessage
/// pour assurer l'intégrité des appels (ou déclancher une exception).
///
public bool Cancel { get; set; }
///
/// Message de retour à passer à l'appelant, contenant la valeur renvoyée par la méthode
///
public IMessage ReturnMessage { get; set; }
}
///
/// Classe contenant les informations pour l'évènement
///
public class AfterMethodCallEventArgs : MethodCallMonitorBaseEventArgs
{
///
/// Initialise une nouvelle instance de AfterMethodCallEventArgs pour le message spécifié
///
/// Message d'appel de méthode
/// Indique si l'appel à la méthode a été annulé
public AfterMethodCallEventArgs(IMethodCallMessage message, bool cancelled)
: base(message)
{
this.Cancelled = cancelled;
}
///
/// Indique si l'appel à la méthode a été annulé (résultant de la gestion de l'évènement )
///
public bool Cancelled { get; set; }
///
/// Message de retour à passer à l'appelant, contenant la valeur renvoyée par la méthode
///
public IMessage ReturnMessage { get; set; }
}
#endregion
}