views:

1321

answers:

3

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:

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

Everything works fine on my Vista x64 box.

However, if I try to do exactly the same thing on a Windows 7 (also x64), I have a very strange problem. Here is what I observe:

  1. Manual start of x.abc by double-clicking it from the Explorer works.
  2. Manual start of x.abc from the command prompt works.
  3. 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 a Trace at the very beginning of the ClickOnce application is never reached.
  4. Stranger yet, Process.Start("x.bat") with a file x.bat containing the single line x.abc does not start the ClickOnce application either! Same x.bat started from the Explorer works (of course).

Trying to analyse what happens with ProcMon was not very helpful, as the ClickOnce process of launching an app is very difficult to follow, from my point of view. I observe rundll32 getting to work, but no evidence of any failure.

The program which is doing the Process.Start is a full trust console application with really nothing fancy.

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.

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).

I was able to reproduce the launch failure of x.abc by starting a 32-bit version of the command prompt (i.e. %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.

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).

A: 

that only might start system wide extensions like .bat or even .txt but it can't always launch correct programs through extensions

instead try this API or similar alternative in .net

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.

this returns integer bigger than zero if success and the char value wwill contain a null terminated string that points to the executable that launches this file extension
then you can use it like this

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

as for the application failure it might indicate that the program needs administrative rights to run correctly .
cmd already got admin right so it can make child applications inhert it but not windows API
createprocess allows you to use lpsecurity attributes which can help in launching this program with the correct privileges

VirusEcks
Nice try, but your explanation of why it works on CMD is not correct. All my CMD instances are non privileged. The ones that fail to start the ClickOnce application are running in 32-bit, the ones that succeed are running in 64-bit. I will, however, give your suggestion of how to start the ClickOnce app a try, as soon as I can access the machine which exhibits the failure.
Pierre
Sorry, but FindExecutable does not work for ClickOnce file associations. The following sample code works great for other file extensions, though:class Program{ static void Main(string[] args) { var result = new StringBuilder(512); var code = Program.FindExecutable(@"C:\x.abc", null, result); ... } [DllImport("shell32.dll")] static extern IntPtr FindExecutable(string lpFile, string lpDirectory, StringBuilder lpResult);}Dead end here too...
Pierre
+5  A: 

It looks like you've build your application using 'x32' as target platform what makes Process.Start to spawn a x32-bit process. And as I guess Windows 7 stores file associations for x32 and x64 applications separately.

If you don't have COM or unmanaged x32-bit dependencies you could try building your application for 'Any' target platform instead of 'x32'.

UPDATE:

I investigated some further and found that ClickOnce installer creates the following open verb for any associated file extension (GUID is unique per application):

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

Using Process Monitor I found that x32 bit version fails to open HKCU\Software\Classes\Wow6432Node\CLSID\{dce01888-35e8-4df3-af35-cd971f520d8d} registry key. (x64 version successfully opens it at HKCU\Software\Classes\CLSID\{dce01888-35e8-4df3-af35-cd971f520d8d}.)

So as for me it is ClickOnce bug indeed. 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.)

UPDATE 2: I've added this issue to the Microsoft Connect.

Regent
Thanks for the tip. No, I cannot compile my application as a 64-bit application; it would have solved my problem. Now, you are suggesting that file associations are stored separately for x32 and x64 apps. That's weird. Do you have any supporting arguments for this?
Pierre
I digged a little deeper and found that even in XP there are logically separate registry branches for file associations but pointing to the same logical location... So far it looks like ClickOnce bug...Could you please validate whether file association appear in both HKEY_LOCAL_MACHINE\SOFTWARE\Classes and HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Classes registry keys?
Regent
On Windows 7, HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Classes does not contain any file association information. And I must stress here that with ClickOnce, the file association does not appear under HKEY_LOCAL_MACHINE\SOFTWARE\Classes, but in the HKEY_CURRENT_USER branch (e.g. HKCU\Software\Classes\.abc). And no, there is no interesting file association information on the Wow6432Node of HKCU either.
Pierre
Ah, yes, I hadn't thought of bringing the issue to the Microsoft Connect site. Let's see if that helps...
Pierre
Well done! Thank you.
Vasiliy Borovyak
A: 

Have you tried using 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

Nick Brooks
Process.Start() with UseShellExecute set to true effectively calls to native ShellExecuteEx(). And it will call x32 anyway.
Regent
Indeed, and it does -- alas -- not solve the issue. Thanks anyway for the help.
Pierre