Created
July 28, 2021 17:40
-
-
Save jjbrunton/d3293f496a19f60b30f9a38de4959a8c to your computer and use it in GitHub Desktop.
Thread sync
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Threading.Tasks; | |
namespace FarmerGiles.Internal | |
{ | |
using System.Runtime.InteropServices; | |
static public class ThreadSynchronizer | |
{ | |
[DllImport("user32.dll")] | |
static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong); | |
[DllImport("user32.dll")] | |
static extern int CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, int Msg, int wParam, int lParam); | |
[DllImport("user32.dll")] | |
static extern int GetWindowThreadProcessId(IntPtr handle, out int processId); | |
[DllImport("user32.dll")] | |
static extern bool IsWindowVisible(IntPtr hWnd); | |
[DllImport("user32.dll")] | |
static extern int GetWindowTextLength(IntPtr hWnd); | |
[DllImport("user32.dll")] | |
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); | |
[DllImport("user32.dll")] | |
static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam); | |
[DllImport("user32.dll")] | |
static extern int SendMessage( | |
int hWnd, | |
uint Msg, | |
int wParam, | |
int lParam | |
); | |
[DllImport("kernel32.dll")] | |
static extern uint GetCurrentThreadId(); | |
delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); | |
delegate int WindowProc(IntPtr hWnd, int Msg, int wParam, int lParam); | |
static readonly Queue<Action> actionQueue = new Queue<Action>(); | |
static readonly Queue<Delegate> delegateQueue = new Queue<Delegate>(); | |
static readonly Queue<object> returnValueQueue = new Queue<object>(); | |
const int GWL_WNDPROC = -4; | |
const int WM_USER = 0x0400; | |
static IntPtr oldCallback; | |
static WindowProc newCallback; | |
static int windowHandle; | |
static ThreadSynchronizer() | |
{ | |
EnumWindows(FindWindowProc, IntPtr.Zero); | |
newCallback = WndProc; | |
oldCallback = SetWindowLong((IntPtr)windowHandle, GWL_WNDPROC, Marshal.GetFunctionPointerForDelegate(newCallback)); | |
} | |
static public void RunOnMainThread(Action action) | |
{ | |
if (GetCurrentThreadId() == System.Diagnostics.Process.GetCurrentProcess().Threads[0].Id) | |
{ | |
action(); | |
return; | |
} | |
actionQueue.Enqueue(action); | |
SendUserMessage(); | |
} | |
static public T RunOnMainThread<T>(Func<T> function) | |
{ | |
try | |
{ | |
if (GetCurrentThreadId() == System.Diagnostics.Process.GetCurrentProcess().Threads[0].Id) | |
return function(); | |
delegateQueue.Enqueue(function); | |
SendUserMessage(); | |
return (T)returnValueQueue.Dequeue(); | |
} | |
catch (Exception e) | |
{ | |
return default(T); | |
} | |
} | |
static int WndProc(IntPtr hWnd, int msg, int wParam, int lParam) | |
{ | |
try | |
{ | |
if (msg != WM_USER) return CallWindowProc(oldCallback, hWnd, msg, wParam, lParam); | |
while (actionQueue.Count > 0) | |
actionQueue.Dequeue()?.Invoke(); | |
while (delegateQueue.Count > 0) | |
{ | |
var invokeTarget = delegateQueue.Dequeue(); | |
returnValueQueue.Enqueue(invokeTarget?.DynamicInvoke()); | |
} | |
return 0; | |
} | |
catch (Exception e) | |
{ | |
Console.WriteLine(e.ToString()); | |
} | |
return CallWindowProc(oldCallback, hWnd, msg, wParam, lParam); | |
} | |
static bool FindWindowProc(IntPtr hWnd, IntPtr lParam) | |
{ | |
GetWindowThreadProcessId(hWnd, out int procId); | |
if (procId != System.Diagnostics.Process.GetCurrentProcess().Id) return true; | |
if (!IsWindowVisible(hWnd)) return true; | |
var l = GetWindowTextLength(hWnd); | |
if (l == 0) return true; | |
var builder = new StringBuilder(l + 1); | |
GetWindowText(hWnd, builder, builder.Capacity); | |
if (builder.ToString() == "World of Warcraft") | |
windowHandle = (int)hWnd; | |
return true; | |
} | |
static void SendUserMessage() => SendMessage(windowHandle, WM_USER, 0, 0); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment