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..c208e439 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,77 @@ 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 = ""; + + 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); + + 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(); + } + } + } + catch (Exception e) + { + ShellLogger.Error($"ShellLinkHelper: Unable to query Windows Installer target for {filePath}", e); + } + + if (string.IsNullOrEmpty(target)) + { + try + { + // 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)) + { + 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; + } } }