18

我已经成功开发并部署了一个 ClickOnce 应用程序,它注册了一个关联的文件扩展名,例如*.abc. 当我单击一个名为的文件x.abcx.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")的文件也不会启动 ClickOnce 应用程序!同样从 Explorer 作品开始(当然)。x.batx.abcx.bat

尝试分析发生的情况ProcMon并不是很有帮助,因为在我看来,启动应用程序的 ClickOnce 过程很难遵循。我观察rundll32开始工作,但没有任何失败的证据。

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

我看不出在 Windows 7 上如何处理 ClickOnce 应用程序方面发生了什么变化,以及为什么Process.Start不与从资源管理器启动文件完全相同。值得一提的是,使用更高级版本的Start方法 withProcessStartInfo和设置UseShellExecutetotrue也无济于事。

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

我能够x.abc通过启动 32 位版本的命令提示符(即%windir%\SysWoW64\cmd.exe)然后x.abc在提示符处键入来重现启动失败。我还发现了一个丑陋的解决方法,即通过启动%windir%\Sysnative\cmd.exe /C x.abc而不是x.abc.

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

4

4 回答 4

10

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

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

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

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

使用Process Monitor,我发现 32 位版本无法打开HKCU\Software\Classes\Wow6432Node\CLSID\{dce01888-35e8-4df3-af35-cd971f520d8d}注册表项。(64位版本成功打开在HKCU\Software\Classes\CLSID\{dce01888-35e8-4df3-af35-cd971f520d8d}.)

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

我已将此问题添加到Microsoft Connect(2013 年 2 月 13 日更新:该链接已失效)。

于 2009-12-14T15:04:42.603 回答
0

我提出了一个基于 .BAT 的解决方案,该解决方案易于实施。假设您要启动与*.abc文件关联的 ClickOnce 应用程序,那么您只需将具有相同名称但具有*.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文件的调用者中实现,但有时批处理文件有助于过渡......

于 2011-02-09T13:44:35.167 回答
0

您是否尝试过使用ShellExecute();应用程序接口?

        [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);

你也可以试试Shell(); 作为框架一部分的功能

于 2009-12-17T20:49:26.907 回答
0

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

而是尝试使用此 API 或.NET中的类似替代方法:

FindExecutable:shell32.dll 别名:“FindExecutableA” / “FindExecutableW” 返回类型:int 参数: lpFile 指向指定文件名的以空字符结尾的字符串的指针这可以是文档或可执行文件。 · lpDirectory指向指定默认目录的空终止字符串的指针。 · lpResult指向缓冲区的指针,当函数返回时接收文件名。此文件名是一个以空字符结尾的字符串,指定在 lpFile 参数中指定的文件上运行“打开”关联时启动的可执行文件。

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

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

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

至于应用程序失败,它可能表明程序需要管理权限才能正确运行。cmd已经获得了管理权限,所以它可以让子应用程序继承它,但不能让 windows API 继承。createprocess允许您使用LPSECURITY属性,这有助于以正确的权限启动该程序。

于 2009-12-13T17:10:33.137 回答