//
//
//
//
// $Revision: 2903 $
//
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
namespace Debugger.Interop
{
public delegate T MethodInvokerWithReturnValue();
public enum CallMethod
{
DirectCall,
Manual,
HiddenForm,
HiddenFormWithTimeout
};
public class MTA2STA
{
private readonly Form hiddenForm;
private readonly Thread targetThread;
private CallMethod callMethod = CallMethod.HiddenFormWithTimeout;
private readonly Queue pendingCalls = new Queue();
private readonly ManualResetEvent pendingCallsNotEmpty = new ManualResetEvent(false);
private WaitHandle EnqueueCall(MethodInvoker callDelegate)
{
lock (pendingCalls)
{
ManualResetEvent callDone = new ManualResetEvent(false);
pendingCalls.Enqueue(() =>
{
callDelegate();
callDone.Set();
});
pendingCallsNotEmpty.Set();
return callDone;
}
}
///
/// Wait until a call a made
///
public void WaitForCall()
{
pendingCallsNotEmpty.WaitOne();
}
public void WaitForCall(TimeSpan timeout)
{
pendingCallsNotEmpty.WaitOne(timeout, false);
}
///
/// Performs all waiting calls on the current thread
///
public void PerformAllCalls()
{
while (true)
{
if (!PerformCall())
{
return;
}
}
}
///
/// Performs all waiting calls on the current thread
///
public bool PerformCall()
{
MethodInvoker nextMethod;
lock (pendingCalls)
{
if (pendingCalls.Count > 0)
{
nextMethod = pendingCalls.Dequeue();
}
else
{
pendingCallsNotEmpty.Reset();
return false;
}
}
nextMethod();
return true;
}
public CallMethod CallMethod
{
get { return callMethod; }
set { callMethod = value; }
}
public MTA2STA()
{
targetThread = Thread.CurrentThread;
hiddenForm = new Form();
// Force handle creation
}
///
/// SoftWait waits for any of the given WaitHandles and allows processing of calls during the wait
///
public int SoftWait(params WaitHandle[] waitFor)
{
List waits = new List(waitFor)
{
this.pendingCallsNotEmpty
};
while (true)
{
int i = WaitHandle.WaitAny(waits.ToArray());
PerformAllCalls();
if (i < waits.Count - 1)
{
// If not pendingCallsNotEmpty
return i;
}
}
}
///
/// Schedules invocation of method and returns immediately
///
public WaitHandle AsyncCall(MethodInvoker callDelegate)
{
WaitHandle callDone = EnqueueCall(callDelegate);
TriggerInvoke();
return callDone;
}
public T Call(MethodInvokerWithReturnValue callDelegate)
{
T returnValue = default(T);
Call(delegate { returnValue = callDelegate(); }, true);
return returnValue;
}
public void Call(MethodInvoker callDelegate)
{
Call(callDelegate, false);
}
private void Call(MethodInvoker callDelegate, bool hasReturnValue)
{
// Enqueue the call
WaitHandle callDone = EnqueueCall(callDelegate);
if (targetThread == Thread.CurrentThread)
{
PerformAllCalls();
return;
}
// We have the call waiting in queue, we need to call it (not waiting for it to finish)
TriggerInvoke();
// Wait for the call to finish
if (!hasReturnValue && callMethod == CallMethod.HiddenFormWithTimeout)
{
// Give it 5 seconds to run
if (!callDone.WaitOne(5000, true))
{
Debug.WriteLine("Call time out! (continuing)");
Debug.WriteLine(new StackTrace(true).ToString());
}
}
else
{
callDone.WaitOne();
}
}
private void TriggerInvoke()
{
switch (callMethod)
{
case CallMethod.DirectCall:
PerformAllCalls();
break;
case CallMethod.Manual:
// Nothing we can do - someone else must call SoftWait or Pulse
break;
case CallMethod.HiddenForm:
case CallMethod.HiddenFormWithTimeout:
hiddenForm.BeginInvoke((MethodInvoker)PerformAllCalls);
break;
}
}
public static object MarshalParamTo(object param, Type outputType)
{
if (param is IntPtr)
{
return MarshalIntPtrTo((IntPtr)param, outputType);
}
return param;
}
public static T MarshalIntPtrTo(IntPtr param)
{
return (T)MarshalIntPtrTo(param, typeof(T));
}
public static object MarshalIntPtrTo(IntPtr param, Type outputType)
{
// IntPtr requested as output (must be before the null check so that we pass IntPtr.Zero)
if (outputType == typeof(IntPtr))
{
return param;
}
// The parameter is null pointer
if (param == IntPtr.Zero)
{
return null;
}
// String requested as output
if (outputType == typeof(string))
{
return Marshal.PtrToStringAuto(param);
}
// Marshal a COM object
object comObject = Marshal.GetObjectForIUnknown(param);
return Activator.CreateInstance(outputType, comObject);
}
///
/// Uses reflection to call method. Automaticaly marshals parameters.
///
/// Targed object which contains the method. In case of static mehod pass the Type
/// The name of the function to call
/// Parameters which should be send to the function. Parameters will be marshaled to proper type.
/// Return value of the called function
public static object InvokeMethod(object targetObject, string functionName, object[] functionParameters)
{
MethodInfo method;
if (targetObject is Type)
{
method = ((Type)targetObject).GetMethod(functionName);
}
else
{
method = targetObject.GetType().GetMethod(functionName);
}
ParameterInfo[] methodParamsInfo = method.GetParameters();
object[] convertedParams = new object[methodParamsInfo.Length];
for (int i = 0; i < convertedParams.Length; i++)
{
convertedParams[i] = MarshalParamTo(functionParameters[i], methodParamsInfo[i].ParameterType);
}
try
{
if (targetObject is Type)
{
return method.Invoke(null, convertedParams);
}
return method.Invoke(targetObject, convertedParams);
}
catch (Exception exception)
{
throw new Exception("Invoke of " + functionName + " failed.", exception);
}
}
}
}