From 9931d543653d3a98c085c87faadd5f3a8a75b085 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 19 Dec 2024 00:56:22 -0600 Subject: [PATCH 1/2] Add a helper for resolving link targets Also add missing CloseHandle --- .../Helpers/ShellHelper.cs | 1 + src/ManagedShell.Interop/NativeMethods.Msi.cs | 16 ++++++ .../ShellLinkHelper.cs | 54 +++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 src/ManagedShell.Interop/NativeMethods.Msi.cs diff --git a/src/ManagedShell.Common/Helpers/ShellHelper.cs b/src/ManagedShell.Common/Helpers/ShellHelper.cs index b475e45b..05f85a1b 100644 --- a/src/ManagedShell.Common/Helpers/ShellHelper.cs +++ b/src/ManagedShell.Common/Helpers/ShellHelper.cs @@ -403,6 +403,7 @@ public static string GetAppUserModelIdForHandle(IntPtr hWnd) StringBuilder outAumid = new StringBuilder((int)len); GetApplicationUserModelId(hProcess, ref len, outAumid); + CloseHandle((int)hProcess); if (outAumid.Length > 0) { diff --git a/src/ManagedShell.Interop/NativeMethods.Msi.cs b/src/ManagedShell.Interop/NativeMethods.Msi.cs new file mode 100644 index 00000000..5876a37c --- /dev/null +++ b/src/ManagedShell.Interop/NativeMethods.Msi.cs @@ -0,0 +1,16 @@ +using System.Runtime.InteropServices; +using System.Text; + +namespace ManagedShell.Interop +{ + public partial class NativeMethods + { + const string Msi_DllName = "msi.dll"; + + [DllImport(Msi_DllName, CharSet = CharSet.Unicode)] + public static extern uint MsiGetShortcutTarget(string szShortcutTarget, [Out] StringBuilder szProductCode, [Out] StringBuilder szFeatureId, [Out] StringBuilder szComponentCode); + + [DllImport(Msi_DllName, CharSet = CharSet.Unicode)] + public static extern int MsiGetComponentPath(string szProduct, string szComponent, [Out] StringBuilder lpPathBuf, [In, Out] ref int pcchBuf); + } +} diff --git a/src/ManagedShell.ShellFolders/ShellLinkHelper.cs b/src/ManagedShell.ShellFolders/ShellLinkHelper.cs index 75ad4a0a..8e9b4277 100644 --- a/src/ManagedShell.ShellFolders/ShellLinkHelper.cs +++ b/src/ManagedShell.ShellFolders/ShellLinkHelper.cs @@ -1,7 +1,9 @@ using System; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; +using System.Text; using ManagedShell.Common.Logging; +using ManagedShell.Interop; using ManagedShell.ShellFolders.Enums; using ManagedShell.ShellFolders.Interfaces; @@ -74,5 +76,57 @@ public static IShellLink Load(IntPtr userInputHwnd, string existingLinkPath) return shellLink; } + + public static string GetLinkTarget(IntPtr userInputHwnd, string filePath) + { + IShellLink link = Load(userInputHwnd, filePath); + string target = ""; + + // First, query Windows Installer to see if this is an installed application shortcut + StringBuilder product = new StringBuilder(39); + StringBuilder feature = new StringBuilder(39); + StringBuilder component = new StringBuilder(39); + + uint result = NativeMethods.MsiGetShortcutTarget(filePath, product, feature, component); + + if (result == 0) + { + // This is a Windows Installer shortcut + int pathLength = 1024; + StringBuilder path = new StringBuilder(pathLength); + int installState = NativeMethods.MsiGetComponentPath(product.ToString(), component.ToString(), path, ref pathLength); + if (installState == 1) + { + // Locally installed application + target = path.ToString(); + } + } + + if (string.IsNullOrEmpty(target)) + { + // Check for an associated identifier list to get an IShellItem object from + IntPtr pidl = IntPtr.Zero; + link.GetIDList(out pidl); + + if (pidl != IntPtr.Zero) + { + IShellItem _shellItem; + Interop.SHCreateItemFromIDList(pidl, typeof(IShellItem).GUID, out _shellItem); + ShellItem item = new ShellItem(_shellItem); + target = item.Path; + string aumid = item.GetAumid(); + } + } + + if (string.IsNullOrEmpty(target)) + { + // Get the shortcut path as a last resort + StringBuilder builder = new StringBuilder(260); + link.GetPath(builder, 260, out Structs.WIN32_FIND_DATA pfd, SLGP_FLAGS.SLGP_RAWPATH); + target = builder.ToString(); + } + + return target; + } } } From 349d748c6be84822b67feeed4cf20342e809daee Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Fri, 27 Dec 2024 00:28:42 -0600 Subject: [PATCH 2/2] Add error handling --- .../ShellLinkHelper.cs | 76 ++++++++++++------- 1 file changed, 48 insertions(+), 28 deletions(-) diff --git a/src/ManagedShell.ShellFolders/ShellLinkHelper.cs b/src/ManagedShell.ShellFolders/ShellLinkHelper.cs index 8e9b4277..c208e439 100644 --- a/src/ManagedShell.ShellFolders/ShellLinkHelper.cs +++ b/src/ManagedShell.ShellFolders/ShellLinkHelper.cs @@ -82,48 +82,68 @@ public static string GetLinkTarget(IntPtr userInputHwnd, string filePath) IShellLink link = Load(userInputHwnd, filePath); string target = ""; - // First, query Windows Installer to see if this is an installed application shortcut - StringBuilder product = new StringBuilder(39); - StringBuilder feature = new StringBuilder(39); - StringBuilder component = new StringBuilder(39); + try + { + // First, query Windows Installer to see if this is an installed application shortcut + StringBuilder product = new StringBuilder(39); + StringBuilder feature = new StringBuilder(39); + StringBuilder component = new StringBuilder(39); - uint result = NativeMethods.MsiGetShortcutTarget(filePath, product, feature, component); + uint result = NativeMethods.MsiGetShortcutTarget(filePath, product, feature, component); - if (result == 0) - { - // This is a Windows Installer shortcut - int pathLength = 1024; - StringBuilder path = new StringBuilder(pathLength); - int installState = NativeMethods.MsiGetComponentPath(product.ToString(), component.ToString(), path, ref pathLength); - if (installState == 1) + if (result == 0) { - // Locally installed application - target = path.ToString(); + // This is a Windows Installer shortcut + int pathLength = 1024; + StringBuilder path = new StringBuilder(pathLength); + int installState = NativeMethods.MsiGetComponentPath(product.ToString(), component.ToString(), path, ref pathLength); + if (installState == 1) + { + // Locally installed application + target = path.ToString(); + } } } + catch (Exception e) + { + ShellLogger.Error($"ShellLinkHelper: Unable to query Windows Installer target for {filePath}", e); + } if (string.IsNullOrEmpty(target)) { - // Check for an associated identifier list to get an IShellItem object from - IntPtr pidl = IntPtr.Zero; - link.GetIDList(out pidl); - - if (pidl != IntPtr.Zero) + try { - IShellItem _shellItem; - Interop.SHCreateItemFromIDList(pidl, typeof(IShellItem).GUID, out _shellItem); - ShellItem item = new ShellItem(_shellItem); - target = item.Path; - string aumid = item.GetAumid(); + // Check for an associated identifier list to get an IShellItem object from + IntPtr pidl = IntPtr.Zero; + link.GetIDList(out pidl); + + if (pidl != IntPtr.Zero) + { + IShellItem _shellItem; + Interop.SHCreateItemFromIDList(pidl, typeof(IShellItem).GUID, out _shellItem); + ShellItem item = new ShellItem(_shellItem); + target = item.Path; + } + } + catch (Exception e) + { + ShellLogger.Error($"ShellLinkHelper: Unable to get ID list for {filePath}", e); } } if (string.IsNullOrEmpty(target)) { - // Get the shortcut path as a last resort - StringBuilder builder = new StringBuilder(260); - link.GetPath(builder, 260, out Structs.WIN32_FIND_DATA pfd, SLGP_FLAGS.SLGP_RAWPATH); - target = builder.ToString(); + try + { + // Get the shortcut path as a last resort + StringBuilder builder = new StringBuilder(260); + link.GetPath(builder, 260, out Structs.WIN32_FIND_DATA pfd, SLGP_FLAGS.SLGP_RAWPATH); + target = builder.ToString(); + } + catch (Exception e) + { + ShellLogger.Error($"ShellLinkHelper: Unable to get path for {filePath}", e); + } } return target;