Newer
Older
JumpListUtil / ShellLink.cs
using System;
using System.IO;
using System.Runtime.InteropServices;

using System.Text;
using System.Runtime.InteropServices.ComTypes;

/****************************************************************************
 * Code From: https://emoacht.wordpress.com/2012/11/14/csharp-appusermodelid/
 ***************************************************************************/

namespace NativeHelpers
{
    // Modified from http://smdn.jp/programming/tips/createlnk/
    // Originally from http://www.vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects
    // /Creating_and_Modifying_Shortcuts/article.asp
    // Partly based on Sending toast notifications from desktop apps sample
    public class ShellLink : IDisposable
    {
        #region Fields

        private IShellLinkW shellLinkW = null;

        #endregion

        #region Private Properties (Interfaces)

        private IPersistFile PersistFile
        {
            get
            {
                IPersistFile PersistFile = shellLinkW as IPersistFile;

                if (PersistFile == null)
                    throw new COMException("Failed to create IPersistFile.");
                else
                    return PersistFile;
            }
        }

        private IPropertyStore PropertyStore
        {
            get
            {
                IPropertyStore PropertyStore = shellLinkW as IPropertyStore;

                if (PropertyStore == null)
                    throw new COMException("Failed to create IPropertyStore.");
                else
                    return PropertyStore;
            }
        }

        #endregion

        #region Public Properties (Minimal)

        // Path of loaded shortcut file
        public string ShortcutFile
        {
            get
            {
                string shortcutFile;

                PersistFile.GetCurFile(out shortcutFile);

                return shortcutFile;
            }
        }

        // Path of target file
        public string TargetPath
        {
            get
            {
                // No limitation to length of buffer string in the case of Unicode though.
                StringBuilder targetPath = new StringBuilder(NativeValues.MAX_PATH);

                WIN32_FIND_DATAW data = new WIN32_FIND_DATAW();

                NativeCalls.VerifySucceeded(shellLinkW.GetPath(targetPath, targetPath.Capacity, ref data,
                                                   NativeValues.SLGP_UNCPRIORITY));

                return targetPath.ToString();
            }
            set
            {
                NativeCalls.VerifySucceeded(shellLinkW.SetPath(value));
            }
        }

        public string Arguments
        {
            get
            {
                // No limitation to length of buffer string in the case of Unicode though.
                StringBuilder arguments = new StringBuilder(NativeValues.INFOTIPSIZE);

                NativeCalls.VerifySucceeded(shellLinkW.GetArguments(arguments, arguments.Capacity));

                return arguments.ToString();
            }
            set
            {
                NativeCalls.VerifySucceeded(shellLinkW.SetArguments(value));
            }
        }

        public string Description
        {
            get
            {
                // No limitation to length of buffer string in the case of Unicode though.
                StringBuilder description = new StringBuilder(NativeValues.INFOTIPSIZE);

                NativeCalls.VerifySucceeded(shellLinkW.GetDescription(description, description.Capacity));

                return description.ToString();
            }
            set
            {
                NativeCalls.VerifySucceeded(shellLinkW.SetDescription(value));
            }
        }

        public string WorkingDirectory
        {
            get
            {
                // No limitation to length of buffer string in the case of Unicode though.
                StringBuilder workingDirectory = new StringBuilder(NativeValues.INFOTIPSIZE);

                NativeCalls.VerifySucceeded(shellLinkW.GetWorkingDirectory(workingDirectory, workingDirectory.Capacity));

                return workingDirectory.ToString();
            }
            set
            {
                NativeCalls.VerifySucceeded(shellLinkW.SetWorkingDirectory(value));
            }
        }

        public Tuple<string, int> IconLocation
        {
            get
            {
                // No limitation to length of buffer string in the case of Unicode though.
                StringBuilder icon = new StringBuilder(NativeValues.INFOTIPSIZE);

                int index;
                NativeCalls.VerifySucceeded(shellLinkW.GetIconLocation(icon, icon.Capacity, out index));

                return new Tuple<string, int>(icon.ToString(), index);
            }
            set
            {
                NativeCalls.VerifySucceeded(shellLinkW.SetIconLocation(value.Item1, value.Item2));
            }
        }

        // AppUserModelID to be used for Windows 7 or later.
        public string AppUserModelID
        {
            get
            {
                using (PropVariant pv = new PropVariant())
                {
                    NativeCalls.VerifySucceeded(PropertyStore.GetValue(NativeValues.AppUserModelIDKey, pv));

                    if (pv.Value == null)
                        return "Null";
                    else
                        return pv.Value;
                }
            }
            set
            {
                using (PropVariant pv = new PropVariant(value))
                {
                    NativeCalls.VerifySucceeded(PropertyStore.SetValue(NativeValues.AppUserModelIDKey, pv));
                    NativeCalls.VerifySucceeded(PropertyStore.Commit());
                }
            }
        }

        #endregion

        #region Constructor

        public ShellLink()
            : this(null)
        { }

        // Construct with loading shortcut file.
        public ShellLink(string file)
        {
            try
            {
                shellLinkW = (IShellLinkW)new CShellLink();
            }
            catch
            {
                throw new COMException("Failed to create ShellLink object.");
            }

            if (file != null)
                Load(file);
        }

        #endregion

        #region Destructor

        ~ShellLink()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (shellLinkW != null)
            {
                // Release all references.
                Marshal.FinalReleaseComObject(shellLinkW);
                shellLinkW = null;
            }
        }

        #endregion

        #region Methods

        // Save shortcut file.
        public void Save()
        {
            string file = ShortcutFile;

            if (file == null)
                throw new InvalidOperationException("File name is not given.");
            else
                Save(file);
        }

        public void Save(string file)
        {
            if (file == null)
                throw new ArgumentNullException("File name is required.");
            else
                PersistFile.Save(file, true);
        }

        // Load shortcut file.
        public void Load(string file)
        {
            if (!File.Exists(file))
                throw new FileNotFoundException("File is not found.", file);
            else
                PersistFile.Load(file, NativeValues.STGM_READWRITE);
        }

        #endregion
    }
}