Unable to launch shortcut (lnk) files from 32-bit C# application when the full path resolves to a 64-bit directory

StackOverflow https://stackoverflow.com/questions/19523419

Вопрос

I'm trying to launch programs in the Start Menu from a C# application, and nearly all of the items in the Start Menu are shortcut (lnk) files. When using Process.Start to launch these files, I found that I was getting "The system cannot find the path specified" error if the full path of the lnk file pointed to the C:\Program Files directory. I did some research with File System Redirection in Windows, so I tried disabling it, but I'm still getting the same error:

// disable file system redirection:
IntPtr ptr = new IntPtr();
bool isWow64FsRedirectionDisabled = Wow64DisableWow64FsRedirection(ref ptr);
// run the file:
System.Diagnostics.Process.Start("c:\\splitter.lnk");

This returns "The system cannot find the path specified." However, if I launch c:\splitter.lnk from the Start > Run dialog box, the program runs just fine. You can reproduce this issue on any 64-bit machine by creating a shortcut for any 64-bit app, placing it on the C drive, and attempting to run it using the code above.

Is there a better way to launch .lnk files to avoid this problem? Or am I not disabling file redirection properly?

EDIT: I also tried setting UseShellExecute to true to have the operating system run the file, but that still fails, which is interesting because running the same path from the Start > Run dialog box works just fine:

Process process = new Process();
process.StartInfo.UseShellExecute = true;
process.StartInfo.FileName = "c:\\splitter.lnk";
process.Start();

EDIT 2: I figured that instead of trying to launch the LNK file directly, I would get the target for it, and then run the target. I tried using How to resolve a .lnk in c# and How to follow a .lnk file programmatically, but both methods return the full path as C:\Program Files (x86)\Splitter.exe instead of the actual path of C:\Program Files\Splitter.exe.

Perhaps I can use one of the above methods to get the target of the LNK file. Then I can see if the target contains Program Files (x86). If it does, replace it with Program Files and check if the file exists. If it exists in Program Files, run that. If not, run the file from the Program Files (x86) location. This would be a messy workaround, but I don't know what else to try at this point. Any suggestions would be appreciated.

Это было полезно?

Решение

I was able to provide a workaround for this issue by using Sam Saffron's example script at How to resolve a .lnk in c#. I modified the ResolveShortcut function to the following:

public static string ResolveShortcut(string filename)
{
    // this gets the full path from a shortcut (.lnk file).
    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);
    string final_string = sb.ToString();
    if (final_string.Length == 0)
        final_string = filename;
    // If the the shortcut's target resolves to the Program Files or System32 directory, and the user is on a
    // 64-bit machine, the final string may actually point to C:\Program Files (x86) or C:\Windows\SYSWOW64.
    // This is due to File System Redirection in Windows -- http://msdn.microsoft.com/en-us/library/aa365743%28VS.85%29.aspx.
    // Unfortunately the solution there doesn't appear to work for 32-bit apps on 64-bit machines.
    // We will provide a workaround here:
    string new_path = Validate_Shortcut_Path(final_string, "SysWOW64", "System32");
    if (File.Exists(new_path) == true && File.Exists(final_string) == false)
    {
        // the file is actually stored in System32 instead of SysWOW64. Let's update it.
        final_string = new_path;
    }
    new_path = Validate_Shortcut_Path(final_string, "Program Files (x86)", "Program Files");
    if (File.Exists(new_path) == true && File.Exists(final_string) == false)
    {
        // the file is actually stored in Program Files instead of Program Files (x86). Let's update it.
        final_string = new_path;
    }
    // the lnk may incorrectly resolve to the C:\Windows\Installer directory. Check for this.
    if (final_string.ToLower().IndexOf("windows\\installer") > -1)
        final_string = filename;
    if (File.Exists(final_string))
        return final_string;
    else
        return filename;
    }

public static string Validate_Shortcut_Path(string final_string, string find_what, string replace_with)
{
    string final_string_lower = final_string.ToLower();
    string find_what_lower = find_what.ToLower();
    int find_value = final_string_lower.IndexOf(find_what_lower);
    if (find_value > -1)
    {
        // the shortcut resolved to the find_what directory, which can be SysWOW64 or Program Files (x86), 
        // but this may not be correct. Let's check by replacing it with another value.
        string new_string = final_string.Substring(0, find_value) + replace_with + final_string.Substring(find_value + find_what.Length);
        if (File.Exists(new_string) == true && File.Exists(final_string) == false)
        {
            // the file is actually stored at a different location. Let's update it.
            final_string = new_string;
        }
    }
    return final_string;
}

If anyone is aware of a better way to do this, I am open to ideas. Otherwise I will use this method and accept this workaround as the answer.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top