【问题标题】:Clickonce program will not start when launched from shell_execute从 shell_execute 启动时,Clickonce 程序不会启动
【发布时间】:2013-11-08 15:51:08
【问题描述】:

我有一个非常旧的程序,我无法控制。它使用它的默认应用程序启动一个文件类型,如下所示(我无法修改此代码):

LET Err (SHELL_EXECUTE 'open' (FIX_MESG '"{1}"' File_name) '' '')

^^上述代码有效,只要该文件类型不与 ClickOnce 程序相关联。

旧程序是 32 位,操作系统是 Windows 7 64 位。我可以将我的 clickonce 程序编译为任何程序,但似乎没有一个有效。 (我试过 x86、x64 和 anyCPU)

如何让 32 位程序使用 shell 执行在 64 位操作系统上启动 ClickOnce 程序?

更多详情: 这是一个可重现的错误。构建 2 个程序。程序 1 是 Clickonce 程序。将其与任何文件类型相关联。程序 2 将执行一个 shell 执行命令来打开 clickonce 程序关联的任何文件类型。如果你将程序 2 编译为 x86,它会给你一个成功的响应,但什么也不做。

控制台shell执行程序的测试代码:

private static void Main()
{
    int value = ShellExecuteA(IntPtr.Zero, "open", @"C:\Users\bsee\Desktop\testfile.ecn2", "", "", 0);
        if (value > 32)
            MessageBox.Show("Clickonce reports success. But did it actually start?");
}

[DllImport("Shell32.dll")]
public static extern int ShellExecuteA(IntPtr hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirecotry, int nShowCmd);

【问题讨论】:

  • 我看不出这是一个 C# 问题。
  • '似乎没有工作' - 是否有错误消息?你能看到任何从任务管理器开始的东西吗?
  • @Rikalous 没有错误消息。事实上,shell_execute 在测试时返回一个超过 32 的值,这在技术上意味着成功。
  • @OndrejJanacek 可能不是。 clickonce 应用程序是 C#。如果有任何可能是特定于语言的错误,我想我会探索这种可能性。
  • @Rikalous 线程中接受的答案建议了一个调用实际文件的批处理文件。这不会修改现有程序。是否有什么原因导致此解决方案对您不成功?

标签: c# clickonce shellexecute


【解决方案1】:

这是一个很常见的问题,很多谷歌点击但很少有解决方案。您的代码 sn-p 存在缺陷,无法诊断问题,当然也是您必须与之互操作的应用程序中的问题。它会很好地启动文件关联,问题是它无法完成这项工作。您的代码 sn-p 无法诊断此问题,您必须改用 Process 类并在流程完成时获取 ExitCode。它不会是 0。

让我们来谈谈哪里出了问题。 ClickOnce 会为您创建文件关联,并将为文件扩展名编写“打开”动词,如下所示:

 rundll32.exe dfshim.dll, ShOpenVerbExtension {guid} %1

还为 {guid} 写入另一个注册表项,写入HKCU\Software\Classes\CLSID\{guid}。 dfshim.dll 的 ShOpenVerbExtension() 函数将在哪里寻找 {guid}。此密钥在 64 位操作系统上是特殊的,有两个版本。 registry redirector 将重新映射请求以打开 32 位应用程序的密钥,而是转到 Software\Wow6432Node。这是 64 位版本 Windows 中的一种基本机制,可防止 32 位应用程序意外加载 64 位组件时出现问题。对 COM 尤其重要。

还有两个版本的rundll32.exe。一个在 c:\windows\system32,64 位版本,另一个在 c:\windows\syswow64,32 位版本。该 32 位版本将看到注册表项的重定向视图。使用哪个由另一个重定向决定,由文件重定向器实现。它会自动将 32 位应用程序对 c:\windows\system32 中的文件发出的请求重新映射到 c:\windows\syswow64。另一个重要的对策是阻止 32 位应用程序在意外加载 64 位 Windows DLL 时失败。

也许您在这里看到了问题,问题在于您需要与之互操作的这个应用程序是一个 32 位应用程序。因此,它将始终从 c:\windows\syswow64 运行 32 位版本的 rundll32.exe。 dfshim.dll 现在最终会在注册表的 System\Wow6432Node 部分中查找,并且不会在那里找到 {guid}。重大失败鲸鱼,它不显示任何类型的错误消息,如果应用程序也不检查 ExitCode,则根本没有诊断。

值得注意的是,您可以轻松地使您的测试程序成功。 Project + Properties,Build 选项卡,将 Platform Target 设置更改为 AnyCPU。在 VS2012 及更高版本上关闭“首选 32 位”选项。您的 ShellExecute() 调用现在将使用 c:\windows\system32\rundll32.exe 程序,并且 dfshim 可以轻松检索 {guid}。

您还可以轻松修复它的 32 位版本。只需使用 Regedit.exe 编辑文件关联的 open 动词并将其更改为%windir%\sysnative\rundll32.exe。名称的“sysnative”部分是文件重定向器理解的特殊名称,将请求映射到 system32,因此您将始终获得 64 位版本的程序。

ClickOnce 团队也可以修复它,并且根据连接文章已经这样做了,他们可能最终写了 both CLSID\{guid} 键。该修复不包含在 .NET 4.0 中,不确定它是否包含在 4.5 中。编辑:确实如此。

许多可能的修复方法,核心问题是您没有足够的控制权。您当然无法让该应用程序在 64 位模式下运行,您通常无法确保目标计算机具有 .NET 的更新版本,您无法更改 ClickOnce 安装程序写入注册表项的内容。

只剩下两个选择:不要使用 ClickOnce 或要求客户自己编辑注册表项。您可以制作一个 .reg 文件,以尽量减少出错的可能性。

【讨论】:

  • 这正是我正在寻找的详细程度。谢谢!我在测试计算机上安装了 .net 4.5,它完全按预期工作。
  • 很好的答案。遇到了类似的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-11-24
  • 2012-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多