diff --git a/Sources/EasyExtensions.Windows/ShellLink.cs b/Sources/EasyExtensions.Windows/ShellLink.cs
index 49f7d53..cbcf7af 100644
--- a/Sources/EasyExtensions.Windows/ShellLink.cs
+++ b/Sources/EasyExtensions.Windows/ShellLink.cs
@@ -192,85 +192,132 @@ void GetPath([Out(), MarshalAs(UnmanagedType.LPStr)] StringBuilder pszFile,
}
+ ///
+ /// IShellLinkW interface for managing shell links (shortcuts) in Windows.
+ ///
[ComImport()]
[Guid("000214F9-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IShellLinkW
{
- //[helpstring("Retrieves the path and filename of a shell link object")]
- void GetPath(
- [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile,
- int cchMaxPath,
- ref WIN32_FIND_DATAW pfd,
- uint fFlags);
-
- //[helpstring("Retrieves the list of shell link item identifiers")]
+ ///
+ /// Retrieves the path and filename of a shell link object.
+ ///
+ /// The buffer that receives the path and filename.
+ /// The size, in characters, of the buffer pointed to by the pszFile parameter.
+ /// The WIN32_FIND_DATA structure that receives information about the shell link object.
+ /// Flags that specify the type of path information to retrieve.
+ void GetPath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, ref WIN32_FIND_DATAW pfd, uint fFlags);
+
+ ///
+ /// Retrieves the list of shell link item identifiers.
+ ///
+ /// The address of an ITEMIDLIST pointer that receives the list of item identifiers.
void GetIDList(out IntPtr ppidl);
- //[helpstring("Sets the list of shell link item identifiers")]
+ ///
+ /// Sets the list of shell link item identifiers.
+ ///
+ /// The address of an ITEMIDLIST that specifies the list of item identifiers to be set.
void SetIDList(IntPtr pidl);
- //[helpstring("Retrieves the shell link description string")]
- void GetDescription(
- [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile,
- int cchMaxName);
+ ///
+ /// Retrieves the shell link description string.
+ ///
+ /// The buffer that receives the description string.
+ /// The size, in characters, of the buffer pointed to by the pszFile parameter.
+ void GetDescription([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxName);
- //[helpstring("Sets the shell link description string")]
- void SetDescription(
- [MarshalAs(UnmanagedType.LPWStr)] string pszName);
+ ///
+ /// Sets the shell link description string.
+ ///
+ /// The new description string.
+ void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
- //[helpstring("Retrieves the name of the shell link working directory")]
- void GetWorkingDirectory(
- [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir,
- int cchMaxPath);
+ ///
+ /// Retrieves the name of the shell link working directory.
+ ///
+ /// The buffer that receives the working directory.
+ /// The size, in characters, of the buffer pointed to by the pszDir parameter.
+ void GetWorkingDirectory([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath);
- //[helpstring("Sets the name of the shell link working directory")]
- void SetWorkingDirectory(
- [MarshalAs(UnmanagedType.LPWStr)] string pszDir);
+ ///
+ /// Sets the name of the shell link working directory.
+ ///
+ /// The new working directory.
+ void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
- //[helpstring("Retrieves the shell link command-line arguments")]
- void GetArguments(
- [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs,
- int cchMaxPath);
+ ///
+ /// Retrieves the shell link command-line arguments.
+ ///
+ /// The buffer that receives the command-line arguments.
+ /// The size, in characters, of the buffer pointed to by the pszArgs parameter.
+ void GetArguments([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath);
- //[helpstring("Sets the shell link command-line arguments")]
- void SetArguments(
- [MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
+ ///
+ /// Sets the shell link command-line arguments.
+ ///
+ /// The new command-line arguments.
+ void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
- //[propget, helpstring("Retrieves or sets the shell link hot key")]
+ ///
+ /// Retrieves the shell link hot key.
+ ///
+ /// The address of a variable that receives the hot key.
void GetHotkey(out short pwHotkey);
- //[propput, helpstring("Retrieves or sets the shell link hot key")]
+
+ ///
+ /// Sets the shell link hot key.
+ ///
+ /// The new hot key.
void SetHotkey(short pwHotkey);
- //[propget, helpstring("Retrieves or sets the shell link show command")]
+ ///
+ /// Retrieves the shell link show command.
+ ///
+ /// The address of a variable that receives the show command.
void GetShowCmd(out uint piShowCmd);
- //[propput, helpstring("Retrieves or sets the shell link show command")]
+
+ ///
+ /// Sets the shell link show command.
+ ///
+ /// The new show command.
void SetShowCmd(uint piShowCmd);
- //[helpstring("Retrieves the location (path and index) of the shell link icon")]
- void GetIconLocation(
- [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath,
- int cchIconPath,
- out int piIcon);
-
- //[helpstring("Sets the location (path and index) of the shell link icon")]
- void SetIconLocation(
- [MarshalAs(UnmanagedType.LPWStr)] string pszIconPath,
- int iIcon);
-
- //[helpstring("Sets the shell link relative path")]
- void SetRelativePath(
- [MarshalAs(UnmanagedType.LPWStr)] string pszPathRel,
- uint dwReserved);
-
- //[helpstring("Resolves a shell link. The system searches for the shell link object and updates the shell link path and its list of identifiers (if necessary)")]
- void Resolve(
- IntPtr hWnd,
- uint fFlags);
-
- //[helpstring("Sets the shell link path and filename")]
- void SetPath(
- [MarshalAs(UnmanagedType.LPWStr)] string pszFile);
+ ///
+ /// Retrieves the location (path and index) of the shell link icon.
+ ///
+ /// The buffer that receives the icon path.
+ /// The size, in characters, of the buffer pointed to by the pszIconPath parameter.
+ /// The address of a variable that receives the icon index.
+ void GetIconLocation([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon);
+
+ ///
+ /// Sets the location (path and index) of the shell link icon.
+ ///
+ /// The new icon path.
+ /// The new icon index.
+ void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
+
+ ///
+ /// Sets the shell link relative path.
+ ///
+ /// The new relative path.
+ /// Reserved. Must be zero.
+ void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, uint dwReserved);
+
+ ///
+ /// Resolves a shell link. The system searches for the shell link object and updates the shell link path and its list of identifiers (if necessary).
+ ///
+ /// A handle to the window that the system uses as a parent for any dialog boxes that it displays.
+ /// Flags that control the resolution process.
+ void Resolve(IntPtr hWnd, uint fFlags);
+
+ ///
+ /// Sets the shell link path and filename.
+ ///
+ /// The new path and filename.
+ void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
}
#endregion
@@ -353,12 +400,12 @@ private struct FILETIME
}
#endregion
- #region UnManaged Methods
- private class UnManagedMethods
+ #region Unmanaged Methods
+ private class UnmanagedMethods
{
[DllImport("Shell32", CharSet = CharSet.Auto)]
internal extern static int ExtractIconEx([MarshalAs(UnmanagedType.LPTStr)]
- string lpszFile, int nIconIndex, IntPtr[] phIconLarge, IntPtr[] phIconSmall, int nIcons);
+ string lpszFile, int nIconIndex, IntPtr[]? phIconLarge, IntPtr[]? phIconSmall, int nIcons);
[DllImport("user32")]
internal static extern int DestroyIcon(IntPtr hIcon);
@@ -380,10 +427,12 @@ public enum EShellLinkResolveFlags : uint
/// on ME/2000 or above, use the other flags instead.
///
SLR_ANY_MATCH = 0x2,
+
///
/// Call the Microsoft Windows Installer.
///
SLR_INVOKE_MSI = 0x80,
+
///
/// Disable distributed link tracking. By default,
/// distributed link tracking tracks removable media
@@ -393,6 +442,7 @@ public enum EShellLinkResolveFlags : uint
/// SLR_NOLINKINFO disables both types of tracking.
///
SLR_NOLINKINFO = 0x40,
+
///
/// Do not display a dialog box if the link cannot be resolved.
/// When SLR_NO_UI is set, a time-out value that specifies the
@@ -403,23 +453,28 @@ public enum EShellLinkResolveFlags : uint
/// set to the default value of 3,000 milliseconds (3 seconds).
///
SLR_NO_UI = 0x1,
+
///
/// Not documented in SDK. Assume same as SLR_NO_UI but
/// intended for applications without a hWnd.
///
SLR_NO_UI_WITH_MSG_PUMP = 0x101,
+
///
/// Do not update the link information.
///
SLR_NOUPDATE = 0x8,
+
///
/// Do not execute the search heuristics.
///
SLR_NOSEARCH = 0x10,
+
///
/// Do not use distributed link tracking.
///
SLR_NOTRACK = 0x20,
+
///
/// If the link object has changed, update its path and list
/// of identifiers. If SLR_UPDATE is set, you do not need to
@@ -432,7 +487,7 @@ public enum EShellLinkResolveFlags : uint
///
/// Window Show Command enumeration
///
- public enum LinkDisplayMode : uint
+ internal enum LinkDisplayMode : uint
{
///
/// Show the window in its default state
@@ -533,15 +588,15 @@ public string ShortcutFile
/// Gets a System.Drawing.Icon containing the icon for this
/// ShellLink object.
///
- public Icon LargeIcon => GetIcon(true);
+ public Icon? LargeIcon => GetIcon(true);
///
/// Gets a System.Drawing.Icon containing the icon for this
/// ShellLink object.
///
- public Icon SmallIcon => GetIcon(false);
+ public Icon? SmallIcon => GetIcon(false);
- private Icon GetIcon(bool large)
+ private Icon? GetIcon(bool large)
{
StringBuilder iconPath = new StringBuilder(260, 260);
// Get icon index and path:
@@ -572,24 +627,14 @@ private Icon GetIcon(bool large)
IntPtr[] hIconEx = new IntPtr[1] { IntPtr.Zero };
if (large)
{
- _ = UnManagedMethods.ExtractIconEx(
- iconFile,
- iconIndex,
- hIconEx,
- null,
- 1);
+ _ = UnmanagedMethods.ExtractIconEx(iconFile, iconIndex, hIconEx, null, 1);
}
else
{
- _ = UnManagedMethods.ExtractIconEx(
- iconFile,
- iconIndex,
- null,
- hIconEx,
- 1);
+ _ = UnmanagedMethods.ExtractIconEx(iconFile, iconIndex, null, hIconEx, 1);
}
// If success then return as a GDI+ object
- Icon icon = null;
+ Icon? icon = null;
if (hIconEx[0] != IntPtr.Zero)
{
icon = Icon.FromHandle(hIconEx[0]);
@@ -607,35 +652,37 @@ public string IconPath
get
{
StringBuilder iconPath = new StringBuilder(260, 260);
- if (linkA == null)
+ if (linkA == null && linkW != null)
{
linkW.GetIconLocation(iconPath, iconPath.Capacity, out _);
}
- else
+ else if (linkA != null && linkW == null)
{
linkA.GetIconLocation(iconPath, iconPath.Capacity, out _);
}
+ else
+ {
+ throw new InvalidOperationException("Unable to get icon location");
+ }
return iconPath.ToString();
}
set
{
StringBuilder iconPath = new StringBuilder(260, 260);
int iconIndex;
- if (linkA == null)
+ if (linkA == null && linkW != null)
{
linkW.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex);
+ linkW.SetIconLocation(value, iconIndex);
}
- else
+ else if (linkA != null && linkW == null)
{
linkA.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex);
- }
- if (linkA == null)
- {
- linkW.SetIconLocation(value, iconIndex);
+ linkA.SetIconLocation(value, iconIndex);
}
else
{
- linkA.SetIconLocation(value, iconIndex);
+ throw new InvalidOperationException("Unable to get icon location");
}
}
}
@@ -649,33 +696,31 @@ public int IconIndex
{
StringBuilder iconPath = new StringBuilder(260, 260);
int iconIndex;
- if (linkA == null)
+ if (linkA == null && linkW != null)
{
linkW.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex);
}
- else
+ else if (linkA != null && linkW == null)
{
linkA.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex);
}
+ else
+ {
+ throw new InvalidOperationException("Unable to get icon location");
+ }
return iconIndex;
}
set
{
StringBuilder iconPath = new StringBuilder(260, 260);
- if (linkA == null)
+ if (linkA == null && linkW != null)
{
linkW.GetIconLocation(iconPath, iconPath.Capacity, out _);
- }
- else
- {
- linkA.GetIconLocation(iconPath, iconPath.Capacity, out _);
- }
- if (linkA == null)
- {
linkW.SetIconLocation(iconPath.ToString(), value);
}
- else
+ else if (linkA != null && linkW == null)
{
+ linkA.GetIconLocation(iconPath, iconPath.Capacity, out _);
linkA.SetIconLocation(iconPath.ToString(), value);
}
}
@@ -689,28 +734,36 @@ public string Target
get
{
StringBuilder target = new StringBuilder(260, 260);
- if (linkA == null)
+ if (linkA == null && linkW != null)
{
WIN32_FIND_DATAW fd = new WIN32_FIND_DATAW();
linkW.GetPath(target, target.Capacity, ref fd, (uint)EShellLinkGP.SLGP_UNCPRIORITY);
}
- else
+ else if (linkA != null && linkW == null)
{
WIN32_FIND_DATAA fd = new WIN32_FIND_DATAA();
linkA.GetPath(target, target.Capacity, ref fd, (uint)EShellLinkGP.SLGP_UNCPRIORITY);
}
+ else
+ {
+ throw new InvalidOperationException("Unable to get target path");
+ }
return target.ToString();
}
set
{
- if (linkA == null)
+ if (linkA == null && linkW != null)
{
linkW.SetPath(value);
}
- else
+ else if (linkA != null && linkW == null)
{
linkA.SetPath(value);
}
+ else
+ {
+ throw new InvalidOperationException("Unable to set target path");
+ }
}
}
@@ -722,26 +775,34 @@ public string WorkingDirectory
get
{
StringBuilder path = new StringBuilder(260, 260);
- if (linkA == null)
+ if (linkA == null && linkW != null)
{
linkW.GetWorkingDirectory(path, path.Capacity);
}
- else
+ else if (linkA != null && linkW == null)
{
linkA.GetWorkingDirectory(path, path.Capacity);
}
+ else
+ {
+ throw new InvalidOperationException("Unable to get working directory");
+ }
return path.ToString();
}
set
{
- if (linkA == null)
+ if (linkA == null && linkW != null)
{
linkW.SetWorkingDirectory(value);
}
- else
+ else if (linkA != null && linkW == null)
{
linkA.SetWorkingDirectory(value);
}
+ else
+ {
+ throw new InvalidOperationException("Unable to set working directory");
+ }
}
}
@@ -753,26 +814,34 @@ public string Description
get
{
StringBuilder description = new StringBuilder(1024, 1024);
- if (linkA == null)
+ if (linkA == null && linkW != null)
{
linkW.GetDescription(description, description.Capacity);
}
- else
+ else if (linkA != null && linkW == null)
{
linkA.GetDescription(description, description.Capacity);
}
+ else
+ {
+ throw new InvalidOperationException("Unable to get description");
+ }
return description.ToString();
}
set
{
- if (linkA == null)
+ if (linkA == null && linkW != null)
{
linkW.SetDescription(value);
}
- else
+ else if (linkA != null && linkW == null)
{
linkA.SetDescription(value);
}
+ else
+ {
+ throw new InvalidOperationException("Unable to set description");
+ }
}
}
@@ -784,26 +853,34 @@ public string Arguments
get
{
StringBuilder arguments = new StringBuilder(260, 260);
- if (linkA == null)
+ if (linkA == null && linkW != null)
{
linkW.GetArguments(arguments, arguments.Capacity);
}
- else
+ else if (linkA != null && linkW == null)
{
linkA.GetArguments(arguments, arguments.Capacity);
}
+ else
+ {
+ throw new InvalidOperationException("Unable to get arguments");
+ }
return arguments.ToString();
}
set
{
- if (linkA == null)
+ if (linkA == null && linkW != null)
{
linkW.SetArguments(value);
}
- else
+ else if (linkA != null && linkW == null)
{
linkA.SetArguments(value);
}
+ else
+ {
+ throw new InvalidOperationException("Unable to set arguments");
+ }
}
}
@@ -851,35 +928,32 @@ public void Save()
/// Saves the shortcut to the specified file
///
/// The shortcut file (.lnk)
- public void Save(
- string linkFile
- )
+ public void Save(string linkFile)
{
// Save the object to disk
- if (linkA == null)
+ if (linkA == null && linkW != null)
{
((IPersistFile)linkW).Save(linkFile, true);
shortcutFile = linkFile;
}
- else
+ else if (linkA != null && linkW == null)
{
((IPersistFile)linkA).Save(linkFile, true);
shortcutFile = linkFile;
}
+ else
+ {
+ throw new InvalidOperationException("Unable to save shortcut");
+ }
}
///
/// Loads a shortcut from the specified file
///
/// The shortcut file (.lnk) to load
- public void Open(
- string linkFile
- )
+ public void Open(string linkFile)
{
- Open(linkFile,
- IntPtr.Zero,
- (EShellLinkResolveFlags.SLR_ANY_MATCH | EShellLinkResolveFlags.SLR_NO_UI),
- 1);
+ Open(linkFile, IntPtr.Zero, EShellLinkResolveFlags.SLR_ANY_MATCH | EShellLinkResolveFlags.SLR_NO_UI, 1);
}
///
@@ -889,11 +963,7 @@ string linkFile
/// The shortcut file (.lnk) to load
/// The window handle of the application's UI, if any
/// Flags controlling resolution behaviour
- public void Open(
- string linkFile,
- IntPtr hWnd,
- EShellLinkResolveFlags resolveFlags
- )
+ public void Open(string linkFile, IntPtr hWnd, EShellLinkResolveFlags resolveFlags)
{
Open(linkFile, hWnd, resolveFlags, 1);
}
@@ -907,12 +977,7 @@ EShellLinkResolveFlags resolveFlags
/// The window handle of the application's UI, if any
/// Flags controlling resolution behaviour
/// Timeout if SLR_NO_UI is specified, in ms.
- public void Open(
- string linkFile,
- IntPtr hWnd,
- EShellLinkResolveFlags resolveFlags,
- ushort timeOut
- )
+ public void Open(string linkFile, IntPtr hWnd, EShellLinkResolveFlags resolveFlags, ushort timeOut)
{
uint flags;
@@ -930,13 +995,13 @@ ushort timeOut
{
((IPersistFile)linkW).Load(linkFile, 0); //STGM_DIRECT)
linkW.Resolve(hWnd, flags);
- this.shortcutFile = linkFile;
+ shortcutFile = linkFile;
}
else if (linkA != null && linkW == null)
{
((IPersistFile)linkA).Load(linkFile, 0); //STGM_DIRECT)
linkA.Resolve(hWnd, flags);
- this.shortcutFile = linkFile;
+ shortcutFile = linkFile;
}
else
{
diff --git a/Sources/EasyExtensions.Windows/WinApi.cs b/Sources/EasyExtensions.Windows/WinApi.cs
index 3ffa922..d468a1c 100644
--- a/Sources/EasyExtensions.Windows/WinApi.cs
+++ b/Sources/EasyExtensions.Windows/WinApi.cs
@@ -78,5 +78,64 @@ public static bool MoveToRecycleBin(this FileInfo file)
}
return DeleteFileOrFolder(file.FullName);
}
+
+ ///
+ /// Create a shortcut file.
+ ///
+ /// Target file path.
+ /// Link file path.
+ /// Icon file path.
+ /// Working directory path.
+ /// Thrown when target file or icon file not found.
+ /// Thrown when working directory not found.
+ /// Thrown when cannot create link file at specified location.
+ /// Thrown when link file must have .lnk extension.
+ public static void CreateLink(string targetFile, string linkFile, string? iconFile = null, string? workingDirectory = null)
+ {
+ FileInfo fileInfo = new FileInfo(targetFile);
+ if (!fileInfo.Exists)
+ {
+ throw new FileNotFoundException("Target file not found.", targetFile);
+ }
+ if (!linkFile.EndsWith(".lnk", StringComparison.OrdinalIgnoreCase))
+ {
+ throw new FormatException("Link file must have .lnk extension.");
+ }
+ try
+ {
+ File.WriteAllText(linkFile, string.Empty);
+ File.Delete(linkFile);
+ }
+ catch (Exception ex)
+ {
+ throw new IOException("Cannot create link file at specified location.", ex);
+ }
+ if (iconFile != null)
+ {
+ if (!File.Exists(iconFile))
+ {
+ throw new FileNotFoundException("Icon file not found.", iconFile);
+ }
+ }
+ if (workingDirectory != null)
+ {
+ if (!Directory.Exists(workingDirectory))
+ {
+ throw new DirectoryNotFoundException("Working directory not found.");
+ }
+ }
+
+ workingDirectory ??= fileInfo.DirectoryName;
+ iconFile ??= targetFile;
+
+ ShellLink sl = new ShellLink
+ {
+ Target = targetFile,
+ IconPath = iconFile,
+ ShortcutFile = linkFile,
+ WorkingDirectory = workingDirectory
+ };
+ sl.Save();
+ }
}
}