From a395936a6c6b8fa0f3d7c34ccbb0b876d88d035b Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Tue, 10 Dec 2024 11:56:32 +0800
Subject: [PATCH 01/21] Replace FormApplication & DllImport with CSWin32
---
.../Flow.Launcher.Plugin.Sys.csproj | 7 +++
Plugins/Flow.Launcher.Plugin.Sys/Main.cs | 44 ++++---------------
.../NativeMethods.txt | 6 +++
3 files changed, 21 insertions(+), 36 deletions(-)
create mode 100644 Plugins/Flow.Launcher.Plugin.Sys/NativeMethods.txt
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj b/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj
index b797b3cf43a..dbc36ad424b 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj
@@ -57,4 +57,11 @@
PreserveNewest
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs
index 6e55582280f..f80f3c9ddf4 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs
@@ -2,17 +2,16 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
-using System.Runtime.InteropServices;
using System.Windows;
-using System.Windows.Forms;
-using System.Windows.Interop;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin.SharedCommands;
+using Windows.Win32;
+using Windows.Win32.Foundation;
+using Windows.Win32.System.Shutdown;
using Application = System.Windows.Application;
using Control = System.Windows.Controls.Control;
-using FormsApplication = System.Windows.Forms.Application;
namespace Flow.Launcher.Plugin.Sys
{
@@ -21,33 +20,6 @@ public class Main : IPlugin, ISettingProvider, IPluginI18n
private PluginInitContext context;
private Dictionary KeywordTitleMappings = new Dictionary();
- #region DllImport
-
- internal const int EWX_LOGOFF = 0x00000000;
- internal const int EWX_SHUTDOWN = 0x00000001;
- internal const int EWX_REBOOT = 0x00000002;
- internal const int EWX_FORCE = 0x00000004;
- internal const int EWX_POWEROFF = 0x00000008;
- internal const int EWX_FORCEIFHUNG = 0x00000010;
-
- [DllImport("user32")]
- private static extern bool ExitWindowsEx(uint uFlags, uint dwReason);
-
- [DllImport("user32")]
- private static extern void LockWorkStation();
-
- [DllImport("Shell32.dll", CharSet = CharSet.Unicode)]
- private static extern uint SHEmptyRecycleBin(IntPtr hWnd, uint dwFlags);
-
- // http://www.pinvoke.net/default.aspx/Enums/HRESULT.html
- private enum HRESULT : uint
- {
- S_FALSE = 0x0001,
- S_OK = 0x0000
- }
-
- #endregion
-
public Control CreateSettingPanel()
{
var results = Commands();
@@ -206,7 +178,7 @@ private List Commands()
MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result == MessageBoxResult.Yes)
- ExitWindowsEx(EWX_LOGOFF, 0);
+ PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_LOGOFF, 0);
return true;
}
@@ -219,7 +191,7 @@ private List Commands()
IcoPath = "Images\\lock.png",
Action = c =>
{
- LockWorkStation();
+ PInvoke.LockWorkStation();
return true;
}
},
@@ -229,7 +201,7 @@ private List Commands()
SubTitle = context.API.GetTranslation("flowlauncher_plugin_sys_sleep"),
Glyph = new GlyphInfo (FontFamily:"/Resources/#Segoe Fluent Icons", Glyph:"\xec46"),
IcoPath = "Images\\sleep.png",
- Action = c => FormsApplication.SetSuspendState(PowerState.Suspend, false, false)
+ Action = c => PInvoke.SetSuspendState(false, false, false)
},
new Result
{
@@ -274,8 +246,8 @@ private List Commands()
// http://www.pinvoke.net/default.aspx/shell32/SHEmptyRecycleBin.html
// FYI, couldn't find documentation for this but if the recycle bin is already empty, it will return -2147418113 (0x8000FFFF (E_UNEXPECTED))
// 0 for nothing
- var result = SHEmptyRecycleBin(new WindowInteropHelper(Application.Current.MainWindow).Handle, 0);
- if (result != (uint) HRESULT.S_OK && result != (uint) 0x8000FFFF)
+ var result = PInvoke.SHEmptyRecycleBin(new(), string.Empty, 0);
+ if (result != HRESULT.S_OK && result != HRESULT.E_UNEXPECTED)
{
context.API.ShowMsgBox($"Error emptying recycle bin, error code: {result}\n" +
"please refer to https://msdn.microsoft.com/en-us/library/windows/desktop/aa378137",
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/NativeMethods.txt b/Plugins/Flow.Launcher.Plugin.Sys/NativeMethods.txt
new file mode 100644
index 00000000000..8fcb6cae91e
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.Sys/NativeMethods.txt
@@ -0,0 +1,6 @@
+ExitWindowsEx
+LockWorkStation
+SHEmptyRecycleBin
+S_OK
+E_UNEXPECTED
+SetSuspendState
\ No newline at end of file
From ca01b25a39b6c35d58b021fd153d796def9d7e05 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Tue, 10 Dec 2024 13:29:21 +0800
Subject: [PATCH 02/21] Replace DllImport & flags with CSWin32
---
.../Flow.Launcher.Plugin.ProcessKiller.csproj | 7 +++
.../NativeMethods.txt | 2 +
.../ProcessHelper.cs | 52 ++++++++-----------
3 files changed, 31 insertions(+), 30 deletions(-)
create mode 100644 Plugins/Flow.Launcher.Plugin.ProcessKiller/NativeMethods.txt
diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj
index 876bac1e754..4e216b7b26a 100644
--- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj
+++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj
@@ -50,6 +50,13 @@
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/NativeMethods.txt b/Plugins/Flow.Launcher.Plugin.ProcessKiller/NativeMethods.txt
new file mode 100644
index 00000000000..7fa794755e1
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/NativeMethods.txt
@@ -0,0 +1,2 @@
+QueryFullProcessImageName
+OpenProcess
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs
index 0acc39fbb1c..d8e5f4fb7a9 100644
--- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs
+++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs
@@ -1,11 +1,13 @@
-using Flow.Launcher.Infrastructure;
+using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Logger;
+using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
-using System.Runtime.InteropServices;
-using System.Text;
+using Windows.Win32;
+using Windows.Win32.Foundation;
+using Windows.Win32.System.Threading;
namespace Flow.Launcher.Plugin.ProcessKiller
{
@@ -84,43 +86,33 @@ public void TryKill(Process p)
}
}
- public string TryGetProcessFilename(Process p)
+ public unsafe string TryGetProcessFilename(Process p)
{
try
{
- int capacity = 2000;
- StringBuilder builder = new StringBuilder(capacity);
- IntPtr ptr = OpenProcess(ProcessAccessFlags.QueryLimitedInformation, false, p.Id);
- if (!QueryFullProcessImageName(ptr, 0, builder, ref capacity))
+ var handle = PInvoke.OpenProcess(PROCESS_ACCESS_RIGHTS.PROCESS_QUERY_LIMITED_INFORMATION, false, (uint)p.Id);
+ if (handle.Value == IntPtr.Zero)
{
- return String.Empty;
+ return string.Empty;
}
- return builder.ToString();
+ using var safeHandle = new SafeProcessHandle(handle.Value, true);
+ uint capacity = 2000;
+ char[] buffer = new char[capacity];
+ fixed (char* pBuffer = buffer)
+ {
+ if (!PInvoke.QueryFullProcessImageName(safeHandle, PROCESS_NAME_FORMAT.PROCESS_NAME_WIN32, (PWSTR)pBuffer, ref capacity))
+ {
+ return string.Empty;
+ }
+ }
+
+ return new string(buffer, 0, (int)capacity);
}
catch
{
- return "";
+ return string.Empty;
}
}
-
- [Flags]
- private enum ProcessAccessFlags : uint
- {
- QueryLimitedInformation = 0x00001000
- }
-
- [DllImport("kernel32.dll", SetLastError = true)]
- private static extern bool QueryFullProcessImageName(
- [In] IntPtr hProcess,
- [In] int dwFlags,
- [Out] StringBuilder lpExeName,
- ref int lpdwSize);
-
- [DllImport("kernel32.dll", SetLastError = true)]
- private static extern IntPtr OpenProcess(
- ProcessAccessFlags processAccess,
- bool bInheritHandle,
- int processId);
}
}
From 313f86b6485e6f32f1d23e58fcd09692ae322e8c Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Tue, 10 Dec 2024 15:09:06 +0800
Subject: [PATCH 03/21] Replace DllImport & flags with CSWin32
---
.../Flow.Launcher.Plugin.Program.csproj | 8 ++
.../NativeMethods.txt | 10 ++
.../Programs/ShellLinkHelper.cs | 130 +++++-------------
.../Programs/ShellLocalization.cs | 67 +++++----
4 files changed, 82 insertions(+), 133 deletions(-)
create mode 100644 Plugins/Flow.Launcher.Plugin.Program/NativeMethods.txt
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj
index c0ad63cfeb4..99c1a12e9b3 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj
+++ b/Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj
@@ -52,6 +52,10 @@
PreserveNewest
+
+
+
+
@@ -61,6 +65,10 @@
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.Program/NativeMethods.txt b/Plugins/Flow.Launcher.Plugin.Program/NativeMethods.txt
new file mode 100644
index 00000000000..ecd547dffcd
--- /dev/null
+++ b/Plugins/Flow.Launcher.Plugin.Program/NativeMethods.txt
@@ -0,0 +1,10 @@
+SHGetLocalizedName
+LoadString
+LoadLibraryEx
+FreeLibrary
+ExpandEnvironmentStrings
+S_OK
+SLGP_FLAGS
+WIN32_FIND_DATAW
+SLR_FLAGS
+IShellLinkW
\ No newline at end of file
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs
index 78c66d60485..fae9c84c985 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs
@@ -1,97 +1,17 @@
using System;
-using System.Text;
using System.Runtime.InteropServices;
-using Accessibility;
using System.Runtime.InteropServices.ComTypes;
using Flow.Launcher.Plugin.Program.Logger;
+using Windows.Win32.Foundation;
+using Windows.Win32.UI.Shell;
+using Windows.Win32.Storage.FileSystem;
namespace Flow.Launcher.Plugin.Program.Programs
{
class ShellLinkHelper
{
- [Flags()]
- public enum SLGP_FLAGS
- {
- SLGP_SHORTPATH = 0x1,
- SLGP_UNCPRIORITY = 0x2,
- SLGP_RAWPATH = 0x4
- }
-
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
- public struct WIN32_FIND_DATAW
- {
- public uint dwFileAttributes;
- public long ftCreationTime;
- public long ftLastAccessTime;
- public long ftLastWriteTime;
- public uint nFileSizeHigh;
- public uint nFileSizeLow;
- public uint dwReserved0;
- public uint dwReserved1;
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
- public string cFileName;
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
- public string cAlternateFileName;
- }
-
- [Flags()]
- public enum SLR_FLAGS
- {
- SLR_NO_UI = 0x1,
- SLR_ANY_MATCH = 0x2,
- SLR_UPDATE = 0x4,
- SLR_NOUPDATE = 0x8,
- SLR_NOSEARCH = 0x10,
- SLR_NOTRACK = 0x20,
- SLR_NOLINKINFO = 0x40,
- SLR_INVOKE_MSI = 0x80
- }
-
-
+
// Reference : http://www.pinvoke.net/default.aspx/Interfaces.IShellLinkW
- /// The IShellLink interface allows Shell links to be created, modified, and resolved
- [ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F9-0000-0000-C000-000000000046")]
- interface IShellLinkW
- {
- /// Retrieves the path and file name of a Shell link object
- void GetPath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, ref WIN32_FIND_DATAW pfd, SLGP_FLAGS fFlags);
- /// Retrieves the list of item identifiers for a Shell link object
- void GetIDList(out IntPtr ppidl);
- /// Sets the pointer to an item identifier list (PIDL) for a Shell link object.
- void SetIDList(IntPtr pidl);
- /// Retrieves the description string for a Shell link object
- void GetDescription([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName);
- /// Sets the description for a Shell link object. The description can be any application-defined string
- void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
- /// Retrieves the name of the working directory for a Shell link object
- void GetWorkingDirectory([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath);
- /// Sets the name of the working directory for a Shell link object
- void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
- /// Retrieves the command-line arguments associated with a Shell link object
- void GetArguments([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath);
- /// Sets the command-line arguments for a Shell link object
- void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
- /// Retrieves the hot key for a Shell link object
- void GetHotkey(out short pwHotkey);
- /// Sets a hot key for a Shell link object
- void SetHotkey(short wHotkey);
- /// Retrieves the show command for a Shell link object
- void GetShowCmd(out int piShowCmd);
- /// Sets the show command for a Shell link object. The show command sets the initial show state of the window.
- void SetShowCmd(int iShowCmd);
- /// Retrieves the location (path and index) of the icon for a Shell link object
- void GetIconLocation([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath,
- int cchIconPath, out int piIcon);
- /// Sets the location (path and index) of the icon for a Shell link object
- void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
- /// Sets the relative path to the Shell link object
- void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved);
- /// Attempts to find the target of a Shell link, even if it has been moved or renamed
- void Resolve(ref Accessibility._RemotableHandle hwnd, SLR_FLAGS fFlags);
- /// Sets the path and file name of a Shell link object
- void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
- }
-
[ComImport(), Guid("00021401-0000-0000-C000-000000000046")]
public class ShellLink
{
@@ -102,29 +22,40 @@ public class ShellLink
public string arguments = string.Empty;
// Retrieve the target path using Shell Link
- public string retrieveTargetPath(string path)
+ public unsafe string retrieveTargetPath(string path)
{
var link = new ShellLink();
const int STGM_READ = 0;
((IPersistFile)link).Load(path, STGM_READ);
- var hwnd = new _RemotableHandle();
- ((IShellLinkW)link).Resolve(ref hwnd, 0);
+ var hwnd = new HWND(IntPtr.Zero);
+ ((IShellLinkW)link).Resolve(hwnd, 0);
const int MAX_PATH = 260;
- StringBuilder buffer = new StringBuilder(MAX_PATH);
+ char[] buffer = new char[MAX_PATH];
var data = new WIN32_FIND_DATAW();
- ((IShellLinkW)link).GetPath(buffer, buffer.Capacity, ref data, SLGP_FLAGS.SLGP_SHORTPATH);
- var target = buffer.ToString();
+ var target = string.Empty;
+ fixed (char* bufferChar = buffer)
+ {
+ ((IShellLinkW)link).GetPath((PWSTR)bufferChar, MAX_PATH, &data, (uint)SLGP_FLAGS.SLGP_SHORTPATH);
+ int validLength = Array.IndexOf(buffer, '\0');
+ if (validLength < 0) validLength = MAX_PATH;
+ target = new string(buffer, 0, validLength);
+ }
// To set the app description
- if (!String.IsNullOrEmpty(target))
+ if (!string.IsNullOrEmpty(target))
{
try
{
- buffer = new StringBuilder(MAX_PATH);
- ((IShellLinkW)link).GetDescription(buffer, MAX_PATH);
- description = buffer.ToString();
+ char[] buffer1 = new char[MAX_PATH];
+ fixed (char* buffer1Char = buffer1)
+ {
+ ((IShellLinkW)link).GetDescription((PWSTR)buffer1Char, MAX_PATH);
+ int validLength = Array.IndexOf(buffer1, '\0');
+ if (validLength < 0) validLength = MAX_PATH;
+ description = new string(buffer1, 0, validLength);
+ }
}
catch (COMException e)
{
@@ -134,9 +65,14 @@ public string retrieveTargetPath(string path)
e);
}
- buffer.Clear();
- ((IShellLinkW)link).GetArguments(buffer, MAX_PATH);
- arguments = buffer.ToString();
+ char[] buffer2 = new char[MAX_PATH];
+ fixed (char* buffer2Char = buffer2)
+ {
+ ((IShellLinkW)link).GetArguments((PWSTR)buffer2Char, MAX_PATH);
+ int validLength = Array.IndexOf(buffer2, '\0');
+ if (validLength < 0) validLength = MAX_PATH;
+ arguments = new string(buffer2, 0, validLength);
+ }
}
// To release unmanaged memory
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLocalization.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLocalization.cs
index 4f344d89ecc..e36618b0de5 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLocalization.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLocalization.cs
@@ -1,8 +1,8 @@
using System;
using System.IO;
-using System.Runtime.InteropServices;
-using System.Text;
-
+using Windows.Win32;
+using Windows.Win32.Foundation;
+using Windows.Win32.System.LibraryLoader;
namespace Flow.Launcher.Plugin.Program.Programs
{
@@ -13,51 +13,46 @@ namespace Flow.Launcher.Plugin.Program.Programs
///
public static class ShellLocalization
{
- internal const uint DONTRESOLVEDLLREFERENCES = 0x00000001;
- internal const uint LOADLIBRARYASDATAFILE = 0x00000002;
-
- [DllImport("shell32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode)]
- internal static extern int SHGetLocalizedName(string pszPath, StringBuilder pszResModule, ref int cch, out int pidsRes);
-
- [DllImport("user32.dll", EntryPoint = "LoadStringW", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode)]
- internal static extern int LoadString(IntPtr hModule, int resourceID, StringBuilder resourceValue, int len);
-
- [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, EntryPoint = "LoadLibraryExW")]
- internal static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags);
-
- [DllImport("kernel32.dll", ExactSpelling = true)]
- internal static extern int FreeLibrary(IntPtr hModule);
-
- [DllImport("kernel32.dll", EntryPoint = "ExpandEnvironmentStringsW", CharSet = CharSet.Unicode, ExactSpelling = true)]
- internal static extern uint ExpandEnvironmentStrings(string lpSrc, StringBuilder lpDst, int nSize);
-
///
/// Returns the localized name of a shell item.
///
/// Path to the shell item (e. g. shortcut 'File Explorer.lnk').
/// The localized name as string or .
- public static string GetLocalizedName(string path)
+ public static unsafe string GetLocalizedName(string path)
{
- StringBuilder resourcePath = new StringBuilder(1024);
- StringBuilder localizedName = new StringBuilder(1024);
- int len, id;
- len = resourcePath.Capacity;
+ int capacity = 1024;
+ char[] resourcePathBuffer = new char[capacity];
// If there is no resource to localize a file name the method returns a non zero value.
- if (SHGetLocalizedName(path, resourcePath, ref len, out id) == 0)
+ fixed (char* resourcePath = resourcePathBuffer)
{
- _ = ExpandEnvironmentStrings(resourcePath.ToString(), resourcePath, resourcePath.Capacity);
- IntPtr hMod = LoadLibraryEx(resourcePath.ToString(), IntPtr.Zero, DONTRESOLVEDLLREFERENCES | LOADLIBRARYASDATAFILE);
- if (hMod != IntPtr.Zero)
+ var result = PInvoke.SHGetLocalizedName(path, (PWSTR)resourcePath, (uint)capacity, out var id);
+ if (result == HRESULT.S_OK)
{
- if (LoadString(hMod, id, localizedName, localizedName.Capacity) != 0)
+ 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)
{
- string lString = localizedName.ToString();
- _ = FreeLibrary(hMod);
- return lString;
- }
+ 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;
+ }
+ }
- _ = FreeLibrary(hMod);
+ PInvoke.FreeLibrary(new(safeHandle));
+ }
}
}
From ccfc39e54a35fb552cc563f1fa8660e99ffe1037 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Tue, 10 Dec 2024 15:12:35 +0800
Subject: [PATCH 04/21] Fix string length issue
---
Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs
index d8e5f4fb7a9..6df12fae98b 100644
--- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs
+++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs
@@ -105,9 +105,11 @@ public unsafe string TryGetProcessFilename(Process p)
{
return string.Empty;
}
- }
- return new string(buffer, 0, (int)capacity);
+ int validLength = Array.IndexOf(buffer, '\0');
+ if (validLength < 0) validLength = (int)capacity;
+ return new string(buffer, 0, validLength);
+ }
}
catch
{
From 79f8f053d9288282c56579f41f284e96c19f2de5 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Tue, 10 Dec 2024 15:24:19 +0800
Subject: [PATCH 05/21] Replace DllImport with CSWin32
---
.../Flow.Launcher.Plugin.csproj | 10 +++++-
Flow.Launcher.Plugin/NativeMethods.txt | 3 ++
.../SharedCommands/ShellCommand.cs | 31 ++++++++++++-------
3 files changed, 32 insertions(+), 12 deletions(-)
create mode 100644 Flow.Launcher.Plugin/NativeMethods.txt
diff --git a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj
index 35b9af1c9db..2feb21b12aa 100644
--- a/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj
+++ b/Flow.Launcher.Plugin/Flow.Launcher.Plugin.csproj
@@ -57,7 +57,11 @@
-
+
+
+
+
+
@@ -68,6 +72,10 @@
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/Flow.Launcher.Plugin/NativeMethods.txt b/Flow.Launcher.Plugin/NativeMethods.txt
new file mode 100644
index 00000000000..e3e2b705eb0
--- /dev/null
+++ b/Flow.Launcher.Plugin/NativeMethods.txt
@@ -0,0 +1,3 @@
+EnumThreadWindows
+GetWindowText
+GetWindowTextLength
\ No newline at end of file
diff --git a/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs b/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs
index 49f78b458d7..191a7630958 100644
--- a/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs
+++ b/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs
@@ -2,18 +2,15 @@
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
-using System.Runtime.InteropServices;
-using System.Text;
using System.Threading;
+using Windows.Win32;
+using Windows.Win32.Foundation;
namespace Flow.Launcher.Plugin.SharedCommands
{
public static class ShellCommand
{
public delegate bool EnumThreadDelegate(IntPtr hwnd, IntPtr lParam);
- [DllImport("user32.dll")] static extern bool EnumThreadWindows(uint threadId, EnumThreadDelegate lpfn, IntPtr lParam);
- [DllImport("user32.dll")] static extern int GetWindowText(IntPtr hwnd, StringBuilder lpString, int nMaxCount);
- [DllImport("user32.dll")] static extern int GetWindowTextLength(IntPtr hwnd);
private static bool containsSecurityWindow;
@@ -42,21 +39,33 @@ private static void CheckSecurityWindow()
{
ProcessThreadCollection ptc = Process.GetCurrentProcess().Threads;
for (int i = 0; i < ptc.Count; i++)
- EnumThreadWindows((uint)ptc[i].Id, CheckSecurityThread, IntPtr.Zero);
+ PInvoke.EnumThreadWindows((uint)ptc[i].Id, CheckSecurityThread, IntPtr.Zero);
}
- private static bool CheckSecurityThread(IntPtr hwnd, IntPtr lParam)
+ private static BOOL CheckSecurityThread(HWND hwnd, LPARAM lParam)
{
if (GetWindowTitle(hwnd) == "Windows Security")
containsSecurityWindow = true;
return true;
}
- private static string GetWindowTitle(IntPtr hwnd)
+ private static unsafe string GetWindowTitle(HWND hwnd)
{
- StringBuilder sb = new StringBuilder(GetWindowTextLength(hwnd) + 1);
- GetWindowText(hwnd, sb, sb.Capacity);
- return sb.ToString();
+ var capacity = PInvoke.GetWindowTextLength(hwnd) + 1;
+ char[] buffer = new 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;
+ }
+
+ int validLength = Array.IndexOf(buffer, '\0');
+ if (validLength < 0) validLength = capacity;
+ return new string(buffer, 0, validLength);
+ }
}
public static ProcessStartInfo SetProcessStartInfo(this string fileName, string workingDirectory = "", string arguments = "", string verb = "", bool createNoWindow = false)
From 22170ee43a9fc9b01838cef65f0f45594473d952 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Tue, 10 Dec 2024 20:44:28 +0800
Subject: [PATCH 06/21] Replace DllImport & flags with CSWin32
---
.../FileExplorerHelper.cs | 10 +-
.../Flow.Launcher.Infrastructure.csproj | 8 +
.../Hotkey/GlobalHotkey.cs | 58 +++----
.../Hotkey/InterceptKeys.cs | 38 -----
.../Hotkey/KeyEvent.cs | 12 +-
.../Image/ThumbnailReader.cs | 150 ++++++------------
.../NativeMethods.txt | 20 +++
7 files changed, 114 insertions(+), 182 deletions(-)
delete mode 100644 Flow.Launcher.Infrastructure/Hotkey/InterceptKeys.cs
create mode 100644 Flow.Launcher.Infrastructure/NativeMethods.txt
diff --git a/Flow.Launcher.Infrastructure/FileExplorerHelper.cs b/Flow.Launcher.Infrastructure/FileExplorerHelper.cs
index 76695a4e31e..b738b9c88f6 100644
--- a/Flow.Launcher.Infrastructure/FileExplorerHelper.cs
+++ b/Flow.Launcher.Infrastructure/FileExplorerHelper.cs
@@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Runtime.InteropServices;
+using Windows.Win32;
namespace Flow.Launcher.Infrastructure
{
@@ -54,10 +54,6 @@ private static dynamic GetActiveExplorer()
return explorerWindows.Zip(zOrders).MinBy(x => x.Second).First;
}
- [DllImport("user32.dll")]
- [return: MarshalAs(UnmanagedType.Bool)]
- private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
-
private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
///
@@ -70,9 +66,9 @@ private static IEnumerable GetZOrder(List hWnds)
var index = 0;
var numRemaining = hWnds.Count;
- EnumWindows((wnd, _) =>
+ PInvoke.EnumWindows((wnd, _) =>
{
- var searchIndex = hWnds.FindIndex(x => x.HWND == wnd.ToInt32());
+ var searchIndex = hWnds.FindIndex(x => x.HWND == wnd.Value);
if (searchIndex != -1)
{
z[searchIndex] = index;
diff --git a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj
index ec35ef9d679..1475252caee 100644
--- a/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj
+++ b/Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj
@@ -35,6 +35,10 @@
false
+
+
+
+
@@ -56,6 +60,10 @@
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/Flow.Launcher.Infrastructure/Hotkey/GlobalHotkey.cs b/Flow.Launcher.Infrastructure/Hotkey/GlobalHotkey.cs
index f847ab18906..75af843b973 100644
--- a/Flow.Launcher.Infrastructure/Hotkey/GlobalHotkey.cs
+++ b/Flow.Launcher.Infrastructure/Hotkey/GlobalHotkey.cs
@@ -1,6 +1,11 @@
-using System;
+using System;
+using System.Diagnostics;
using System.Runtime.InteropServices;
using Flow.Launcher.Plugin;
+using Windows.Win32;
+using Windows.Win32.Foundation;
+using Windows.Win32.UI.Input.KeyboardAndMouse;
+using Windows.Win32.UI.WindowsAndMessaging;
namespace Flow.Launcher.Infrastructure.Hotkey
{
@@ -10,44 +15,41 @@ namespace Flow.Launcher.Infrastructure.Hotkey
///
public unsafe class GlobalHotkey : IDisposable
{
- private static readonly IntPtr hookId;
-
-
-
- public delegate bool KeyboardCallback(KeyEvent keyEvent, int vkCode, SpecialKeyState state);
- internal static Func hookedKeyboardCallback;
+ private static readonly UnhookWindowsHookExSafeHandle hookId;
- //Modifier key constants
- private const int VK_SHIFT = 0x10;
- private const int VK_CONTROL = 0x11;
- private const int VK_ALT = 0x12;
- private const int VK_WIN = 91;
+ public delegate bool KeyboardCallback(int keyEvent, int vkCode, SpecialKeyState state);
+ internal static Func hookedKeyboardCallback;
static GlobalHotkey()
{
// Set the hook
- hookId = InterceptKeys.SetHook(& LowLevelKeyboardProc);
+ using Process curProcess = Process.GetCurrentProcess();
+ using ProcessModule curModule = curProcess.MainModule;
+ hookId = PInvoke.SetWindowsHookEx(
+ WINDOWS_HOOK_ID.WH_KEYBOARD_LL,
+ LowLevelKeyboardProc,
+ PInvoke.GetModuleHandle(curModule.ModuleName), 0);
}
public static SpecialKeyState CheckModifiers()
{
SpecialKeyState state = new SpecialKeyState();
- if ((InterceptKeys.GetKeyState(VK_SHIFT) & 0x8000) != 0)
+ if ((PInvoke.GetKeyState((int)VIRTUAL_KEY.VK_SHIFT) & 0x8000) != 0)
{
//SHIFT is pressed
state.ShiftPressed = true;
}
- if ((InterceptKeys.GetKeyState(VK_CONTROL) & 0x8000) != 0)
+ if ((PInvoke.GetKeyState((int)VIRTUAL_KEY.VK_CONTROL) & 0x8000) != 0)
{
//CONTROL is pressed
state.CtrlPressed = true;
}
- if ((InterceptKeys.GetKeyState(VK_ALT) & 0x8000) != 0)
+ if ((PInvoke.GetKeyState((int)VIRTUAL_KEY.VK_MENU) & 0x8000) != 0)
{
//ALT is pressed
state.AltPressed = true;
}
- if ((InterceptKeys.GetKeyState(VK_WIN) & 0x8000) != 0)
+ if ((PInvoke.GetKeyState((int)VIRTUAL_KEY.VK_LWIN) & 0x8000) != 0)
{
//WIN is pressed
state.WinPressed = true;
@@ -56,33 +58,33 @@ public static SpecialKeyState CheckModifiers()
return state;
}
- [UnmanagedCallersOnly]
- private static IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam)
+ private static LRESULT LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
bool continues = true;
if (nCode >= 0)
{
- if (wParam.ToUInt32() == (int)KeyEvent.WM_KEYDOWN ||
- wParam.ToUInt32() == (int)KeyEvent.WM_KEYUP ||
- wParam.ToUInt32() == (int)KeyEvent.WM_SYSKEYDOWN ||
- wParam.ToUInt32() == (int)KeyEvent.WM_SYSKEYUP)
+ var wParamValue = (int)wParam.Value;
+ if (wParamValue == (int)KeyEvent.WM_KEYDOWN ||
+ wParamValue == (int)KeyEvent.WM_KEYUP ||
+ wParamValue == (int)KeyEvent.WM_SYSKEYDOWN ||
+ wParamValue == (int)KeyEvent.WM_SYSKEYUP)
{
if (hookedKeyboardCallback != null)
- continues = hookedKeyboardCallback((KeyEvent)wParam.ToUInt32(), Marshal.ReadInt32(lParam), CheckModifiers());
+ continues = hookedKeyboardCallback((KeyEvent)wParamValue, Marshal.ReadInt32(lParam), CheckModifiers());
}
}
if (continues)
{
- return InterceptKeys.CallNextHookEx(hookId, nCode, wParam, lParam);
+ return PInvoke.CallNextHookEx(hookId, nCode, wParam, lParam);
}
- return (IntPtr)(-1);
+ return new LRESULT(-1);
}
public void Dispose()
{
- InterceptKeys.UnhookWindowsHookEx(hookId);
+ PInvoke.UnhookWindowsHookEx(new HHOOK(hookId.DangerousGetHandle()));
}
~GlobalHotkey()
@@ -90,4 +92,4 @@ public void Dispose()
Dispose();
}
}
-}
\ No newline at end of file
+}
diff --git a/Flow.Launcher.Infrastructure/Hotkey/InterceptKeys.cs b/Flow.Launcher.Infrastructure/Hotkey/InterceptKeys.cs
deleted file mode 100644
index d33bac34cea..00000000000
--- a/Flow.Launcher.Infrastructure/Hotkey/InterceptKeys.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-
-namespace Flow.Launcher.Infrastructure.Hotkey
-{
- internal static unsafe class InterceptKeys
- {
- public delegate IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam);
-
- private const int WH_KEYBOARD_LL = 13;
-
- public static IntPtr SetHook(delegate* unmanaged proc)
- {
- using (Process curProcess = Process.GetCurrentProcess())
- using (ProcessModule curModule = curProcess.MainModule)
- {
- return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
- }
- }
-
- [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
- public static extern IntPtr SetWindowsHookEx(int idHook, delegate* unmanaged lpfn, IntPtr hMod, uint dwThreadId);
-
- [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool UnhookWindowsHookEx(IntPtr hhk);
-
- [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
- public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, UIntPtr wParam, IntPtr lParam);
-
- [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
- public static extern IntPtr GetModuleHandle(string lpModuleName);
-
- [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
- public static extern short GetKeyState(int keyCode);
- }
-}
\ No newline at end of file
diff --git a/Flow.Launcher.Infrastructure/Hotkey/KeyEvent.cs b/Flow.Launcher.Infrastructure/Hotkey/KeyEvent.cs
index 15e3068830f..95bb258377b 100644
--- a/Flow.Launcher.Infrastructure/Hotkey/KeyEvent.cs
+++ b/Flow.Launcher.Infrastructure/Hotkey/KeyEvent.cs
@@ -1,3 +1,5 @@
+using Windows.Win32;
+
namespace Flow.Launcher.Infrastructure.Hotkey
{
public enum KeyEvent
@@ -5,21 +7,21 @@ public enum KeyEvent
///
/// Key down
///
- WM_KEYDOWN = 256,
+ WM_KEYDOWN = (int)PInvoke.WM_KEYDOWN,
///
/// Key up
///
- WM_KEYUP = 257,
+ WM_KEYUP = (int)PInvoke.WM_KEYUP,
///
/// System key up
///
- WM_SYSKEYUP = 261,
+ WM_SYSKEYUP = (int)PInvoke.WM_SYSKEYUP,
///
/// System key down
///
- WM_SYSKEYDOWN = 260
+ WM_SYSKEYDOWN = (int)PInvoke.WM_SYSKEYDOWN
}
-}
\ No newline at end of file
+}
diff --git a/Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs b/Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs
index 247238bb68f..49d6da8c249 100644
--- a/Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs
+++ b/Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs
@@ -1,12 +1,19 @@
using System;
using System.Runtime.InteropServices;
using System.IO;
+using System.Windows;
using System.Windows.Interop;
using System.Windows.Media.Imaging;
-using System.Windows;
+using Windows.Win32;
+using Windows.Win32.Foundation;
+using Windows.Win32.UI.Shell;
+using Windows.Win32.Graphics.Gdi;
namespace Flow.Launcher.Infrastructure.Image
{
+ ///
+ /// Subclass of
+ ///
[Flags]
public enum ThumbnailOptions
{
@@ -22,91 +29,13 @@ public class WindowsThumbnailProvider
{
// Based on https://stackoverflow.com/questions/21751747/extract-thumbnail-for-any-file-in-windows
- private const string IShellItem2Guid = "7E9FB0D3-919F-4307-AB2E-9B1860310C93";
-
- [DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
- internal static extern int SHCreateItemFromParsingName(
- [MarshalAs(UnmanagedType.LPWStr)] string path,
- IntPtr pbc,
- ref Guid riid,
- [MarshalAs(UnmanagedType.Interface)] out IShellItem shellItem);
-
- [DllImport("gdi32.dll")]
- [return: MarshalAs(UnmanagedType.Bool)]
- internal static extern bool DeleteObject(IntPtr hObject);
-
- [ComImport]
- [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
- [Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")]
- internal interface IShellItem
- {
- void BindToHandler(IntPtr pbc,
- [MarshalAs(UnmanagedType.LPStruct)]Guid bhid,
- [MarshalAs(UnmanagedType.LPStruct)]Guid riid,
- out IntPtr ppv);
-
- void GetParent(out IShellItem ppsi);
- void GetDisplayName(SIGDN sigdnName, out IntPtr ppszName);
- void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs);
- void Compare(IShellItem psi, uint hint, out int piOrder);
- };
-
- internal enum SIGDN : uint
- {
- NORMALDISPLAY = 0,
- PARENTRELATIVEPARSING = 0x80018001,
- PARENTRELATIVEFORADDRESSBAR = 0x8001c001,
- DESKTOPABSOLUTEPARSING = 0x80028000,
- PARENTRELATIVEEDITING = 0x80031001,
- DESKTOPABSOLUTEEDITING = 0x8004c000,
- FILESYSPATH = 0x80058000,
- URL = 0x80068000
- }
-
- internal enum HResult
- {
- Ok = 0x0000,
- False = 0x0001,
- InvalidArguments = unchecked((int)0x80070057),
- OutOfMemory = unchecked((int)0x8007000E),
- NoInterface = unchecked((int)0x80004002),
- Fail = unchecked((int)0x80004005),
- ExtractionFailed = unchecked((int)0x8004B200),
- ElementNotFound = unchecked((int)0x80070490),
- TypeElementNotFound = unchecked((int)0x8002802B),
- NoObject = unchecked((int)0x800401E5),
- Win32ErrorCanceled = 1223,
- Canceled = unchecked((int)0x800704C7),
- ResourceInUse = unchecked((int)0x800700AA),
- AccessDenied = unchecked((int)0x80030005)
- }
-
- [ComImportAttribute()]
- [GuidAttribute("bcc18b79-ba16-442f-80c4-8a59c30c463b")]
- [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
- internal interface IShellItemImageFactory
- {
- [PreserveSig]
- HResult GetImage(
- [In, MarshalAs(UnmanagedType.Struct)] NativeSize size,
- [In] ThumbnailOptions flags,
- [Out] out IntPtr phbm);
- }
-
- [StructLayout(LayoutKind.Sequential)]
- internal struct NativeSize
- {
- private int width;
- private int height;
-
- public int Width { set { width = value; } }
- public int Height { set { height = value; } }
- };
+ private static readonly Guid GUID_IShellItem = typeof(IShellItem).GUID;
+ private static readonly HRESULT S_ExtractionFailed = (HRESULT)0x8004B200;
public static BitmapSource GetThumbnail(string fileName, int width, int height, ThumbnailOptions options)
{
- IntPtr hBitmap = GetHBitmap(Path.GetFullPath(fileName), width, height, options);
+ HBITMAP hBitmap = GetHBitmap(Path.GetFullPath(fileName), width, height, options);
try
{
@@ -115,39 +44,52 @@ public static BitmapSource GetThumbnail(string fileName, int width, int height,
finally
{
// delete HBitmap to avoid memory leaks
- DeleteObject(hBitmap);
+ PInvoke.DeleteObject(hBitmap);
}
}
-
- private static IntPtr GetHBitmap(string fileName, int width, int height, ThumbnailOptions options)
+
+ private static unsafe HBITMAP GetHBitmap(string fileName, int width, int height, ThumbnailOptions options)
{
- IShellItem nativeShellItem;
- Guid shellItem2Guid = new Guid(IShellItem2Guid);
- int retCode = SHCreateItemFromParsingName(fileName, IntPtr.Zero, ref shellItem2Guid, out nativeShellItem);
+ var retCode = PInvoke.SHCreateItemFromParsingName(
+ fileName,
+ null,
+ GUID_IShellItem,
+ out var nativeShellItem);
- if (retCode != 0)
+ if (retCode != HRESULT.S_OK)
throw Marshal.GetExceptionForHR(retCode);
- NativeSize nativeSize = new NativeSize
+ if (nativeShellItem is not IShellItemImageFactory imageFactory)
{
- Width = width,
- Height = height
- };
+ Marshal.ReleaseComObject(nativeShellItem);
+ throw new InvalidOperationException("Failed to get IShellItemImageFactory");
+ }
- IntPtr hBitmap;
- HResult hr = ((IShellItemImageFactory)nativeShellItem).GetImage(nativeSize, options, out hBitmap);
+ SIZE size = new SIZE
+ {
+ cx = width,
+ cy = height
+ };
- // if extracting image thumbnail and failed, extract shell icon
- if (options == ThumbnailOptions.ThumbnailOnly && hr == HResult.ExtractionFailed)
+ HBITMAP hBitmap = default;
+ try
{
- hr = ((IShellItemImageFactory) nativeShellItem).GetImage(nativeSize, ThumbnailOptions.IconOnly, out hBitmap);
+ try
+ {
+ imageFactory.GetImage(size, (SIIGBF)options, &hBitmap);
+ }
+ catch (COMException ex) when (ex.HResult == S_ExtractionFailed && options == ThumbnailOptions.ThumbnailOnly)
+ {
+ // Fallback to IconOnly if ThumbnailOnly fails
+ imageFactory.GetImage(size, (SIIGBF)ThumbnailOptions.IconOnly, &hBitmap);
+ }
+ }
+ finally
+ {
+ Marshal.ReleaseComObject(nativeShellItem);
}
- Marshal.ReleaseComObject(nativeShellItem);
-
- if (hr == HResult.Ok) return hBitmap;
-
- throw new COMException($"Error while extracting thumbnail for {fileName}", Marshal.GetExceptionForHR((int)hr));
+ return hBitmap;
}
}
-}
\ No newline at end of file
+}
diff --git a/Flow.Launcher.Infrastructure/NativeMethods.txt b/Flow.Launcher.Infrastructure/NativeMethods.txt
new file mode 100644
index 00000000000..a083f79aad8
--- /dev/null
+++ b/Flow.Launcher.Infrastructure/NativeMethods.txt
@@ -0,0 +1,20 @@
+SHCreateItemFromParsingName
+DeleteObject
+IShellItem
+IShellItemImageFactory
+ExtractionFailed
+S_OK
+
+SetWindowsHookEx
+UnhookWindowsHookEx
+CallNextHookEx
+GetModuleHandle
+GetKeyState
+VIRTUAL_KEY
+
+WM_KEYDOWN
+WM_KEYUP
+WM_SYSKEYDOWN
+WM_SYSKEYUP
+
+EnumWindows
\ No newline at end of file
From ce8b42bc6edb3e5759c7700dd11c71dfb4bff1d4 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Tue, 10 Dec 2024 22:01:55 +0800
Subject: [PATCH 07/21] Replace DllImport & flags with CSWin32
---
Flow.Launcher/Flow.Launcher.csproj | 4 +
Flow.Launcher/Helper/DWMDropShadow.cs | 19 ++--
.../Helper/WallpaperPathRetrieval.cs | 17 ++--
Flow.Launcher/Helper/WindowsInteropHelper.cs | 89 +++++++------------
Flow.Launcher/MainWindow.xaml.cs | 9 +-
Flow.Launcher/NativeMethods.txt | 14 +++
6 files changed, 65 insertions(+), 87 deletions(-)
create mode 100644 Flow.Launcher/NativeMethods.txt
diff --git a/Flow.Launcher/Flow.Launcher.csproj b/Flow.Launcher/Flow.Launcher.csproj
index afc3fbbaa0f..7bd59e8c138 100644
--- a/Flow.Launcher/Flow.Launcher.csproj
+++ b/Flow.Launcher/Flow.Launcher.csproj
@@ -90,6 +90,10 @@
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/Flow.Launcher/Helper/DWMDropShadow.cs b/Flow.Launcher/Helper/DWMDropShadow.cs
index e448acd4c9e..7cb719d074d 100644
--- a/Flow.Launcher/Helper/DWMDropShadow.cs
+++ b/Flow.Launcher/Helper/DWMDropShadow.cs
@@ -1,20 +1,15 @@
using System;
-using System.Drawing.Printing;
-using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
+using Windows.Win32;
+using Windows.Win32.Graphics.Dwm;
+using Windows.Win32.UI.Controls;
namespace Flow.Launcher.Helper;
public class DwmDropShadow
{
- [DllImport("dwmapi.dll", PreserveSig = true)]
- private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);
-
- [DllImport("dwmapi.dll")]
- private static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref Margins pMarInset);
-
///
/// Drops a standard shadow to a WPF Window, even if the window isborderless. Only works with DWM (Vista and Seven).
/// This method is much more efficient than setting AllowsTransparency to true and using the DropShadow effect,
@@ -43,18 +38,18 @@ private static void window_SourceInitialized(object sender, EventArgs e) //fixed
///
/// Window to which the shadow will be applied
/// True if the method succeeded, false if not
- private static bool DropShadow(Window window)
+ private unsafe static bool DropShadow(Window window)
{
try
{
WindowInteropHelper helper = new WindowInteropHelper(window);
int val = 2;
- int ret1 = DwmSetWindowAttribute(helper.Handle, 2, ref val, 4);
+ int ret1 = PInvoke.DwmSetWindowAttribute(new(helper.Handle), DWMWINDOWATTRIBUTE.DWMWA_NCRENDERING_POLICY, &val, 4);
if (ret1 == 0)
{
- Margins m = new Margins { Bottom = 0, Left = 0, Right = 0, Top = 0 };
- int ret2 = DwmExtendFrameIntoClientArea(helper.Handle, ref m);
+ var m = new MARGINS { cyBottomHeight = 0, cxLeftWidth = 0, cxRightWidth = 0, cyTopHeight = 0 };
+ int ret2 = PInvoke.DwmExtendFrameIntoClientArea(new(helper.Handle), &m);
return ret2 == 0;
}
else
diff --git a/Flow.Launcher/Helper/WallpaperPathRetrieval.cs b/Flow.Launcher/Helper/WallpaperPathRetrieval.cs
index e08e227cc33..8ab06e78d08 100644
--- a/Flow.Launcher/Helper/WallpaperPathRetrieval.cs
+++ b/Flow.Launcher/Helper/WallpaperPathRetrieval.cs
@@ -1,24 +1,21 @@
-using System;
-using System.Linq;
-using System.Runtime.InteropServices;
+using System.Linq;
using System.Text;
using System.Windows.Media;
using Microsoft.Win32;
+using Windows.Win32;
+using Windows.Win32.UI.WindowsAndMessaging;
namespace Flow.Launcher.Helper;
public static class WallpaperPathRetrieval
{
- [DllImport("user32.dll", CharSet = CharSet.Unicode)]
- private static extern Int32 SystemParametersInfo(UInt32 action,
- Int32 uParam, StringBuilder vParam, UInt32 winIni);
- private static readonly UInt32 SPI_GETDESKWALLPAPER = 0x73;
- private static int MAX_PATH = 260;
+
+ private static readonly int MAX_PATH = 260;
- public static string GetWallpaperPath()
+ public static unsafe string GetWallpaperPath()
{
var wallpaper = new StringBuilder(MAX_PATH);
- SystemParametersInfo(SPI_GETDESKWALLPAPER, MAX_PATH, wallpaper, 0);
+ PInvoke.SystemParametersInfo(SYSTEM_PARAMETERS_INFO_ACTION.SPI_GETDESKWALLPAPER, (uint)MAX_PATH, &wallpaper, 0);
var str = wallpaper.ToString();
if (string.IsNullOrEmpty(str))
diff --git a/Flow.Launcher/Helper/WindowsInteropHelper.cs b/Flow.Launcher/Helper/WindowsInteropHelper.cs
index 89fbec967a8..ae48034e81e 100644
--- a/Flow.Launcher/Helper/WindowsInteropHelper.cs
+++ b/Flow.Launcher/Helper/WindowsInteropHelper.cs
@@ -1,75 +1,50 @@
using System;
using System.Drawing;
-using System.Runtime.InteropServices;
-using System.Text;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Interop;
using System.Windows.Media;
+using Windows.Win32;
+using Windows.Win32.Foundation;
+using Windows.Win32.UI.WindowsAndMessaging;
using Point = System.Windows.Point;
namespace Flow.Launcher.Helper;
public class WindowsInteropHelper
{
- private const int GWL_STYLE = -16; //WPF's Message code for Title Bar's Style
- private const int WS_SYSMENU = 0x80000; //WPF's Message code for System Menu
- private static IntPtr _hwnd_shell;
- private static IntPtr _hwnd_desktop;
+ private static HWND _hwnd_shell;
+ private static HWND _hwnd_desktop;
//Accessors for shell and desktop handlers
//Will set the variables once and then will return them
- private static IntPtr HWND_SHELL
+ private static HWND HWND_SHELL
{
get
{
- return _hwnd_shell != IntPtr.Zero ? _hwnd_shell : _hwnd_shell = GetShellWindow();
+ return _hwnd_shell != HWND.Null ? _hwnd_shell : _hwnd_shell = PInvoke.GetShellWindow();
}
}
- private static IntPtr HWND_DESKTOP
+
+ private static HWND HWND_DESKTOP
{
get
{
- return _hwnd_desktop != IntPtr.Zero ? _hwnd_desktop : _hwnd_desktop = GetDesktopWindow();
+ return _hwnd_desktop != HWND.Null ? _hwnd_desktop : _hwnd_desktop = PInvoke.GetDesktopWindow();
}
}
- [DllImport("user32.dll", SetLastError = true)]
- internal static extern int GetWindowLong(IntPtr hWnd, int nIndex);
-
- [DllImport("user32.dll")]
- internal static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
-
- [DllImport("user32.dll")]
- internal static extern IntPtr GetForegroundWindow();
-
- [DllImport("user32.dll")]
- internal static extern IntPtr GetDesktopWindow();
-
- [DllImport("user32.dll")]
- internal static extern IntPtr GetShellWindow();
-
- [DllImport("user32.dll", SetLastError = true)]
- internal static extern int GetWindowRect(IntPtr hwnd, out RECT rc);
-
- [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
- internal static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
-
- [DllImport("user32.DLL")]
- public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
-
-
const string WINDOW_CLASS_CONSOLE = "ConsoleWindowClass";
const string WINDOW_CLASS_WINTAB = "Flip3D";
const string WINDOW_CLASS_PROGMAN = "Progman";
const string WINDOW_CLASS_WORKERW = "WorkerW";
- public static bool IsWindowFullscreen()
+ public unsafe static bool IsWindowFullscreen()
{
//get current active window
- IntPtr hWnd = GetForegroundWindow();
+ var hWnd = PInvoke.GetForegroundWindow();
- if (hWnd.Equals(IntPtr.Zero))
+ if (hWnd.Equals(HWND.Null))
{
return false;
}
@@ -80,9 +55,16 @@ public static bool IsWindowFullscreen()
return false;
}
- StringBuilder sb = new StringBuilder(256);
- GetClassName(hWnd, sb, sb.Capacity);
- string windowClass = sb.ToString();
+ string windowClass;
+ int capacity = 256;
+ char[] buffer = new char[capacity];
+ fixed (char* pBuffer = buffer)
+ {
+ PInvoke.GetClassName(hWnd, pBuffer, capacity);
+ int validLength = Array.IndexOf(buffer, '\0');
+ if (validLength < 0) validLength = capacity;
+ windowClass = new string(buffer, 0, validLength);
+ }
//for Win+Tab (Flip3D)
if (windowClass == WINDOW_CLASS_WINTAB)
@@ -90,20 +72,19 @@ public static bool IsWindowFullscreen()
return false;
}
- RECT appBounds;
- GetWindowRect(hWnd, out appBounds);
+ PInvoke.GetWindowRect(hWnd, out var appBounds);
//for console (ConsoleWindowClass), we have to check for negative dimensions
if (windowClass == WINDOW_CLASS_CONSOLE)
{
- return appBounds.Top < 0 && appBounds.Bottom < 0;
+ return appBounds.top < 0 && appBounds.bottom < 0;
}
//for desktop (Progman or WorkerW, depends on the system), we have to check
if (windowClass is WINDOW_CLASS_PROGMAN or WINDOW_CLASS_WORKERW)
{
- IntPtr hWndDesktop = FindWindowEx(hWnd, IntPtr.Zero, "SHELLDLL_DefView", null);
- hWndDesktop = FindWindowEx(hWndDesktop, IntPtr.Zero, "SysListView32", "FolderView");
+ var hWndDesktop = PInvoke.FindWindowEx(hWnd, HWND.Null, "SHELLDLL_DefView", null);
+ hWndDesktop = PInvoke.FindWindowEx(hWndDesktop, HWND.Null, "SysListView32", "FolderView");
if (!hWndDesktop.Equals(IntPtr.Zero))
{
return false;
@@ -111,7 +92,7 @@ public static bool IsWindowFullscreen()
}
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;
}
///
@@ -120,8 +101,8 @@ public static bool IsWindowFullscreen()
///
public static void DisableControlBox(Window win)
{
- var hwnd = new WindowInteropHelper(win).Handle;
- SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU);
+ 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);
}
///
@@ -146,14 +127,4 @@ public static Point TransformPixelsToDIP(Visual visual, double unitX, double uni
}
return new Point((int)(matrix.M11 * unitX), (int)(matrix.M22 * unitY));
}
-
-
- [StructLayout(LayoutKind.Sequential)]
- public struct RECT
- {
- public int Left;
- public int Top;
- public int Right;
- public int Bottom;
- }
}
diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs
index 0f8b8f6d70c..b4f1fb0317f 100644
--- a/Flow.Launcher/MainWindow.xaml.cs
+++ b/Flow.Launcher/MainWindow.xaml.cs
@@ -26,7 +26,7 @@
using DataObject = System.Windows.DataObject;
using System.Windows.Media;
using System.Windows.Interop;
-using System.Runtime.InteropServices;
+using Windows.Win32;
namespace Flow.Launcher
{
@@ -34,9 +34,6 @@ public partial class MainWindow
{
#region Private Fields
- [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
- public static extern IntPtr SetForegroundWindow(IntPtr hwnd);
-
private readonly Storyboard _progressBarStoryboard = new Storyboard();
private bool isProgressBarStoryboardPaused;
private Settings _settings;
@@ -424,7 +421,7 @@ private void InitializeNotifyIcon()
// Get context menu handle and bring it to the foreground
if (PresentationSource.FromVisual(contextMenu) is HwndSource hwndSource)
{
- _ = SetForegroundWindow(hwndSource.Handle);
+ PInvoke.SetForegroundWindow(new(hwndSource.Handle));
}
contextMenu.Focus();
@@ -692,7 +689,7 @@ public Screen SelectedScreen()
screen = Screen.PrimaryScreen;
break;
case SearchWindowScreens.Focus:
- IntPtr foregroundWindowHandle = WindowsInteropHelper.GetForegroundWindow();
+ var foregroundWindowHandle = PInvoke.GetForegroundWindow().Value;
screen = Screen.FromHandle(foregroundWindowHandle);
break;
case SearchWindowScreens.Custom:
diff --git a/Flow.Launcher/NativeMethods.txt b/Flow.Launcher/NativeMethods.txt
new file mode 100644
index 00000000000..bbf55502e5a
--- /dev/null
+++ b/Flow.Launcher/NativeMethods.txt
@@ -0,0 +1,14 @@
+DwmSetWindowAttribute
+DwmExtendFrameIntoClientArea
+SystemParametersInfo
+SetForegroundWindow
+
+GetWindowLong
+SetWindowLong
+GetForegroundWindow
+GetDesktopWindow
+GetShellWindow
+GetWindowRect
+GetClassName
+FindWindowEx
+WINDOW_STYLE
\ No newline at end of file
From dbe626d2384865b0a90af2939d8a2e75c4e223e1 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Wed, 11 Dec 2024 09:57:13 +0800
Subject: [PATCH 08/21] Fix key event return result issue
---
.../Hotkey/GlobalHotkey.cs | 33 ++++++++++---------
1 file changed, 18 insertions(+), 15 deletions(-)
diff --git a/Flow.Launcher.Infrastructure/Hotkey/GlobalHotkey.cs b/Flow.Launcher.Infrastructure/Hotkey/GlobalHotkey.cs
index 75af843b973..04694ed167f 100644
--- a/Flow.Launcher.Infrastructure/Hotkey/GlobalHotkey.cs
+++ b/Flow.Launcher.Infrastructure/Hotkey/GlobalHotkey.cs
@@ -15,20 +15,23 @@ namespace Flow.Launcher.Infrastructure.Hotkey
///
public unsafe class GlobalHotkey : IDisposable
{
+ private static readonly HOOKPROC _procKeyboard = HookKeyboardCallback;
private static readonly UnhookWindowsHookExSafeHandle hookId;
- public delegate bool KeyboardCallback(int keyEvent, int vkCode, SpecialKeyState state);
+ public delegate bool KeyboardCallback(KeyEvent keyEvent, int vkCode, SpecialKeyState state);
internal static Func hookedKeyboardCallback;
static GlobalHotkey()
{
// Set the hook
- using Process curProcess = Process.GetCurrentProcess();
- using ProcessModule curModule = curProcess.MainModule;
- hookId = PInvoke.SetWindowsHookEx(
- WINDOWS_HOOK_ID.WH_KEYBOARD_LL,
- LowLevelKeyboardProc,
- PInvoke.GetModuleHandle(curModule.ModuleName), 0);
+ hookId = SetHook(_procKeyboard, WINDOWS_HOOK_ID.WH_KEYBOARD_LL);
+ }
+
+ private static UnhookWindowsHookExSafeHandle SetHook(HOOKPROC proc, WINDOWS_HOOK_ID hookId)
+ {
+ using var curProcess = Process.GetCurrentProcess();
+ using var curModule = curProcess.MainModule;
+ return PInvoke.SetWindowsHookEx(hookId, proc, PInvoke.GetModuleHandle(curModule.ModuleName), 0);
}
public static SpecialKeyState CheckModifiers()
@@ -58,20 +61,19 @@ public static SpecialKeyState CheckModifiers()
return state;
}
- private static LRESULT LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
+ private static LRESULT HookKeyboardCallback(int nCode, WPARAM wParam, LPARAM lParam)
{
bool continues = true;
if (nCode >= 0)
{
- var wParamValue = (int)wParam.Value;
- if (wParamValue == (int)KeyEvent.WM_KEYDOWN ||
- wParamValue == (int)KeyEvent.WM_KEYUP ||
- wParamValue == (int)KeyEvent.WM_SYSKEYDOWN ||
- wParamValue == (int)KeyEvent.WM_SYSKEYUP)
+ if (wParam.Value == (int)KeyEvent.WM_KEYDOWN ||
+ wParam.Value == (int)KeyEvent.WM_KEYUP ||
+ wParam.Value == (int)KeyEvent.WM_SYSKEYDOWN ||
+ wParam.Value == (int)KeyEvent.WM_SYSKEYUP)
{
if (hookedKeyboardCallback != null)
- continues = hookedKeyboardCallback((KeyEvent)wParamValue, Marshal.ReadInt32(lParam), CheckModifiers());
+ continues = hookedKeyboardCallback((KeyEvent)wParam.Value, Marshal.ReadInt32(lParam), CheckModifiers());
}
}
@@ -79,7 +81,8 @@ private static LRESULT LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lPa
{
return PInvoke.CallNextHookEx(hookId, nCode, wParam, lParam);
}
- return new LRESULT(-1);
+
+ return new LRESULT(1);
}
public void Dispose()
From be72bb73aa8b1783c6ec9877f8067096e5a7ac84 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Wed, 11 Dec 2024 09:57:49 +0800
Subject: [PATCH 09/21] Fix VIRTUAL_KEY.VK_RWIN check issue
---
Flow.Launcher.Infrastructure/Hotkey/GlobalHotkey.cs | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/Flow.Launcher.Infrastructure/Hotkey/GlobalHotkey.cs b/Flow.Launcher.Infrastructure/Hotkey/GlobalHotkey.cs
index 04694ed167f..2bc7db9bf65 100644
--- a/Flow.Launcher.Infrastructure/Hotkey/GlobalHotkey.cs
+++ b/Flow.Launcher.Infrastructure/Hotkey/GlobalHotkey.cs
@@ -52,7 +52,8 @@ public static SpecialKeyState CheckModifiers()
//ALT is pressed
state.AltPressed = true;
}
- if ((PInvoke.GetKeyState((int)VIRTUAL_KEY.VK_LWIN) & 0x8000) != 0)
+ if ((PInvoke.GetKeyState((int)VIRTUAL_KEY.VK_LWIN) & 0x8000) != 0 ||
+ (PInvoke.GetKeyState((int)VIRTUAL_KEY.VK_RWIN) & 0x8000) != 0)
{
//WIN is pressed
state.WinPressed = true;
From 49fc0b8fd07d5856eb265981e6dbccaad834d2a3 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Wed, 11 Dec 2024 10:04:03 +0800
Subject: [PATCH 10/21] Remove useless DllImport & flags
---
Flow.Launcher/Helper/SingleInstance.cs | 226 -------------------------
1 file changed, 226 deletions(-)
diff --git a/Flow.Launcher/Helper/SingleInstance.cs b/Flow.Launcher/Helper/SingleInstance.cs
index 739fed378e0..e0e3075f636 100644
--- a/Flow.Launcher/Helper/SingleInstance.cs
+++ b/Flow.Launcher/Helper/SingleInstance.cs
@@ -1,11 +1,5 @@
using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.IO;
-using System.Runtime.InteropServices;
using System.IO.Pipes;
-using System.Security;
-using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
@@ -14,172 +8,6 @@
// modified to allow single instace restart
namespace Flow.Launcher.Helper
{
- internal enum WM
- {
- NULL = 0x0000,
- CREATE = 0x0001,
- DESTROY = 0x0002,
- MOVE = 0x0003,
- SIZE = 0x0005,
- ACTIVATE = 0x0006,
- SETFOCUS = 0x0007,
- KILLFOCUS = 0x0008,
- ENABLE = 0x000A,
- SETREDRAW = 0x000B,
- SETTEXT = 0x000C,
- GETTEXT = 0x000D,
- GETTEXTLENGTH = 0x000E,
- PAINT = 0x000F,
- CLOSE = 0x0010,
- QUERYENDSESSION = 0x0011,
- QUIT = 0x0012,
- QUERYOPEN = 0x0013,
- ERASEBKGND = 0x0014,
- SYSCOLORCHANGE = 0x0015,
- SHOWWINDOW = 0x0018,
- ACTIVATEAPP = 0x001C,
- SETCURSOR = 0x0020,
- MOUSEACTIVATE = 0x0021,
- CHILDACTIVATE = 0x0022,
- QUEUESYNC = 0x0023,
- GETMINMAXINFO = 0x0024,
-
- WINDOWPOSCHANGING = 0x0046,
- WINDOWPOSCHANGED = 0x0047,
-
- CONTEXTMENU = 0x007B,
- STYLECHANGING = 0x007C,
- STYLECHANGED = 0x007D,
- DISPLAYCHANGE = 0x007E,
- GETICON = 0x007F,
- SETICON = 0x0080,
- NCCREATE = 0x0081,
- NCDESTROY = 0x0082,
- NCCALCSIZE = 0x0083,
- NCHITTEST = 0x0084,
- NCPAINT = 0x0085,
- NCACTIVATE = 0x0086,
- GETDLGCODE = 0x0087,
- SYNCPAINT = 0x0088,
- NCMOUSEMOVE = 0x00A0,
- NCLBUTTONDOWN = 0x00A1,
- NCLBUTTONUP = 0x00A2,
- NCLBUTTONDBLCLK = 0x00A3,
- NCRBUTTONDOWN = 0x00A4,
- NCRBUTTONUP = 0x00A5,
- NCRBUTTONDBLCLK = 0x00A6,
- NCMBUTTONDOWN = 0x00A7,
- NCMBUTTONUP = 0x00A8,
- NCMBUTTONDBLCLK = 0x00A9,
-
- SYSKEYDOWN = 0x0104,
- SYSKEYUP = 0x0105,
- SYSCHAR = 0x0106,
- SYSDEADCHAR = 0x0107,
- COMMAND = 0x0111,
- SYSCOMMAND = 0x0112,
-
- MOUSEMOVE = 0x0200,
- LBUTTONDOWN = 0x0201,
- LBUTTONUP = 0x0202,
- LBUTTONDBLCLK = 0x0203,
- RBUTTONDOWN = 0x0204,
- RBUTTONUP = 0x0205,
- RBUTTONDBLCLK = 0x0206,
- MBUTTONDOWN = 0x0207,
- MBUTTONUP = 0x0208,
- MBUTTONDBLCLK = 0x0209,
- MOUSEWHEEL = 0x020A,
- XBUTTONDOWN = 0x020B,
- XBUTTONUP = 0x020C,
- XBUTTONDBLCLK = 0x020D,
- MOUSEHWHEEL = 0x020E,
-
-
- CAPTURECHANGED = 0x0215,
-
- ENTERSIZEMOVE = 0x0231,
- EXITSIZEMOVE = 0x0232,
-
- IME_SETCONTEXT = 0x0281,
- IME_NOTIFY = 0x0282,
- IME_CONTROL = 0x0283,
- IME_COMPOSITIONFULL = 0x0284,
- IME_SELECT = 0x0285,
- IME_CHAR = 0x0286,
- IME_REQUEST = 0x0288,
- IME_KEYDOWN = 0x0290,
- IME_KEYUP = 0x0291,
-
- NCMOUSELEAVE = 0x02A2,
-
- DWMCOMPOSITIONCHANGED = 0x031E,
- DWMNCRENDERINGCHANGED = 0x031F,
- DWMCOLORIZATIONCOLORCHANGED = 0x0320,
- DWMWINDOWMAXIMIZEDCHANGE = 0x0321,
-
- #region Windows 7
- DWMSENDICONICTHUMBNAIL = 0x0323,
- DWMSENDICONICLIVEPREVIEWBITMAP = 0x0326,
- #endregion
-
- USER = 0x0400,
-
- // This is the hard-coded message value used by WinForms for Shell_NotifyIcon.
- // It's relatively safe to reuse.
- TRAYMOUSEMESSAGE = 0x800, //WM_USER + 1024
- APP = 0x8000
- }
-
- [SuppressUnmanagedCodeSecurity]
- internal static class NativeMethods
- {
- ///
- /// Delegate declaration that matches WndProc signatures.
- ///
- public delegate IntPtr MessageHandler(WM uMsg, IntPtr wParam, IntPtr lParam, out bool handled);
-
- [DllImport("shell32.dll", EntryPoint = "CommandLineToArgvW", CharSet = CharSet.Unicode)]
- private static extern IntPtr _CommandLineToArgvW([MarshalAs(UnmanagedType.LPWStr)] string cmdLine, out int numArgs);
-
-
- [DllImport("kernel32.dll", EntryPoint = "LocalFree", SetLastError = true)]
- private static extern IntPtr _LocalFree(IntPtr hMem);
-
-
- public static string[] CommandLineToArgvW(string cmdLine)
- {
- IntPtr argv = IntPtr.Zero;
- try
- {
- int numArgs = 0;
-
- argv = _CommandLineToArgvW(cmdLine, out numArgs);
- if (argv == IntPtr.Zero)
- {
- throw new Win32Exception();
- }
- var result = new string[numArgs];
-
- for (int i = 0; i < numArgs; i++)
- {
- IntPtr currArg = Marshal.ReadIntPtr(argv, i * Marshal.SizeOf(typeof(IntPtr)));
- result[i] = Marshal.PtrToStringUni(currArg);
- }
-
- return result;
- }
- finally
- {
-
- IntPtr p = _LocalFree(argv);
- // Otherwise LocalFree failed.
- // Assert.AreEqual(IntPtr.Zero, p);
- }
- }
-
- }
-
public interface ISingleInstanceApp
{
void OnSecondAppStarted();
@@ -219,10 +47,6 @@ public static class SingleInstance
#endregion
- #region Public Properties
-
- #endregion
-
#region Public Methods
///
@@ -264,56 +88,6 @@ public static void Cleanup()
#region Private Methods
- ///
- /// Gets command line args - for ClickOnce deployed applications, command line args may not be passed directly, they have to be retrieved.
- ///
- /// List of command line arg strings.
- private static IList GetCommandLineArgs( string uniqueApplicationName )
- {
- string[] args = null;
-
- try
- {
- // The application was not clickonce deployed, get args from standard API's
- args = Environment.GetCommandLineArgs();
- }
- catch (NotSupportedException)
- {
-
- // The application was clickonce deployed
- // Clickonce deployed apps cannot recieve traditional commandline arguments
- // As a workaround commandline arguments can be written to a shared location before
- // the app is launched and the app can obtain its commandline arguments from the
- // shared location
- string appFolderPath = Path.Combine(
- Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), uniqueApplicationName);
-
- string cmdLinePath = Path.Combine(appFolderPath, "cmdline.txt");
- if (File.Exists(cmdLinePath))
- {
- try
- {
- using (TextReader reader = new StreamReader(cmdLinePath, Encoding.Unicode))
- {
- args = NativeMethods.CommandLineToArgvW(reader.ReadToEnd());
- }
-
- File.Delete(cmdLinePath);
- }
- catch (IOException)
- {
- }
- }
- }
-
- if (args == null)
- {
- args = new string[] { };
- }
-
- return new List(args);
- }
-
///
/// Creates a remote server pipe for communication.
/// Once receives signal from client, will activate first instance.
From 8a05c606e4ce63ecdde6aacf8b3d6ae7b778f91d Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Wed, 11 Dec 2024 11:08:57 +0800
Subject: [PATCH 11/21] Fix possible double release
---
Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs b/Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs
index 49d6da8c249..2fb8cf3632d 100644
--- a/Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs
+++ b/Flow.Launcher.Infrastructure/Image/ThumbnailReader.cs
@@ -62,6 +62,7 @@ private static unsafe HBITMAP GetHBitmap(string fileName, int width, int height,
if (nativeShellItem is not IShellItemImageFactory imageFactory)
{
Marshal.ReleaseComObject(nativeShellItem);
+ nativeShellItem = null;
throw new InvalidOperationException("Failed to get IShellItemImageFactory");
}
@@ -86,7 +87,10 @@ private static unsafe HBITMAP GetHBitmap(string fileName, int width, int height,
}
finally
{
- Marshal.ReleaseComObject(nativeShellItem);
+ if (nativeShellItem != null)
+ {
+ Marshal.ReleaseComObject(nativeShellItem);
+ }
}
return hBitmap;
From a088f91541ea18c637c368706443793739acdc46 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Sat, 14 Dec 2024 11:17:29 +0800
Subject: [PATCH 12/21] Add comments for string truncation
---
Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs | 1 +
Flow.Launcher/Helper/WindowsInteropHelper.cs | 2 ++
Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs | 1 +
.../Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs | 2 ++
4 files changed, 6 insertions(+)
diff --git a/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs b/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs
index 191a7630958..9639f1b1ad4 100644
--- a/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs
+++ b/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs
@@ -62,6 +62,7 @@ private static unsafe string GetWindowTitle(HWND hwnd)
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);
diff --git a/Flow.Launcher/Helper/WindowsInteropHelper.cs b/Flow.Launcher/Helper/WindowsInteropHelper.cs
index ae48034e81e..6656b0c5b26 100644
--- a/Flow.Launcher/Helper/WindowsInteropHelper.cs
+++ b/Flow.Launcher/Helper/WindowsInteropHelper.cs
@@ -61,6 +61,8 @@ public unsafe static bool IsWindowFullscreen()
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);
diff --git a/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs
index 6df12fae98b..abc254d8694 100644
--- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs
+++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs
@@ -106,6 +106,7 @@ public unsafe string TryGetProcessFilename(Process p)
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);
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs
index fae9c84c985..05cbfd2b12e 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs
@@ -38,6 +38,8 @@ public unsafe string retrieveTargetPath(string path)
fixed (char* bufferChar = buffer)
{
((IShellLinkW)link).GetPath((PWSTR)bufferChar, MAX_PATH, &data, (uint)SLGP_FLAGS.SLGP_SHORTPATH);
+
+ // Truncate the buffer to the actual length of the string
int validLength = Array.IndexOf(buffer, '\0');
if (validLength < 0) validLength = MAX_PATH;
target = new string(buffer, 0, validLength);
From a899ff83e5a336505cdf0b64d2ef48f2d1fd417a Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Sat, 14 Dec 2024 11:46:09 +0800
Subject: [PATCH 13/21] Improve code quality
---
.../Programs/ShellLinkHelper.cs | 22 +++++++++----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs
index 05cbfd2b12e..ad1f3ab564d 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs
@@ -38,11 +38,7 @@ public unsafe string retrieveTargetPath(string path)
fixed (char* bufferChar = buffer)
{
((IShellLinkW)link).GetPath((PWSTR)bufferChar, MAX_PATH, &data, (uint)SLGP_FLAGS.SLGP_SHORTPATH);
-
- // Truncate the buffer to the actual length of the string
- int validLength = Array.IndexOf(buffer, '\0');
- if (validLength < 0) validLength = MAX_PATH;
- target = new string(buffer, 0, validLength);
+ target = GetStringFromBuffer(buffer, MAX_PATH);
}
// To set the app description
@@ -54,9 +50,7 @@ public unsafe string retrieveTargetPath(string path)
fixed (char* buffer1Char = buffer1)
{
((IShellLinkW)link).GetDescription((PWSTR)buffer1Char, MAX_PATH);
- int validLength = Array.IndexOf(buffer1, '\0');
- if (validLength < 0) validLength = MAX_PATH;
- description = new string(buffer1, 0, validLength);
+ description = GetStringFromBuffer(buffer1, MAX_PATH);
}
}
catch (COMException e)
@@ -71,9 +65,7 @@ public unsafe string retrieveTargetPath(string path)
fixed (char* buffer2Char = buffer2)
{
((IShellLinkW)link).GetArguments((PWSTR)buffer2Char, MAX_PATH);
- int validLength = Array.IndexOf(buffer2, '\0');
- if (validLength < 0) validLength = MAX_PATH;
- arguments = new string(buffer2, 0, validLength);
+ arguments = GetStringFromBuffer(buffer2, MAX_PATH);
}
}
@@ -82,5 +74,13 @@ public unsafe string retrieveTargetPath(string path)
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);
+ }
}
}
From b96c7b12d51ec89b3355c8bc7a241f8173935d10 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Sat, 14 Dec 2024 11:51:13 +0800
Subject: [PATCH 14/21] Remove useless PInvoke method import
---
Flow.Launcher.Infrastructure/NativeMethods.txt | 1 -
1 file changed, 1 deletion(-)
diff --git a/Flow.Launcher.Infrastructure/NativeMethods.txt b/Flow.Launcher.Infrastructure/NativeMethods.txt
index a083f79aad8..f117534a1ff 100644
--- a/Flow.Launcher.Infrastructure/NativeMethods.txt
+++ b/Flow.Launcher.Infrastructure/NativeMethods.txt
@@ -2,7 +2,6 @@
DeleteObject
IShellItem
IShellItemImageFactory
-ExtractionFailed
S_OK
SetWindowsHookEx
From 532b5dc4c90b03399136299cc514aecd65d59eb5 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Thu, 19 Dec 2024 14:08:46 +0800
Subject: [PATCH 15/21] Replace flags with CSWin32
---
Flow.Launcher/MainWindow.xaml.cs | 6 ++----
Flow.Launcher/NativeMethods.txt | 5 ++++-
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs
index b4f1fb0317f..8ca153afc34 100644
--- a/Flow.Launcher/MainWindow.xaml.cs
+++ b/Flow.Launcher/MainWindow.xaml.cs
@@ -78,21 +78,19 @@ public MainWindow()
InitializeComponent();
}
- private const int WM_ENTERSIZEMOVE = 0x0231;
- private const int WM_EXITSIZEMOVE = 0x0232;
private int _initialWidth;
private int _initialHeight;
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
- if (msg == WM_ENTERSIZEMOVE)
+ if (msg == PInvoke.WM_ENTERSIZEMOVE)
{
_initialWidth = (int)Width;
_initialHeight = (int)Height;
handled = true;
}
- if (msg == WM_EXITSIZEMOVE)
+ if (msg == PInvoke.WM_EXITSIZEMOVE)
{
if (_initialHeight != (int)Height)
{
diff --git a/Flow.Launcher/NativeMethods.txt b/Flow.Launcher/NativeMethods.txt
index bbf55502e5a..2b147c05f52 100644
--- a/Flow.Launcher/NativeMethods.txt
+++ b/Flow.Launcher/NativeMethods.txt
@@ -11,4 +11,7 @@ GetShellWindow
GetWindowRect
GetClassName
FindWindowEx
-WINDOW_STYLE
\ No newline at end of file
+WINDOW_STYLE
+
+WM_ENTERSIZEMOVE
+WM_EXITSIZEMOVE
\ No newline at end of file
From 3ab1fb15b639f13fa46ab5fcfceb581e20b3682a Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Thu, 19 Dec 2024 16:34:32 +0800
Subject: [PATCH 16/21] Fix clipboard action under sta thread issue
---
.../NativeMethods.txt | 5 +-
.../UserSettings/Settings.cs | 2 +-
Flow.Launcher.Infrastructure/Win32Helper.cs | 141 ++++++++++++++++++
Flow.Launcher/PublicAPIInstance.cs | 45 +++---
4 files changed, 170 insertions(+), 23 deletions(-)
create mode 100644 Flow.Launcher.Infrastructure/Win32Helper.cs
diff --git a/Flow.Launcher.Infrastructure/NativeMethods.txt b/Flow.Launcher.Infrastructure/NativeMethods.txt
index f117534a1ff..d8777ff277c 100644
--- a/Flow.Launcher.Infrastructure/NativeMethods.txt
+++ b/Flow.Launcher.Infrastructure/NativeMethods.txt
@@ -16,4 +16,7 @@ WM_KEYUP
WM_SYSKEYDOWN
WM_SYSKEYUP
-EnumWindows
\ No newline at end of file
+EnumWindows
+
+OleInitialize
+OleUninitialize
\ No newline at end of file
diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs
index 0c7de10fd78..5493ad72410 100644
--- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs
+++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs
@@ -230,7 +230,7 @@ public SearchPrecisionScore QuerySearchPrecision
[JsonIgnore]
public ObservableCollection BuiltinShortcuts { get; set; } = new()
{
- new BuiltinShortcutModel("{clipboard}", "shortcut_clipboard_description", Clipboard.GetText),
+ new BuiltinShortcutModel("{clipboard}", "shortcut_clipboard_description", () => Win32Helper.StartSTATaskAsync(Clipboard.GetText).Result),
new BuiltinShortcutModel("{active_explorer_path}", "shortcut_active_explorer_path", FileExplorerHelper.GetActiveExplorerPath)
};
diff --git a/Flow.Launcher.Infrastructure/Win32Helper.cs b/Flow.Launcher.Infrastructure/Win32Helper.cs
new file mode 100644
index 00000000000..76e9cfe5bcc
--- /dev/null
+++ b/Flow.Launcher.Infrastructure/Win32Helper.cs
@@ -0,0 +1,141 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Windows.Win32;
+
+namespace Flow.Launcher.Infrastructure
+{
+ ///
+ /// Provides static helper for Win32.
+ /// Codes are edited from: https://github.com/files-community/Files.
+ ///
+ public static class Win32Helper
+ {
+ public static Task StartSTATaskAsync(Action action)
+ {
+ var taskCompletionSource = new TaskCompletionSource();
+ Thread thread = new(() =>
+ {
+ PInvoke.OleInitialize();
+
+ try
+ {
+ action();
+ taskCompletionSource.SetResult();
+ }
+ catch (System.Exception)
+ {
+ taskCompletionSource.SetResult();
+ }
+ finally
+ {
+ PInvoke.OleUninitialize();
+ }
+ })
+ {
+ IsBackground = true,
+ Priority = ThreadPriority.Normal
+ };
+
+ thread.SetApartmentState(ApartmentState.STA);
+ thread.Start();
+
+ return taskCompletionSource.Task;
+ }
+
+ public static Task StartSTATaskAsync(Func func)
+ {
+ var taskCompletionSource = new TaskCompletionSource();
+ Thread thread = new(async () =>
+ {
+ PInvoke.OleInitialize();
+
+ try
+ {
+ await func();
+ taskCompletionSource.SetResult();
+ }
+ catch (System.Exception)
+ {
+ taskCompletionSource.SetResult();
+ }
+ finally
+ {
+ PInvoke.OleUninitialize();
+ }
+ })
+ {
+ IsBackground = true,
+ Priority = ThreadPriority.Normal
+ };
+
+ thread.SetApartmentState(ApartmentState.STA);
+ thread.Start();
+
+ return taskCompletionSource.Task;
+ }
+
+ public static Task StartSTATaskAsync(Func func)
+ {
+ var taskCompletionSource = new TaskCompletionSource();
+
+ Thread thread = new(() =>
+ {
+ PInvoke.OleInitialize();
+
+ try
+ {
+ taskCompletionSource.SetResult(func());
+ }
+ catch (System.Exception)
+ {
+ taskCompletionSource.SetResult(default);
+ }
+ finally
+ {
+ PInvoke.OleUninitialize();
+ }
+ })
+ {
+ IsBackground = true,
+ Priority = ThreadPriority.Normal
+ };
+
+ thread.SetApartmentState(ApartmentState.STA);
+ thread.Start();
+
+ return taskCompletionSource.Task;
+ }
+
+ public static Task StartSTATaskAsync(Func> func)
+ {
+ var taskCompletionSource = new TaskCompletionSource();
+
+ Thread thread = new(async () =>
+ {
+ PInvoke.OleInitialize();
+ try
+ {
+ taskCompletionSource.SetResult(await func());
+ }
+ catch (System.Exception)
+ {
+ taskCompletionSource.SetResult(default);
+ }
+ finally
+ {
+ PInvoke.OleUninitialize();
+ }
+ })
+ {
+ IsBackground = true,
+ Priority = ThreadPriority.Normal
+ };
+
+ thread.SetApartmentState(ApartmentState.STA);
+ thread.Start();
+
+ return taskCompletionSource.Task;
+ }
+ }
+}
diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs
index f4712770d7d..dcdb798fff2 100644
--- a/Flow.Launcher/PublicAPIInstance.cs
+++ b/Flow.Launcher/PublicAPIInstance.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
@@ -117,35 +117,38 @@ public void ShellRun(string cmd, string filename = "cmd.exe")
ShellCommand.Execute(startInfo);
}
- public void CopyToClipboard(string stringToCopy, bool directCopy = false, bool showDefaultNotification = true)
+ public async void CopyToClipboard(string stringToCopy, bool directCopy = false, bool showDefaultNotification = true)
{
if (string.IsNullOrEmpty(stringToCopy))
return;
- var isFile = File.Exists(stringToCopy);
- if (directCopy && (isFile || Directory.Exists(stringToCopy)))
+ await Win32Helper.StartSTATaskAsync(() =>
{
- var paths = new StringCollection
+ var isFile = File.Exists(stringToCopy);
+ if (directCopy && (isFile || Directory.Exists(stringToCopy)))
{
- stringToCopy
- };
+ var paths = new StringCollection
+ {
+ stringToCopy
+ };
- Clipboard.SetFileDropList(paths);
+ Clipboard.SetFileDropList(paths);
- if (showDefaultNotification)
- ShowMsg(
- $"{GetTranslation("copy")} {(isFile ? GetTranslation("fileTitle") : GetTranslation("folderTitle"))}",
- GetTranslation("completedSuccessfully"));
- }
- else
- {
- Clipboard.SetDataObject(stringToCopy);
+ if (showDefaultNotification)
+ ShowMsg(
+ $"{GetTranslation("copy")} {(isFile ? GetTranslation("fileTitle") : GetTranslation("folderTitle"))}",
+ GetTranslation("completedSuccessfully"));
+ }
+ else
+ {
+ Clipboard.SetDataObject(stringToCopy);
- if (showDefaultNotification)
- ShowMsg(
- $"{GetTranslation("copy")} {GetTranslation("textTitle")}",
- GetTranslation("completedSuccessfully"));
- }
+ if (showDefaultNotification)
+ ShowMsg(
+ $"{GetTranslation("copy")} {GetTranslation("textTitle")}",
+ GetTranslation("completedSuccessfully"));
+ }
+ });
}
public void StartLoadingBar() => _mainVM.ProgressBarVisibility = Visibility.Visible;
From 1073821b655e3dd29062fd19cc4a6f2b976aa292 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Fri, 20 Dec 2024 18:55:57 +0800
Subject: [PATCH 17/21] Improve recyble bin clear issue noticification
---
Plugins/Flow.Launcher.Plugin.Sys/Main.cs | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs
index 8bcd5cd6b75..5bfc68ea613 100644
--- a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs
+++ b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs
@@ -252,8 +252,10 @@ private List Commands()
var result = PInvoke.SHEmptyRecycleBin(new(), string.Empty, 0);
if (result != HRESULT.S_OK && result != HRESULT.E_UNEXPECTED)
{
- context.API.ShowMsgBox($"Error emptying recycle bin, error code: {result}\n" +
- "please refer to https://msdn.microsoft.com/en-us/library/windows/desktop/aa378137",
+ context.API.ShowMsgBox("Failed to empty the recycle bin. This might happen if:\n" +
+ "- A file in the recycle bin is in use\n" +
+ "- You don't have permission to delete some items\n" +
+ "Please close any applications that might be using these files and try again.",
"Error",
MessageBoxButton.OK, MessageBoxImage.Error);
}
From 6f899092b59a344b3ceb3d2edd28700e585c7335 Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Tue, 24 Dec 2024 11:59:04 +0800
Subject: [PATCH 18/21] Move dll import codes to win32 helper
---
Flow.Launcher.Core/Resource/Theme.cs | 98 +-----------------
Flow.Launcher.Infrastructure/Win32Helper.cs | 106 +++++++++++++++++++-
2 files changed, 104 insertions(+), 100 deletions(-)
diff --git a/Flow.Launcher.Core/Resource/Theme.cs b/Flow.Launcher.Core/Resource/Theme.cs
index 0d8c0d90163..1d840930663 100644
--- a/Flow.Launcher.Core/Resource/Theme.cs
+++ b/Flow.Launcher.Core/Resource/Theme.cs
@@ -3,10 +3,8 @@
using System.IO;
using System.Linq;
using System.Xml;
-using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
-using System.Windows.Interop;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Effects;
@@ -98,12 +96,12 @@ public bool ChangeTheme(string theme)
_oldTheme = Path.GetFileNameWithoutExtension(_oldResource.Source.AbsolutePath);
}
- BlurEnabled = IsBlurTheme();
+ BlurEnabled = Win32Helper.IsBlurTheme();
if (Settings.UseDropShadowEffect && !BlurEnabled)
AddDropShadowEffectToCurrentTheme();
- SetBlurForWindow();
+ Win32Helper.SetBlurForWindow(Application.Current.MainWindow, BlurEnabled);
}
catch (DirectoryNotFoundException)
{
@@ -357,98 +355,6 @@ public void RemoveDropShadowEffectFromCurrentTheme()
UpdateResourceDictionary(dict);
}
- #region Blur Handling
- /*
- Found on https://github.com/riverar/sample-win10-aeroglass
- */
- private enum AccentState
- {
- ACCENT_DISABLED = 0,
- ACCENT_ENABLE_GRADIENT = 1,
- ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
- ACCENT_ENABLE_BLURBEHIND = 3,
- ACCENT_INVALID_STATE = 4
- }
-
- [StructLayout(LayoutKind.Sequential)]
- private struct AccentPolicy
- {
- public AccentState AccentState;
- public int AccentFlags;
- public int GradientColor;
- public int AnimationId;
- }
-
- [StructLayout(LayoutKind.Sequential)]
- private struct WindowCompositionAttributeData
- {
- public WindowCompositionAttribute Attribute;
- public IntPtr Data;
- public int SizeOfData;
- }
-
- private enum WindowCompositionAttribute
- {
- WCA_ACCENT_POLICY = 19
- }
- [DllImport("user32.dll")]
- private static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data);
-
- ///
- /// Sets the blur for a window via SetWindowCompositionAttribute
- ///
- public void SetBlurForWindow()
- {
- if (BlurEnabled)
- {
- SetWindowAccent(Application.Current.MainWindow, AccentState.ACCENT_ENABLE_BLURBEHIND);
- }
- else
- {
- SetWindowAccent(Application.Current.MainWindow, AccentState.ACCENT_DISABLED);
- }
- }
-
- private bool IsBlurTheme()
- {
- if (Environment.OSVersion.Version >= new Version(6, 2))
- {
- var resource = Application.Current.TryFindResource("ThemeBlurEnabled");
-
- if (resource is bool)
- return (bool)resource;
-
- return false;
- }
-
- return false;
- }
-
- private void SetWindowAccent(Window w, AccentState state)
- {
- var windowHelper = new WindowInteropHelper(w);
-
- windowHelper.EnsureHandle();
-
- var accent = new AccentPolicy { AccentState = state };
- var accentStructSize = Marshal.SizeOf(accent);
-
- var accentPtr = Marshal.AllocHGlobal(accentStructSize);
- Marshal.StructureToPtr(accent, accentPtr, false);
-
- var data = new WindowCompositionAttributeData
- {
- Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY,
- SizeOfData = accentStructSize,
- Data = accentPtr
- };
-
- SetWindowCompositionAttribute(windowHelper.Handle, ref data);
-
- Marshal.FreeHGlobal(accentPtr);
- }
- #endregion
-
public record ThemeData(string FileNameWithoutExtension, string Name, bool? IsDark = null, bool? HasBlur = null);
}
}
diff --git a/Flow.Launcher.Infrastructure/Win32Helper.cs b/Flow.Launcher.Infrastructure/Win32Helper.cs
index 76e9cfe5bcc..6d6c7286412 100644
--- a/Flow.Launcher.Infrastructure/Win32Helper.cs
+++ b/Flow.Launcher.Infrastructure/Win32Helper.cs
@@ -1,16 +1,21 @@
using System;
+using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
+using System.Windows.Interop;
+using System.Windows;
using Windows.Win32;
namespace Flow.Launcher.Infrastructure
{
- ///
- /// Provides static helper for Win32.
- /// Codes are edited from: https://github.com/files-community/Files.
- ///
public static class Win32Helper
{
+ #region STA Thread
+
+ /*
+ Found on https://github.com/files-community/Files
+ */
+
public static Task StartSTATaskAsync(Action action)
{
var taskCompletionSource = new TaskCompletionSource();
@@ -137,5 +142,98 @@ public static Task StartSTATaskAsync(Func func)
return taskCompletionSource.Task;
}
+
+ #endregion
+
+ #region Blur Handling
+
+ /*
+ Found on https://github.com/riverar/sample-win10-aeroglass
+ */
+
+ private enum AccentState
+ {
+ ACCENT_DISABLED = 0,
+ ACCENT_ENABLE_GRADIENT = 1,
+ ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
+ ACCENT_ENABLE_BLURBEHIND = 3,
+ ACCENT_INVALID_STATE = 4
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct AccentPolicy
+ {
+ public AccentState AccentState;
+ public int AccentFlags;
+ public int GradientColor;
+ public int AnimationId;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct WindowCompositionAttributeData
+ {
+ public WindowCompositionAttribute Attribute;
+ public IntPtr Data;
+ public int SizeOfData;
+ }
+
+ private enum WindowCompositionAttribute
+ {
+ WCA_ACCENT_POLICY = 19
+ }
+
+ [DllImport("user32.dll")]
+ private static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data);
+
+ ///
+ /// Checks if the blur theme is enabled
+ ///
+ public static bool IsBlurTheme()
+ {
+ if (Environment.OSVersion.Version >= new Version(6, 2))
+ {
+ var resource = Application.Current.TryFindResource("ThemeBlurEnabled");
+
+ if (resource is bool b)
+ return b;
+
+ return false;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Sets the blur for a window via SetWindowCompositionAttribute
+ ///
+ public static void SetBlurForWindow(Window w, bool blur)
+ {
+ SetWindowAccent(w, blur ? AccentState.ACCENT_ENABLE_BLURBEHIND : AccentState.ACCENT_DISABLED);
+ }
+
+ private static void SetWindowAccent(Window w, AccentState state)
+ {
+ var windowHelper = new WindowInteropHelper(w);
+
+ windowHelper.EnsureHandle();
+
+ var accent = new AccentPolicy { AccentState = state };
+ var accentStructSize = Marshal.SizeOf(accent);
+
+ var accentPtr = Marshal.AllocHGlobal(accentStructSize);
+ Marshal.StructureToPtr(accent, accentPtr, false);
+
+ var data = new WindowCompositionAttributeData
+ {
+ Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY,
+ SizeOfData = accentStructSize,
+ Data = accentPtr
+ };
+
+ SetWindowCompositionAttribute(windowHelper.Handle, ref data);
+
+ Marshal.FreeHGlobal(accentPtr);
+ }
+ #endregion
}
}
From 0153f71083b755ff2df85c16757e7341e4069d2e Mon Sep 17 00:00:00 2001
From: Hongtao Zhang
Date: Wed, 25 Dec 2024 11:20:09 -0600
Subject: [PATCH 19/21] use stackalloc if possible and fix some incorrect use
of safehandle
---
.../Hotkey/GlobalHotkey.cs | 2 +-
.../SharedCommands/ShellCommand.cs | 19 +++----
Flow.Launcher/Helper/DWMDropShadow.cs | 17 +++---
.../Helper/WallpaperPathRetrieval.cs | 24 +++++----
Flow.Launcher/Helper/WindowsInteropHelper.cs | 41 +++++++++++----
.../ProcessHelper.cs | 7 +--
.../Programs/ShellLinkHelper.cs | 30 ++++-------
.../Programs/ShellLocalization.cs | 52 +++++++++----------
8 files changed, 96 insertions(+), 96 deletions(-)
diff --git a/Flow.Launcher.Infrastructure/Hotkey/GlobalHotkey.cs b/Flow.Launcher.Infrastructure/Hotkey/GlobalHotkey.cs
index 2bc7db9bf65..b2a14075581 100644
--- a/Flow.Launcher.Infrastructure/Hotkey/GlobalHotkey.cs
+++ b/Flow.Launcher.Infrastructure/Hotkey/GlobalHotkey.cs
@@ -88,7 +88,7 @@ private static LRESULT HookKeyboardCallback(int nCode, WPARAM wParam, LPARAM lPa
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 9639f1b1ad4..9ea582118a7 100644
--- a/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs
+++ b/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs
@@ -25,6 +25,7 @@ public static Process RunAsDifferentUser(ProcessStartInfo processStartInfo)
CheckSecurityWindow();
Thread.Sleep(25);
}
+
while (containsSecurityWindow) // while this process contains a "Windows Security" dialog, stay open
{
containsSecurityWindow = false;
@@ -52,24 +53,20 @@ private static BOOL CheckSecurityThread(HWND hwnd, LPARAM lParam)
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 7cb719d074d..58817d70e03 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 @@ private static void window_SourceInitialized(object sender, EventArgs e) //fixed
///
/// 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 8ab06e78d08..8a42d480ff9 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 Color GetWallpaperColor()
{
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 6656b0c5b26..caf3f0a7f60 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 unsafe static bool IsWindowFullscreen()
}
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 unsafe static bool IsWindowFullscreen()
{
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 unsafe static bool IsWindowFullscreen()
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 static Point TransformPixelsToDIP(Visual visual, double unitX, double uni
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 abc254d8694..519e8a79297 100644
--- a/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs
+++ b/Plugins/Flow.Launcher.Plugin.ProcessKiller/ProcessHelper.cs
@@ -98,7 +98,7 @@ public unsafe string TryGetProcessFilename(Process p)
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 @@ public unsafe string TryGetProcessFilename(Process p)
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 ad1f3ab564d..f194ae973d6 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs
@@ -31,14 +31,14 @@ public unsafe string retrieveTargetPath(string path)
((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 @@ public unsafe string retrieveTargetPath(string path)
{
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 @@ public unsafe string retrieveTargetPath(string path)
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 @@ public unsafe string retrieveTargetPath(string path)
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 e36618b0de5..fac3ab181e7 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 @@ public static class ShellLocalization
/// 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;
+ }
+
+ 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();
- PInvoke.FreeLibrary(new(safeHandle));
- }
+ if (PInvoke.LoadString(handle, (uint)id, bufferPtr, capacity) != 0)
+ {
+ var lString = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(bufferPtr).ToString();
+ return lString;
}
}
From 02a45661c4f836e9c21aff68f9872795cf1ee04c Mon Sep 17 00:00:00 2001
From: Jack251970 <1160210343@qq.com>
Date: Thu, 26 Dec 2024 13:04:15 +0800
Subject: [PATCH 20/21] Add consistent error handling for GetArguments
---
.../Programs/ShellLinkHelper.cs | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs
index f194ae973d6..a77b2ace839 100644
--- a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs
+++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLinkHelper.cs
@@ -35,10 +35,18 @@ public unsafe string retrieveTargetPath(string path)
var data = new WIN32_FIND_DATAW();
var target = string.Empty;
- fixed (char* bufferPtr = buffer)
+ try
{
- ((IShellLinkW)link).GetPath((PWSTR)bufferPtr, MAX_PATH, &data, (uint)SLGP_FLAGS.SLGP_SHORTPATH);
- target = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(bufferPtr).ToString();
+ fixed (char* bufferPtr = buffer)
+ {
+ ((IShellLinkW)link).GetPath((PWSTR)bufferPtr, MAX_PATH, &data, (uint)SLGP_FLAGS.SLGP_SHORTPATH);
+ target = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(bufferPtr).ToString();
+ }
+ }
+ catch (COMException e)
+ {
+ ProgramLogger.LogException($"|IShellLinkW|retrieveTargetPath|{path}" +
+ "|Error occurred while getting program arguments", e);
}
// To set the app description
@@ -66,11 +74,11 @@ public unsafe string retrieveTargetPath(string path)
arguments = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(bufferPtr).ToString();
}
}
-
+
// To release unmanaged memory
Marshal.ReleaseComObject(link);
return target;
- }
+ }
}
}
From 9350e82b7793414871dcc59ae95a5b84268aa365 Mon Sep 17 00:00:00 2001
From: Hongtao Zhang
Date: Fri, 27 Dec 2024 12:57:17 -0600
Subject: [PATCH 21/21] only stackalloc the getwindowtitle buffer when length
is small.
---
Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs b/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs
index 9ea582118a7..a0440e30de9 100644
--- a/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs
+++ b/Flow.Launcher.Plugin/SharedCommands/ShellCommand.cs
@@ -54,12 +54,12 @@ private static unsafe string GetWindowTitle(HWND hwnd)
{
var capacity = PInvoke.GetWindowTextLength(hwnd) + 1;
int length;
- Span buffer = stackalloc char[capacity];
+ Span buffer = capacity < 1024 ? stackalloc char[capacity] : new 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.
- length = PInvoke.GetWindowText(hwnd, (PWSTR)pBuffer, capacity);
+ length = PInvoke.GetWindowText(hwnd, pBuffer, capacity);
}
return buffer[..length].ToString();