-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRegistrationService.cs
More file actions
165 lines (139 loc) · 5.65 KB
/
Copy pathRegistrationService.cs
File metadata and controls
165 lines (139 loc) · 5.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
using Microsoft.Win32;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
namespace BatteryDown;
internal static class RegistrationService
{
public const string AppUserModelId = "LiteZero.BatteryDown";
public const string ProtocolName = "batterydown";
public static void EnsureRegistered()
{
var executablePath = Environment.ProcessPath
?? throw new InvalidOperationException("无法确定程序路径。");
RegisterProtocol(executablePath);
CreateStartMenuShortcut(executablePath);
}
public static void RemoveRegistration()
{
Registry.CurrentUser.DeleteSubKeyTree($@"Software\Classes\{ProtocolName}", false);
var shortcutPath = GetShortcutPath();
if (File.Exists(shortcutPath))
{
File.Delete(shortcutPath);
}
}
private static void RegisterProtocol(string executablePath)
{
using var protocolKey = Registry.CurrentUser.CreateSubKey($@"Software\Classes\{ProtocolName}");
protocolKey.SetValue(null, "URL:Battery Down Protocol");
protocolKey.SetValue("URL Protocol", string.Empty);
using var iconKey = protocolKey.CreateSubKey("DefaultIcon");
iconKey.SetValue(null, $"\"{executablePath}\",0");
using var commandKey = protocolKey.CreateSubKey(@"shell\open\command");
commandKey.SetValue(null, $"\"{executablePath}\" \"%1\"");
}
private static void CreateStartMenuShortcut(string executablePath)
{
var shortcutPath = GetShortcutPath();
Directory.CreateDirectory(Path.GetDirectoryName(shortcutPath)!);
var shellLink = (IShellLinkW)(object)new ShellLink();
try
{
shellLink.SetPath(executablePath);
shellLink.SetDescription("Battery Down 设置");
shellLink.SetWorkingDirectory(Path.GetDirectoryName(executablePath)!);
shellLink.SetIconLocation(executablePath, 0);
var propertyStore = (IPropertyStore)shellLink;
using var appId = PropVariant.FromString(AppUserModelId);
var key = PropertyKey.AppUserModelId;
propertyStore.SetValue(ref key, appId);
propertyStore.Commit();
((IPersistFile)shellLink).Save(shortcutPath, true);
SHChangeNotify(0x08000000, 0, IntPtr.Zero, IntPtr.Zero); // SHCNE_ASSOCCHANGED
}
finally
{
Marshal.FinalReleaseComObject(shellLink);
}
}
private static string GetShortcutPath() => Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.StartMenu),
"Programs",
"Battery Down.lnk");
[DllImport("shell32.dll")]
private static extern void SHChangeNotify(
uint eventId,
uint flags,
IntPtr item1,
IntPtr item2);
[ComImport]
[Guid("00021401-0000-0000-C000-000000000046")]
private sealed class ShellLink;
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214F9-0000-0000-C000-000000000046")]
private interface IShellLinkW
{
void GetPath(IntPtr file, int maxPath, IntPtr findData, uint flags);
void GetIDList(out IntPtr itemIdList);
void SetIDList(IntPtr itemIdList);
void GetDescription(IntPtr name, int maxName);
void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string name);
void GetWorkingDirectory(IntPtr directory, int maxPath);
void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string directory);
void GetArguments(IntPtr arguments, int maxPath);
void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string arguments);
void GetHotkey(out short hotkey);
void SetHotkey(short hotkey);
void GetShowCmd(out int showCommand);
void SetShowCmd(int showCommand);
void GetIconLocation(IntPtr iconPath, int iconPathLength, out int iconIndex);
void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string iconPath, int iconIndex);
void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string path, uint reserved);
void Resolve(IntPtr windowHandle, uint flags);
void SetPath([MarshalAs(UnmanagedType.LPWStr)] string path);
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")]
private interface IPropertyStore
{
void GetCount(out uint propertyCount);
void GetAt(uint propertyIndex, out PropertyKey key);
void GetValue(ref PropertyKey key, out PropVariant value);
void SetValue(ref PropertyKey key, [In] PropVariant value);
void Commit();
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
private struct PropertyKey
{
public Guid FormatId;
public uint PropertyId;
public static PropertyKey AppUserModelId => new()
{
FormatId = new Guid("9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3"),
PropertyId = 5,
};
}
[StructLayout(LayoutKind.Explicit)]
private sealed class PropVariant : IDisposable
{
[FieldOffset(0)]
private readonly ushort valueType;
[FieldOffset(8)]
private readonly IntPtr pointerValue;
private PropVariant(string value)
{
valueType = 31;
pointerValue = Marshal.StringToCoTaskMemUni(value);
}
public static PropVariant FromString(string value) => new(value);
public void Dispose()
{
PropVariantClear(this);
GC.SuppressFinalize(this);
}
[DllImport("ole32.dll")]
private static extern int PropVariantClear([In, Out] PropVariant value);
}
}