using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using ComTypes = System.Runtime.InteropServices.ComTypes;
public class ShellLink : IDisposable
InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("000214F9-0000-0000-C000-000000000046")]
private interface IShellLinkW
uint GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile,
int cchMaxPath, ref WIN32_FIND_DATAW pfd, uint fFlags);
uint GetIDList(out IntPtr ppidl);
uint SetIDList(IntPtr pidl);
uint GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName,
uint SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
uint GetWorkingirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir,
uint SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
uint GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs,
uint SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
uint GetHotKey(out ushort pwHotkey);
uint SetHotKey(ushort wHotKey);
uint GetShowCmd(out int piShowCmd);
uint SetShowCmd(int iShowCmd);
uint GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath,
int cchIconPath, out int piIcon);
uint SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
uint SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel,
uint Resolve(IntPtr hwnd, uint fFlags);
uint SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
ClassInterface(ClassInterfaceType.None),
Guid("00021401-0000-0000-C000-000000000046")]
private class CShellLink { }
[StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)]
private struct WIN32_FIND_DATAW
public uint dwFileAttributes;
public ComTypes.FILETIME ftCreationTime;
public ComTypes.FILETIME ftLastAccessTime;
public ComTypes.FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public string cAlternateFileName;
InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")]
private interface IPropertyStore
uint GetCount([Out] out uint cProps);
uint GetAt([In] uint iProp, out PropertyKey pkey);
uint GetValue([In] ref PropertyKey key, [Out] PropVariant pv);
uint SetValue([In] ref PropertyKey key, [In] PropVariant pv);
[StructLayout(LayoutKind.Sequential, Pack = 4)]
private struct PropertyKey
private Int32 propertyId;
#region Public Properties
public PropertyKey(Guid formatId, Int32 propertyId)
this.formatId = formatId;
this.propertyId = propertyId;
public PropertyKey(string formatId, Int32 propertyId)
this.formatId = new Guid(formatId);
this.propertyId = propertyId;
[StructLayout(LayoutKind.Explicit)]
private sealed class PropVariant : IDisposable
#region Public Properties
get { return (VarEnum)valueType; }
set { valueType = (ushort)value; }
public bool IsNullOrEmpty
return (valueType == (ushort)VarEnum.VT_EMPTY ||
valueType == (ushort)VarEnum.VT_NULL);
return Marshal.PtrToStringUni(ptr);
public PropVariant(string value)
throw new ArgumentException("Failed to set value.");
valueType = (ushort)VarEnum.VT_LPWSTR;
ptr = Marshal.StringToCoTaskMemUni(value);
GC.SuppressFinalize(this);
[DllImport("Ole32.dll", PreserveSig = false)]
private extern static void PropVariantClear([In, Out] PropVariant pvar);
private IShellLinkW shellLinkW = null;
private readonly PropertyKey AppUserModelIDKey =
new PropertyKey("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}", 5);
private const int MAX_PATH = 260;
private const int INFOTIPSIZE = 1024;
private const int STGM_READ = 0x00000000;
private const uint SLGP_UNCPRIORITY = 0x0002;
#region Private Properties (Interfaces)
private IPersistFile PersistFile
IPersistFile PersistFile = shellLinkW as IPersistFile;
throw new COMException("Failed to create IPersistFile.");
private IPropertyStore PropertyStore
IPropertyStore PropertyStore = shellLinkW as IPropertyStore;
if (PropertyStore == null)
throw new COMException("Failed to create IPropertyStore.");
#region Public Properties (Minimal)
public string ShortcutFile
PersistFile.GetCurFile(out shortcutFile);
StringBuilder targetPath = new StringBuilder(MAX_PATH);
WIN32_FIND_DATAW data = new WIN32_FIND_DATAW();
VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data,
return targetPath.ToString();
VerifySucceeded(shellLinkW.SetPath(value));
StringBuilder arguments = new StringBuilder(INFOTIPSIZE);
VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity));
return arguments.ToString();
VerifySucceeded(shellLinkW.SetArguments(value));
public string AppUserModelID
using (PropVariant pv = new PropVariant())
VerifySucceeded(PropertyStore.GetValue(AppUserModelIDKey, pv));
using (PropVariant pv = new PropVariant(value))
VerifySucceeded(PropertyStore.SetValue(AppUserModelIDKey, pv));
VerifySucceeded(PropertyStore.Commit());
public ShellLink(string file)
shellLinkW = (IShellLinkW)new CShellLink();
throw new COMException("Failed to create ShellLink object.");
GC.SuppressFinalize(this);
protected virtual void Dispose(bool disposing)
Marshal.FinalReleaseComObject(shellLinkW);
string file = ShortcutFile;
throw new InvalidOperationException("File name is not given.");
public void Save(string file)
throw new ArgumentNullException("File name is required.");
PersistFile.Save(file, true);
public void Load(string file)
throw new FileNotFoundException("File is not found.", file);
PersistFile.Load(file, STGM_READ);
public static void VerifySucceeded(uint hresult)
throw new InvalidOperationException("Failed with HRESULT: " +
Then, this is a demo app using this class.
using System.Windows.Forms;
namespace ManageShellLink
public partial class Main : Form
private void Main_Load(object sender, EventArgs e)
textBox_ShortcutFile.Text = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "Calc.lnk");
textBox_TargetPath.Text = "%SystemRoot%\\System32\\calc.exe";
textBox_Arguments.Text = " ";
textBox_AppUserModelID.Text = "Microsoft.Windows.Calc";
private void button_Create_Click(object sender, EventArgs e)
if (String.IsNullOrWhiteSpace(textBox_ShortcutFile.Text))
MessageBox.Show("Shortcut File is not provided.", "",
MessageBoxButtons.OK, MessageBoxIcon.Error);
if (!String.IsNullOrWhiteSpace(textBox_AppUserModelID.Text) &
((128 < textBox_AppUserModelID.Text.Length) |
textBox_AppUserModelID.Text.Contains(" ")))
MessageBox.Show("AppUserModelID must be no more than 128 characters " +
"and cannot contain spaces.", "",
MessageBoxButtons.OK, MessageBoxIcon.Error);
using (ShellLink shortcut = new ShellLink())
shortcut.TargetPath = textBox_TargetPath.Text;
shortcut.Arguments = textBox_Arguments.Text;
shortcut.AppUserModelID = textBox_AppUserModelID.Text;
shortcut.Save(textBox_ShortcutFile.Text);
MessageBox.Show("Created shortcut file.", "",
MessageBoxButtons.OK, MessageBoxIcon.Information);
MessageBox.Show("Could not created shortcut file. " + ex.Message, "",
MessageBoxButtons.OK, MessageBoxIcon.Error);
private void button_Read_Click(object sender, EventArgs e)
if (!File.Exists(textBox_ShortcutFile.Text))
MessageBox.Show("Such shortcut file does not exist.", "",
MessageBoxButtons.OK, MessageBoxIcon.Error);
using (ShellLink shortcut = new ShellLink(textBox_ShortcutFile.Text))
textBox_TargetPath.Text = shortcut.TargetPath;
textBox_Arguments.Text = shortcut.Arguments;
textBox_AppUserModelID.Text = shortcut.AppUserModelID;
MessageBox.Show("Red shortcut file.", "",
MessageBoxButtons.OK, MessageBoxIcon.Information);
MessageBox.Show("Could not read shortcut file. " + ex.Message, "",
MessageBoxButtons.OK, MessageBoxIcon.Error);