建立/更新 捷徑檔案 Shortcut File (*.lnk)

  • 6799
  • 0
  • C#
  • 2014-08-22

建立/更新 捷徑檔案 Shortcut File (*.lnk)

要透過程式來產生或更新捷徑檔的方式,一般來說可以加入COM的參考 Microsoft Shell Controls And Automation,或是使用 VBS 呼叫 WScript 方式產生批次文件來建立捷徑檔,但這都不是十分便利的方式。還有另一個方式可以來產生捷徑,就是透過 ComImport 方式。

 

1.使用COM的參考 Microsoft Shell Controls And Automation


Shell32.ShellClass objShell = new Shell32.ShellClass();
Shell32.ShellLinkObject objLinker;

objLinker = (Shell32.ShellLinkObject)objShell.NameSpace("捷徑檔所在路徑").ParseName(My.Computer.FileSystem.GetName("捷徑檔案名稱.lnk")).GetLink;
objLinker.Description = "捷徑檔案的註解說明";
objLinker.Path = "執行的檔案名稱";
objLinker.SetIconLocation("圖示檔案名稱", 0);	//0 為圖示的 Index
objLinker.ShowCommand = 1;			//視窗樣式。1=正常,2=最小化,3=最大化
objLinker.WorkingDirectory = "工作路徑";
objLinker.Save();

並沒有很困難,只不過編譯後會附帶一個DLL檔案,如果少了這個DLL檔就不能執行程式。

 

2.使用 VBS 呼叫 WScript

這個方式其實就是產生一個批次檔,檔案內容如下


set WshShell = WScript.CreateObject("WScript.Shell" )
strDesktop = WshShell.SpecialFolders("AllUsersDesktop" )
set oShellLink = WshShell.CreateShortcut(strDesktop & "\捷徑檔案名稱.lnk" )
oShellLink.TargetPath = "執行檔案名稱"
oShellLink.WindowStyle = 1
oShellLink.IconLocation = "圖示檔案"
oShellLink.Description = "捷徑的註解說明"
oShellLink.WorkingDirectory = "工作路徑"
oShellLink.Save

然後透過 Process.Start 來執行此批次程式,就可以產生捷徑檔了。這個方式也不是很困難,只是得要注意是不是可以正確的執行這個VBS。

 

3. 使用ComImport

這個方式並不是很多人曉得,因為要先寫一大段的程式才能達到目地。只不過這方式不需要額外的DLL,也不需要產生批次檔案來執行,在我來說是相當不錯的一個方式。將程式寫成一個類別或是編譯為DLL,以後要使用時,引用一下參考就行了。

ShellLink 類別


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

namespace ShellLink
{
    public class ShellLink : IDisposable
    {
        [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F9-0000-0000-C000-000000000046")]
        private interface IShellLinkW
        {
            uint GetExecuteFile([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, int cchMaxName);
            uint SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);

            uint GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath);
            uint SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);

