BizHawk/BizHawk.Common/Win32Hacks.cs

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();
}
}
}