mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge pull request #3122 from Jack251970/dev3
Replace DllImport and flags with CSWin32 & Remove useless DllImport and flags & Fix right Win key click issue
This commit is contained in:
commit
fd3576d52c
31 changed files with 661 additions and 845 deletions
|
|
@ -3,10 +3,8 @@ using System.Collections.Generic;
|
|||
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 @@ namespace Flow.Launcher.Core.Resource
|
|||
_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 @@ namespace Flow.Launcher.Core.Resource
|
|||
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);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the blur for a window via SetWindowCompositionAttribute
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 @@ namespace Flow.Launcher.Infrastructure
|
|||
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);
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -70,9 +66,9 @@ namespace Flow.Launcher.Infrastructure
|
|||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -35,6 +35,10 @@
|
|||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="NativeMethods.txt" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\SolutionAssemblyInfo.cs" Link="Properties\SolutionAssemblyInfo.cs" />
|
||||
<None Include="FodyWeavers.xml" />
|
||||
|
|
@ -56,6 +60,10 @@
|
|||
</PackageReference>
|
||||
<PackageReference Include="MemoryPack" Version="1.21.3" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Threading" Version="17.12.19" />
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.106">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="NLog" Version="4.7.10" />
|
||||
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
|
||||
|
|
|
|||
|
|
@ -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,45 @@ namespace Flow.Launcher.Infrastructure.Hotkey
|
|||
/// </summary>
|
||||
public unsafe class GlobalHotkey : IDisposable
|
||||
{
|
||||
private static readonly IntPtr hookId;
|
||||
|
||||
|
||||
|
||||
private static readonly HOOKPROC _procKeyboard = HookKeyboardCallback;
|
||||
private static readonly UnhookWindowsHookExSafeHandle hookId;
|
||||
|
||||
public delegate bool KeyboardCallback(KeyEvent keyEvent, int vkCode, SpecialKeyState state);
|
||||
internal static Func<KeyEvent, int, SpecialKeyState, bool> hookedKeyboardCallback;
|
||||
|
||||
//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;
|
||||
|
||||
static GlobalHotkey()
|
||||
{
|
||||
// Set the hook
|
||||
hookId = InterceptKeys.SetHook(& LowLevelKeyboardProc);
|
||||
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()
|
||||
{
|
||||
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 ||
|
||||
(PInvoke.GetKeyState((int)VIRTUAL_KEY.VK_RWIN) & 0x8000) != 0)
|
||||
{
|
||||
//WIN is pressed
|
||||
state.WinPressed = true;
|
||||
|
|
@ -56,33 +62,33 @@ namespace Flow.Launcher.Infrastructure.Hotkey
|
|||
return state;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
private static IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam)
|
||||
private static LRESULT HookKeyboardCallback(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)
|
||||
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)wParam.ToUInt32(), Marshal.ReadInt32(lParam), CheckModifiers());
|
||||
continues = hookedKeyboardCallback((KeyEvent)wParam.Value, 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);
|
||||
hookId.Dispose();
|
||||
}
|
||||
|
||||
~GlobalHotkey()
|
||||
|
|
@ -90,4 +96,4 @@ namespace Flow.Launcher.Infrastructure.Hotkey
|
|||
Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<int, UIntPtr, IntPtr, IntPtr> 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<int, UIntPtr, IntPtr, IntPtr> 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
using Windows.Win32;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.Hotkey
|
||||
{
|
||||
public enum KeyEvent
|
||||
|
|
@ -5,21 +7,21 @@ namespace Flow.Launcher.Infrastructure.Hotkey
|
|||
/// <summary>
|
||||
/// Key down
|
||||
/// </summary>
|
||||
WM_KEYDOWN = 256,
|
||||
WM_KEYDOWN = (int)PInvoke.WM_KEYDOWN,
|
||||
|
||||
/// <summary>
|
||||
/// Key up
|
||||
/// </summary>
|
||||
WM_KEYUP = 257,
|
||||
WM_KEYUP = (int)PInvoke.WM_KEYUP,
|
||||
|
||||
/// <summary>
|
||||
/// System key up
|
||||
/// </summary>
|
||||
WM_SYSKEYUP = 261,
|
||||
WM_SYSKEYUP = (int)PInvoke.WM_SYSKEYUP,
|
||||
|
||||
/// <summary>
|
||||
/// System key down
|
||||
/// </summary>
|
||||
WM_SYSKEYDOWN = 260
|
||||
WM_SYSKEYDOWN = (int)PInvoke.WM_SYSKEYDOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Subclass of <see cref="Windows.Win32.UI.Shell.SIIGBF"/>
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum ThumbnailOptions
|
||||
{
|
||||
|
|
@ -22,91 +29,13 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
{
|
||||
// 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,56 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
finally
|
||||
{
|
||||
// delete HBitmap to avoid memory leaks
|
||||
DeleteObject(hBitmap);
|
||||
PInvoke.DeleteObject(hBitmap);
|
||||
}
|
||||
}
|
||||
|
||||
private static IntPtr 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);
|
||||
|
||||
if (retCode != 0)
|
||||
private static unsafe HBITMAP GetHBitmap(string fileName, int width, int height, ThumbnailOptions options)
|
||||
{
|
||||
var retCode = PInvoke.SHCreateItemFromParsingName(
|
||||
fileName,
|
||||
null,
|
||||
GUID_IShellItem,
|
||||
out var nativeShellItem);
|
||||
|
||||
if (retCode != HRESULT.S_OK)
|
||||
throw Marshal.GetExceptionForHR(retCode);
|
||||
|
||||
NativeSize nativeSize = new NativeSize
|
||||
if (nativeShellItem is not IShellItemImageFactory imageFactory)
|
||||
{
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
IntPtr hBitmap;
|
||||
HResult hr = ((IShellItemImageFactory)nativeShellItem).GetImage(nativeSize, options, out hBitmap);
|
||||
|
||||
// if extracting image thumbnail and failed, extract shell icon
|
||||
if (options == ThumbnailOptions.ThumbnailOnly && hr == HResult.ExtractionFailed)
|
||||
{
|
||||
hr = ((IShellItemImageFactory) nativeShellItem).GetImage(nativeSize, ThumbnailOptions.IconOnly, out hBitmap);
|
||||
Marshal.ReleaseComObject(nativeShellItem);
|
||||
nativeShellItem = null;
|
||||
throw new InvalidOperationException("Failed to get IShellItemImageFactory");
|
||||
}
|
||||
|
||||
Marshal.ReleaseComObject(nativeShellItem);
|
||||
SIZE size = new SIZE
|
||||
{
|
||||
cx = width,
|
||||
cy = height
|
||||
};
|
||||
|
||||
if (hr == HResult.Ok) return hBitmap;
|
||||
HBITMAP hBitmap = default;
|
||||
try
|
||||
{
|
||||
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
|
||||
{
|
||||
if (nativeShellItem != null)
|
||||
{
|
||||
Marshal.ReleaseComObject(nativeShellItem);
|
||||
}
|
||||
}
|
||||
|
||||
throw new COMException($"Error while extracting thumbnail for {fileName}", Marshal.GetExceptionForHR((int)hr));
|
||||
return hBitmap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
22
Flow.Launcher.Infrastructure/NativeMethods.txt
Normal file
22
Flow.Launcher.Infrastructure/NativeMethods.txt
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
SHCreateItemFromParsingName
|
||||
DeleteObject
|
||||
IShellItem
|
||||
IShellItemImageFactory
|
||||
S_OK
|
||||
|
||||
SetWindowsHookEx
|
||||
UnhookWindowsHookEx
|
||||
CallNextHookEx
|
||||
GetModuleHandle
|
||||
GetKeyState
|
||||
VIRTUAL_KEY
|
||||
|
||||
WM_KEYDOWN
|
||||
WM_KEYUP
|
||||
WM_SYSKEYDOWN
|
||||
WM_SYSKEYUP
|
||||
|
||||
EnumWindows
|
||||
|
||||
OleInitialize
|
||||
OleUninitialize
|
||||
|
|
@ -230,7 +230,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
[JsonIgnore]
|
||||
public ObservableCollection<BuiltinShortcutModel> 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)
|
||||
};
|
||||
|
||||
|
|
|
|||
239
Flow.Launcher.Infrastructure/Win32Helper.cs
Normal file
239
Flow.Launcher.Infrastructure/Win32Helper.cs
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
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
|
||||
{
|
||||
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();
|
||||
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<Task> 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<T?> StartSTATaskAsync<T>(Func<T> func)
|
||||
{
|
||||
var taskCompletionSource = new TaskCompletionSource<T?>();
|
||||
|
||||
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<T?> StartSTATaskAsync<T>(Func<Task<T>> func)
|
||||
{
|
||||
var taskCompletionSource = new TaskCompletionSource<T?>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#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);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the blur theme is enabled
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the blur for a window via SetWindowCompositionAttribute
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -57,7 +57,11 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Readme.md" Pack="true" PackagePath="\"/>
|
||||
<AdditionalFiles Include="NativeMethods.txt" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Readme.md" Pack="true" PackagePath="\" />
|
||||
<None Include="FodyWeavers.xml" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
@ -68,6 +72,10 @@
|
|||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2024.3.0" />
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.106">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
3
Flow.Launcher.Plugin/NativeMethods.txt
Normal file
3
Flow.Launcher.Plugin/NativeMethods.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
EnumThreadWindows
|
||||
GetWindowText
|
||||
GetWindowTextLength
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
@ -28,6 +25,7 @@ namespace Flow.Launcher.Plugin.SharedCommands
|
|||
CheckSecurityWindow();
|
||||
Thread.Sleep(25);
|
||||
}
|
||||
|
||||
while (containsSecurityWindow) // while this process contains a "Windows Security" dialog, stay open
|
||||
{
|
||||
containsSecurityWindow = false;
|
||||
|
|
@ -42,24 +40,33 @@ namespace Flow.Launcher.Plugin.SharedCommands
|
|||
{
|
||||
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;
|
||||
int length;
|
||||
Span<char> 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, 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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -90,6 +90,10 @@
|
|||
</PackageReference>
|
||||
<PackageReference Include="InputSimulator" Version="1.0.4" />
|
||||
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.106">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<!-- ModernWpfUI v0.9.5 introduced WinRT changes that causes Notification platform unavailable error on some machines -->
|
||||
<!-- https://github.com/Flow-Launcher/Flow.Launcher/issues/1772#issuecomment-1502440801 -->
|
||||
<PackageReference Include="ModernWpfUI" Version="0.9.4" />
|
||||
|
|
|
|||
|
|
@ -1,20 +1,16 @@
|
|||
using System;
|
||||
using System.Drawing.Printing;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Interop;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// 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,24 +39,22 @@ public class DwmDropShadow
|
|||
/// </summary>
|
||||
/// <param name="window">Window to which the shadow will be applied</param>
|
||||
/// <returns>True if the method succeeded, false if not</returns>
|
||||
private static bool DropShadow(Window window)
|
||||
private static unsafe bool DropShadow(Window window)
|
||||
{
|
||||
try
|
||||
{
|
||||
WindowInteropHelper helper = new WindowInteropHelper(window);
|
||||
int val = 2;
|
||||
int ret1 = DwmSetWindowAttribute(helper.Handle, 2, ref val, 4);
|
||||
var ret1 = PInvoke.DwmSetWindowAttribute(new (helper.Handle), DWMWINDOWATTRIBUTE.DWMWA_NCRENDERING_POLICY, &val, 4);
|
||||
|
||||
if (ret1 == 0)
|
||||
if (ret1 == HRESULT.S_OK)
|
||||
{
|
||||
Margins m = new Margins { Bottom = 0, Left = 0, Right = 0, Top = 0 };
|
||||
int ret2 = DwmExtendFrameIntoClientArea(helper.Handle, ref m);
|
||||
return ret2 == 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
var m = new MARGINS { cyBottomHeight = 0, cxLeftWidth = 0, cxRightWidth = 0, cyTopHeight = 0 };
|
||||
var ret2 = PInvoke.DwmExtendFrameIntoClientArea(new(helper.Handle), &m);
|
||||
return ret2 == HRESULT.S_OK;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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 @@ using System.Windows;
|
|||
// 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
|
||||
{
|
||||
/// <summary>
|
||||
/// Delegate declaration that matches WndProc signatures.
|
||||
/// </summary>
|
||||
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 @@ namespace Flow.Launcher.Helper
|
|||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -264,56 +88,6 @@ namespace Flow.Launcher.Helper
|
|||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets command line args - for ClickOnce deployed applications, command line args may not be passed directly, they have to be retrieved.
|
||||
/// </summary>
|
||||
/// <returns>List of command line arg strings.</returns>
|
||||
private static IList<string> 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<string>(args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a remote server pipe for communication.
|
||||
/// Once receives signal from client, will activate first instance.
|
||||
|
|
|
|||
|
|
@ -2,29 +2,27 @@
|
|||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Windows.Documents;
|
||||
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);
|
||||
|
||||
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()
|
||||
|
|
@ -35,13 +33,14 @@ public static class WallpaperPathRetrieval
|
|||
{
|
||||
try
|
||||
{
|
||||
var parts = strResult.Trim().Split(new[] {' '}, 3).Select(byte.Parse).ToList();
|
||||
var parts = strResult.Trim().Split(new[] { ' ' }, 3).Select(byte.Parse).ToList();
|
||||
return Color.FromRgb(parts[0], parts[1], parts[2]);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
return Colors.Transparent;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,75 +1,52 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
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 +57,17 @@ public class WindowsInteropHelper
|
|||
return false;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder(256);
|
||||
GetClassName(hWnd, sb, sb.Capacity);
|
||||
string windowClass = sb.ToString();
|
||||
string windowClass;
|
||||
const int capacity = 256;
|
||||
Span<char> buffer = stackalloc char[capacity];
|
||||
int validLength;
|
||||
fixed (char* pBuffer = buffer)
|
||||
{
|
||||
validLength = PInvoke.GetClassName(hWnd, pBuffer, capacity);
|
||||
}
|
||||
|
||||
windowClass = buffer[..validLength].ToString();
|
||||
|
||||
|
||||
//for Win+Tab (Flip3D)
|
||||
if (windowClass == WINDOW_CLASS_WINTAB)
|
||||
|
|
@ -90,28 +75,28 @@ public class WindowsInteropHelper
|
|||
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");
|
||||
if (!hWndDesktop.Equals(IntPtr.Zero))
|
||||
var hWndDesktop = PInvoke.FindWindowEx(hWnd, HWND.Null, "SHELLDLL_DefView", null);
|
||||
hWndDesktop = PInvoke.FindWindowEx(hWndDesktop, HWND.Null, "SysListView32", "FolderView");
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -120,8 +105,24 @@ public class WindowsInteropHelper
|
|||
/// </summary>
|
||||
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);
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -144,16 +145,7 @@ public class WindowsInteropHelper
|
|||
using var src = new HwndSource(new HwndSourceParameters());
|
||||
matrix = src.CompositionTarget.TransformFromDevice;
|
||||
}
|
||||
|
||||
return new Point((int)(matrix.M11 * unitX), (int)(matrix.M22 * unitY));
|
||||
}
|
||||
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RECT
|
||||
{
|
||||
public int Left;
|
||||
public int Top;
|
||||
public int Right;
|
||||
public int Bottom;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ using System.Media;
|
|||
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 @@ namespace Flow.Launcher
|
|||
{
|
||||
#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;
|
||||
|
|
@ -81,21 +78,19 @@ namespace Flow.Launcher
|
|||
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)
|
||||
{
|
||||
|
|
@ -424,7 +419,7 @@ namespace Flow.Launcher
|
|||
// 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 +687,7 @@ namespace Flow.Launcher
|
|||
screen = Screen.PrimaryScreen;
|
||||
break;
|
||||
case SearchWindowScreens.Focus:
|
||||
IntPtr foregroundWindowHandle = WindowsInteropHelper.GetForegroundWindow();
|
||||
var foregroundWindowHandle = PInvoke.GetForegroundWindow().Value;
|
||||
screen = Screen.FromHandle(foregroundWindowHandle);
|
||||
break;
|
||||
case SearchWindowScreens.Custom:
|
||||
|
|
|
|||
17
Flow.Launcher/NativeMethods.txt
Normal file
17
Flow.Launcher/NativeMethods.txt
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
DwmSetWindowAttribute
|
||||
DwmExtendFrameIntoClientArea
|
||||
SystemParametersInfo
|
||||
SetForegroundWindow
|
||||
|
||||
GetWindowLong
|
||||
SetWindowLong
|
||||
GetForegroundWindow
|
||||
GetDesktopWindow
|
||||
GetShellWindow
|
||||
GetWindowRect
|
||||
GetClassName
|
||||
FindWindowEx
|
||||
WINDOW_STYLE
|
||||
|
||||
WM_ENTERSIZEMOVE
|
||||
WM_EXITSIZEMOVE
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
|
|
@ -117,35 +117,38 @@ namespace Flow.Launcher
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -50,6 +50,13 @@
|
|||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.106">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />
|
||||
<ProjectReference Include="..\..\Flow.Launcher.Plugin\Flow.Launcher.Plugin.csproj" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
QueryFullProcessImageName
|
||||
OpenProcess
|
||||
|
|
@ -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 @@ namespace Flow.Launcher.Plugin.ProcessKiller
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
Span<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 buffer[..(int)capacity].ToString();
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,6 +52,10 @@
|
|||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="NativeMethods.txt" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Flow.Launcher.Infrastructure\Flow.Launcher.Infrastructure.csproj" />
|
||||
|
|
@ -61,6 +65,10 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="ini-parser" Version="2.5.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.106">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
10
Plugins/Flow.Launcher.Plugin.Program/NativeMethods.txt
Normal file
10
Plugins/Flow.Launcher.Plugin.Program/NativeMethods.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
SHGetLocalizedName
|
||||
LoadString
|
||||
LoadLibraryEx
|
||||
FreeLibrary
|
||||
ExpandEnvironmentStrings
|
||||
S_OK
|
||||
SLGP_FLAGS
|
||||
WIN32_FIND_DATAW
|
||||
SLR_FLAGS
|
||||
IShellLinkW
|
||||
|
|
@ -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
|
||||
{
|
||||
/// <summary>Retrieves the path and file name of a Shell link object</summary>
|
||||
void GetPath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, ref WIN32_FIND_DATAW pfd, SLGP_FLAGS fFlags);
|
||||
/// <summary>Retrieves the list of item identifiers for a Shell link object</summary>
|
||||
void GetIDList(out IntPtr ppidl);
|
||||
/// <summary>Sets the pointer to an item identifier list (PIDL) for a Shell link object.</summary>
|
||||
void SetIDList(IntPtr pidl);
|
||||
/// <summary>Retrieves the description string for a Shell link object</summary>
|
||||
void GetDescription([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName);
|
||||
/// <summary>Sets the description for a Shell link object. The description can be any application-defined string</summary>
|
||||
void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
|
||||
/// <summary>Retrieves the name of the working directory for a Shell link object</summary>
|
||||
void GetWorkingDirectory([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath);
|
||||
/// <summary>Sets the name of the working directory for a Shell link object</summary>
|
||||
void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
|
||||
/// <summary>Retrieves the command-line arguments associated with a Shell link object</summary>
|
||||
void GetArguments([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath);
|
||||
/// <summary>Sets the command-line arguments for a Shell link object</summary>
|
||||
void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
|
||||
/// <summary>Retrieves the hot key for a Shell link object</summary>
|
||||
void GetHotkey(out short pwHotkey);
|
||||
/// <summary>Sets a hot key for a Shell link object</summary>
|
||||
void SetHotkey(short wHotkey);
|
||||
/// <summary>Retrieves the show command for a Shell link object</summary>
|
||||
void GetShowCmd(out int piShowCmd);
|
||||
/// <summary>Sets the show command for a Shell link object. The show command sets the initial show state of the window.</summary>
|
||||
void SetShowCmd(int iShowCmd);
|
||||
/// <summary>Retrieves the location (path and index) of the icon for a Shell link object</summary>
|
||||
void GetIconLocation([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath,
|
||||
int cchIconPath, out int piIcon);
|
||||
/// <summary>Sets the location (path and index) of the icon for a Shell link object</summary>
|
||||
void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
|
||||
/// <summary>Sets the relative path to the Shell link object</summary>
|
||||
void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved);
|
||||
/// <summary>Attempts to find the target of a Shell link, even if it has been moved or renamed</summary>
|
||||
void Resolve(ref Accessibility._RemotableHandle hwnd, SLR_FLAGS fFlags);
|
||||
/// <summary>Sets the path and file name of a Shell link object</summary>
|
||||
void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
|
||||
}
|
||||
|
||||
[ComImport(), Guid("00021401-0000-0000-C000-000000000046")]
|
||||
public class ShellLink
|
||||
{
|
||||
|
|
@ -102,29 +22,43 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
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);
|
||||
Span<char> buffer = stackalloc 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;
|
||||
try
|
||||
{
|
||||
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
|
||||
if (!String.IsNullOrEmpty(target))
|
||||
if (!string.IsNullOrEmpty(target))
|
||||
{
|
||||
try
|
||||
{
|
||||
buffer = new StringBuilder(MAX_PATH);
|
||||
((IShellLinkW)link).GetDescription(buffer, MAX_PATH);
|
||||
description = buffer.ToString();
|
||||
fixed (char* bufferPtr = buffer)
|
||||
{
|
||||
((IShellLinkW)link).GetDescription(bufferPtr, MAX_PATH);
|
||||
description = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(bufferPtr).ToString();
|
||||
}
|
||||
}
|
||||
catch (COMException e)
|
||||
{
|
||||
|
|
@ -134,15 +68,17 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
e);
|
||||
}
|
||||
|
||||
buffer.Clear();
|
||||
((IShellLinkW)link).GetArguments(buffer, MAX_PATH);
|
||||
arguments = buffer.ToString();
|
||||
fixed (char* bufferPtr = buffer)
|
||||
{
|
||||
((IShellLinkW)link).GetArguments(bufferPtr, MAX_PATH);
|
||||
arguments = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(bufferPtr).ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// To release unmanaged memory
|
||||
Marshal.ReleaseComObject(link);
|
||||
|
||||
return target;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
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 +14,41 @@ namespace Flow.Launcher.Plugin.Program.Programs
|
|||
/// </summary>
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the localized name of a shell item.
|
||||
/// </summary>
|
||||
/// <param name="path">Path to the shell item (e. g. shortcut 'File Explorer.lnk').</param>
|
||||
/// <returns>The localized name as string or <see cref="string.Empty"/>.</returns>
|
||||
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;
|
||||
const int capacity = 1024;
|
||||
Span<char> buffer = 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* bufferPtr = buffer)
|
||||
{
|
||||
_ = ExpandEnvironmentStrings(resourcePath.ToString(), resourcePath, resourcePath.Capacity);
|
||||
IntPtr hMod = LoadLibraryEx(resourcePath.ToString(), IntPtr.Zero, DONTRESOLVEDLLREFERENCES | LOADLIBRARYASDATAFILE);
|
||||
if (hMod != IntPtr.Zero)
|
||||
var result = PInvoke.SHGetLocalizedName(path, bufferPtr, capacity, out var id);
|
||||
if (result != HRESULT.S_OK)
|
||||
{
|
||||
if (LoadString(hMod, id, localizedName, localizedName.Capacity) != 0)
|
||||
{
|
||||
string lString = localizedName.ToString();
|
||||
_ = FreeLibrary(hMod);
|
||||
return lString;
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
_ = FreeLibrary(hMod);
|
||||
var resourcePathStr = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(bufferPtr).ToString();
|
||||
_ = PInvoke.ExpandEnvironmentStrings(resourcePathStr, bufferPtr, capacity);
|
||||
using var handle = PInvoke.LoadLibraryEx(resourcePathStr,
|
||||
LOAD_LIBRARY_FLAGS.DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_FLAGS.LOAD_LIBRARY_AS_DATAFILE);
|
||||
if (handle.IsInvalid)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
// not sure about the behavior of Pinvoke.LoadString, so we clear the buffer before using it (so it must be a null-terminated string)
|
||||
buffer.Clear();
|
||||
|
||||
if (PInvoke.LoadString(handle, (uint)id, bufferPtr, capacity) != 0)
|
||||
{
|
||||
var lString = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(bufferPtr).ToString();
|
||||
return lString;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,4 +57,11 @@
|
|||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.106">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -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 @@ namespace Flow.Launcher.Plugin.Sys
|
|||
private PluginInitContext context;
|
||||
private Dictionary<string, string> KeywordTitleMappings = new Dictionary<string, string>();
|
||||
|
||||
#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();
|
||||
|
|
@ -209,7 +181,7 @@ namespace Flow.Launcher.Plugin.Sys
|
|||
MessageBoxButton.YesNo, MessageBoxImage.Warning);
|
||||
|
||||
if (result == MessageBoxResult.Yes)
|
||||
ExitWindowsEx(EWX_LOGOFF, 0);
|
||||
PInvoke.ExitWindowsEx(EXIT_WINDOWS_FLAGS.EWX_LOGOFF, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -222,7 +194,7 @@ namespace Flow.Launcher.Plugin.Sys
|
|||
IcoPath = "Images\\lock.png",
|
||||
Action = c =>
|
||||
{
|
||||
LockWorkStation();
|
||||
PInvoke.LockWorkStation();
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
|
@ -232,7 +204,7 @@ namespace Flow.Launcher.Plugin.Sys
|
|||
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
|
||||
{
|
||||
|
|
@ -277,11 +249,13 @@ namespace Flow.Launcher.Plugin.Sys
|
|||
// 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",
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
6
Plugins/Flow.Launcher.Plugin.Sys/NativeMethods.txt
Normal file
6
Plugins/Flow.Launcher.Plugin.Sys/NativeMethods.txt
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
ExitWindowsEx
|
||||
LockWorkStation
|
||||
SHEmptyRecycleBin
|
||||
S_OK
|
||||
E_UNEXPECTED
|
||||
SetSuspendState
|
||||
Loading…
Reference in a new issue