156 lines
5.4 KiB
C#
156 lines
5.4 KiB
C#
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
using System.Runtime.InteropServices.ComTypes;
|
|
|
|
namespace EmbyToolbox.Interop;
|
|
|
|
internal static class ToastShortcutRegistration
|
|
{
|
|
private const int StgmReadwrite = 0x00000002;
|
|
|
|
private static readonly PropertyKey AppUserModelIdKey = new(
|
|
new Guid("9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3"),
|
|
5);
|
|
|
|
public static string? LastDiagnostics { get; private set; }
|
|
|
|
public static bool TryEnsureStartMenuShortcut(string appUserModelId)
|
|
{
|
|
LastDiagnostics = null;
|
|
|
|
if (!OperatingSystem.IsWindowsVersionAtLeast(10, 0, 10240))
|
|
{
|
|
LastDiagnostics = "требуется Windows 10 (10240) или новее.";
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
var exePath = Environment.ProcessPath;
|
|
if (string.IsNullOrWhiteSpace(exePath) || !File.Exists(exePath))
|
|
{
|
|
LastDiagnostics = "не удалось определить путь к exe.";
|
|
return false;
|
|
}
|
|
|
|
var programs = Environment.GetFolderPath(Environment.SpecialFolder.StartMenu);
|
|
var shortcutPath = Path.Combine(programs, "Programs", "Emby Toolbox.lnk");
|
|
Directory.CreateDirectory(Path.GetDirectoryName(shortcutPath)!);
|
|
|
|
var shellLinkObject = (object)new CShellLink();
|
|
var shellLink = (IShellLinkW)shellLinkObject;
|
|
shellLink.SetPath(exePath);
|
|
shellLink.SetArguments(string.Empty);
|
|
shellLink.SetWorkingDirectory(Path.GetDirectoryName(exePath) ?? AppContext.BaseDirectory);
|
|
shellLink.SetDescription("Emby Toolbox");
|
|
|
|
if (File.Exists(Path.Combine(AppContext.BaseDirectory, "Resources", "AppIcon.ico")))
|
|
{
|
|
shellLink.SetIconLocation(Path.Combine(AppContext.BaseDirectory, "Resources", "AppIcon.ico"), 0);
|
|
}
|
|
else
|
|
{
|
|
shellLink.SetIconLocation(exePath, 0);
|
|
}
|
|
|
|
using var appId = PropVariant.FromString(appUserModelId);
|
|
var propertyStore = (IPropertyStore)shellLink;
|
|
propertyStore.SetValue(AppUserModelIdKey, appId);
|
|
propertyStore.Commit();
|
|
|
|
var persistFile = (IPersistFile)shellLink;
|
|
persistFile.Save(shortcutPath, true);
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LastDiagnostics = $"{ex.GetType().Name}: {ex.Message}";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
[ComImport]
|
|
[Guid("00021401-0000-0000-C000-000000000046")]
|
|
private sealed class CShellLink
|
|
{
|
|
}
|
|
|
|
[ComImport]
|
|
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
|
[Guid("000214F9-0000-0000-C000-000000000046")]
|
|
private interface IShellLinkW
|
|
{
|
|
void GetPath(IntPtr pszFile, int cchMaxPath, IntPtr pfd, uint fFlags);
|
|
void GetIDList(out IntPtr ppidl);
|
|
void SetIDList(IntPtr pidl);
|
|
void GetDescription(IntPtr pszName, int cchMaxName);
|
|
void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
|
|
void GetWorkingDirectory(IntPtr pszDir, int cchMaxPath);
|
|
void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
|
|
void GetArguments(IntPtr pszArgs, int cchMaxPath);
|
|
void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
|
|
void GetHotkey(out short pwHotkey);
|
|
void SetHotkey(short wHotkey);
|
|
void GetShowCmd(out int piShowCmd);
|
|
void SetShowCmd(int iShowCmd);
|
|
void GetIconLocation(IntPtr pszIconPath, int cchIconPath, out int piIcon);
|
|
void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
|
|
void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, uint dwReserved);
|
|
void Resolve(IntPtr hwnd, uint fFlags);
|
|
void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
|
|
}
|
|
|
|
[ComImport]
|
|
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
|
[Guid("00000138-0000-0000-C000-000000000046")]
|
|
private interface IPropertyStore
|
|
{
|
|
void GetCount(out uint cProps);
|
|
void GetAt(uint iProp, out PropertyKey pkey);
|
|
void GetValue(ref PropertyKey key, out PropVariant pv);
|
|
void SetValue(in PropertyKey key, in PropVariant pv);
|
|
void Commit();
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
|
private readonly struct PropertyKey(Guid formatId, int propertyId)
|
|
{
|
|
private readonly Guid _formatId = formatId;
|
|
private readonly int _propertyId = propertyId;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
private sealed class PropVariant : IDisposable
|
|
{
|
|
private ushort _valueType;
|
|
private ushort _reserved1;
|
|
private ushort _reserved2;
|
|
private ushort _reserved3;
|
|
private IntPtr _value;
|
|
private IntPtr _reserved4;
|
|
|
|
public static PropVariant FromString(string value)
|
|
{
|
|
return new PropVariant
|
|
{
|
|
_valueType = 31, // VT_LPWSTR
|
|
_value = Marshal.StringToCoTaskMemUni(value)
|
|
};
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
PropVariantClear(this);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
~PropVariant()
|
|
{
|
|
PropVariantClear(this);
|
|
}
|
|
|
|
[DllImport("ole32.dll")]
|
|
private static extern int PropVariantClear([In, Out] PropVariant pvar);
|
|
}
|
|
}
|