111 lines
4.4 KiB
C#
111 lines
4.4 KiB
C#
using System.IO;
|
||
using System.Runtime.InteropServices;
|
||
using System.Runtime.InteropServices.ComTypes;
|
||
using System.Text;
|
||
|
||
namespace AeroMCSetup;
|
||
|
||
/// <summary>
|
||
/// Verknüpfungen (.lnk) über die Shell-Schnittstellen – inklusive AppUserModelID, damit
|
||
/// Taskleiste und Windows-Benachrichtigungen AeroMC wie bisher zuordnen (wie beim alten NSIS-Setup).
|
||
/// </summary>
|
||
public static class Shortcut
|
||
{
|
||
public static void Create(string link, string target, string description, string appUserModelId)
|
||
{
|
||
Directory.CreateDirectory(Path.GetDirectoryName(link)!);
|
||
var shellLink = (IShellLinkW)new CShellLink();
|
||
try
|
||
{
|
||
shellLink.SetPath(target);
|
||
shellLink.SetWorkingDirectory(Path.GetDirectoryName(target)!);
|
||
shellLink.SetIconLocation(target, 0);
|
||
shellLink.SetDescription(description);
|
||
|
||
var store = (IPropertyStore)shellLink;
|
||
var key = new PropertyKey(new Guid("9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3"), 5); // PKEY_AppUserModel_ID
|
||
var value = new PropVariant { vt = 31 /* VT_LPWSTR */, p = Marshal.StringToCoTaskMemUni(appUserModelId) };
|
||
try
|
||
{
|
||
store.SetValue(ref key, ref value);
|
||
store.Commit();
|
||
}
|
||
finally { Marshal.FreeCoTaskMem(value.p); }
|
||
|
||
((IPersistFile)shellLink).Save(link, true);
|
||
}
|
||
finally { Marshal.ReleaseComObject(shellLink); }
|
||
}
|
||
|
||
/// <summary>Ziel einer vorhandenen Verknüpfung, oder null.</summary>
|
||
public static string? Target(string link)
|
||
{
|
||
if (!File.Exists(link)) return null;
|
||
var shellLink = (IShellLinkW)new CShellLink();
|
||
try
|
||
{
|
||
((IPersistFile)shellLink).Load(link, 0);
|
||
var sb = new StringBuilder(1024);
|
||
shellLink.GetPath(sb, sb.Capacity, IntPtr.Zero, 0);
|
||
return sb.ToString();
|
||
}
|
||
catch { return null; }
|
||
finally { Marshal.ReleaseComObject(shellLink); }
|
||
}
|
||
|
||
// ---------- COM ----------
|
||
|
||
[ComImport, Guid("00021401-0000-0000-C000-000000000046")]
|
||
private class CShellLink { }
|
||
|
||
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F9-0000-0000-C000-000000000046")]
|
||
private interface IShellLinkW
|
||
{
|
||
void GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, IntPtr pfd, uint fFlags);
|
||
void GetIDList(out IntPtr ppidl);
|
||
void SetIDList(IntPtr pidl);
|
||
void GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName);
|
||
void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
|
||
void GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath);
|
||
void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
|
||
void GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder 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([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder 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("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")]
|
||
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(ref PropertyKey key, ref PropVariant pv);
|
||
void Commit();
|
||
}
|
||
|
||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
||
private struct PropertyKey(Guid fmtid, uint pid)
|
||
{
|
||
public Guid fmtid = fmtid;
|
||
public uint pid = pid;
|
||
}
|
||
|
||
// PROPVARIANT: 16 Byte (32 Bit) bzw. 24 Byte (64 Bit)
|
||
[StructLayout(LayoutKind.Sequential)]
|
||
private struct PropVariant
|
||
{
|
||
public ushort vt;
|
||
public ushort r1, r2, r3;
|
||
public IntPtr p;
|
||
public IntPtr p2;
|
||
}
|
||
}
|