From 9ae4a0007c318475f35b67b9eff67e6247d735ed Mon Sep 17 00:00:00 2001 From: David Hall Date: Sat, 21 Jan 2023 15:39:00 -0700 Subject: [PATCH] Reverted User32.BasicMessageWindow to previous, simpler implementation --- PInvoke/User32/BasicMessageWindow.cs | 362 ++--------------------------------- 1 file changed, 15 insertions(+), 347 deletions(-) diff --git a/PInvoke/User32/BasicMessageWindow.cs b/PInvoke/User32/BasicMessageWindow.cs index 2dff4a59..1f643ef7 100644 --- a/PInvoke/User32/BasicMessageWindow.cs +++ b/PInvoke/User32/BasicMessageWindow.cs @@ -19,30 +19,15 @@ namespace Vanara.PInvoke; [UnmanagedFunctionPointer(CallingConvention.Winapi)] public delegate bool BasicMessageWindowFilter(HWND hwnd, uint msg, IntPtr wParam, IntPtr lParam, out IntPtr lReturn); -/// An interface that represents a Win32 window with created and destroyed events. -public interface IWindowCore -{ - /// Gets the window handle. - /// The window handle. - HWND Handle { get; } - - /// Occurs when the window is created and has a valid handle. - event Action Created; - - /// Occurs when the window has been destroyed. - event Action Destroyed; -} - /// Simple window to process messages. /// /// /// -public class BasicMessageWindow : MarshalByRefObject, IDisposable, IHandle, IWindowCore +public class BasicMessageWindow : MarshalByRefObject, IDisposable, IHandle { private readonly WeakReference weakSelfRef; private SafeHWND hwnd; private bool isDisposed; - private ParamIndexer paramIndexer; private WindowClass wCls; /// Initializes a new instance of the class. @@ -54,168 +39,20 @@ public class BasicMessageWindow : MarshalByRefObject, IDisposable, IHandle, IWin hwnd = CreateWindow(); } - private BasicMessageWindow() { } - /// Finalizes an instance of the class. ~BasicMessageWindow() => Dispose(false); - /// Occurs when the window is created and has a valid handle. - public event Action Created; - - /// Occurs when the window has been destroyed. - public event Action Destroyed; - - /// - /// Gets or sets the dimensions of the bounding rectangle of the specified window. The dimensions are given in screen coordinates that - /// are relative to the upper-left corner of the screen. - /// - /// A RECT structure with the screen coordinates of the upper-left and lower-right corners of the window. - public RECT Bounds - { - get { Win32Error.ThrowLastErrorIfFalse(GetWindowRect(Handle, out RECT r)); return r; } - set => SetPosition(value.Location, value.Size); - } - - /// Gets the name of the windows class. - /// The name of the windows class. - public string ClassName => wCls?.ClassName; - - /// - /// Gets the coordinates of a window's client area. The client coordinates specify the upper-left and lower-right corners of the client - /// area. Because client coordinates are relative to the upper-left corner of a window's client area, the coordinates of the upper-left - /// corner are (0,0). - /// - /// - /// A RECT structure with the client coordinates. The left and top members are zero. The right and bottom members contain the width and - /// height of the window. - /// - public RECT ClientRect - { - get { Win32Error.ThrowLastErrorIfFalse(GetClientRect(Handle, out RECT r)); return r; } - } - - /// Gets a value indicating whether this is enabled. - /// if enabled; otherwise, . - public bool Enabled => IsWindowEnabled(Handle); - - /// Gets a value indicating whether the window has input focus. - /// if focused; otherwise, . - public bool Focused => GetFocus().Equals(Handle); - - /// Gets the window handle. - /// The window handle. + /// Gets the handle. + /// The handle. public HWND Handle => hwnd ?? HWND.NULL; /// Gets or sets the callback method used to filter window messages. /// The callback method. public BasicMessageWindowFilter MessageFilter { get; set; } - /// Gets or sets information about the window. - /// The information indexer. - public ISupportIndexer Param => paramIndexer ??= new ParamIndexer(this); - - /// - /// Gets or sets the position of the window. The dimensions are given in screen coordinates that are relative to the upper-left corner of - /// the screen. - /// - /// The position of window. - public POINT Position - { - get - { - Win32Error.ThrowLastErrorIfFalse(GetWindowRect(Handle, out RECT r)); - return r.Location; - } - set => Win32Error.ThrowLastErrorIfFalse(SetWindowPos(Handle, default, value.X, value.Y, -1, -1, SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOZORDER)); - } - - /// Gets or sets the window's show state. - /// The show state. - public ShowWindowCommand ShowState - { - get - { - if (!Visible) - return ShowWindowCommand.SW_HIDE; - if (IsIconic(Handle)) - return ShowWindowCommand.SW_MINIMIZE; - if (IsZoomed(Handle)) - return ShowWindowCommand.SW_MAXIMIZE; - return ShowWindowCommand.SW_NORMAL; - } - set => Win32Error.ThrowLastErrorIfFalse(ShowWindow(Handle, value)); - } - - /// Gets or sets the size of the window. - /// The size of window. - public SIZE Size - { - get - { - Win32Error.ThrowLastErrorIfFalse(GetWindowRect(Handle, out RECT r)); - return r.Size; - } - set => Win32Error.ThrowLastErrorIfFalse(SetWindowPos(Handle, default, -1, -1, value.cx, value.cy, SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOZORDER)); - } - - /// Gets or sets the value of the style bit for the window. - /// The styles value. - public WindowStyles Styles - { - get => (WindowStyles)Param[WindowLongFlags.GWL_STYLE].ToInt32(); - set => Param[WindowLongFlags.GWL_STYLE] = (IntPtr)(int)value; - } - - /// Gets or sets the value of the extended style bit for the window. - /// The extended styles value. - public WindowStylesEx StylesEx - { - get => (WindowStylesEx)Param[WindowLongFlags.GWL_EXSTYLE].ToInt32(); - set => Param[WindowLongFlags.GWL_EXSTYLE] = (IntPtr)(int)value; - } - - /// Gets or sets the text of the window's title bar (if it has one). - /// The text of the title bar. - public string Text - { - get - { - if (!Handle.IsNull) - { - int len = GetWindowTextLength(Handle); - if (len > 0) - { - StringBuilder sb = new(len); - if (GetWindowText(Handle, sb, sb.Capacity) > 0) - { - return sb.ToString(); - } - } - } - return string.Empty; - } - set => Win32Error.ThrowLastErrorIfFalse(SetWindowText(Handle, value)); - } - - /// Gets or sets a value indicating whether this window is visible. - /// if visible; otherwise, . - public bool Visible - { - get => IsWindowVisible(Handle); - set => ShowState = value ? ShowWindowCommand.SW_SHOW : ShowWindowCommand.SW_HIDE; - } - - /// Converts a rectangle from this window's client coordinates to screen coordinates. - /// A in client coordinates. - /// The resulting in screen coordinates. - public virtual RECT ClientToScreen(in RECT clientRect) - { - RECT screenRect = clientRect; - SetLastError(0); - Win32Error.ThrowLastErrorIf(MapWindowPoints(Handle, IntPtr.Zero, ref screenRect), i => i == 0); - Win32Error.ThrowLastErrorIfFalse(AdjustWindowRectEx(ref screenRect, Styles, GetMenu(Handle) != IntPtr.Zero, StylesEx)); - return screenRect; - } + /// Gets the name of the class. + /// The name of the class. + public string ClassName => wCls?.ClassName; /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. public void Dispose() @@ -224,157 +61,21 @@ public class BasicMessageWindow : MarshalByRefObject, IDisposable, IHandle, IWin System.GC.SuppressFinalize(this); } - /// Sets input focus to the window. - public void Focus() => Win32Error.ThrowLastErrorIf(SetFocus(Handle), h => h.IsNull); - - /// - /// Invalidates the specified region of the windows (adds it to the window's update region, which is the area that will be repainted at - /// the next paint operation), and causes a paint message to be sent to the window. Optionally, invalidates the child windows assigned to - /// the window. - /// - /// to invalidate the window's child windows; otherwise, . - /// - /// A that represents the region to invalidate. This value can be , in which case the entire - /// client region is invalidated. - /// - public void Invalidate(bool erase = false, PRECT pRect = null) => Win32Error.ThrowLastErrorIfFalse(InvalidateRect(Handle, pRect, erase)); - - /// Converts a rectangle from screen coordinates to this window's client coordinates. - /// A in screen coordinates. - /// The resulting in client coordinates. - public virtual RECT ScreenToClient(in RECT screenRect) - { - RECT clientRect = screenRect; - SetLastError(0); - Win32Error.ThrowLastErrorIf(MapWindowPoints(IntPtr.Zero, Handle, ref clientRect), i => i == 0); - RECT invRect = default; - Win32Error.ThrowLastErrorIfFalse(AdjustWindowRectEx(ref invRect, Styles, GetMenu(Handle) != IntPtr.Zero, StylesEx)); - SubtractRect(out clientRect, clientRect, invRect); - return clientRect; - } - - /// - /// Changes the size, position, and Z order of a child, pop-up, or top-level window. These windows are ordered according to their - /// appearance on the screen. The topmost window receives the highest rank and is the first window in the Z order. - /// - /// - /// Type: HWND - /// - /// A handle to the window to precede the positioned window in the Z order. This parameter must be a window handle or one of the - /// following values. - /// - /// - /// - /// Value - /// Meaning - /// - /// - /// HWND_BOTTOM (HWND)1 - /// - /// Places the window at the bottom of the Z order. If the hWnd parameter identifies a topmost window, the window loses its topmost - /// status and is placed at the bottom of all other windows. - /// - /// - /// - /// HWND_NOTOPMOST (HWND)-2 - /// - /// Places the window above all non-topmost windows (that is, behind all topmost windows). This flag has no effect if the window is - /// already a non-topmost window. - /// - /// - /// - /// HWND_TOP (HWND)0 - /// Places the window at the top of the Z order. - /// - /// - /// HWND_TOPMOST (HWND)-1 - /// Places the window above all non-topmost windows. The window maintains its topmost position even when it is deactivated. - /// - /// - /// For more information about how this parameter is used, see the following Remarks section. - /// - /// The new position of the window, in client coordinates. - /// The new width of the window, in pixels. - /// The window sizing and positioning flags. This parameter can be one of more values from . - /// - /// Type: Type: BOOL - /// If the function succeeds, the return value is nonzero. - /// If the function fails, the return value is zero. To get extended error information, call GetLastError. - /// - /// - /// - /// As part of the Vista re-architecture, all services were moved off the interactive desktop into Session 0. hwnd and window manager - /// operations are only effective inside a session and cross-session attempts to manipulate the hwnd will fail. For more information, see - /// The Windows Vista Developer Story: Application Compatibility Cookbook. - /// - /// - /// If you have changed certain window data using SetWindowLong, you must call SetWindowPos for the changes to take effect. Use - /// the following combination for uFlags: . - /// - /// - /// A window can be made a topmost window either by setting the hWndInsertAfter parameter to HWND_TOPMOST and ensuring that the - /// SWP_NOZORDER flag is not set, or by setting a window's position in the Z order so that it is above any existing topmost - /// windows. When a non-topmost window is made topmost, its owned windows are also made topmost. Its owners, however, are not changed. - /// - /// - /// If neither the SWP_NOACTIVATE nor SWP_NOZORDER flag is specified (that is, when the application requests that a window - /// be simultaneously activated and its position in the Z order changed), the value specified in hWndInsertAfter is used only in the - /// following circumstances. - /// - /// - /// - /// Neither the HWND_TOPMOST nor HWND_NOTOPMOST flag is specified in hWndInsertAfter. - /// - /// - /// The window identified by hWnd is not the active window. - /// - /// - /// - /// An application cannot activate an inactive window without also bringing it to the top of the Z order. Applications can change an - /// activated window's position in the Z order without restrictions, or it can activate a window and then move it to the top of the - /// topmost or non-topmost windows. - /// - /// - /// If a topmost window is repositioned to the bottom ( HWND_BOTTOM) of the Z order or after any non-topmost window, it is no - /// longer topmost. When a topmost window is made non-topmost, its owners and its owned windows are also made non-topmost windows. - /// - /// - /// A non-topmost window can own a topmost window, but the reverse cannot occur. Any window (for example, a dialog box) owned by a - /// topmost window is itself made a topmost window, to ensure that all owned windows stay above their owner. - /// - /// If an application is not in the foreground, and should be in the foreground, it must call the SetForegroundWindow function. - /// To use SetWindowPos to bring a window to the top, the process that owns the window must have SetForegroundWindow permission. - /// - public void SetPosition(POINT position, SIZE size, HWND hWndInsertAfter = default, SetWindowPosFlags flags = SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_NOZORDER) => - Win32Error.ThrowLastErrorIfFalse(SetWindowPos(Handle, hWndInsertAfter, position.X, position.Y, size.cx, size.cy, flags)); - - /// - /// The Validate function validates the client area within a rectangle by removing the rectangle from the update region of the - /// this window. - /// - /// - /// A structure that contains the client coordinates of the rectangle to be removed from the update region. If this - /// parameter is , the entire client area is removed. - /// - public void Validate(PRECT pRect = null) => Win32Error.ThrowLastErrorIfFalse(ValidateRect(Handle, pRect)); - /// 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. + /// + /// Method used to create the window. When overriding, the hWnd field must be set to the handle of the created window. + /// protected virtual SafeHWND CreateWindow() { lock (this) { if (!Handle.IsNull) - { throw new InvalidOperationException("Window handle already exists."); - } - - HINSTANCE hInst = GetModuleHandle(); - wCls = new WindowClass($"{GetType().Name}+{Guid.NewGuid()}", hInst, WndProc); - return Win32Error.ThrowLastErrorIfInvalid(CreateWindowEx(0, wCls.Atom, hWndParent: HWND.HWND_MESSAGE, hInstance: hInst)); + wCls = new WindowClass($"{GetType().Name}+{Guid.NewGuid()}", default, WndProc, hbrBkgd: WindowClass.NullBrush); + return Win32Error.ThrowLastErrorIfInvalid(CreateWindowEx(0, wCls.ClassName, hWndParent: HWND.HWND_MESSAGE, hInstance: wCls.wc.hInstance)); } } @@ -386,7 +87,6 @@ public class BasicMessageWindow : MarshalByRefObject, IDisposable, IHandle, IWin { if (isDisposed) return; - isDisposed = true; hwnd?.Dispose(); // Calls DestroyWindow @@ -398,40 +98,8 @@ public class BasicMessageWindow : MarshalByRefObject, IDisposable, IHandle, IWin { if (msg == (uint)WindowMessage.WM_NCCREATE) return (IntPtr)1; - - try - { - return !weakSelfRef.IsAlive || weakSelfRef.Target is null || MessageFilter is null || !MessageFilter.Invoke(hwnd, msg, wParam, lParam, out IntPtr lRet) - ? DefWindowProc(hwnd, msg, wParam, lParam) : lRet; - } - finally - { - switch ((WindowMessage)msg) - { - case WindowMessage.WM_CREATE: - Created?.Invoke(); - break; - case WindowMessage.WM_NCDESTROY: - Destroyed?.Invoke(); - break; - } - } + return !weakSelfRef.IsAlive || weakSelfRef.Target is null || MessageFilter is null || !MessageFilter.Invoke(hwnd, msg, wParam, lParam, out IntPtr lRet) + ? DefWindowProc(hwnd, msg, wParam, lParam) + : lRet; } - - private class ParamIndexer : ISupportIndexer - { - private readonly BasicMessageWindow win; - - public ParamIndexer(BasicMessageWindow win) => this.win = win; - - public IntPtr this[WindowLongFlags flag] - { - get => GetWindowLongAuto(win.Handle, flag); - set - { - SetLastError(0); - Win32Error.ThrowLastErrorIf(SetWindowLong(win.Handle, flag, (int)value), i => i == 0); - } - } - } -} \ No newline at end of file +}