fix: 修复任务栏固定和右键菜单图标不显示问题
This commit is contained in:
115
IconGenerator.cs
115
IconGenerator.cs
@@ -1,66 +1,115 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Drawing.Drawing2D;
|
using System.Drawing.Drawing2D;
|
||||||
|
using System.Drawing.Imaging;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace TimerApp
|
namespace TimerApp
|
||||||
{
|
{
|
||||||
public static class IconGenerator
|
public static class IconGenerator
|
||||||
{
|
{
|
||||||
public static Icon GenerateClockIcon()
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
|
private static extern bool DestroyIcon(IntPtr hIcon);
|
||||||
|
|
||||||
|
public static Icon GenerateClockIcon(int size = 64)
|
||||||
{
|
{
|
||||||
int size = 64;
|
if (size < 16)
|
||||||
|
size = 16;
|
||||||
|
|
||||||
using (Bitmap bmp = new Bitmap(size, size))
|
using (Bitmap bmp = new Bitmap(size, size))
|
||||||
using (Graphics g = Graphics.FromImage(bmp))
|
using (Graphics g = Graphics.FromImage(bmp))
|
||||||
{
|
{
|
||||||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||||||
g.Clear(Color.Transparent);
|
g.Clear(Color.Transparent);
|
||||||
|
|
||||||
// 绘制闹钟主体
|
DrawClock(g, size);
|
||||||
int margin = 4;
|
|
||||||
|
IntPtr hIcon = bmp.GetHicon();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using Icon icon = Icon.FromHandle(hIcon);
|
||||||
|
return (Icon)icon.Clone();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
DestroyIcon(hIcon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WriteClockIco(string filePath, int size = 256)
|
||||||
|
{
|
||||||
|
byte[] icoBytes = CreateClockIcoBytes(size);
|
||||||
|
File.WriteAllBytes(filePath, icoBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] CreateClockIcoBytes(int size)
|
||||||
|
{
|
||||||
|
if (size <= 0)
|
||||||
|
size = 256;
|
||||||
|
if (size > 256)
|
||||||
|
size = 256;
|
||||||
|
if (size < 16)
|
||||||
|
size = 16;
|
||||||
|
|
||||||
|
byte[] pngBytes;
|
||||||
|
using (Bitmap bmp = new Bitmap(size, size))
|
||||||
|
using (Graphics g = Graphics.FromImage(bmp))
|
||||||
|
{
|
||||||
|
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||||||
|
g.Clear(Color.Transparent);
|
||||||
|
DrawClock(g, size);
|
||||||
|
|
||||||
|
using var ms = new MemoryStream();
|
||||||
|
bmp.Save(ms, ImageFormat.Png);
|
||||||
|
pngBytes = ms.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
using var icoStream = new MemoryStream(6 + 16 + pngBytes.Length);
|
||||||
|
using (var bw = new BinaryWriter(icoStream, System.Text.Encoding.UTF8, leaveOpen: true))
|
||||||
|
{
|
||||||
|
bw.Write((ushort)0);
|
||||||
|
bw.Write((ushort)1);
|
||||||
|
bw.Write((ushort)1);
|
||||||
|
|
||||||
|
bw.Write((byte)(size == 256 ? 0 : size));
|
||||||
|
bw.Write((byte)(size == 256 ? 0 : size));
|
||||||
|
bw.Write((byte)0);
|
||||||
|
bw.Write((byte)0);
|
||||||
|
bw.Write((ushort)1);
|
||||||
|
bw.Write((ushort)32);
|
||||||
|
bw.Write((uint)pngBytes.Length);
|
||||||
|
bw.Write((uint)(6 + 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
icoStream.Write(pngBytes, 0, pngBytes.Length);
|
||||||
|
return icoStream.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DrawClock(Graphics g, int size)
|
||||||
|
{
|
||||||
|
int margin = Math.Max(2, size / 16);
|
||||||
int clockSize = size - margin * 2;
|
int clockSize = size - margin * 2;
|
||||||
Rectangle rect = new Rectangle(margin, margin, clockSize, clockSize);
|
Rectangle rect = new Rectangle(margin, margin, clockSize, clockSize);
|
||||||
|
|
||||||
// 外圈
|
|
||||||
using (Pen pen = new Pen(Color.White, 4))
|
|
||||||
{
|
|
||||||
g.DrawEllipse(pen, rect);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 两个耳朵 (闹钟铃)
|
|
||||||
using (Brush earBrush = new SolidBrush(Color.White))
|
using (Brush earBrush = new SolidBrush(Color.White))
|
||||||
{
|
{
|
||||||
// 左耳
|
g.FillPie(earBrush, 0, 0, size / 2, size / 2, 200, 50);
|
||||||
g.FillPie(earBrush, 0, 0, size/2, size/2, 200, 50);
|
g.FillPie(earBrush, size / 2, 0, size / 2, size / 2, 290, 50);
|
||||||
// 右耳
|
|
||||||
g.FillPie(earBrush, size/2, 0, size/2, size/2, 290, 50);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重绘外圈盖住耳朵连接处
|
using (Pen pen = new Pen(Color.White, Math.Max(2, size / 16)))
|
||||||
using (Brush bgBrush = new SolidBrush(Color.FromArgb(30, 30, 30))) // 深色背景
|
|
||||||
{
|
|
||||||
g.FillEllipse(bgBrush, rect);
|
|
||||||
}
|
|
||||||
using (Pen pen = new Pen(Color.White, 3))
|
|
||||||
{
|
{
|
||||||
g.DrawEllipse(pen, rect);
|
g.DrawEllipse(pen, rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 指针
|
|
||||||
Point center = new Point(size / 2, size / 2);
|
Point center = new Point(size / 2, size / 2);
|
||||||
using (Pen handPen = new Pen(Color.LightGreen, 3))
|
using (Pen handPen = new Pen(Color.LightGreen, Math.Max(2, size / 22)))
|
||||||
{
|
{
|
||||||
handPen.EndCap = LineCap.Round;
|
handPen.EndCap = LineCap.Round;
|
||||||
// 时针
|
g.DrawLine(handPen, center, new Point(center.X + Math.Max(2, size * 10 / 64), center.Y - Math.Max(2, size * 10 / 64)));
|
||||||
g.DrawLine(handPen, center, new Point(center.X + 10, center.Y - 10));
|
g.DrawLine(handPen, center, new Point(center.X, center.Y - Math.Max(3, size * 18 / 64)));
|
||||||
// 分针
|
|
||||||
g.DrawLine(handPen, center, new Point(center.X, center.Y - 18));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 转换为图标
|
|
||||||
// 注意:GetHicon 需要释放
|
|
||||||
IntPtr hIcon = bmp.GetHicon();
|
|
||||||
return Icon.FromHandle(hIcon);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
15
MainForm.cs
15
MainForm.cs
@@ -133,13 +133,20 @@ namespace TimerApp
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Generate and set custom icon
|
// Generate and set custom icon
|
||||||
Icon icon = IconGenerator.GenerateClockIcon();
|
Icon icon = IconGenerator.GenerateClockIcon(64);
|
||||||
this.Icon = icon;
|
this.Icon = icon;
|
||||||
notifyIcon1.Icon = icon;
|
notifyIcon1.Icon = icon;
|
||||||
|
|
||||||
|
contextMenuStrip1.ShowImageMargin = true;
|
||||||
|
toolStripMenuItemShow.Image = null;
|
||||||
|
toolStripMenuItemExit.Image = null;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
notifyIcon1.Icon = SystemIcons.Application;
|
notifyIcon1.Icon = SystemIcons.Application;
|
||||||
|
contextMenuStrip1.ShowImageMargin = true;
|
||||||
|
toolStripMenuItemShow.Image = null;
|
||||||
|
toolStripMenuItemExit.Image = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateStatusUI();
|
UpdateStatusUI();
|
||||||
@@ -541,6 +548,11 @@ namespace TimerApp
|
|||||||
_hasShownMinimizeTip = true;
|
_hasShownMinimizeTip = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
notifyIcon1.Visible = false;
|
||||||
|
notifyIcon1.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
|
private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
|
||||||
@@ -564,6 +576,7 @@ namespace TimerApp
|
|||||||
{
|
{
|
||||||
_monitor.Stop();
|
_monitor.Stop();
|
||||||
notifyIcon1.Visible = false;
|
notifyIcon1.Visible = false;
|
||||||
|
notifyIcon1.Dispose();
|
||||||
Application.Exit();
|
Application.Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,11 @@ static class Program
|
|||||||
[STAThread]
|
[STAThread]
|
||||||
static void Main()
|
static void Main()
|
||||||
{
|
{
|
||||||
|
TaskbarIntegration.InitializeProcess();
|
||||||
// To customize application configuration such as set high DPI settings or default font,
|
// To customize application configuration such as set high DPI settings or default font,
|
||||||
// see https://aka.ms/applicationconfiguration.
|
// see https://aka.ms/applicationconfiguration.
|
||||||
ApplicationConfiguration.Initialize();
|
ApplicationConfiguration.Initialize();
|
||||||
|
TaskbarIntegration.InitializeShortcuts();
|
||||||
Application.Run(new MainForm());
|
Application.Run(new MainForm());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
305
TaskbarIntegration.cs
Normal file
305
TaskbarIntegration.cs
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace TimerApp
|
||||||
|
{
|
||||||
|
internal static class TaskbarIntegration
|
||||||
|
{
|
||||||
|
private const string AppId = "TimerApp";
|
||||||
|
private const string DisplayName = "TimerApp";
|
||||||
|
|
||||||
|
[DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||||
|
private static extern int SetCurrentProcessExplicitAppUserModelID(string AppID);
|
||||||
|
|
||||||
|
[DllImport("shell32.dll")]
|
||||||
|
private static extern void SHChangeNotify(uint wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2);
|
||||||
|
|
||||||
|
private const uint SHCNE_ASSOCCHANGED = 0x08000000;
|
||||||
|
private const uint SHCNF_IDLIST = 0x0000;
|
||||||
|
|
||||||
|
public static void InitializeProcess()
|
||||||
|
{
|
||||||
|
TrySetAppUserModelId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void InitializeShortcuts()
|
||||||
|
{
|
||||||
|
string? iconPath = TryEnsureIconFile();
|
||||||
|
if (iconPath is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TryEnsureStartMenuShortcut(iconPath);
|
||||||
|
TryUpdatePinnedTaskbarShortcuts(iconPath);
|
||||||
|
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, IntPtr.Zero, IntPtr.Zero);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void TrySetAppUserModelId()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SetCurrentProcessExplicitAppUserModelID(AppId);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string? TryEnsureIconFile()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string dir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "TimerApp");
|
||||||
|
Directory.CreateDirectory(dir);
|
||||||
|
|
||||||
|
string iconPath = Path.Combine(dir, "TimerApp.ico");
|
||||||
|
IconGenerator.WriteClockIco(iconPath, 256);
|
||||||
|
|
||||||
|
return iconPath;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void TryEnsureStartMenuShortcut(string iconPath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string exePath = Environment.ProcessPath ?? string.Empty;
|
||||||
|
if (string.IsNullOrWhiteSpace(exePath) || !File.Exists(exePath))
|
||||||
|
return;
|
||||||
|
|
||||||
|
string programsDir = Path.Combine(
|
||||||
|
Environment.GetFolderPath(Environment.SpecialFolder.StartMenu),
|
||||||
|
"Programs"
|
||||||
|
);
|
||||||
|
Directory.CreateDirectory(programsDir);
|
||||||
|
|
||||||
|
string lnkPath = Path.Combine(programsDir, "TimerApp.lnk");
|
||||||
|
CreateOrUpdateShortcut(lnkPath, exePath, iconPath, AppId);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void TryUpdatePinnedTaskbarShortcuts(string iconPath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string exePath = Environment.ProcessPath ?? string.Empty;
|
||||||
|
if (string.IsNullOrWhiteSpace(exePath) || !File.Exists(exePath))
|
||||||
|
return;
|
||||||
|
|
||||||
|
string pinnedDir = Path.Combine(
|
||||||
|
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||||
|
"Microsoft",
|
||||||
|
"Internet Explorer",
|
||||||
|
"Quick Launch",
|
||||||
|
"User Pinned",
|
||||||
|
"TaskBar"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!Directory.Exists(pinnedDir))
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (string lnkPath in Directory.EnumerateFiles(pinnedDir, "*.lnk", SearchOption.TopDirectoryOnly))
|
||||||
|
{
|
||||||
|
if (!TryGetShortcutTarget(lnkPath, out string targetPath))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!string.Equals(Path.GetFullPath(targetPath), Path.GetFullPath(exePath), StringComparison.OrdinalIgnoreCase))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
CreateOrUpdateShortcut(lnkPath, exePath, iconPath, AppId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryGetShortcutTarget(string lnkPath, out string targetPath)
|
||||||
|
{
|
||||||
|
targetPath = string.Empty;
|
||||||
|
IShellLinkW? link = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
link = (IShellLinkW)new ShellLink();
|
||||||
|
((IPersistFile)link).Load(lnkPath, 0);
|
||||||
|
|
||||||
|
var sb = new StringBuilder(260);
|
||||||
|
link.GetPath(sb, sb.Capacity, out _, 0);
|
||||||
|
string path = sb.ToString();
|
||||||
|
if (string.IsNullOrWhiteSpace(path))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
targetPath = path;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (link is not null)
|
||||||
|
Marshal.FinalReleaseComObject(link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CreateOrUpdateShortcut(string lnkPath, string exePath, string iconPath, string appId)
|
||||||
|
{
|
||||||
|
IShellLinkW? link = null;
|
||||||
|
IPropertyStore? store = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
link = (IShellLinkW)new ShellLink();
|
||||||
|
link.SetPath(exePath);
|
||||||
|
link.SetIconLocation(iconPath, 0);
|
||||||
|
link.SetArguments(string.Empty);
|
||||||
|
link.SetWorkingDirectory(Path.GetDirectoryName(exePath) ?? string.Empty);
|
||||||
|
|
||||||
|
store = (IPropertyStore)link;
|
||||||
|
using var pvAppId = PropVariant.FromString(appId);
|
||||||
|
using var pvRelaunchCommand = PropVariant.FromString(exePath);
|
||||||
|
using var pvRelaunchName = PropVariant.FromString(DisplayName);
|
||||||
|
using var pvRelaunchIcon = PropVariant.FromString(iconPath);
|
||||||
|
|
||||||
|
store.SetValue(PropertyKey.PKEY_AppUserModel_ID, pvAppId);
|
||||||
|
store.SetValue(PropertyKey.PKEY_AppUserModel_RelaunchCommand, pvRelaunchCommand);
|
||||||
|
store.SetValue(PropertyKey.PKEY_AppUserModel_RelaunchDisplayNameResource, pvRelaunchName);
|
||||||
|
store.SetValue(PropertyKey.PKEY_AppUserModel_RelaunchIconResource, pvRelaunchIcon);
|
||||||
|
store.Commit();
|
||||||
|
|
||||||
|
((IPersistFile)link).Save(lnkPath, true);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (store is not null)
|
||||||
|
Marshal.FinalReleaseComObject(store);
|
||||||
|
if (link is not null)
|
||||||
|
Marshal.FinalReleaseComObject(link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ComImport]
|
||||||
|
[Guid("00021401-0000-0000-C000-000000000046")]
|
||||||
|
private class ShellLink
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[ComImport]
|
||||||
|
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||||
|
[Guid("000214F9-0000-0000-C000-000000000046")]
|
||||||
|
private interface IShellLinkW
|
||||||
|
{
|
||||||
|
void GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cch, out WIN32_FIND_DATAW pfd, uint fFlags);
|
||||||
|
void GetIDList(out IntPtr ppidl);
|
||||||
|
void SetIDList(IntPtr pidl);
|
||||||
|
void GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cch);
|
||||||
|
void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
|
||||||
|
void GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cch);
|
||||||
|
void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
|
||||||
|
void GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cch);
|
||||||
|
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 cch, 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("0000010B-0000-0000-C000-000000000046")]
|
||||||
|
private interface IPersistFile
|
||||||
|
{
|
||||||
|
void GetClassID(out Guid pClassID);
|
||||||
|
void IsDirty();
|
||||||
|
void Load([MarshalAs(UnmanagedType.LPWStr)] string pszFileName, uint dwMode);
|
||||||
|
void Save([MarshalAs(UnmanagedType.LPWStr)] string pszFileName, [MarshalAs(UnmanagedType.Bool)] bool fRemember);
|
||||||
|
void SaveCompleted([MarshalAs(UnmanagedType.LPWStr)] string pszFileName);
|
||||||
|
void GetCurFile([MarshalAs(UnmanagedType.LPWStr)] out string ppszFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ComImport]
|
||||||
|
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||||
|
[Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")]
|
||||||
|
private interface IPropertyStore
|
||||||
|
{
|
||||||
|
uint GetCount(out uint cProps);
|
||||||
|
uint GetAt(uint iProp, out PropertyKey pkey);
|
||||||
|
uint GetValue(ref PropertyKey key, out PropVariant pv);
|
||||||
|
uint SetValue(ref PropertyKey key, [In] PropVariant pv);
|
||||||
|
uint Commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||||
|
private struct WIN32_FIND_DATAW
|
||||||
|
{
|
||||||
|
public uint dwFileAttributes;
|
||||||
|
public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
|
||||||
|
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
|
||||||
|
public System.Runtime.InteropServices.ComTypes.FILETIME 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
private struct PropertyKey
|
||||||
|
{
|
||||||
|
public Guid fmtid;
|
||||||
|
public uint pid;
|
||||||
|
|
||||||
|
public PropertyKey(Guid fmtid, uint pid)
|
||||||
|
{
|
||||||
|
this.fmtid = fmtid;
|
||||||
|
this.pid = pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PropertyKey PKEY_AppUserModel_ID { get; } = new PropertyKey(new Guid("9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3"), 5);
|
||||||
|
public static PropertyKey PKEY_AppUserModel_RelaunchCommand { get; } = new PropertyKey(new Guid("9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3"), 2);
|
||||||
|
public static PropertyKey PKEY_AppUserModel_RelaunchIconResource { get; } = new PropertyKey(new Guid("9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3"), 3);
|
||||||
|
public static PropertyKey PKEY_AppUserModel_RelaunchDisplayNameResource { get; } = new PropertyKey(new Guid("9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3"), 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
|
private sealed class PropVariant : IDisposable
|
||||||
|
{
|
||||||
|
[FieldOffset(0)]
|
||||||
|
private readonly ushort vt;
|
||||||
|
[FieldOffset(8)]
|
||||||
|
private readonly IntPtr pointerValue;
|
||||||
|
|
||||||
|
private PropVariant(string value)
|
||||||
|
{
|
||||||
|
vt = 31;
|
||||||
|
pointerValue = Marshal.StringToCoTaskMemUni(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PropVariant FromString(string value) => new PropVariant(value);
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
PropVariantClear(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("ole32.dll")]
|
||||||
|
private static extern int PropVariantClear([In, Out] PropVariant pvar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user