//
//
//
//
// $Revision: 2207 $
//
using System;
using System.Collections.Generic;
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
{
Form hiddenForm;
IntPtr hiddenFormHandle;
System.Threading.Thread targetThread;
CallMethod callMethod = CallMethod.HiddenFormWithTimeout;
Queue pendingCalls = new Queue();
ManualResetEvent pendingCallsNotEmpty = new ManualResetEvent(false);
WaitHandle EnqueueCall(MethodInvoker callDelegate)
{
lock (pendingCalls) {
ManualResetEvent callDone = new ManualResetEvent(false);
pendingCalls.Enqueue(delegate{
callDelegate();
callDone.Set();
});
pendingCallsNotEmpty.Set();
return callDone;
}
}
///
/// Wait until a call a made
///
public void WaitForCall()
{
pendingCallsNotEmpty.WaitOne(100,false);
}
public void WaitForCall(TimeSpan timeout)
{
pendingCallsNotEmpty.WaitOne(timeout, false);
}
///
/// 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;
}
///
/// Performs all waiting calls on the current thread
///
public void PerformAllCalls()
{
try
{
while (true) {
MethodInvoker nextMethod;
lock (pendingCalls) {
if (pendingCalls.Count > 0) {
int k = pendingCalls.Count;
nextMethod = pendingCalls.Dequeue();
} else {
//pendingCalls.Clear();
pendingCallsNotEmpty.Reset();
return;
}
}
nextMethod();
}
}
catch
{
}
}
public CallMethod CallMethod {
get {
return callMethod;
}
set {
callMethod = value;
}
}
public MTA2STA()
{
targetThread = System.Threading.Thread.CurrentThread;
hiddenForm = new Form();
// Force handle creation
hiddenFormHandle = hiddenForm.Handle;
}
///
/// 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);
waits.Add(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);
}
void Call(MethodInvoker callDelegate, bool hasReturnValue)
{
// Enqueue the call
WaitHandle callDone = EnqueueCall(callDelegate);
if (targetThread == System.Threading.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(100, true)) {
System.Console.WriteLine("Call time out! Continuing...");
}
} else {
callDone.WaitOne();
}
}
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);
} else {
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 ((IntPtr)param == IntPtr.Zero) {
return null;
}
// String requested as output
if (outputType == typeof(string)) {
return Marshal.PtrToStringAuto((IntPtr)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)
{
System.Reflection.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);
} else {
return method.Invoke(targetObject, convertedParams);
}
} catch (System.Exception exception) {
throw new Debugger.DebuggerException("Invoke of " + functionName + " failed.", exception);
}
}
}
}