ClickOnce 应用程序无法通过 Process.Start(“x.abc”) 启动,且 *.abc 与 ClickOnce 应用程序关联
-
19-09-2019 - |
题
例如,我已成功开发并部署了一个 ClickOnce 应用程序,该应用程序注册了关联的文件扩展名 *.abc
. 。当我点击一个名为 x.abc
或者如果我输入 x.abc
从命令提示符处,ClickOnce 应用程序启动,我可以通过专用 API 检索文件。我还可以使用以下代码以编程方式启动应用程序:
System.Diagnostics.Process.Start ("x.abc");
在我的 Windows Vista 64 位机器上一切正常。
但是,如果我尝试在 Windows 7(也是 64 位)上执行完全相同的操作,则会遇到一个非常奇怪的问题。这是我观察到的:
- 手动启动
x.abc
通过从资源管理器中双击它即可。 - 手动启动
x.abc
从命令提示符有效。 Process.Start("x.abc")
不启动应用程序;但是,返回的进程对象显示没有错误,并且 ClickOnce 应用程序以某种方式立即退出。但即使是一个Trace
ClickOnce 应用程序一开始的位置永远不会达到。- 还陌生,
Process.Start("x.bat")
有一个文件x.bat
包含单行x.abc
也不启动 ClickOnce 应用程序!相同的x.bat
从 Explorer 作品开始(当然)。
尝试分析发生了什么 ProcMon
不是很有帮助,因为从我的角度来看,启动应用程序的 ClickOnce 过程非常难以遵循。我观察到 rundll32
开始工作,但没有任何失败的证据。
正在执行该操作的程序 Process.Start
是一个完全信任的控制台应用程序,实际上没有什么花哨的。
我看不出 Windows 7 上处理 ClickOnce 应用程序的方式发生了什么变化以及原因 Process.Start
与从资源管理器启动文件的效果不完全相同。值得一提的是,使用更高级的版本 Start
方法与 ProcessStartInfo
和设置 UseShellExecute
到 true
也没有帮助。
开始 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
在提示符下。我还发现了一个丑陋的解决方法,即通过启动从 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安全 可以帮助以正确的权限启动该程序的属性。