From 9b66943dd2d3cab3ecb30319d2ab26c75e778c7d Mon Sep 17 00:00:00 2001 From: Trym Lund Flogard Date: Sat, 24 Jun 2023 12:55:29 +0200 Subject: [PATCH 1/4] Use widechar variant for win32 pinvokes --- .../NativeControls/Win/WinApi.cs | 2 +- .../Interop/UnmanagedMethods.cs | 42 +++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/samples/ControlCatalog.NetCore/NativeControls/Win/WinApi.cs b/samples/ControlCatalog.NetCore/NativeControls/Win/WinApi.cs index 47d368f7a4..bd0c1bf98e 100644 --- a/samples/ControlCatalog.NetCore/NativeControls/Win/WinApi.cs +++ b/samples/ControlCatalog.NetCore/NativeControls/Win/WinApi.cs @@ -39,7 +39,7 @@ internal unsafe class WinApi [DllImport("user32.dll", SetLastError = true)] public static extern bool DestroyWindow(IntPtr hwnd); - [DllImport("kernel32.dll")] + [DllImport("kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "LoadLibraryW", ExactSpelling = true)] public static extern IntPtr LoadLibrary(string lib); diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index 39363beae3..ce42227292 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1161,7 +1161,7 @@ namespace Avalonia.Win32.Interop [DllImport("user32.dll")] public static extern bool ClientToScreen(IntPtr hWnd, ref POINT lpPoint); - [DllImport("user32.dll", SetLastError = true)] + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "CreateWindowExW", ExactSpelling = true)] public static extern IntPtr CreateWindowEx( int dwExStyle, uint lpClassName, @@ -1215,16 +1215,16 @@ namespace Avalonia.Win32.Interop [DllImport("user32.dll")] public static extern int GetMessageTime(); - [DllImport("kernel32.dll")] + [DllImport("kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "GetModuleHandleW", ExactSpelling = true)] public static extern IntPtr GetModuleHandle(string? lpModuleName); [DllImport("user32.dll")] public static extern int GetSystemMetrics(SystemMetric smIndex); - [DllImport("user32.dll", SetLastError = true)] + [DllImport("user32.dll", SetLastError = true, EntryPoint = "GetWindowLongPtrW", ExactSpelling = true)] public static extern uint GetWindowLongPtr(IntPtr hWnd, int nIndex); - [DllImport("user32.dll", SetLastError = true, EntryPoint = "GetWindowLong")] + [DllImport("user32.dll", SetLastError = true, EntryPoint = "GetWindowLongW", ExactSpelling = true)] public static extern uint GetWindowLong32b(IntPtr hWnd, int nIndex); public static uint GetWindowLong(IntPtr hWnd, int nIndex) @@ -1239,10 +1239,10 @@ namespace Avalonia.Win32.Interop } } - [DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLong")] + [DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLongW", ExactSpelling = true)] private static extern uint SetWindowLong32b(IntPtr hWnd, int nIndex, uint value); - [DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLongPtr")] + [DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLongPtrW", ExactSpelling = true)] private static extern IntPtr SetWindowLong64b(IntPtr hWnd, int nIndex, IntPtr value); public static uint SetWindowLong(IntPtr hWnd, int nIndex, uint value) @@ -1310,7 +1310,7 @@ namespace Avalonia.Win32.Interop [DllImport("user32.dll")] public static extern bool KillTimer(IntPtr hWnd, IntPtr uIDEvent); - [DllImport("user32.dll")] + [DllImport("user32.dll", EntryPoint = "LoadCursorW", ExactSpelling = true)] public static extern IntPtr LoadCursor(IntPtr hInstance, IntPtr lpCursorName); [DllImport("user32.dll")] @@ -1319,7 +1319,7 @@ namespace Avalonia.Win32.Interop [DllImport("user32.dll")] public static extern bool DestroyIcon(IntPtr hIcon); - [DllImport("user32.dll")] + [DllImport("user32.dll", EntryPoint = "PeekMessageW", ExactSpelling = true)] public static extern bool PeekMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg); [DllImport("user32")] @@ -1334,7 +1334,7 @@ namespace Avalonia.Win32.Interop [DllImport("user32.dll")] public static extern bool ReleaseCapture(); - [DllImport("user32.dll", SetLastError = true)] + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "RegisterWindowMessageW", ExactSpelling = true)] public static extern uint RegisterWindowMessage(string lpString); [DllImport("user32.dll")] @@ -1415,7 +1415,7 @@ namespace Avalonia.Win32.Interop [DllImport("user32.dll")] public static extern bool TranslateMessage(ref MSG lpMsg); - [DllImport("user32.dll", CharSet = CharSet.Unicode)] + [DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "UnregisterClassW", ExactSpelling = true)] public static extern bool UnregisterClass(string lpClassName, IntPtr hInstance); [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "SetWindowTextW")] @@ -1439,10 +1439,10 @@ namespace Avalonia.Win32.Interop [DllImport("shell32", CharSet = CharSet.Auto)] public static extern int Shell_NotifyIcon(NIM dwMessage, NOTIFYICONDATA lpData); - [DllImport("user32.dll", EntryPoint = "SetClassLongPtr")] + [DllImport("user32.dll", EntryPoint = "SetClassLongPtrW", ExactSpelling = true)] private static extern IntPtr SetClassLong64(IntPtr hWnd, ClassLongIndex nIndex, IntPtr dwNewLong); - [DllImport("user32.dll", EntryPoint = "SetClassLong")] + [DllImport("user32.dll", EntryPoint = "SetClassLongW", ExactSpelling = true)] private static extern IntPtr SetClassLong32(IntPtr hWnd, ClassLongIndex nIndex, IntPtr dwNewLong); public static IntPtr SetClassLong(IntPtr hWnd, ClassLongIndex nIndex, IntPtr dwNewLong) @@ -1463,10 +1463,10 @@ namespace Avalonia.Win32.Interop return new IntPtr(GetClassLongPtr32(hWnd, nIndex)); } - [DllImport("user32.dll", EntryPoint = "GetClassLong")] + [DllImport("user32.dll", EntryPoint = "GetClassLongW", ExactSpelling = true)] public static extern uint GetClassLongPtr32(IntPtr hWnd, int nIndex); - [DllImport("user32.dll", EntryPoint = "GetClassLongPtr")] + [DllImport("user32.dll", EntryPoint = "GetClassLongPtrW", ExactSpelling = true)] public static extern IntPtr GetClassLongPtr64(IntPtr hWnd, int nIndex); [DllImport("user32.dll", EntryPoint = "SetCursor")] @@ -1527,10 +1527,10 @@ namespace Avalonia.Win32.Interop [DllImport("kernel32.dll", ExactSpelling = true)] public static extern IntPtr GlobalFree(IntPtr hMem); - [DllImport("kernel32.dll", SetLastError = true)] + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "LoadLibraryW", ExactSpelling = true)] public static extern IntPtr LoadLibrary(string fileName); - [DllImport("kernel32.dll", SetLastError = true)] + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "LoadLibraryExW", ExactSpelling = true)] public static extern IntPtr LoadLibraryEx(string fileName, IntPtr hFile, int flags); [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)] @@ -1645,7 +1645,7 @@ namespace Avalonia.Win32.Interop [DllImport("opengl32.dll", CharSet = CharSet.Ansi)] public static extern IntPtr wglGetProcAddress(string name); - [DllImport("kernel32.dll", SetLastError = true)] + [DllImport("kernel32.dll", SetLastError = true, EntryPoint = "CreateFileMappingW", ExactSpelling = true)] public static extern IntPtr CreateFileMapping(IntPtr hFile, IntPtr lpFileMappingAttributes, uint flProtect, @@ -1668,16 +1668,16 @@ namespace Avalonia.Win32.Interop [DllImport("ole32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] internal static extern void ReleaseStgMedium(ref STGMEDIUM medium); - [DllImport("user32.dll", BestFitMapping = false, CharSet = CharSet.Auto, SetLastError = true)] + [DllImport("user32.dll", BestFitMapping = false, CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "GetClipboardFormatNameW", ExactSpelling = true)] public static extern int GetClipboardFormatName(int format, StringBuilder lpString, int cchMax); - [DllImport("user32.dll", BestFitMapping = false, CharSet = CharSet.Auto, SetLastError = true)] + [DllImport("user32.dll", BestFitMapping = false, CharSet = CharSet.Unicode, SetLastError = true, EntryPoint = "RegisterClipboardFormatW", ExactSpelling = true)] public static extern int RegisterClipboardFormat(string format); [DllImport("kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = true, SetLastError = true)] public static extern IntPtr GlobalSize(IntPtr hGlobal); - [DllImport("shell32.dll", BestFitMapping = false, CharSet = CharSet.Auto)] + [DllImport("shell32.dll", BestFitMapping = false, CharSet = CharSet.Unicode, EntryPoint = "DragQueryFileW", ExactSpelling = true)] public static extern int DragQueryFile(IntPtr hDrop, int iFile, StringBuilder? lpszFile, int cch); [DllImport("ole32.dll", CharSet = CharSet.Auto, ExactSpelling = true, PreserveSig = false)] @@ -1893,7 +1893,7 @@ namespace Avalonia.Win32.Interop [DllImport("imm32.dll")] public static extern bool ImmSetCompositionFont(IntPtr hIMC, ref LOGFONT lf); - [DllImport("imm32.dll", SetLastError = false, CharSet = CharSet.Unicode)] + [DllImport("imm32.dll", SetLastError = false, CharSet = CharSet.Unicode, EntryPoint = "ImmGetCompositionStringW", ExactSpelling = true)] public static extern int ImmGetCompositionString(IntPtr hIMC, GCS dwIndex, [Out, Optional] IntPtr lpBuf, uint dwBufLen); public static string? ImmGetCompositionString(IntPtr hIMC, GCS dwIndex) From 6a3925b91b67e2a3412a7d3e3e11ba24589dfeff Mon Sep 17 00:00:00 2001 From: Trym Lund Flogard Date: Sat, 24 Jun 2023 13:49:26 +0200 Subject: [PATCH 2/4] Remove support for undocumented blur #11855 --- .../Interop/UnmanagedMethods.cs | 3 -- src/Windows/Avalonia.Win32/WindowImpl.cs | 53 ++----------------- 2 files changed, 5 insertions(+), 51 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index ce42227292..e58abcadf0 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1827,9 +1827,6 @@ namespace Avalonia.Win32.Interop return result; } - [DllImport("user32.dll")] - internal static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data); - [Flags] public enum GCS : uint { diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 057cdb2db0..3fc2751e43 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -371,7 +371,7 @@ namespace Avalonia.Win32 if (level == WindowTransparencyLevel.Transparent) SetTransparencyTransparent(windowsVersion); else if (level == WindowTransparencyLevel.Blur) - SetTransparencyBlur(windowsVersion); + continue; // Unsupported on all versions (unreachable) else if (level == WindowTransparencyLevel.AcrylicBlur) SetTransparencyAcrylicBlur(windowsVersion); else if (level == WindowTransparencyLevel.Mica) @@ -409,9 +409,9 @@ namespace Avalonia.Win32 if (level == WindowTransparencyLevel.Transparent) return windowsVersion >= PlatformConstants.Windows8; - // Blur only supported on Windows 8 and lower. + // Support for legacy, undocumented DWM blur has been removed. if (level == WindowTransparencyLevel.Blur) - return windowsVersion < PlatformConstants.Windows10; + return false; // Acrylic is supported on Windows >= 10.0.15063. if (level == WindowTransparencyLevel.AcrylicBlur) @@ -426,37 +426,14 @@ namespace Avalonia.Win32 private void SetTransparencyTransparent(Version windowsVersion) { - // Transparent only supported with composition on Windows 8+. - if (!_isUsingComposition || windowsVersion < PlatformConstants.Windows8) + // Transparent only supported with composition on Windows 10+. + if (!_isUsingComposition || windowsVersion < PlatformConstants.Windows10) return; - if (windowsVersion < PlatformConstants.Windows10) - { - // Some of the AccentState Enum's values have different meanings on Windows 8.x than on - // Windows 10, hence using ACCENT_ENABLE_BLURBEHIND to disable blurbehind ¯\_(ツ)_/¯. - // Hey, I'm just porting what was here before. - SetAccentState(AccentState.ACCENT_ENABLE_BLURBEHIND); - var blurInfo = new DWM_BLURBEHIND(false); - DwmEnableBlurBehindWindow(_hwnd, ref blurInfo); - } - SetUseHostBackdropBrush(false); _blurHost?.SetBlur(BlurEffect.None); } - private void SetTransparencyBlur(Version windowsVersion) - { - // Blur only supported with composition on Windows 8 and lower. - if (!_isUsingComposition || windowsVersion >= PlatformConstants.Windows10) - return; - - // Some of the AccentState Enum's values have different meanings on Windows 8.x than on - // Windows 10. - SetAccentState(AccentState.ACCENT_DISABLED); - var blurInfo = new DWM_BLURBEHIND(true); - DwmEnableBlurBehindWindow(_hwnd, ref blurInfo); - } - private void SetTransparencyAcrylicBlur(Version windowsVersion) { // Acrylic blur only supported with composition on Windows >= 10.0.15063. @@ -477,26 +454,6 @@ namespace Avalonia.Win32 _blurHost?.SetBlur(BlurEffect.Mica); } - private void SetAccentState(AccentState state) - { - var accent = new AccentPolicy(); - var accentStructSize = Marshal.SizeOf(accent); - - //Some of the AccentState Enum's values have different meanings on Windows 8.x than on Windows 10 - accent.AccentState = state; - - var accentPtr = Marshal.AllocHGlobal(accentStructSize); - Marshal.StructureToPtr(accent, accentPtr, false); - - var data = new WindowCompositionAttributeData(); - data.Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY; - data.SizeOfData = accentStructSize; - data.Data = accentPtr; - - SetWindowCompositionAttribute(_hwnd, ref data); - Marshal.FreeHGlobal(accentPtr); - } - private void SetUseHostBackdropBrush(bool useHostBackdropBrush) { if (Win32Platform.WindowsVersion < WinUiCompositionShared.MinHostBackdropVersion) From 544cf0fdabd2c9f44219d79513caf690f4dd5135 Mon Sep 17 00:00:00 2001 From: Trym Lund Flogard Date: Wed, 28 Jun 2023 22:36:50 +0200 Subject: [PATCH 3/4] Revert "Remove support for undocumented blur #11855" This reverts commit 6a3925b91b67e2a3412a7d3e3e11ba24589dfeff. --- .../Interop/UnmanagedMethods.cs | 3 ++ src/Windows/Avalonia.Win32/WindowImpl.cs | 53 +++++++++++++++++-- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index e58abcadf0..ce42227292 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1827,6 +1827,9 @@ namespace Avalonia.Win32.Interop return result; } + [DllImport("user32.dll")] + internal static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data); + [Flags] public enum GCS : uint { diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 3fc2751e43..057cdb2db0 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -371,7 +371,7 @@ namespace Avalonia.Win32 if (level == WindowTransparencyLevel.Transparent) SetTransparencyTransparent(windowsVersion); else if (level == WindowTransparencyLevel.Blur) - continue; // Unsupported on all versions (unreachable) + SetTransparencyBlur(windowsVersion); else if (level == WindowTransparencyLevel.AcrylicBlur) SetTransparencyAcrylicBlur(windowsVersion); else if (level == WindowTransparencyLevel.Mica) @@ -409,9 +409,9 @@ namespace Avalonia.Win32 if (level == WindowTransparencyLevel.Transparent) return windowsVersion >= PlatformConstants.Windows8; - // Support for legacy, undocumented DWM blur has been removed. + // Blur only supported on Windows 8 and lower. if (level == WindowTransparencyLevel.Blur) - return false; + return windowsVersion < PlatformConstants.Windows10; // Acrylic is supported on Windows >= 10.0.15063. if (level == WindowTransparencyLevel.AcrylicBlur) @@ -426,14 +426,37 @@ namespace Avalonia.Win32 private void SetTransparencyTransparent(Version windowsVersion) { - // Transparent only supported with composition on Windows 10+. - if (!_isUsingComposition || windowsVersion < PlatformConstants.Windows10) + // Transparent only supported with composition on Windows 8+. + if (!_isUsingComposition || windowsVersion < PlatformConstants.Windows8) return; + if (windowsVersion < PlatformConstants.Windows10) + { + // Some of the AccentState Enum's values have different meanings on Windows 8.x than on + // Windows 10, hence using ACCENT_ENABLE_BLURBEHIND to disable blurbehind ¯\_(ツ)_/¯. + // Hey, I'm just porting what was here before. + SetAccentState(AccentState.ACCENT_ENABLE_BLURBEHIND); + var blurInfo = new DWM_BLURBEHIND(false); + DwmEnableBlurBehindWindow(_hwnd, ref blurInfo); + } + SetUseHostBackdropBrush(false); _blurHost?.SetBlur(BlurEffect.None); } + private void SetTransparencyBlur(Version windowsVersion) + { + // Blur only supported with composition on Windows 8 and lower. + if (!_isUsingComposition || windowsVersion >= PlatformConstants.Windows10) + return; + + // Some of the AccentState Enum's values have different meanings on Windows 8.x than on + // Windows 10. + SetAccentState(AccentState.ACCENT_DISABLED); + var blurInfo = new DWM_BLURBEHIND(true); + DwmEnableBlurBehindWindow(_hwnd, ref blurInfo); + } + private void SetTransparencyAcrylicBlur(Version windowsVersion) { // Acrylic blur only supported with composition on Windows >= 10.0.15063. @@ -454,6 +477,26 @@ namespace Avalonia.Win32 _blurHost?.SetBlur(BlurEffect.Mica); } + private void SetAccentState(AccentState state) + { + var accent = new AccentPolicy(); + var accentStructSize = Marshal.SizeOf(accent); + + //Some of the AccentState Enum's values have different meanings on Windows 8.x than on Windows 10 + accent.AccentState = state; + + var accentPtr = Marshal.AllocHGlobal(accentStructSize); + Marshal.StructureToPtr(accent, accentPtr, false); + + var data = new WindowCompositionAttributeData(); + data.Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY; + data.SizeOfData = accentStructSize; + data.Data = accentPtr; + + SetWindowCompositionAttribute(_hwnd, ref data); + Marshal.FreeHGlobal(accentPtr); + } + private void SetUseHostBackdropBrush(bool useHostBackdropBrush) { if (Win32Platform.WindowsVersion < WinUiCompositionShared.MinHostBackdropVersion) From 8f7cf7cc4a8d8d5f937f3d20b61f026ec007696f Mon Sep 17 00:00:00 2001 From: Trym Lund Flogard Date: Wed, 28 Jun 2023 22:47:11 +0200 Subject: [PATCH 4/4] Invoke SetWindowCompositionAttribute dynamically without exposing it to ILC --- .../Avalonia.Win32/Interop/UnmanagedMethods.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index ce42227292..68f3e3c670 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1827,8 +1827,21 @@ namespace Avalonia.Win32.Interop return result; } - [DllImport("user32.dll")] - internal static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data); + internal static int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data) + { + var user32 = LoadLibrary("user32.dll"); + var pfnSetWindowCompositionAttribute = (delegate* unmanaged[Stdcall])GetProcAddress(user32, nameof(SetWindowCompositionAttribute)); + if (pfnSetWindowCompositionAttribute == null) + { + // This preserves the same behavior as using the DllImport attribute. + throw new EntryPointNotFoundException("The unsupported SetWindowCompositionAttribute-function has been removed from the operating system."); + } + + fixed (WindowCompositionAttributeData* pData = &data) + { + return pfnSetWindowCompositionAttribute(hwnd, pData); + } + } [Flags] public enum GCS : uint