From 3aa52d622fee1061d6b19d4c0446141f30a71710 Mon Sep 17 00:00:00 2001 From: David Hall Date: Fri, 6 Oct 2023 19:35:05 -0600 Subject: [PATCH] Added nullability to Magnification and tests --- PInvoke/Magnification/Magnification.cs | 73 +++++++-------- PInvoke/User32/WindowBase.cs | 2 +- .../PInvoke/Magnification/MagnificationTests.cs | 101 +++++++++++++-------- 3 files changed, 98 insertions(+), 78 deletions(-) diff --git a/PInvoke/Magnification/Magnification.cs b/PInvoke/Magnification/Magnification.cs index ee63c6ee..8e10a65e 100644 --- a/PInvoke/Magnification/Magnification.cs +++ b/PInvoke/Magnification/Magnification.cs @@ -394,6 +394,38 @@ public static partial class Magnification [return: MarshalAs(UnmanagedType.Bool)] public static extern bool MagSetColorEffect(HWND hwnd, in MAGCOLOREFFECT pEffect); + /// Sets the color transformation matrix for a magnifier control. + /// + /// Type: HWND + /// The magnification window. + /// + /// + /// Type: PMAGCOLOREFFECT + /// The color transformation matrix, or NULL to remove the current color effect, if any. + /// + /// + /// Type: BOOL + /// Returns TRUE if successful, or FALSE otherwise. + /// + /// + /// + /// The magnifier control uses the color transformation matrix to apply a color effect to the entire magnifier window. If the + /// function is called multiple times, the most recent color transform is used. + /// + /// This function requires Windows Display Driver Model (WDDM)-capable video cards. + /// Examples + /// The following example sets a color transformation matrix that converts the colors displayed in the magnifier to grayscale. + /// + /// // Description: // Converts the colors displayed in the magnifier window to grayscale, or // returns the colors to normal. // Parameters: // hwndMag - Handle of the magnifier control. // fInvert - TRUE to convert to grayscale, or FALSE for normal colors. // BOOL ConvertToGrayscale(HWND hwndMag, BOOL fConvert) { // Convert the screen colors in the magnifier window. if (fConvert) { MAGCOLOREFFECT magEffectGrayscale = {{ // MagEffectGrayscale { 0.3f, 0.3f, 0.3f, 0.0f, 0.0f }, { 0.6f, 0.6f, 0.6f, 0.0f, 0.0f }, { 0.1f, 0.1f, 0.1f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 0.0f, 0.0f, 1.0f } }}; return MagSetColorEffect(hwndMag, &magEffectGrayscale); } // Return the colors to normal. else { return MagSetColorEffect(hwndMag, NULL); } } + /// + /// + // https://docs.microsoft.com/en-us/windows/win32/api/magnification/nf-magnification-magsetcoloreffect BOOL MagSetColorEffect( HWND + // hwnd, PMAGCOLOREFFECT pEffect ); + [DllImport(Lib_Magnification, SetLastError = false, ExactSpelling = true)] + [PInvokeData("magnification.h", MSDNShortId = "NF:magnification.MagSetColorEffect")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool MagSetColorEffect(HWND hwnd, [In, Optional] IntPtr pEffect); + /// Changes the color transformation matrix associated with the full-screen magnifier. /// /// Type: PMAGCOLOREFFECT @@ -529,7 +561,7 @@ public static partial class Magnification [DllImport(Lib_Magnification, SetLastError = false, ExactSpelling = true)] [PInvokeData("magnification.h", MSDNShortId = "NF:magnification.MagSetImageScalingCallback")] [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool MagSetImageScalingCallback(HWND hwnd, MagImageScalingCallback callback); + public static extern bool MagSetImageScalingCallback(HWND hwnd, MagImageScalingCallback? callback); /// /// Sets the current active input transformation for pen and touch input, represented as a source rectangle and a destination rectangle. @@ -579,7 +611,7 @@ public static partial class Magnification [DllImport(Lib_Magnification, SetLastError = true, ExactSpelling = true)] [PInvokeData("magnification.h", MSDNShortId = "NF:magnification.MagSetInputTransform")] [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool MagSetInputTransform([MarshalAs(UnmanagedType.Bool)] bool fEnabled, [Optional] in RECT pRectSource, [Optional] in RECT pRectDest); + public static extern bool MagSetInputTransform([MarshalAs(UnmanagedType.Bool)] bool fEnabled, [In, Optional] PRECT? pRectSource, [In, Optional] PRECT? pRectDest); /// Sets the list of windows to be magnified or the list of windows to be excluded from magnification. /// @@ -737,7 +769,7 @@ public static partial class Magnification // { float transform[5][5]; } MAGCOLOREFFECT, *PMAGCOLOREFFECT; [PInvokeData("magnification.h", MSDNShortId = "NS:magnification.tagMAGCOLOREFFECT")] [StructLayout(LayoutKind.Sequential)] - public struct MAGCOLOREFFECT + public struct MAGCOLOREFFECT : IEquatable { private const int dimLen = 5; @@ -916,41 +948,6 @@ public static partial class Magnification public static bool operator !=(MAGCOLOREFFECT lhs, MAGCOLOREFFECT rhs) => !(lhs == rhs); /// An Identity Matrix for MAGCOLOREFFECT. - -/* Unmerged change from project 'Vanara.PInvoke.Magnification (net48)' -Before: - public static readonly MAGCOLOREFFECT Identity = new MAGCOLOREFFECT { transform00 = 1, transform11 = 1, transform22 = 1, transform33 = 1, transform44 = 1 }; -After: - public static readonly MAGCOLOREFFECT Identity = new() { transform00 = 1, transform11 = 1, transform22 = 1, transform33 = 1, transform44 = 1 }; -*/ - -/* Unmerged change from project 'Vanara.PInvoke.Magnification (net7.0)' -Before: - public static readonly MAGCOLOREFFECT Identity = new MAGCOLOREFFECT { transform00 = 1, transform11 = 1, transform22 = 1, transform33 = 1, transform44 = 1 }; -After: - public static readonly MAGCOLOREFFECT Identity = new() { transform00 = 1, transform11 = 1, transform22 = 1, transform33 = 1, transform44 = 1 }; -*/ - -/* Unmerged change from project 'Vanara.PInvoke.Magnification (netcoreapp3.1)' -Before: - public static readonly MAGCOLOREFFECT Identity = new MAGCOLOREFFECT { transform00 = 1, transform11 = 1, transform22 = 1, transform33 = 1, transform44 = 1 }; -After: - public static readonly MAGCOLOREFFECT Identity = new() { transform00 = 1, transform11 = 1, transform22 = 1, transform33 = 1, transform44 = 1 }; -*/ - -/* Unmerged change from project 'Vanara.PInvoke.Magnification (netstandard2.0)' -Before: - public static readonly MAGCOLOREFFECT Identity = new MAGCOLOREFFECT { transform00 = 1, transform11 = 1, transform22 = 1, transform33 = 1, transform44 = 1 }; -After: - public static readonly MAGCOLOREFFECT Identity = new() { transform00 = 1, transform11 = 1, transform22 = 1, transform33 = 1, transform44 = 1 }; -*/ - -/* Unmerged change from project 'Vanara.PInvoke.Magnification (net45)' -Before: - public static readonly MAGCOLOREFFECT Identity = new MAGCOLOREFFECT { transform00 = 1, transform11 = 1, transform22 = 1, transform33 = 1, transform44 = 1 }; -After: - public static readonly MAGCOLOREFFECT Identity = new() { transform00 = 1, transform11 = 1, transform22 = 1, transform33 = 1, transform44 = 1 }; -*/ public static readonly MAGCOLOREFFECT Identity = new() { transform00 = 1, transform11 = 1, transform22 = 1, transform33 = 1, transform44 = 1 }; } diff --git a/PInvoke/User32/WindowBase.cs b/PInvoke/User32/WindowBase.cs index 0f58dcef..83796bf9 100644 --- a/PInvoke/User32/WindowBase.cs +++ b/PInvoke/User32/WindowBase.cs @@ -291,7 +291,7 @@ public class WindowBase : MarshalByRefObject, IDisposable, IWindowInstance, IWin /// Performs an implicit conversion from to . /// The instance. /// The result of the conversion. - public static implicit operator HWND(WindowBase w) => w.Handle; + public static implicit operator HWND(WindowBase? w) => w?.Handle ?? HWND.NULL; [DebuggerStepThrough] private void CheckDetached() diff --git a/UnitTests/PInvoke/Magnification/MagnificationTests.cs b/UnitTests/PInvoke/Magnification/MagnificationTests.cs index 337ecdcb..71a8bd7b 100644 --- a/UnitTests/PInvoke/Magnification/MagnificationTests.cs +++ b/UnitTests/PInvoke/Magnification/MagnificationTests.cs @@ -1,6 +1,8 @@ using NUnit.Framework; using NUnit.Framework.Internal; +using System.Diagnostics; using static Vanara.PInvoke.Magnification; +using static Vanara.PInvoke.User32; namespace Vanara.PInvoke.Tests; @@ -14,6 +16,13 @@ public class MagnificationTests { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 0.0f, 0.0f, 1.0f }}); + //static readonly MAGCOLOREFFECT invert = new(new[,] { + // { -1.0f, 0.0f, 0.0f, 0.0f, 0.0f }, + // { 0.0f, -1.0f, 0.0f, 0.0f, 0.0f }, + // { 0.0f, 0.0f, -1.0f, 0.0f, 0.0f }, + // { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f }, + // { 1.0f, 1.0f, 1.0f, 0.0f, 1.0f }}); + [OneTimeSetUp] public void _Setup() { @@ -26,6 +35,58 @@ public class MagnificationTests Assert.IsTrue(MagUninitialize()); } + [Test] + public void GetSetMagEffect() + { + VisibleWindow? hwndHost = null; + HWND hwndMag = default; + new System.Threading.Thread(() => + { + const string WindowClassName = "Magnification Test"; + + HINSTANCE hInst = Kernel32.GetModuleHandle().ReleaseOwnership(); + hwndHost = new VisibleWindow(WindowClassName, hInst, WindowClassName, new SIZE(300, 200), exStyle: WindowStylesEx.WS_EX_TOPMOST | WindowStylesEx.WS_EX_LAYERED | WindowStylesEx.WS_EX_TRANSPARENT); + Assert.False(hwndHost.Handle.IsNull); + + // Make the window opaque. + SetLayeredWindowAttributes(hwndHost, 0, 255, LayeredWindowAttributes.LWA_ALPHA); + + // Create a magnifier control that fills the client area. + var r = hwndHost.ClientRect; + var tmp = CreateWindow(WC_MAGNIFIER, "MagnifierWindow", WindowStyles.WS_CHILD | (WindowStyles)MagnifierStyles.MS_SHOWMAGNIFIEDCURSOR | WindowStyles.WS_VISIBLE, + 0, 0, r.Width, r.Height, hwndHost, default, hInst); + if ((hwndMag = tmp.ReleaseOwnership()) != HWND.NULL) + { + hwndHost.Show(); + new MessagePump().Run(hwndHost); + } + }).Start(); + System.Threading.Thread.Yield(); + System.Threading.Thread.Sleep(500); + + try + { + Assert.That(hwndMag, ResultIs.ValidHandle); + + Assert.That(MagGetColorEffect(GetDesktopWindow(), out var eff), ResultIs.FailureCode(Win32Error.ERROR_NOT_SUPPORTED)); + Assert.That(MagSetColorEffect(GetDesktopWindow(), eff), ResultIs.FailureCode(Win32Error.ERROR_NOT_SUPPORTED)); + + MAGTRANSFORM matrix = new(2f); + Assert.That(MagSetWindowTransform(hwndMag, matrix), ResultIs.Successful); + Assert.That(MagGetWindowTransform(hwndMag, out var m2), ResultIs.Successful); + Assert.AreEqual(matrix, m2); + + Assert.That(MagGetColorEffect(hwndMag, out eff), ResultIs.Successful); + Assert.True(eff.IsIdentity); + Assert.That(MagSetColorEffect(hwndMag, grayeff), ResultIs.Successful); + Assert.That(MagSetColorEffect(hwndMag), ResultIs.Successful); + } + finally + { + hwndHost?.Close(); + } + } + [Test] public void MagGetSetFullscreenTransformTest() { @@ -47,7 +108,7 @@ public class MagnificationTests { Assert.That(MagSetFullscreenColorEffect(grayeff), ResultIs.Successful); Assert.That(MagGetFullscreenColorEffect(out var grayeff_get), ResultIs.Successful); - Assert.That(grayeff.transform, Is.EquivalentTo(grayeff_get.transform)); + Assert.AreEqual(grayeff, grayeff_get); System.Threading.Thread.Sleep(1000); Assert.That(MagSetFullscreenColorEffect(MAGCOLOREFFECT.Identity), ResultIs.Successful); } @@ -110,42 +171,4 @@ public class MagnificationTests Assert.AreEqual(11.2f, tfx[1, 2]); Assert.AreEqual(12.2f, tfx[2, 2]); } - - /* - private static bool CreateMagnifier(HINSTANCE hInstance) - { - const string WindowClassName = "Magnification Test"; - - // Register the host window class. - var wcex = new WNDCLASSEX - { - cbSize = (uint)Marshal.SizeOf(), - style = 0, - lpfnWndProc = HostWndProc, - hInstance = hInstance, - hCursor = LoadCursor(default, IDC_ARROW), - hbrBackground = (HBRUSH)(1 + COLOR_BTNFACE), - lpszClassName = WindowClassName - }; - - if (RegisterClassEx(wcex) == 0) - return false; - - // Create the host window. - hwndHost = CreateWindowEx(WindowStylesEx.WS_EX_TOPMOST | WindowStylesEx.WS_EX_LAYERED | WindowStylesEx.WS_EX_TRANSPARENT, - WindowClassName, WindowClassName, WindowStyles.WS_CLIPCHILDREN, 0, 0, 0, 0, default, default, hInstance, default); - if (hwndHost.IsInvalid) - return false; - - // Make the window opaque. - SetLayeredWindowAttributes(hwndHost, 0, 255, LayeredWindowAttributes.LWA_ALPHA); - - // Create a magnifier control that fills the client area. - hwndMag = CreateWindow(WC_MAGNIFIER, "MagnifierWindow", WindowStyles.WS_CHILD | WindowStyles.MS_SHOWMAGNIFIEDCURSOR | WindowStyles.WS_VISIBLE, - 0, 0, LENS_WIDTH, LENS_HEIGHT, hwndHost, NULL, hInstance, NULL); - return !hwndMag.IsInvalid; - } - - private static IntPtr HostWndProc(HWND hwnd, uint uMsg, IntPtr wParam, IntPtr lParam) => throw new NotImplementedException(); - */ } \ No newline at end of file