            uint GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath);
            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 dwReserved);
            uint Resolve(IntPtr hwnd, uint fFlags);

            uint SetExecuteFile([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
        }

        [ComImport, 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;
            public uint dwReserved0;
            public uint dwReserved1;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]
            public string cFileName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
            public string cAlternateFileName;
        }

        [ComImport, 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);
            uint Commit();
        }

        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        private struct PropertyKey
        {
            private Guid formatId;
            private Int32 propertyId;
            public Guid FormatId { get { return formatId; } }
            public Int32 PropertyId { get { return propertyId; } }
            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
        {
            [FieldOffset(0)]
            ushort valueType;

            [FieldOffset(8)]
            IntPtr ptr;

            public VarEnum VarType
            {
                get { return (VarEnum)valueType; }
                set { valueType = (ushort)value; }
            }

            public bool IsNullOrEmpty { get { return (valueType == (ushort)VarEnum.VT_EMPTY || valueType == (ushort)VarEnum.VT_NULL); } }
            public string Value { get { return Marshal.PtrToStringUni(ptr); } }
            public PropVariant() { }
            public PropVariant(string value)
            {
                if (value == null) throw new ArgumentException("Failed to set value.");
                valueType = (ushort)VarEnum.VT_LPWSTR;
                ptr = Marshal.StringToCoTaskMemUni(value);
            }

            ~PropVariant() { Dispose(); }
            public void Dispose()
            {
                PropVariantClear(this);
                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;

        private ComTypes.IPersistFile PersistFile
        {
            get
            {
                ComTypes.IPersistFile PersistFile = shellLinkW as ComTypes.IPersistFile;
                if (PersistFile == null) throw new COMException("Failed to create IPersistFile.");
                return PersistFile;
            }
        }

        private IPropertyStore PropertyStore
        {
            get
            {
                IPropertyStore PropertyStore = shellLinkW as IPropertyStore;
                if (PropertyStore == null) throw new COMException("Failed to create IPropertyStore.");
                return PropertyStore;
            }
        }


        /// <summary>
        /// 讀取目前載入的捷徑檔案名稱
        /// </summary>
        public string CurrentShortcutFile
        {
            get
            {
                string strFileName;
                PersistFile.GetCurFile(out strFileName);
                return strFileName;
            }
        }

        /// <summary>
        /// 設定/讀取 執行的檔案
        /// </summary>
        public string ExecuteFile
        {
            get
            {
                StringBuilder FileName = new StringBuilder(MAX_PATH);
                WIN32_FIND_DATAW data = new WIN32_FIND_DATAW();
                VerifySucceeded(shellLinkW.GetExecuteFile(FileName, FileName.Capacity, ref data, SLGP_UNCPRIORITY));
                return FileName.ToString();
            }
            set
            {
                VerifySucceeded(shellLinkW.SetExecuteFile(value));
            }
        }

        /// <summary>
        /// 設定/讀取 執行檔案的參數
        /// </summary>
        public string ExecuteArguments
        {
            get
            {
                StringBuilder ExecuteArgs = new StringBuilder(INFOTIPSIZE);
                VerifySucceeded(shellLinkW.GetArguments(ExecuteArgs, ExecuteArgs.Capacity));
                return ExecuteArgs.ToString();
            }
            set
            {
                VerifySucceeded(shellLinkW.SetArguments(value));
            }
        }

        /// <summary>
        /// 設定/讀取 工作路徑
        /// </summary>
        public string WorkPath
        {
            get
            {
                StringBuilder WorkDirectory = new StringBuilder(MAX_PATH);
                VerifySucceeded(shellLinkW.GetWorkingDirectory(WorkDirectory, WorkDirectory.Capacity));
                return WorkDirectory.ToString();
            }
            set
            {
                VerifySucceeded(shellLinkW.SetWorkingDirectory(value));
            }
        }

        /// <summary>
        /// 設定/讀取 檔案註解
        /// </summary>
        public string Descriptions
        {
            get
            {
                StringBuilder FileDescription = new StringBuilder(MAX_PATH);
                VerifySucceeded(shellLinkW.GetDescription(FileDescription, FileDescription.Capacity));
                return FileDescription.ToString();
            }
            set
            {
                VerifySucceeded(shellLinkW.SetDescription(value));
            }
        }

        /// <summary>
        /// 設定/讀取 圖示檔案
        /// </summary>
        public string IconLocation
        {
            get
            {
                StringBuilder IconConfig = new StringBuilder(MAX_PATH);
                int IconIndex;
                VerifySucceeded(shellLinkW.GetIconLocation(IconConfig, IconConfig.Capacity, out IconIndex));
                return IconConfig.ToString() + "," + IconIndex.ToString();
            }
            set
            {
                if (value.Split(',').Length == 2)
                {
                    VerifySucceeded(shellLinkW.SetIconLocation(value.Split(',')[0], Convert.ToInt32(value.Split(',')[1])));
                }
            }
        }

        /// <summary>
        /// 設定/讀取 Application User Model IDs For Win7 以上作業系統
        /// </summary>
        public string AppUserModelID
        {
            get
            {
                using (PropVariant pv = new PropVariant())
                {
                    VerifySucceeded(PropertyStore.GetValue(AppUserModelIDKey, pv));

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

        public ShellLink() : this(null) { }

        /// <summary>
        /// 初始化後載入捷徑檔案
        /// </summary>
        /// <param name="FullLinkFileName">完整的捷徑檔案名稱</param>
        public ShellLink(string FullLinkFileName)
        {
            try
            {
                shellLinkW = (IShellLinkW)new CShellLink();
            }
            catch
            {
                throw new COMException("Failed to create ShellLink object.");
            }

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

        ~ShellLink() { Dispose(false); }

        /// <summary>
        /// 釋放 ShellLink 使用的資源
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

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

        /// <summary>
        /// 儲存捷徑檔案
        /// </summary>
        public void Save()
        {
            string SaveFileName = CurrentShortcutFile;

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

        /// <summary>
        /// 儲存捷徑檔案
        /// </summary>
        /// <param name="FullLinkFileName">完整的捷徑檔案名稱</param>
        public void Save(string FullLinkFileName)
        {
            if (FullLinkFileName == null) throw new ArgumentNullException("File name is required.");
            PersistFile.Save(FullLinkFileName, true);
        }

        /// <summary>
        /// 讀取捷徑檔案 
        /// </summary>
        /// <param name="FullLinkFileName">完整的捷徑檔案名稱</param>
        public void Load(string FullLinkFileName)
        {
            if (!File.Exists(FullLinkFileName)) throw new FileNotFoundException("File is not found.", FullLinkFileName);
            PersistFile.Load(FullLinkFileName, STGM_READ);
        }

        /// <summary>
        /// 確認程式執行
        /// </summary>
        /// <param name="hresult">回傳值</param>
        public static void VerifySucceeded(uint hresult)
        {
            if (hresult > 1) throw new InvalidOperationException("Failed with HRESULT: " + hresult.ToString("X"));
        }
    }
}

使用方式: 新增捷徑


ShellLink slLinkObject = new ShellLink();
slLinkObject.WorkPath = "工作路徑";
slLinkObject.IconLocation = "圖示檔案,0";	// 0 為圖示檔的 Index
slLinkObject.ExecuteFile = "執行的檔案";
slLinkObject.Save("捷徑檔案名稱(.lnk)");
slLinkObject.Dispose();

使用方式: 編輯捷徑


ShellLink slLinkObject = new ShellLink("捷徑檔案名稱(.lnk)");
slLinkObject.WorkPath = "工作路徑";
slLinkObject.IconLocation = "圖示檔案,0";	// 0 為圖示檔的 Index
slLinkObject.ExecuteFile = "執行的檔案";
slLinkObject.Save();
slLinkObject.Dispose();

程式是運氣與直覺堆砌而成的奇蹟。
若不具備這兩者,不可能以這樣的工時實現這樣的規格。
修改規格是對奇蹟吐槽的褻瀆行為。
而追加修改則是相信奇蹟還會重現的魯莽行動。