.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

252 lines
6.8 KiB

// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
namespace Debugger
{
public delegate T MethodInvokerWithReturnValue<T>();
public enum CallMethod {DirectCall, Manual, HiddenForm, HiddenFormWithTimeout};
public class MTA2STA
{
Form hiddenForm;
IntPtr hiddenFormHandle;
System.Threading.Thread targetThread;
CallMethod callMethod = CallMethod.HiddenFormWithTimeout;
Queue<MethodInvoker> pendingCalls = new Queue<MethodInvoker>();
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;
}
}
/// <summary>
/// Wait until a call a made
/// </summary>
public void WaitForCall()
{
pendingCallsNotEmpty.WaitOne();
}
public void WaitForCall(TimeSpan timeout)
{
pendingCallsNotEmpty.WaitOne(timeout, false);
}
/// <summary>
/// Performs all waiting calls on the current thread
/// </summary>
public void PerformAllCalls()
{
while (true) {
if (!PerformCall()) {
return;
}
}
}
/// <summary>
/// Performs all waiting calls on the current thread
/// </summary>
/// <remarks>
/// Private - user should always drain the queue - otherwise Prcoess.RaisePausedEvents might fail
/// </remarks>
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 = System.Threading.Thread.CurrentThread;
hiddenForm = new Form();
// Force handle creation
hiddenFormHandle = hiddenForm.Handle;
}
/// <summary>
/// SoftWait waits for any of the given WaitHandles and allows processing of calls during the wait
/// </summary>
public int SoftWait(params WaitHandle[] waitFor)
{
List<WaitHandle> waits = new List<WaitHandle> (waitFor);
waits.Add(pendingCallsNotEmpty);
while(true) {
int i = WaitHandle.WaitAny(waits.ToArray());
PerformAllCalls();
if (i < waits.Count - 1) { // If not pendingCallsNotEmpty
return i;
}
}
}
/// <summary>
/// Schedules invocation of method and returns immediately
/// </summary>
public WaitHandle AsyncCall(MethodInvoker callDelegate)
{
WaitHandle callDone = EnqueueCall(callDelegate);
TriggerInvoke();
return callDone;
}
public T Call<T>(MethodInvokerWithReturnValue<T> 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(5000, true)) {
System.Console.WriteLine("Call time out! (continuing)");
System.Console.WriteLine(new System.Diagnostics.StackTrace(true).ToString());
}
} 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<T>(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);
Debugger.Interop.TrackedComObjects.Track(comObject);
return comObject;
}
/// <summary>
/// Uses reflection to call method. Automaticaly marshals parameters.
/// </summary>
/// <param name="targetObject">Targed object which contains the method. In case of static mehod pass the Type</param>
/// <param name="functionName">The name of the function to call</param>
/// <param name="functionParameters">Parameters which should be send to the function. Parameters will be marshaled to proper type.</param>
/// <returns>Return value of the called function</returns>
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);
}
}
}
}