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)