diff --git a/BizHawk.Common/BizHawk.Common.csproj b/BizHawk.Common/BizHawk.Common.csproj index 14419f9858..4c17748b74 100644 --- a/BizHawk.Common/BizHawk.Common.csproj +++ b/BizHawk.Common/BizHawk.Common.csproj @@ -72,6 +72,7 @@ + diff --git a/BizHawk.Common/Win32Hacks.cs b/BizHawk.Common/Win32Hacks.cs new file mode 100644 index 0000000000..5405076205 --- /dev/null +++ b/BizHawk.Common/Win32Hacks.cs @@ -0,0 +1,193 @@ +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(); + } + + } + +} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/NativeAPI/mupen64plusCoreApi.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/NativeAPI/mupen64plusCoreApi.cs index acc2c18ac8..2c8f31c0f3 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/NativeAPI/mupen64plusCoreApi.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/NativeAPI/mupen64plusCoreApi.cs @@ -566,7 +566,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64.NativeApi public void frame_advance() { m64pCoreDoCommandPtr(m64p_command.M64CMD_ADVANCE_FRAME, 0, IntPtr.Zero); - m64pFrameComplete.WaitOne(); + + //the way we should be able to do it: + //m64pFrameComplete.WaitOne(); + + //however. since this is probably an STAThread, this call results in message pumps running. + //those message pumps are only supposed to respond to critical COM stuff, but in fact they interfere with other things. + //so here are two workaround methods. + + //method 1. + //BizHawk.Common.Win32ThreadHacks.HackyPinvokeWaitOne(m64pFrameComplete); + + //method 2. + BizHawk.Common.Win32ThreadHacks.HackyComWaitOne(m64pFrameComplete); } public int SaveState(byte[] buffer)