193 lines
6.9 KiB
C#
193 lines
6.9 KiB
C#
using System;
|
|
using System.Threading;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace BizHawk.Common
|
|
{
|
|
//largely from https://raw.githubusercontent.com/noserati/tpl/master/ThreadAffinityTaskScheduler.cs (MIT license)
|
|
public static class Win32ThreadHacks
|
|
{
|
|
internal static class NativeMethods
|
|
{
|
|
[DllImport("kernel32", SetLastError = true, ExactSpelling = true)]
|
|
public static extern Int32 WaitForSingleObject(Microsoft.Win32.SafeHandles.SafeWaitHandle handle, uint milliseconds);
|
|
|
|
public const uint QS_KEY = 0x0001;
|
|
public const uint QS_MOUSEMOVE = 0x0002;
|
|
public const uint QS_MOUSEBUTTON = 0x0004;
|
|
public const uint QS_POSTMESSAGE = 0x0008;
|
|
public const uint QS_TIMER = 0x0010;
|
|
public const uint QS_PAINT = 0x0020;
|
|
public const uint QS_SENDMESSAGE = 0x0040;
|
|
public const uint QS_HOTKEY = 0x0080;
|
|
public const uint QS_ALLPOSTMESSAGE = 0x0100;
|
|
public const uint QS_RAWINPUT = 0x0400;
|
|
|
|
public const uint QS_MOUSE = (QS_MOUSEMOVE | QS_MOUSEBUTTON);
|
|
public const uint QS_INPUT = (QS_MOUSE | QS_KEY | QS_RAWINPUT);
|
|
public const uint QS_ALLEVENTS = (QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY);
|
|
public const uint QS_ALLINPUT = (QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY | QS_SENDMESSAGE);
|
|
|
|
public const uint MWMO_INPUTAVAILABLE = 0x0004;
|
|
public const uint MWMO_WAITALL = 0x0001;
|
|
|
|
public const uint PM_REMOVE = 0x0001;
|
|
public const uint PM_NOREMOVE = 0;
|
|
|
|
public const uint WAIT_TIMEOUT = 0x00000102;
|
|
public const uint WAIT_FAILED = 0xFFFFFFFF;
|
|
public const uint INFINITE = 0xFFFFFFFF;
|
|
public const uint WAIT_OBJECT_0 = 0;
|
|
public const uint WAIT_ABANDONED_0 = 0x00000080;
|
|
public const uint WAIT_IO_COMPLETION = 0x000000C0;
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct MSG
|
|
{
|
|
public IntPtr hwnd;
|
|
public uint message;
|
|
public IntPtr wParam;
|
|
public IntPtr lParam;
|
|
public uint time;
|
|
public int x;
|
|
public int y;
|
|
}
|
|
|
|
[DllImport("user32.dll")]
|
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|
public static extern bool PeekMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg);
|
|
|
|
[DllImport("user32.dll")]
|
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|
public static extern bool PostMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
|
|
|
[DllImport("user32.dll")]
|
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|
public static extern bool SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
|
|
|
[DllImport("user32.dll")]
|
|
public static extern IntPtr DispatchMessage([In] ref MSG lpMsg);
|
|
|
|
[DllImport("user32.dll")]
|
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|
public static extern bool TranslateMessage([In] ref MSG lpMsg);
|
|
|
|
[DllImport("ole32.dll", PreserveSig = false)]
|
|
public static extern void OleInitialize(IntPtr pvReserved);
|
|
|
|
[DllImport("ole32.dll", PreserveSig = true)]
|
|
public static extern void OleUninitialize();
|
|
|
|
[DllImport("kernel32.dll")]
|
|
public static extern uint GetTickCount();
|
|
|
|
[DllImport("user32.dll")]
|
|
public static extern uint GetQueueStatus(uint flags);
|
|
|
|
[DllImport("user32.dll", SetLastError = true)]
|
|
public static extern uint MsgWaitForMultipleObjectsEx(
|
|
uint nCount, IntPtr[] pHandles, uint dwMilliseconds, uint dwWakeMask, uint dwFlags);
|
|
|
|
[DllImport("kernel32.dll", SetLastError = true)]
|
|
public static extern uint WaitForMultipleObjects(
|
|
uint nCount, IntPtr[] lpHandles, bool bWaitAll, uint dwMilliseconds);
|
|
|
|
[DllImport("kernel32.dll", SetLastError = true)]
|
|
public static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
|
|
|
|
[DllImport("kernel32.dll", SetLastError = true)]
|
|
public static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName);
|
|
|
|
[DllImport("kernel32.dll", SetLastError = true)]
|
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|
public static extern bool SetEvent(IntPtr hEvent);
|
|
|
|
[DllImport("kernel32.dll", SetLastError = true)]
|
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|
public static extern bool CloseHandle(IntPtr hObject);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Analyze the result of the native wait API
|
|
/// </summary>
|
|
static bool IsNativeWaitSuccessful(uint count, uint nativeResult, out int managedResult)
|
|
{
|
|
if (nativeResult == (NativeMethods.WAIT_OBJECT_0 + count))
|
|
{
|
|
// a is message pending, only valid for MsgWaitForMultipleObjectsEx
|
|
managedResult = unchecked((int)nativeResult);
|
|
return false;
|
|
}
|
|
|
|
if (nativeResult >= NativeMethods.WAIT_OBJECT_0 && nativeResult < (NativeMethods.WAIT_OBJECT_0 + count))
|
|
{
|
|
managedResult = unchecked((int)(nativeResult - NativeMethods.WAIT_OBJECT_0));
|
|
return true;
|
|
}
|
|
|
|
if (nativeResult >= NativeMethods.WAIT_ABANDONED_0 && nativeResult < (NativeMethods.WAIT_ABANDONED_0 + count))
|
|
{
|
|
managedResult = unchecked((int)(nativeResult - NativeMethods.WAIT_ABANDONED_0));
|
|
throw new AbandonedMutexException();
|
|
}
|
|
|
|
if (nativeResult == NativeMethods.WAIT_TIMEOUT)
|
|
{
|
|
managedResult = WaitHandle.WaitTimeout;
|
|
return false;
|
|
}
|
|
|
|
throw new InvalidOperationException();
|
|
}
|
|
|
|
/// <summary>
|
|
/// functionally the same as WaitOne, but does not message pump
|
|
/// </summary>
|
|
public static void HackyPinvokeWaitOne(WaitHandle handle)
|
|
{
|
|
NativeMethods.WaitForSingleObject(handle.SafeWaitHandle, 0xFFFFFFFF);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Functionally the same as WaitOne(), but pumps com messa
|
|
/// </summary>
|
|
/// <param name="handle"></param>
|
|
public static void HackyComWaitOne(WaitHandle handle)
|
|
{
|
|
uint nativeResult; // result of the native wait API (WaitForMultipleObjects or MsgWaitForMultipleObjectsEx)
|
|
int managedResult; // result to return from WaitHelper
|
|
|
|
IntPtr[] waitHandles = new IntPtr[]{
|
|
handle.SafeWaitHandle.DangerousGetHandle() };
|
|
uint count = 1;
|
|
|
|
uint QS_MASK = NativeMethods.QS_ALLINPUT; // message queue status
|
|
QS_MASK = 0; //bizhawk edit?? did we need any messages here?? apparently not???
|
|
|
|
// the core loop
|
|
var msg = new NativeMethods.MSG();
|
|
while (true)
|
|
{
|
|
// MsgWaitForMultipleObjectsEx with MWMO_INPUTAVAILABLE returns,
|
|
// even if there's a message already seen but not removed in the message queue
|
|
nativeResult = NativeMethods.MsgWaitForMultipleObjectsEx(
|
|
count, waitHandles,
|
|
(uint)0xFFFFFFFF,
|
|
QS_MASK,
|
|
NativeMethods.MWMO_INPUTAVAILABLE);
|
|
|
|
if (IsNativeWaitSuccessful(count, nativeResult, out managedResult) || WaitHandle.WaitTimeout == managedResult)
|
|
break;
|
|
// there is a message, pump and dispatch it
|
|
if (NativeMethods.PeekMessage(out msg, IntPtr.Zero, 0, 0, NativeMethods.PM_REMOVE))
|
|
{
|
|
NativeMethods.TranslateMessage(ref msg);
|
|
NativeMethods.DispatchMessage(ref msg);
|
|
}
|
|
}
|
|
//m64pFrameComplete.WaitOne();
|
|
}
|
|
|
|
}
|
|
|
|
} |