mirror of https://github.com/icsharpcode/ILSpy.git
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
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); |
|
} |
|
} |
|
} |
|
}
|
|
|