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); } /// /// Analyze the result of the native wait API /// 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(); } /// /// functionally the same as WaitOne, but does not message pump /// public static void HackyPinvokeWaitOne(WaitHandle handle) { NativeMethods.WaitForSingleObject(handle.SafeWaitHandle, 0xFFFFFFFF); } /// /// Functionally the same as WaitOne(), but pumps com messa /// /// 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(); } } }