From 7bf4c15e506b0a768da775204f6ea25d60fff2c5 Mon Sep 17 00:00:00 2001 From: David Hall Date: Mon, 16 Jan 2023 16:41:23 -0700 Subject: [PATCH] Added MessagePump and ExaminedMessagePump as means to process Get/PeekMessage->DispatchMessage loops. --- PInvoke/User32/MessagePump.cs | 105 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 PInvoke/User32/MessagePump.cs diff --git a/PInvoke/User32/MessagePump.cs b/PInvoke/User32/MessagePump.cs new file mode 100644 index 00000000..e4b49ed7 --- /dev/null +++ b/PInvoke/User32/MessagePump.cs @@ -0,0 +1,105 @@ +using static Vanara.PInvoke.User32; + +namespace Vanara.PInvoke; + +/// Delegate for a method that processes a structure. +/// The structure to process. +public delegate void MsgPumpDelegate(ref MSG msg); + +/// +/// Delegate for a method that processes a structure and returns a value determining if the next step should be processed. +/// +/// The structure to process. +/// if the pump should proceed; otherwise . +public delegate bool MsgPumpPredicateDelegate(ref MSG msg); + +/// Interface defining a message pump. +public interface IMessagePump +{ + /// Runs the message pump on the optionally specified window. + /// The window instance. + /// + /// The result of or . + /// + int Run(IWindowCore mainWindow = null); +} + +/// A basic message pump to use independently or with a window instance. +/// +/// Simple example of a window creation and message pump. +/// using (var win = new BasicMessageWindow() { Text = "Title", Visible = true }) +/// return new MessagePump().Run(win); +/// +public class MessagePump : IMessagePump +{ + /// Easy access to WM_QUIT value. + protected const ushort quitMsg = (ushort)WindowMessage.WM_QUIT; + + /// + public int Run(IWindowCore mainWindow = null) + { + if (mainWindow is not null) + mainWindow.Destroyed += onDestroy; + + try + { + return RunLoop(); + } + finally + { + if (mainWindow is not null) + mainWindow.Destroyed -= onDestroy; + } + static void onDestroy() => PostQuitMessage(0); + } + + /// Defines and executes the message pump. + /// + /// The result of or . + /// + protected virtual int RunLoop() + { + int bRet; + while ((bRet = GetMessage(out MSG msg)) != 0) + { + if (bRet == -1) + Win32Error.ThrowLastError(); + TranslateMessage(msg); + DispatchMessage(msg); + } + return bRet; + } +} + +/// A message pump with events to process each step to use independently or with a window instance. +/// +public class ExaminedMessagePump : MessagePump +{ + /// Occurs after . + public event MsgPumpDelegate PostProcess; + + /// Occurs after and determines if message should be dispatched. + public event MsgPumpPredicateDelegate PostTranslate; + + /// + /// Occurs after and determines if message should be translated or dispatched. + /// + public event MsgPumpPredicateDelegate PreProcess; + + /// + protected override int RunLoop() + { + MSG msg; + do + { + if (PeekMessage(out msg, default, 0, 0, PM.PM_REMOVE) && (PreProcess?.Invoke(ref msg) ?? true)) + { + TranslateMessage(msg); + if (PostTranslate?.Invoke(ref msg) ?? true) + DispatchMessage(msg); + PostProcess?.Invoke(ref msg); + } + } while (Macros.LOWORD(msg.message) != quitMsg); + return 0; + } +} \ No newline at end of file