1

In a C# program I want to be able to identify the file locations of LNK files (the actual file location, not the LNK location). But sometimes ShellLink is returning an old location for a file. For instance it is returning "C:\Program Files (x86)\XXX\xxx.exe" instead of "C:\Program Files\XXX\xxx.exe" (the actual text in the LNK file). I thought that the old location might be cached in the registry but could not find that string. Any ideas on how to ensure that ShellLink identifies the correct location or to fix this particular "bad" link? I have re-created the link and rebooted but that does not help.

/// <summary>Given a LNK file, return the file that it points to.</summary>
public static string ResolveShortcut(string filename)
{
    ShellLink link = new ShellLink();
    ((IPersistFile)link).Load(filename, STGM_READ);
    StringBuilder sb = new StringBuilder(MAX_PATH);
    WIN32_FIND_DATAW data = new WIN32_FIND_DATAW();
    ((IShellLinkW)link).GetPath(sb, sb.Capacity, out data, 0);
    if (sb.Length == 0 && !filename.EndsWith(".LNK", StringComparison.OrdinalIgnoreCase)) sb.Append(filename);
    return sb.ToString();
}
Mick Bruno
  • 1,373
  • 15
  • 13
  • Can you post the code you are using to pull the file locations from ShellLink? – Alexander Ryan Baggett Jun 21 '19 at 16:22
  • If the path to `Program Files` is referenced with the environment variable (`%programfiles%`), the expanded variable (as in `Environment.ExpandEnvironmentVariables()`), returns the path based on the executable's bitness. I.e., if you target x86 or you have `Prefer 32-bit` selected, the enviroment variable will return `[Drive]:\Program Files (x86)`. – Jimi Jun 21 '19 at 16:23
  • The path in the LNK does not use any environment variables. It is essentially what I specified in the question. – Mick Bruno Jun 21 '19 at 16:29
  • Verify the *physical* content with something like [this](https://blez.wordpress.com/2013/02/18/get-file-shortcuts-target-with-c/). – Jimi Jun 21 '19 at 17:18
  • Or the Shell object, as in [Get target of shortcut folder](https://stackoverflow.com/questions/9414152/get-target-of-shortcut-folder) or [Identifying and Resolving Shortcuts/Links of files and folders](https://code.msdn.microsoft.com/windowsdesktop/Identifying-and-Resolving-ca0dfce8) – Jimi Jun 21 '19 at 17:28

2 Answers2

0

In the comments Jimi gave one part of the answer, which was this link which dives into the contents of the LNK: https://blez.wordpress.com/2013/02/18/get-file-shortcuts-target-with-c/

However, this seems a bit dangerous, hoping that Microsoft will never modify this in future Windows updates.

Also, I would love to hear an explanation of why the "official" way of doing this is failing. How/why do these .NET classes report an old location for a link rather than the location that is actually in the link file? The other solution Jimi pointed to failed in the same way mine did, using the Shell, Folder, FolderItem, and ShellLinkObject classes.

I would give Jimi an up vote if he had submitted an answer instead of a comment. Instead, I will just say muchas gracias, domo arigato, and many thanks!

Mick Bruno
  • 1,373
  • 15
  • 13
  • I can't get it to fail. How did you change the Target path of the modified link? Also, try to pass `SLGP_RAWPATH = 0x04` or `SLGP_UNCPRIORITY = 0x08` (I know, the Docs say *Unsupported; do not use*, but you can use it anyway) instead of `0` to `[IShellLinkW].GetPath()` and instead `0` to `[IPersistFile].Load()` – Jimi Jun 22 '19 at 10:16
  • Also, try calling `SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0, 0)`, followed by `IntPtr envPtr = Marshal.StringToHGlobalUni("Environment"); SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, envPtr, SMTO_ABORTIFHUNG, 5000)` to see whether the cache is rebuilt. – Jimi Jun 22 '19 at 10:34
0

I get the correct result with WMI and Win32_ShortcutFile

A test with a given shortcut on Windows 10, VS 2015, part of code generated by WMI Code Creator =>

// Add reference to System.Management
// using System.Management;
string sLinkPath = "C:\\Users\\Christian\\Desktop";
string sLinkName = "MpCmdRun.lnk";
string sRequest = "SELECT * FROM Win32_ShortcutFile where Name=\"" + sLinkPath + "\\" + sLinkName + "\"";
sRequest = sRequest.Replace("\\", "\\\\");
try
{
    ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\CIMV2", sRequest);

    foreach (ManagementObject queryObj in searcher.Get())
    {
        Console.WriteLine("Target: {0}", queryObj["Target"]);
        // Target: C:\Program Files\Windows Defender\MpCmdRun.exe
    }
}
catch (ManagementException me)
{
    System.Windows.Forms.MessageBox.Show("An error occurred while querying for WMI data: " + me.Message);
}
Castorix
  • 1,465
  • 1
  • 9
  • 11