ClickOnce 应用程序无法通过 Process.Start(“x.abc”) 启动,且 *.abc 与 ClickOnce 应用程序关联

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

例如,我已成功开发并部署了一个 ClickOnce 应用程序,该应用程序注册了关联的文件扩展名 *.abc. 。当我点击一个名为 x.abc 或者如果我输入 x.abc 从命令提示符处,ClickOnce 应用程序启动,我可以通过专用 API 检索文件。我还可以使用以下代码以编程方式启动应用程序:

System.Diagnostics.Process.Start ("x.abc");

在我的 Windows Vista 64 位机器上一切正常。

但是,如果我尝试在 Windows 7(也是 64 位)上执行完全相同的操作,则会遇到一个非常奇怪的问题。这是我观察到的:

  1. 手动启动 x.abc 通过从资源管理器中双击它即可。
  2. 手动启动 x.abc 从命令提示符有效。
  3. Process.Start("x.abc") 不启动应用程序;但是,返回的进程对象显示没有错误,并且 ClickOnce 应用程序以某种方式立即退出。但即使是一个 Trace ClickOnce 应用程序一开始的位置永远不会达到。
  4. 还陌生, Process.Start("x.bat") 有一个文件 x.bat 包含单行 x.abc 也不启动 ClickOnce 应用程序!相同的 x.bat 从 Explorer 作品开始(当然)。

尝试分析发生了什么 ProcMon 不是很有帮助,因为从我的角度来看,启动应用程序的 ClickOnce 过程非常难以遵循。我观察到 rundll32 开始工作,但没有任何失败的证据。

正在执行该操作的程序 Process.Start 是一个完全信任的控制台应用程序,实际上没有什么花哨的。

我看不出 Windows 7 上处理 ClickOnce 应用程序的方式发生了什么变化以及原因 Process.Start 与从资源管理器启动文件的效果不完全相同。值得一提的是,使用更高级的版本 Start 方法与 ProcessStartInfo 和设置 UseShellExecutetrue 也没有帮助。

开始 cmdProcess.Start 然后尝试启动 x.abc 显示完全相同的问题。如果我将环境设置与 cmd 手动启动,我看到了方式上的差异 ProgramFiles 被定义(第一个指向 C:\Program Files (x86) 而第二个指向 C:\Program Files)。从我的 .NET 应用程序启动的应用程序在 32 位模拟层 (SysWoW64) 上启动。

我能够重现启动失败 x.abc 通过启动 32 位版本的命令提示符(即 %windir%\SysWoW64\cmd.exe)然后输入 x.abc 在提示符下。我还发现了一个丑陋的解决方法,即通过启动从 32 位环境启动 64 位命令提示符 %windir%\Sysnative\cmd.exe /C x.abc 代替 x.abc.

但我宁愿使用一种干净的方式来执行此操作(或者让 Microsoft 代表告诉我这确实是 Windows 7 和/或 ClickOcce 的问题,并且很快就会修复)。

有帮助吗?

解决方案 3

我提出了一个基于 .BAT 的解决方案,该解决方案很容易实现。假设您要启动与以下内容关联的 ClickOnce 应用程序 *.abc 文件,那么您只需放置一个具有相同名称的文件,但带有 *.bat 扩展名位于同一文件夹中,然后执行批处理文件。这是批处理脚本:

if exist "%windir%\sysnative\cmd.exe" goto :mode64bit

rem For a file named "C:\foo\xyz.bat", this will generate the corresponding
rem "C:\foo\xyz.abc" file, built as the concatenation of the drive (%~d0),
rem the folder (%~p0) and the file name (%~n0), plus ".abc":

"%~d0%~p0%~n0.abc"
goto :end

:mode64bit

rem When running in a 32-bit emulation environment on a real 64-bit system,
rem start the 64-bit version of CMD.EXE, and make if start the ".abc" file
rem for us:

C:\Windows\sysnative\cmd.exe /c "%~d0%~p0%~n0.xgen"

:end

这可以直接在调用者中实现 *.abc 文件,但有时批处理文件有助于过渡......

其他提示

看起来您已经使用“x32”作为目标平台构建了应用程序,这使得 Process.Start 生成一个 x32 位进程。我猜测 Windows 7 会分别存储 32 位和 64 位应用程序的文件关联。

如果您没有 COM 或非托管 32 位依赖项,您可以尝试为“任何”目标平台而不是“x32”构建应用程序。

我进一步调查,发现 ClickOnce 安装程序为任何关联的文件扩展名创建以下打开谓词(GUID 对于每个应用程序都是唯一的):

rundll32.exe dfshim.dll, ShOpenVerbExtension {dce01888-35e8-4df3-af35-cd971f520d8d} %1

使用 过程监控器, ,发现32位版本打不开 HKCU\Software\Classes\Wow6432Node\CLSID\{dce01888-35e8-4df3-af35-cd971f520d8d} 注册表项。(64位版本成功打开它在 HKCU\Software\Classes\CLSID\{dce01888-35e8-4df3-af35-cd971f520d8d}.)

所以对我来说这确实是一个 ClickOnce bug。如果我是你,我会用那个脏东西 %WinDir%\system32\cmd.exe /C test.abc 解决方法。(这似乎有效——从 x32 任务管理器中尝试过。)

我已将此问题添加到 微软连接 (2013年2月13日更新:该链接已损坏)。

你有没有尝试过使用 Shell执行();API?

        [DllImport("Shell32.dll",CharSet=CharSet.Auto)]
    public static extern IntPtr ShellExecute(
        IntPtr hwnd, 
        string lpVerb,
        string lpFile, 
        string lpParameters, 
        string lpDirectory,
        int nShowCmd );

ShellExecute(this.Handle,"open","x.abc","","",3);

您也可以尝试 壳(); 函数是框架的一部分

这只会启动系统范围的扩展,例如 .bat 甚至 .txt, ,但它并不总是能够通过扩展启动正确的程序。

相反,请尝试此 API 或类似的替代方案 。网:

查找可执行文件: shell32.dll 别名: “查找可执行文件A” / “查找可执行文件W” 返回类型: 整数 参数 : · lp文件 指向以 null 结尾的字符串的指针,该字符串指定 文件名。这可以是文档或 可执行文件。 · lp目录 指向以 null 结尾的字符串的指针,该字符串指定 默认目录。 · lp结果 指向缓冲区的指针,当 函数返回。此文件名是 以 null 结尾的字符串,指定 可执行文件在“打开”时启动 关联在文件上运行 在 lpFile 参数中指定。

如果成功,则返回一个大于零的整数,并且 char 值将包含 以空字符结尾的字符串 指向启动此文件扩展名的可执行文件 那么你可以像这样使用它

System.Diagnostics.Process.Start ("program.exe $:\path\x.abc");

代替 program.exe, ,您将使用 API 函数的结果,并且将使用以空格分隔的文件路径,就像命令行一样。

至于应用程序失败,可能表明程序需要管理权限才能正常运行。 cmd 已经获得管理权限,因此可以让子应用程序继承它,但不能让Windows API继承。 createprocess 允许您使用 LP安全 可以帮助以正确的权限启动该程序的属性。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top