From 6aa6d3fd70b1f06e31616fa0cc75a1d5a3aa0bf8 Mon Sep 17 00:00:00 2001 From: dahall Date: Sun, 20 Dec 2020 08:29:09 -0700 Subject: [PATCH] More work and tests for BasicMessageWindow --- PInvoke/User32/BasicMessageWindow.cs | 77 ++++++++------------------------- UnitTests/PInvoke/User32/User32Tests.cs | 21 +++++++++ 2 files changed, 40 insertions(+), 58 deletions(-) diff --git a/PInvoke/User32/BasicMessageWindow.cs b/PInvoke/User32/BasicMessageWindow.cs index 39796a7d..7f8fab54 100644 --- a/PInvoke/User32/BasicMessageWindow.cs +++ b/PInvoke/User32/BasicMessageWindow.cs @@ -1,9 +1,5 @@ using System; -using System.Collections.Generic; using System.Runtime.InteropServices; -using Vanara.Extensions; -using Vanara.InteropServices; -using static Vanara.PInvoke.Gdi32; using static Vanara.PInvoke.Kernel32; using static Vanara.PInvoke.User32; @@ -15,13 +11,9 @@ namespace Vanara.PInvoke /// public class BasicMessageWindow : MarshalByRefObject, IDisposable, IHandle { - private static readonly Dictionary s_lookup = new Dictionary(); - private static readonly WindowProc s_WndProc = new WindowProc(WndProc); + private SafeHWND hwnd; private bool isDisposed; - /// The safe handle of the registered and created window. - protected SafeHWND hWnd; - /// Initializes a new instance of the class. /// /// Specifies the callback method to use to process messages. A value will just use DefWindowProc. @@ -29,17 +21,13 @@ namespace Vanara.PInvoke public BasicMessageWindow(WindowProc callback = null) { Callback = callback; - ClassName = "MessageWindowBaseClass+" + Guid.NewGuid().ToString(); + ClassName = $"MessageWindowBase+{Guid.NewGuid()}"; - CreateWindow(); + hwnd = CreateWindow(); } /// Finalizes an instance of the class. - ~BasicMessageWindow() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: false); - } + ~BasicMessageWindow() => Dispose(false); /// Gets or sets the callback method used to filter window messages. /// The callback method. @@ -51,11 +39,7 @@ namespace Vanara.PInvoke /// Gets the handle. /// The handle. - public HWND Handle => hWnd; - - /// Returns the value of the handle field. - /// An IntPtr representing the value of the handle field. - public IntPtr DangerousGetHandle() => ((IHandle)hWnd).DangerousGetHandle(); + public HWND Handle => hwnd; /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. public void Dispose() @@ -64,23 +48,18 @@ namespace Vanara.PInvoke System.GC.SuppressFinalize(this); } + /// Returns the value of the handle field. + /// An IntPtr representing the value of the handle field. + IntPtr IHandle.DangerousGetHandle() => (IntPtr)Handle; + /// /// Method used to create the window. When overriding, the hWnd field must be set to the handle of the created window. /// - protected virtual void CreateWindow() + protected virtual SafeHWND CreateWindow() { - var wc = new WNDCLASSEX - { - cbSize = (uint)Marshal.SizeOf(typeof(WNDCLASSEX)), - lpfnWndProc = s_WndProc, - hInstance = GetModuleHandle(), - lpszClassName = ClassName, - }; - - RegisterClassEx(wc); - - using var pinnedThisPtr = new PinnedObject(this); - hWnd = CreateWindowEx(lpClassName: ClassName, lpWindowName: "", hWndParent: HWND.HWND_MESSAGE, lpParam: pinnedThisPtr); + if (0 == RegisterClassEx(new WNDCLASSEX { cbSize = (uint)Marshal.SizeOf(typeof(WNDCLASSEX)), lpfnWndProc = WndProc, hInstance = GetModuleHandle(), lpszClassName = ClassName })) + Win32Error.ThrowLastError(); + return Win32Error.ThrowLastErrorIfInvalid(CreateWindowEx(lpClassName: ClassName, lpWindowName: ClassName, hWndParent: HWND.HWND_MESSAGE)); } /// Releases unmanaged and - optionally - managed resources. @@ -93,37 +72,19 @@ namespace Vanara.PInvoke return; isDisposed = true; - hWnd.Dispose(); + + hwnd?.Dispose(); + UnregisterClass(ClassName, GetModuleHandle()); ClassName = null; } - private static IntPtr WndProc(HWND hwnd, uint msg, IntPtr wParam, IntPtr lParam) + private IntPtr WndProc(HWND hwnd, uint msg, IntPtr wParam, IntPtr lParam) { - BasicMessageWindow hwndWrapper = null; - - if (msg == (uint)WindowMessage.WM_CREATE) - { - var createStruct = lParam.ToStructure(); - GCHandle gcHandle = GCHandle.FromIntPtr(createStruct.lpCreateParams); - hwndWrapper = (BasicMessageWindow)gcHandle.Target; - s_lookup.Add(hwnd, hwndWrapper); - } - else if (!s_lookup.TryGetValue(hwnd, out hwndWrapper)) - { - return DefWindowProc(hwnd, msg, wParam, lParam); - } - - if (hwndWrapper is null) - throw new InvalidOperationException(); - - var ret = (hwndWrapper.Callback ?? DefWindowProc)(hwnd, msg, wParam, lParam); + var ret = (Callback ?? DefWindowProc).Invoke(hwnd, msg, wParam, lParam); if (msg == (uint)WindowMessage.WM_NCDESTROY) - { - hwndWrapper.Dispose(true); - System.GC.SuppressFinalize(hwndWrapper); - } + Dispose(true); return ret; } diff --git a/UnitTests/PInvoke/User32/User32Tests.cs b/UnitTests/PInvoke/User32/User32Tests.cs index ceacdd81..1a17fb44 100644 --- a/UnitTests/PInvoke/User32/User32Tests.cs +++ b/UnitTests/PInvoke/User32/User32Tests.cs @@ -23,6 +23,27 @@ namespace Vanara.PInvoke.Tests TestContext.WriteLine($"{array[i].dwID} = {array[i].dwWant} / {array[i].dwBlock}"); } + [Test] + public void WinTest() + { + var timer = System.Diagnostics.Stopwatch.StartNew(); + var gotMsg = false; + using (var win = new Vanara.PInvoke.BasicMessageWindow(meth)) + { + for (int i = 0; i < 100; i++) + System.Threading.Thread.Sleep(20); + } + timer.Stop(); + Assert.True(gotMsg); + + IntPtr meth(HWND hwnd, uint uMsg, IntPtr wParam, IntPtr lParam) + { + TestContext.WriteLine($"{timer.ElapsedMilliseconds} Message: {(WindowMessage)uMsg} ({uMsg})"); + gotMsg = true; + return DefWindowProc(hwnd, uMsg, wParam, lParam); + } + } + [Test()] public void GetWindowLongTest() {