// Copyright (c) Microsoft Corporation // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; using System.Text; using Accessibility; using Microsoft.Plugin.Program.Logger; namespace Microsoft.Plugin.Program.Programs { public class ShellLinkHelper : IShellLinkHelper { [Flags] private enum SLGP_FLAGS { SLGP_SHORTPATH = 0x1, SLGP_UNCPRIORITY = 0x2, SLGP_RAWPATH = 0x4, } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching COM")] private struct WIN32_FIND_DATAW { public uint dwFileAttributes; public long ftCreationTime; public long ftLastAccessTime; public long 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; } [Flags] [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Represents flags specified in IShellLink interface")] public enum SLR_FLAGS { SLR_NO_UI = 0x1, SLR_ANY_MATCH = 0x2, SLR_UPDATE = 0x4, SLR_NOUPDATE = 0x8, SLR_NOSEARCH = 0x10, SLR_NOTRACK = 0x20, SLR_NOLINKINFO = 0x40, SLR_INVOKE_MSI = 0x80, } // Reference : http://www.pinvoke.net/default.aspx/Interfaces.IShellLinkW // The IShellLink interface allows Shell links to be created, modified, and resolved [ComImport] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [Guid("000214F9-0000-0000-C000-000000000046")] private interface IShellLinkW { /// Retrieves the path and file name of a Shell link object void GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, ref WIN32_FIND_DATAW pfd, SLGP_FLAGS fFlags); /// Retrieves the list of item identifiers for a Shell link object void GetIDList(out IntPtr ppidl); /// Sets the pointer to an item identifier list (PIDL) for a Shell link object. void SetIDList(IntPtr pidl); /// Retrieves the description string for a Shell link object void GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName); /// Sets the description for a Shell link object. The description can be any application-defined string void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); /// Retrieves the name of the working directory for a Shell link object void GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath); /// Sets the name of the working directory for a Shell link object void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); /// Retrieves the command-line arguments associated with a Shell link object void GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath); /// Sets the command-line arguments for a Shell link object void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); /// Retrieves the hot key for a Shell link object void GetHotkey(out short pwHotkey); /// Sets a hot key for a Shell link object void SetHotkey(short wHotkey); /// Retrieves the show command for a Shell link object void GetShowCmd(out int piShowCmd); /// Sets the show command for a Shell link object. The show command sets the initial show state of the window. void SetShowCmd(int iShowCmd); /// Retrieves the location (path and index) of the icon for a Shell link object void GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon); /// Sets the location (path and index) of the icon for a Shell link object void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); /// Sets the relative path to the Shell link object void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved); /// Attempts to find the target of a Shell link, even if it has been moved or renamed void Resolve(ref Accessibility._RemotableHandle hwnd, SLR_FLAGS fFlags); /// Sets the path and file name of a Shell link object void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); } [ComImport] [Guid("00021401-0000-0000-C000-000000000046")] private class ShellLink { } // Contains the description of the app public string Description { get; set; } = string.Empty; // Contains the arguments to the app public string Arguments { get; set; } = string.Empty; public bool HasArguments { get; set; } = false; // Retrieve the target path using Shell Link public string RetrieveTargetPath(string path) { var link = new ShellLink(); const int STGM_READ = 0; try { ((IPersistFile)link).Load(path, STGM_READ); } catch (System.IO.FileNotFoundException ex) { ProgramLogger.LogException($"|Win32| ShellLinkHelper.retrieveTargetPath | {path} | Path could not be retrieved", ex); return string.Empty; } var hwnd = default(_RemotableHandle); ((IShellLinkW)link).Resolve(ref hwnd, 0); const int MAX_PATH = 260; StringBuilder buffer = new StringBuilder(MAX_PATH); var data = default(WIN32_FIND_DATAW); ((IShellLinkW)link).GetPath(buffer, buffer.Capacity, ref data, SLGP_FLAGS.SLGP_SHORTPATH); var target = buffer.ToString(); // To set the app description if (!string.IsNullOrEmpty(target)) { buffer = new StringBuilder(MAX_PATH); ((IShellLinkW)link).GetDescription(buffer, MAX_PATH); Description = buffer.ToString(); StringBuilder argumentBuffer = new StringBuilder(MAX_PATH); ((IShellLinkW)link).GetArguments(argumentBuffer, argumentBuffer.Capacity); Arguments = argumentBuffer.ToString(); // Set variable to true if the program takes in any arguments if (argumentBuffer.Length != 0) { HasArguments = true; } } return target; } } }