【问题标题】:Unable to launch shortcut (lnk) files from 32-bit C# application when the full path resolves to a 64-bit directory当完整路径解析为 64 位目录时,无法从 32 位 C# 应用程序启动快捷方式 (lnk) 文件
【发布时间】:2013-10-31 15:37:25
【问题描述】:

我正在尝试从 C# 应用程序启动“开始”菜单中的程序,“开始”菜单中的几乎所有项目都是快捷方式 (lnk) 文件。使用 Process.Start 启动这些文件时,如果 lnk 文件的完整路径指向 C:\Program Files 目录,我发现“系统找不到指定的路径”错误。我在 Windows 中使用 File System Redirection 进行了一些研究,所以我尝试禁用它,但我仍然遇到同样的错误:

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

这将返回“系统找不到指定的路径。”但是,如果我从“开始”>“运行”对话框启动 c:\splitter.lnk,程序运行得很好。您可以在任何 64 位计算机上重现此问题,方法是为任何 64 位应用程序创建快捷方式,将其放在 C 驱动器上,然后尝试使用上面的代码运行它。

有没有更好的方法来启动 .lnk 文件来避免这个问题?还是我没有正确禁用文件重定向?

编辑:我还尝试将 UseShellExecute 设置为 true 以让操作系统运行文件,但这仍然失败,这很有趣,因为从“开始”>“运行”对话框运行相同的路径就可以了:

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

编辑 2:我认为与其尝试直接启动 LNK 文件,不如为其获取目标,然后运行目标。我尝试使用How to resolve a .lnk in c#How to follow a .lnk file programmatically,但两种方法都返回完整路径为 C:\Program Files (x86)\Splitter.exe 而不是 C:\Program Files\Splitter.exe 的实际路径。

也许我可以使用上述方法之一来获取 LNK 文件的目标。然后我可以查看目标是否包含 Program Files (x86)。如果是,请将其替换为 Program Files 并检查该文件是否存在。如果它存在于 Program Files 中,请运行它。如果没有,请从 Program Files (x86) 位置运行该文件。这将是一个混乱的解决方法,但我不知道此时还能尝试什么。任何建议将不胜感激。

【问题讨论】:

  • 要求作为 32 位进程运行并且想要启动链接仅可用于 64 位进程的程序是您不希望遇到的问题想必须解决。调用 Wow64DisableWow64FsRedirection() 在 .NET 程序中非常很麻烦,您不知道 DLL 何时被抖动加载。构建到 AnyCPU 应该始终是您的首选解决方案。
  • 我正在构建 AnyCPU,但仍然遇到错误。
  • 好吧,最好假设消息确实准确并且您实际上使用了错误的路径。您的示例代码当然没有帮助,“c:\\splitter.lnk”根本不符合您关于“c:\\program files”的问题。并且“开始”菜单中的快捷方式不会保存在这些目录中,它们位于 AppData 子目录中。
  • C:\\splitter.lnk 是指向“C:\Program Files\Splitter.exe”的快捷方式。如果我从 C# 应用程序运行 C:\\splitter.lnk,它会失败。如果我从“开始”>“运行”对话框运行它,则它是成功的。路径没有错。

标签: c# windows winforms process.start


【解决方案1】:

我能够通过使用 Sam Saffron 在How to resolve a .lnk in c# 的示例脚本来解决此问题。我将 ResolveShortcut 函数修改为以下内容:

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;
}

如果有人知道更好的方法来做到这一点,我愿意接受想法。否则我将使用此方法并接受此解决方法作为答案。

【讨论】:

    猜你喜欢
    • 2011-10-30
    • 1970-01-01
    • 1970-01-01
    • 2011-05-23
    • 2012-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-27
    相关资源
    最近更新 更多