Desktop-Icons in eigene Kategorien sortieren: Windows-11-Design (hell/dunkel), Desktop-Boxen, automatisches Einsortieren nach Regeln, Rückgängig, Suche, Infobereich mit Tastenkürzel, Sicherung als ZIP und Installer ohne Adminrechte. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
139 lines
4.4 KiB
C#
139 lines
4.4 KiB
C#
using System.Runtime.InteropServices;
|
||
using System.Windows.Input;
|
||
using System.Windows.Interop;
|
||
using Microsoft.Win32;
|
||
|
||
namespace DeskCore;
|
||
|
||
/// <summary>Systemweites Tastenkürzel (RegisterHotKey).</summary>
|
||
public sealed class Hotkey : IDisposable
|
||
{
|
||
public const int ModAlt = 0x1, ModCtrl = 0x2, ModShift = 0x4, ModWin = 0x8;
|
||
private const int ModNoRepeat = 0x4000, WmHotkey = 0x0312, Id = 0xB001;
|
||
|
||
private readonly IntPtr _hwnd;
|
||
private readonly HwndSource _source;
|
||
private bool _registered;
|
||
|
||
public event Action? Pressed;
|
||
|
||
public Hotkey(IntPtr hwnd)
|
||
{
|
||
_hwnd = hwnd;
|
||
_source = HwndSource.FromHwnd(hwnd);
|
||
_source.AddHook(Hook);
|
||
}
|
||
|
||
/// <summary>Registriert das Kürzel. false = schon von einem anderen Programm belegt.</summary>
|
||
public bool Register(int modifiers, int vk)
|
||
{
|
||
Unregister();
|
||
if (vk == 0) return false;
|
||
_registered = RegisterHotKey(_hwnd, Id, modifiers | ModNoRepeat, vk);
|
||
return _registered;
|
||
}
|
||
|
||
public void Unregister()
|
||
{
|
||
if (_registered) UnregisterHotKey(_hwnd, Id);
|
||
_registered = false;
|
||
}
|
||
|
||
private IntPtr Hook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
|
||
{
|
||
if (msg == WmHotkey && wParam.ToInt32() == Id)
|
||
{
|
||
Pressed?.Invoke();
|
||
handled = true;
|
||
}
|
||
return IntPtr.Zero;
|
||
}
|
||
|
||
public static string Describe(int modifiers, int vk)
|
||
{
|
||
if (vk == 0) return "–";
|
||
var parts = new List<string>();
|
||
if ((modifiers & ModWin) != 0) parts.Add("Win");
|
||
if ((modifiers & ModCtrl) != 0) parts.Add("Strg");
|
||
if ((modifiers & ModAlt) != 0) parts.Add("Alt");
|
||
if ((modifiers & ModShift) != 0) parts.Add("Umschalt");
|
||
var key = KeyInterop.KeyFromVirtualKey(vk);
|
||
parts.Add(key switch
|
||
{
|
||
>= Key.D0 and <= Key.D9 => ((char)('0' + (key - Key.D0))).ToString(),
|
||
Key.Space => "Leertaste",
|
||
_ => key.ToString(),
|
||
});
|
||
return string.Join(" + ", parts);
|
||
}
|
||
|
||
public void Dispose()
|
||
{
|
||
Unregister();
|
||
_source.RemoveHook(Hook);
|
||
}
|
||
|
||
[DllImport("user32.dll")] private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk);
|
||
[DllImport("user32.dll")] private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
|
||
}
|
||
|
||
/// <summary>Start mit Windows über HKCU\...\Run.</summary>
|
||
public static class Autostart
|
||
{
|
||
private const string Key = @"Software\Microsoft\Windows\CurrentVersion\Run";
|
||
private const string Name = "DeskCore";
|
||
// Frühere Namen dieser App. "DeskHub" heißt auch ein anderes Programm → nur übernehmen, wenn der Eintrag eindeutig von uns ist.
|
||
private static readonly string[] OldNames = ["DesktopSortierer", "DeskHub"];
|
||
|
||
public static bool Enabled
|
||
{
|
||
get
|
||
{
|
||
try
|
||
{
|
||
using var k = Registry.CurrentUser.OpenSubKey(Key);
|
||
return k?.GetValue(Name) is string;
|
||
}
|
||
catch { return false; }
|
||
}
|
||
set
|
||
{
|
||
try
|
||
{
|
||
using var k = Registry.CurrentUser.OpenSubKey(Key, writable: true) ?? Registry.CurrentUser.CreateSubKey(Key);
|
||
if (value) k.SetValue(Name, $"\"{Environment.ProcessPath}\" --minimized");
|
||
else k.DeleteValue(Name, throwOnMissingValue: false);
|
||
}
|
||
catch { }
|
||
}
|
||
}
|
||
|
||
/// <summary>Alten Autostart-Eintrag ("DesktopSortierer") auf den neuen Namen umstellen.</summary>
|
||
public static void Migrate()
|
||
{
|
||
try
|
||
{
|
||
using var k = Registry.CurrentUser.OpenSubKey(Key, writable: true);
|
||
if (k is null) return;
|
||
bool wasEnabled = false;
|
||
foreach (var old in OldNames)
|
||
{
|
||
if (k.GetValue(old) is not string cmd) continue;
|
||
bool ours = old == "DesktopSortierer" ||
|
||
(cmd.Contains($"{old}.exe", StringComparison.OrdinalIgnoreCase) && cmd.Contains("--minimized"));
|
||
if (!ours) continue;
|
||
k.DeleteValue(old, throwOnMissingValue: false);
|
||
wasEnabled = true;
|
||
}
|
||
if (wasEnabled) Enabled = true;
|
||
}
|
||
catch { }
|
||
}
|
||
|
||
/// <summary>Falls die exe verschoben wurde: Pfad im Autostart aktualisieren.</summary>
|
||
public static void RefreshPath()
|
||
{
|
||
if (Enabled) Enabled = true;
|
||
}
|
||
}
|