diff --git a/Flow.Launcher.Infrastructure/Hotkey/GlobalHotkey.cs b/Flow.Launcher.Infrastructure/Hotkey/GlobalHotkey.cs index 2bc7db9bf..b2a140755 100644 --- a/Flow.Launcher.Infrastructure/Hotkey/GlobalHotkey.cs +++ b/Flow.Launcher.Infrastructure/Hotkey/GlobalHotkey.cs @@ -88,7 +88,7 @@ namespace Flow.Launcher.Infrastructure.Hotkey public void Dispose() { - PInvoke.UnhookWindowsHookEx(new HHOOK(hookId.DangerousGetHandle())); + hookId.Dispose(); } ~GlobalHotkey() diff --git a/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs b/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs index 9639f1b1a..9ea582118 100644 --- a/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs +++ b/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs @@ -25,6 +25,7 @@ namespace Flow.Launcher.Plugin.SharedCommands CheckSecurityWindow(); Thread.Sleep(25); } + while (containsSecurityWindow) // while this process contains a "Windows Security" dialog, stay open { containsSecurityWindow = false; @@ -52,24 +53,20 @@ namespace Flow.Launcher.Plugin.SharedCommands private static unsafe string GetWindowTitle(HWND hwnd) { var capacity = PInvoke.GetWindowTextLength(hwnd) + 1; - char[] buffer = new char[capacity]; + int length; + Span buffer = stackalloc char[capacity]; fixed (char* pBuffer = buffer) { // If the window has no title bar or text, if the title bar is empty, // or if the window or control handle is invalid, the return value is zero. - if (PInvoke.GetWindowText(hwnd, (PWSTR)pBuffer, capacity) == 0) - { - return string.Empty; - } - - // Truncate the buffer to the actual length of the string - int validLength = Array.IndexOf(buffer, '\0'); - if (validLength < 0) validLength = capacity; - return new string(buffer, 0, validLength); + length = PInvoke.GetWindowText(hwnd, (PWSTR)pBuffer, capacity); } + + return buffer[..length].ToString(); } - public static ProcessStartInfo SetProcessStartInfo(this string fileName, string workingDirectory = "", string arguments = "", string verb = "", bool createNoWindow = false) + public static ProcessStartInfo SetProcessStartInfo(this string fileName, string workingDirectory = "", + string arguments = "", string verb = "", bool createNoWindow = false) { var info = new ProcessStartInfo { diff --git a/Flow.Launcher/Helper/DWMDropShadow.cs b/Flow.Launcher/Helper/DWMDropShadow.cs index 7cb719d07..58817d70e 100644 --- a/Flow.Launcher/Helper/DWMDropShadow.cs +++ b/Flow.Launcher/Helper/DWMDropShadow.cs @@ -2,6 +2,7 @@ using System.Windows; using System.Windows.Interop; using Windows.Win32; +using Windows.Win32.Foundation; using Windows.Win32.Graphics.Dwm; using Windows.Win32.UI.Controls; @@ -38,24 +39,22 @@ public class DwmDropShadow /// /// Window to which the shadow will be applied /// True if the method succeeded, false if not - private unsafe static bool DropShadow(Window window) + private static unsafe bool DropShadow(Window window) { try { WindowInteropHelper helper = new WindowInteropHelper(window); int val = 2; - int ret1 = PInvoke.DwmSetWindowAttribute(new(helper.Handle), DWMWINDOWATTRIBUTE.DWMWA_NCRENDERING_POLICY, &val, 4); + var ret1 = PInvoke.DwmSetWindowAttribute(new (helper.Handle), DWMWINDOWATTRIBUTE.DWMWA_NCRENDERING_POLICY, &val, 4); - if (ret1 == 0) + if (ret1 == HRESULT.S_OK) { var m = new MARGINS { cyBottomHeight = 0, cxLeftWidth = 0, cxRightWidth = 0, cyTopHeight = 0 }; - int ret2 = PInvoke.DwmExtendFrameIntoClientArea(new(helper.Handle), &m); - return ret2 == 0; - } - else - { - return false; + var ret2 = PInvoke.DwmExtendFrameIntoClientArea(new(helper.Handle), &m); + return ret2 == HRESULT.S_OK; } + + return false; } catch (Exception) { diff --git a/Flow.Launcher/Helper/WallpaperPathRetrieval.cs b/Flow.Launcher/Helper/WallpaperPathRetrieval.cs index 8ab06e78d..8a42d480f 100644 --- a/Flow.Launcher/Helper/WallpaperPathRetrieval.cs +++ b/Flow.Launcher/Helper/WallpaperPathRetrieval.cs @@ -1,5 +1,8 @@ -using System.Linq; +using System; +using System.Linq; +using System.Runtime.InteropServices; using System.Text; +using System.Windows.Documents; using System.Windows.Media; using Microsoft.Win32; using Windows.Win32; @@ -9,19 +12,17 @@ namespace Flow.Launcher.Helper; public static class WallpaperPathRetrieval { - private static readonly int MAX_PATH = 260; public static unsafe string GetWallpaperPath() { - var wallpaper = new StringBuilder(MAX_PATH); - PInvoke.SystemParametersInfo(SYSTEM_PARAMETERS_INFO_ACTION.SPI_GETDESKWALLPAPER, (uint)MAX_PATH, &wallpaper, 0); - - var str = wallpaper.ToString(); - if (string.IsNullOrEmpty(str)) - return null; - - return str; + var wallpaperPtr = stackalloc char[MAX_PATH]; + PInvoke.SystemParametersInfo(SYSTEM_PARAMETERS_INFO_ACTION.SPI_GETDESKWALLPAPER, (uint)MAX_PATH, + wallpaperPtr, + 0); + var wallpaper = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(wallpaperPtr); + + return wallpaper.ToString(); } public static Color GetWallpaperColor() @@ -32,13 +33,14 @@ public static class WallpaperPathRetrieval { try { - var parts = strResult.Trim().Split(new[] {' '}, 3).Select(byte.Parse).ToList(); + var parts = strResult.Trim().Split(new[] { ' ' }, 3).Select(byte.Parse).ToList(); return Color.FromRgb(parts[0], parts[1], parts[2]); } catch { } } + return Colors.Transparent; } } diff --git a/Flow.Launcher/Helper/WindowsInteropHelper.cs b/Flow.Launcher/Helper/WindowsInteropHelper.cs index 6656b0c5b..caf3f0a7f 100644 --- a/Flow.Launcher/Helper/WindowsInteropHelper.cs +++ b/Flow.Launcher/Helper/WindowsInteropHelper.cs @@ -1,5 +1,7 @@ using System; +using System.ComponentModel; using System.Drawing; +using System.Runtime.InteropServices; using System.Windows; using System.Windows.Forms; using System.Windows.Interop; @@ -56,18 +58,17 @@ public class WindowsInteropHelper } string windowClass; - int capacity = 256; - char[] buffer = new char[capacity]; + const int capacity = 256; + Span buffer = stackalloc char[capacity]; + int validLength; fixed (char* pBuffer = buffer) { - PInvoke.GetClassName(hWnd, pBuffer, capacity); - - // Truncate the buffer to the actual length of the string - int validLength = Array.IndexOf(buffer, '\0'); - if (validLength < 0) validLength = capacity; - windowClass = new string(buffer, 0, validLength); + validLength = PInvoke.GetClassName(hWnd, pBuffer, capacity); } + windowClass = buffer[..validLength].ToString(); + + //for Win+Tab (Flip3D) if (windowClass == WINDOW_CLASS_WINTAB) { @@ -87,14 +88,15 @@ public class WindowsInteropHelper { var hWndDesktop = PInvoke.FindWindowEx(hWnd, HWND.Null, "SHELLDLL_DefView", null); hWndDesktop = PInvoke.FindWindowEx(hWndDesktop, HWND.Null, "SysListView32", "FolderView"); - if (!hWndDesktop.Equals(IntPtr.Zero)) + if (hWndDesktop.Value != (IntPtr.Zero)) { return false; } } Rectangle screenBounds = Screen.FromHandle(hWnd).Bounds; - return (appBounds.bottom - appBounds.top) == screenBounds.Height && (appBounds.right - appBounds.left) == screenBounds.Width; + return (appBounds.bottom - appBounds.top) == screenBounds.Height && + (appBounds.right - appBounds.left) == screenBounds.Width; } /// @@ -104,7 +106,23 @@ public class WindowsInteropHelper public static void DisableControlBox(Window win) { var hwnd = new HWND(new WindowInteropHelper(win).Handle); - PInvoke.SetWindowLong(hwnd, WINDOW_LONG_PTR_INDEX.GWL_STYLE, PInvoke.GetWindowLong(hwnd, WINDOW_LONG_PTR_INDEX.GWL_STYLE) & ~(int)WINDOW_STYLE.WS_SYSMENU); + + var style = PInvoke.GetWindowLong(hwnd, WINDOW_LONG_PTR_INDEX.GWL_STYLE); + + if (style == 0) + { + throw new Win32Exception(Marshal.GetLastPInvokeError()); + } + + style &= ~(int)WINDOW_STYLE.WS_SYSMENU; + + var previousStyle = PInvoke.SetWindowLong(hwnd, WINDOW_LONG_PTR_INDEX.GWL_STYLE, + style); + + if (previousStyle == 0) + { + throw new Win32Exception(Marshal.GetLastPInvokeError()); + } } /// @@ -127,6 +145,7 @@ public class WindowsInteropHelper using var src = new HwndSource(new HwndSourceParameters()); matrix = src.CompositionTarget.TransformFromDevice; } + return new Point((int)(matrix.M11 * unitX), (int)(matrix.M22 * unitY)); } } diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs index abc254d86..519e8a792 100644 --- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs +++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs @@ -98,7 +98,7 @@ namespace Flow.Launcher.Plugin.ProcessKiller using var safeHandle = new SafeProcessHandle(handle.Value, true); uint capacity = 2000; - char[] buffer = new char[capacity]; + Span buffer = new char[capacity]; fixed (char* pBuffer = buffer) { if (!PInvoke.QueryFullProcessImageName(safeHandle, PROCESS_NAME_FORMAT.PROCESS_NAME_WIN32, (PWSTR)pBuffer, ref capacity)) @@ -106,10 +106,7 @@ namespace Flow.Launcher.Plugin.ProcessKiller return string.Empty; } - // Truncate the buffer to the actual length of the string - int validLength = Array.IndexOf(buffer, '\0'); - if (validLength < 0) validLength = (int)capacity; - return new string(buffer, 0, validLength); + return buffer[..(int)capacity].ToString(); } } catch diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs index ad1f3ab56..f194ae973 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs @@ -31,14 +31,14 @@ namespace Flow.Launcher.Plugin.Program.Programs ((IShellLinkW)link).Resolve(hwnd, 0); const int MAX_PATH = 260; - char[] buffer = new char[MAX_PATH]; + Span buffer = stackalloc char[MAX_PATH]; var data = new WIN32_FIND_DATAW(); var target = string.Empty; - fixed (char* bufferChar = buffer) + fixed (char* bufferPtr = buffer) { - ((IShellLinkW)link).GetPath((PWSTR)bufferChar, MAX_PATH, &data, (uint)SLGP_FLAGS.SLGP_SHORTPATH); - target = GetStringFromBuffer(buffer, MAX_PATH); + ((IShellLinkW)link).GetPath((PWSTR)bufferPtr, MAX_PATH, &data, (uint)SLGP_FLAGS.SLGP_SHORTPATH); + target = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(bufferPtr).ToString(); } // To set the app description @@ -46,11 +46,10 @@ namespace Flow.Launcher.Plugin.Program.Programs { try { - char[] buffer1 = new char[MAX_PATH]; - fixed (char* buffer1Char = buffer1) + fixed (char* bufferPtr = buffer) { - ((IShellLinkW)link).GetDescription((PWSTR)buffer1Char, MAX_PATH); - description = GetStringFromBuffer(buffer1, MAX_PATH); + ((IShellLinkW)link).GetDescription(bufferPtr, MAX_PATH); + description = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(bufferPtr).ToString(); } } catch (COMException e) @@ -61,11 +60,10 @@ namespace Flow.Launcher.Plugin.Program.Programs e); } - char[] buffer2 = new char[MAX_PATH]; - fixed (char* buffer2Char = buffer2) + fixed (char* bufferPtr = buffer) { - ((IShellLinkW)link).GetArguments((PWSTR)buffer2Char, MAX_PATH); - arguments = GetStringFromBuffer(buffer2, MAX_PATH); + ((IShellLinkW)link).GetArguments(bufferPtr, MAX_PATH); + arguments = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(bufferPtr).ToString(); } } @@ -74,13 +72,5 @@ namespace Flow.Launcher.Plugin.Program.Programs return target; } - - private static unsafe string GetStringFromBuffer(char[] buffer, int maxLength) - { - // Truncate the buffer to the actual length of the string - int validLength = Array.IndexOf(buffer, '\0'); - if (validLength < 0) validLength = maxLength; - return new string(buffer, 0, validLength); - } } } diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLocalization.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLocalization.cs index e36618b0d..fac3ab181 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLocalization.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLocalization.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Runtime.InteropServices; using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.System.LibraryLoader; @@ -20,39 +21,34 @@ namespace Flow.Launcher.Plugin.Program.Programs /// The localized name as string or . public static unsafe string GetLocalizedName(string path) { - int capacity = 1024; - char[] resourcePathBuffer = new char[capacity]; + const int capacity = 1024; + Span buffer = new char[capacity]; // If there is no resource to localize a file name the method returns a non zero value. - fixed (char* resourcePath = resourcePathBuffer) + fixed (char* bufferPtr = buffer) { - var result = PInvoke.SHGetLocalizedName(path, (PWSTR)resourcePath, (uint)capacity, out var id); - if (result == HRESULT.S_OK) + var result = PInvoke.SHGetLocalizedName(path, bufferPtr, capacity, out var id); + if (result != HRESULT.S_OK) { - int validLength = Array.IndexOf(resourcePathBuffer, '\0'); - if (validLength < 0) validLength = capacity; - var resourcePathStr = new string(resourcePathBuffer, 0, validLength); - _ = PInvoke.ExpandEnvironmentStrings(resourcePathStr, resourcePath, (uint)capacity); - var handle = PInvoke.LoadLibraryEx(resourcePathStr, - LOAD_LIBRARY_FLAGS.DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_FLAGS.LOAD_LIBRARY_AS_DATAFILE); - IntPtr safeHandle = handle.DangerousGetHandle(); - if (safeHandle != IntPtr.Zero) - { - char[] localizedNameBuffer = new char[capacity]; - fixed (char* localizedName = localizedNameBuffer) - { - if (PInvoke.LoadString(handle, (uint)id, (PWSTR)localizedName, capacity) != 0) - { - validLength = Array.IndexOf(localizedNameBuffer, '\0'); - if (validLength < 0) validLength = capacity; - var lString = new string(localizedNameBuffer, 0, validLength); - PInvoke.FreeLibrary(new(safeHandle)); - return lString; - } - } + return string.Empty; + } - PInvoke.FreeLibrary(new(safeHandle)); - } + var resourcePathStr = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(bufferPtr).ToString(); + _ = PInvoke.ExpandEnvironmentStrings(resourcePathStr, bufferPtr, capacity); + using var handle = PInvoke.LoadLibraryEx(resourcePathStr, + LOAD_LIBRARY_FLAGS.DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_FLAGS.LOAD_LIBRARY_AS_DATAFILE); + if (handle.IsInvalid) + { + return string.Empty; + } + + // not sure about the behavior of Pinvoke.LoadString, so we clear the buffer before using it (so it must be a null-terminated string) + buffer.Clear(); + + if (PInvoke.LoadString(handle, (uint)id, bufferPtr, capacity) != 0) + { + var lString = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(bufferPtr).ToString(); + return lString; } }