I have successfully developed and deployed a ClickOnce application which registers an associated file extension, for instance *.abc
. When I click on a file named x.abc
or if I type x.abc
from the command prompt, the ClickOnce application starts, and I can retrieve the file through the dedicated API. I can also launch the application programmatically with the following code:
我已成功开发并部署了ClickOnce应用程序,该应用程序注册了相关的文件扩展名,例如* .abc。当我单击名为x.abc的文件或者从命令提示符下键入x.abc时,ClickOnce应用程序启动,我可以通过专用API检索文件。我还可以使用以下代码以编程方式启动应用程序:
System.Diagnostics.Process.Start ("x.abc");
Everything works fine on my Windows Vista 64 bit box.
我的Windows Vista 64位盒上的一切正常。
However, if I try to do exactly the same thing on a Windows 7 (also 64 bit ), I have a very strange problem. Here is what I observe:
但是,如果我尝试在Windows 7(也是64位)上做同样的事情,我有一个非常奇怪的问题。这是我观察到的:
- Manual start of
x.abc
by double-clicking it from the Explorer works. - 通过在资源管理器中双击手动启动x.abc。
- Manual start of
x.abc
from the command prompt works. - 从命令提示符手动启动x.abc。
-
Process.Start("x.abc")
does not start the application; however, the process object returned shows that there was not error and that the ClickOnce application somehow exited immediately. But even aTrace
at the very beginning of the ClickOnce application is never reached. - Process.Start(“x.abc”)不启动应用程序;但是,返回的进程对象显示没有错误,并且ClickOnce应用程序以某种方式立即退出。但是,即使是ClickOnce应用程序一开始的Trace也永远不会到达。
- Stranger yet,
Process.Start("x.bat")
with a filex.bat
containing the single linex.abc
does not start the ClickOnce application either! Samex.bat
started from the Explorer works (of course). - 奇怪的是,带有包含单行x.abc的文件x.bat的Process.Start(“x.bat”)也没有启动ClickOnce应用程序!同样的x.bat从Explorer工作开始(当然)。
Trying to analyse what happens with ProcMon
was not very helpful, as the ClickOnce process of launching an application is very difficult to follow, from my point of view. I observe rundll32
getting to work, but no evidence of any failure.
试图分析ProcMon发生的事情并不是很有帮助,因为从我的观点来看,启动应用程序的ClickOnce过程非常难以遵循。我观察到rundll32开始工作,但没有任何失败的证据。
The program which is doing the Process.Start
is a full trust console application with really nothing fancy.
正在执行Process.Start的程序是一个完全信任的控制台应用程序,实际上没什么特别的。
I can't see what changed with respect to how ClickOnce applications are handled on Windows 7 and why Process.Start
would not do exactly the same as launching the file from Explorer. It's worth to mention that using more advanced versions of the Start
method with ProcessStartInfo
and setting UseShellExecute
to true
did not help either.
我看不出在Windows 7上处理ClickOnce应用程序的方式发生了什么变化,以及为什么Process.Start与从Explorer中启动文件完全不同。值得一提的是,在ProcessStartInfo中使用更高级的Start方法版本并将UseShellExecute设置为true也无济于事。
Starting cmd
with Process.Start
and then trying to launch x.abc
shows exactly the same problem. If I compare the environment settings with a cmd
started manually, I see differences in how ProgramFiles
is defined (the first one points to C:\Program Files (x86)
whereas the second points to C:\Program Files
). The applications started from my .NET application are started on the 32-bit emulation layer (SysWoW64).
使用Process.Start启动cmd然后尝试启动x.abc会显示完全相同的问题。如果我将环境设置与手动启动的cmd进行比较,我会看到ProgramFiles的定义方式不同(第一个指向C:\ Program Files(x86),而第二个指向C:\ Program Files)。从我的.NET应用程序启动的应用程序是在32位仿真层(SysWoW64)上启动的。
I was able to reproduce the launch failure of x.abc
by starting a 32-bit version of the command prompt (that is, %windir%\SysWoW64\cmd.exe
) and then typing x.abc
at the prompt. I have also found an ugly workaround, which is to start a 64-bit command prompt from the 32-bit environment by launching %windir%\Sysnative\cmd.exe /C x.abc
instead of x.abc
.
我能够通过启动32位版本的命令提示符(即%windir%\ SysWoW64 \ cmd.exe)然后在提示符下键入x.abc来重现x.abc的启动失败。我还发现了一个丑陋的解决方法,即通过启动%windir%\ Sysnative \ cmd.exe / C x.abc而不是x.abc从32位环境启动64位命令提示符。
But I'd rather use a clean way of doing it (or have a Microsoft representative tell me that this is indeed an issue with Windows 7 and/or ClickOncce and that it will be fixed soon).
但我宁愿使用干净的方式(或者让微软代表告诉我,这确实是Windows 7和/或ClickOncce的问题,并且很快就会修复)。
4 个解决方案
#1
10
It looks like you've build your application using 'x32' as the target platform which makes Process.Start
spawn a x32-bit process. And as I guess Windows 7 stores file associations for 32-bit and 64-bit applications separately.
看起来您使用'x32'构建应用程序作为目标平台,使Process.Start产生x32位进程。而且我猜Windows 7分别存储32位和64位应用程序的文件关联。
If you don't have COM or unmanaged 32-bit dependencies you could try building your application for 'Any' target platform instead of 'x32'.
如果您没有COM或非托管32位依赖项,则可以尝试为“任何”目标平台而不是“x32”构建应用程序。
I investigated some further and found that ClickOnce installer creates the following open verb for any associated file extension (GUID is unique per application):
我进一步调查了一下,发现ClickOnce安装程序为任何关联的文件扩展名创建了以下开放动词(GUID对于每个应用程序是唯一的):
rundll32.exe dfshim.dll, ShOpenVerbExtension {dce01888-35e8-4df3-af35-cd971f520d8d} %1
Using Process Monitor, I found that the 32-bit version fails to open HKCU\Software\Classes\Wow6432Node\CLSID\{dce01888-35e8-4df3-af35-cd971f520d8d}
registry key. (the 64-bit version successfully opens it at HKCU\Software\Classes\CLSID\{dce01888-35e8-4df3-af35-cd971f520d8d}
.)
使用Process Monitor,我发现32位版本无法打开HKCU \ Software \ Classes \ Wow6432Node \ CLSID \ {dce01888-35e8-4df3-af35-cd971f520d8d}注册表项。 (64位版本在HKCU \ Software \ Classes \ CLSID \ {dce01888-35e8-4df3-af35-cd971f520d8d}成功打开它。)
So for me it is indeed a ClickOnce bug. If I were you, I would use that dirty %WinDir%\system32\cmd.exe /C test.abc
workaround. (Which appears to work -- tried from x32 Task Manager.)
所以对我来说这确实是一个ClickOnce错误。如果我是你,我会使用那个脏的%WinDir%\ system32 \ cmd.exe / C test.abc解决方法。 (这似乎工作 - 尝试从x32任务管理器。)
I've added this issue to the
Microsoft Connect
(update 2013-02-13: that link is rotten).
我已将此问题添加到Microsoft Connect(更新2013-02-13:该链接已损坏)。
#2
0
Have you tried using ShellExecute(); API?
你尝试过使用ShellExecute(); 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);
You could also try the Shell(); function which is a part of framework
你也可以试试Shell();功能是框架的一部分
#3
0
I've come up with a .BAT based solution, which is easy to implement. Say that you want to launch the ClickOnce application associated with *.abc
files, then you simply put a file with the same name, but with the *.bat
extension in the same folder, and then execute the batch file instead. Here is the batch script:
我想出了一个基于.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
This could be implemented directly in the caller of the *.abc
files, but sometimes a batch file helps in the transition...
这可以直接在* .abc文件的调用者中实现,但有时批处理文件有助于转换...
#4
0
That only might start system wide extensions like .bat
or even .txt
, but it can't always launch correct programs through extensions.
这只能启动系统范围的扩展,如.bat甚至.txt,但它不能总是通过扩展启动正确的程序。
Instead try this API or a similar alternative in .NET:
而是在.NET中尝试此API或类似的替代方案:
FindExecutable : shell32.dll Alias : "FindExecutableA" / "FindExecutableW" return type : int parameters : · lpFile Pointer to a null-terminated string specifying a filename. This can be a document or executable file. · lpDirectory Pointer to a null-terminated string specifying the default directory. · lpResult Pointer to a buffer to receive the filename when the function returns. This filename is a null-terminated string specifying the executable file started when an “open” association is run on the file specified in the lpFile parameter.
FindExecutable:shell32.dll别名:“FindExecutableA”/“FindExecutableW”返回类型:int参数:·lpFile指向以null结尾的字符串指定文件名的指针。这可以是文档或可执行文件。 lpDirectory指向以null结尾的字符串的指针,指定默认目录。 lpResult指向缓冲区的指针,用于在函数返回时接收文件名。此文件名是以空字符结尾的字符串,指定在lpFile参数中指定的文件上运行“打开”关联时启动的可执行文件。
This returns an integer bigger than zero if success and the char value will contain a null-terminated string that points to the executable that launches this file extension then you can use it like this
如果成功,则返回大于零的整数,并且char值将包含以空字符结尾的字符串,该字符串指向启动此文件扩展名的可执行文件,然后您可以像这样使用它
System.Diagnostics.Process.Start ("program.exe $:\path\x.abc");
Instead of program.exe
, you'll use the result of the API function, and you'll use the path to the file separated with a space just like a command line.
您将使用API函数的结果而不是program.exe,并且您将使用与空格分隔的文件的路径,就像命令行一样。
As for the application failure, it might indicate that the program needs administrative rights to run correctly. cmd
already got administrative rights, so it can make child applications inherit it, but not windows API. createprocess
allows you to use LPSECURITY attributes which can help in launching this program with the correct privileges.
至于应用程序失败,它可能表明程序需要管理权限才能正确运行。 cmd已经获得了管理权限,因此它可以使子应用程序继承它,但不能继承它。 createprocess允许您使用LPSECURITY属性,这有助于以正确的权限启动此程序。
#1
10
It looks like you've build your application using 'x32' as the target platform which makes Process.Start
spawn a x32-bit process. And as I guess Windows 7 stores file associations for 32-bit and 64-bit applications separately.
看起来您使用'x32'构建应用程序作为目标平台,使Process.Start产生x32位进程。而且我猜Windows 7分别存储32位和64位应用程序的文件关联。
If you don't have COM or unmanaged 32-bit dependencies you could try building your application for 'Any' target platform instead of 'x32'.
如果您没有COM或非托管32位依赖项,则可以尝试为“任何”目标平台而不是“x32”构建应用程序。
I investigated some further and found that ClickOnce installer creates the following open verb for any associated file extension (GUID is unique per application):
我进一步调查了一下,发现ClickOnce安装程序为任何关联的文件扩展名创建了以下开放动词(GUID对于每个应用程序是唯一的):
rundll32.exe dfshim.dll, ShOpenVerbExtension {dce01888-35e8-4df3-af35-cd971f520d8d} %1
Using Process Monitor, I found that the 32-bit version fails to open HKCU\Software\Classes\Wow6432Node\CLSID\{dce01888-35e8-4df3-af35-cd971f520d8d}
registry key. (the 64-bit version successfully opens it at HKCU\Software\Classes\CLSID\{dce01888-35e8-4df3-af35-cd971f520d8d}
.)
使用Process Monitor,我发现32位版本无法打开HKCU \ Software \ Classes \ Wow6432Node \ CLSID \ {dce01888-35e8-4df3-af35-cd971f520d8d}注册表项。 (64位版本在HKCU \ Software \ Classes \ CLSID \ {dce01888-35e8-4df3-af35-cd971f520d8d}成功打开它。)
So for me it is indeed a ClickOnce bug. If I were you, I would use that dirty %WinDir%\system32\cmd.exe /C test.abc
workaround. (Which appears to work -- tried from x32 Task Manager.)
所以对我来说这确实是一个ClickOnce错误。如果我是你,我会使用那个脏的%WinDir%\ system32 \ cmd.exe / C test.abc解决方法。 (这似乎工作 - 尝试从x32任务管理器。)
I've added this issue to the
Microsoft Connect
(update 2013-02-13: that link is rotten).
我已将此问题添加到Microsoft Connect(更新2013-02-13:该链接已损坏)。
#2
0
Have you tried using ShellExecute(); API?
你尝试过使用ShellExecute(); 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);
You could also try the Shell(); function which is a part of framework
你也可以试试Shell();功能是框架的一部分
#3
0
I've come up with a .BAT based solution, which is easy to implement. Say that you want to launch the ClickOnce application associated with *.abc
files, then you simply put a file with the same name, but with the *.bat
extension in the same folder, and then execute the batch file instead. Here is the batch script:
我想出了一个基于.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
This could be implemented directly in the caller of the *.abc
files, but sometimes a batch file helps in the transition...
这可以直接在* .abc文件的调用者中实现,但有时批处理文件有助于转换...
#4
0
That only might start system wide extensions like .bat
or even .txt
, but it can't always launch correct programs through extensions.
这只能启动系统范围的扩展,如.bat甚至.txt,但它不能总是通过扩展启动正确的程序。
Instead try this API or a similar alternative in .NET:
而是在.NET中尝试此API或类似的替代方案:
FindExecutable : shell32.dll Alias : "FindExecutableA" / "FindExecutableW" return type : int parameters : · lpFile Pointer to a null-terminated string specifying a filename. This can be a document or executable file. · lpDirectory Pointer to a null-terminated string specifying the default directory. · lpResult Pointer to a buffer to receive the filename when the function returns. This filename is a null-terminated string specifying the executable file started when an “open” association is run on the file specified in the lpFile parameter.
FindExecutable:shell32.dll别名:“FindExecutableA”/“FindExecutableW”返回类型:int参数:·lpFile指向以null结尾的字符串指定文件名的指针。这可以是文档或可执行文件。 lpDirectory指向以null结尾的字符串的指针,指定默认目录。 lpResult指向缓冲区的指针,用于在函数返回时接收文件名。此文件名是以空字符结尾的字符串,指定在lpFile参数中指定的文件上运行“打开”关联时启动的可执行文件。
This returns an integer bigger than zero if success and the char value will contain a null-terminated string that points to the executable that launches this file extension then you can use it like this
如果成功,则返回大于零的整数,并且char值将包含以空字符结尾的字符串,该字符串指向启动此文件扩展名的可执行文件,然后您可以像这样使用它
System.Diagnostics.Process.Start ("program.exe $:\path\x.abc");
Instead of program.exe
, you'll use the result of the API function, and you'll use the path to the file separated with a space just like a command line.
您将使用API函数的结果而不是program.exe,并且您将使用与空格分隔的文件的路径,就像命令行一样。
As for the application failure, it might indicate that the program needs administrative rights to run correctly. cmd
already got administrative rights, so it can make child applications inherit it, but not windows API. createprocess
allows you to use LPSECURITY attributes which can help in launching this program with the correct privileges.
至于应用程序失败,它可能表明程序需要管理权限才能正确运行。 cmd已经获得了管理权限,因此它可以使子应用程序继承它,但不能继承它。 createprocess允许您使用LPSECURITY属性,这有助于以正确的权限启动此程序。