16

It is sometimes desirable to have your application open the default application for a file. For example, to open a PDF file you might use:

System.Diagnostics.Process.Start("Filename.pdf");


To open an image, you'd just use the same code with a different filename:

System.Diagnostics.Process.Start("Filename.gif");


Some extensions (.gif for example) just about always have a default handler, even in a base Windows installation. However, some extensions (.pdf for example) often don't have an application installed to handle them.

In these cases, it'd be desirable to determine if an application is associated with the extension of the file you wish to open before you make the call to Process.Start(fileName).

I'm wondering how you might best implement something like this:

static bool ApplicationAssociated(string extension)
{
    var extensionHasAssociatedApplication = false;

    var condition = // Determine if there is an application installed that is associated with the provided file extension.;
    if (condition)
    {
        extensionHasAssociatedApplication = true;
    }

    return extensionHasAssociatedApplication;
}
bopapa_1979
  • 8,949
  • 10
  • 51
  • 76
  • It's somewhere in the register editor. Together with the handlers for magnet:, mailto:, call: schemes etc. It's almost in the root if I remember correct – f2lollpll Mar 02 '12 at 20:47

6 Answers6

32

I would recommend following the advice in David's answer BUT since you need to detect an association:

To check whether a file has an association you can use the native function FindExecutable which is basically what Windows Explorer uses internally... it gives a nice error code (SE_ERR_NOASSOC) if there is no association. Upon success it gives a path to the respective executable.

Thee DllImport for it is

[DllImport("shell32.dll")]
static extern int FindExecutable(string lpFile, string lpDirectory, [Out] StringBuilder lpResult);

Another option would be to walk the registry for example (not recommended since complex due to several aspets like WoW64 etc.):

The real association is stored in the key that HKEY_CLASSES_ROOT\.pdf points to - in my case AcroExch.Document, so we checkoutHKEY_CLASSES_ROOT\AcroExch.Document. There you can see (and change) what command is going to be used to launch that type of file:

HKEY_CLASSES_ROOT\AcroExch.Document\shell\open\command
Community
  • 1
  • 1
Yahia
  • 69,653
  • 9
  • 115
  • 144
  • 3
    The `FindExecutable` approach requires a given file to exist, which is not ideal for just getting information about a file type. Imagine having to create dummy files every time you do that when your approach is not checking for a specific file, but really just a file type :/ – Ben Philipp Feb 02 '20 at 21:48
  • This answer is helpful to me. I have the file but the App changes name overtime. This helps me get the current running App. – mdang Jun 06 '21 at 05:58
6

@Yahia gets the nod. I'm posting my quick solution for posterity so you can see what I went with. There are lots of possible improvements to this code, but this will give you the idea:

public static bool HasExecutable(string path)
{
    var executable = FindExecutable(path);
    return !string.IsNullOrEmpty(executable);
}

private static string FindExecutable(string path)
{
    var executable = new StringBuilder(1024);
    FindExecutable(path, string.Empty, executable);
    return executable.ToString();
}

[DllImport("shell32.dll", EntryPoint = "FindExecutable")]
private static extern long FindExecutable(string lpFile, string lpDirectory, StringBuilder lpResult);
bopapa_1979
  • 8,949
  • 10
  • 51
  • 76
  • Why did you use StringBuilder(1024) instead of StringBuilder()? – mggSoft Aug 31 '16 at 10:44
  • 4
    @mggSoft The native method FindExecutable requires a buffer to return the executable path. The documentation specifies _This parameter must contain a valid non-null value and is assumed to be of length MAX_PATH_. So while 1024 is a bit overkill, overriding the default StringBuilder capacity with the value of MAX_PATH (260) is recommended. – Melvyn Oct 03 '16 at 13:34
  • This answer worked for vlc player mp4 association, but not for .pdf that gets opened by IE in my case. For pdf I got 32 response `SE_ERR_NOASSOC`. Is there a way to get IE in case of pdf? – Matas Vaitkevicius Apr 28 '20 at 10:20
  • @MatasVaitkevicius I wish I knew a better answer for you. – bopapa_1979 May 14 '20 at 21:45
5

In a situation like this the best approach is to try to open the document and detect failure. Trying to predict whether or not a file association is in place just leads to you reimplementing the shell execute APIs. It's very hard to get that exactly right and rather needless since they already exist!

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • I agree this is the simplest. However, the one major issue with this approach, is that you have to somehow hide the document you're opening - you don't really want the user to see it. – zmbq Mar 02 '12 at 20:52
  • Thanks for the constructive comment. I'd prefer that approach as well. I cannot, for reasons I won't bore you with. The spec is very specific, and while I don't agree with the reasons, I do have a job to do. – bopapa_1979 Mar 02 '12 at 20:53
  • @zmbq If you don't want the user to see the document then you simply refrain from opening it. – David Heffernan Mar 02 '12 at 20:53
  • I assumed OP had something else in mind than just open the file. If he doesn't, then sure - that's absolutely the way to go. – zmbq Mar 02 '12 at 20:55
  • 1
    @Eric Then you are into all sorts of registry reading fun. To do it well you'll need to look in both HKLM and HKCU. You'll need to watch out for the redirector on WOW64. You can get quite a reliable test here but you'll likely not manage a fool-proof 100% accurate reimplementation. Maybe that would be enough to satisfy the PHB. – David Heffernan Mar 02 '12 at 20:56
  • @David Heffernan - So, about a year later, and I finally convinced my client that your approach was the best one. I didn't spend a year on it :) but it finally came up again in a code review. Cheers. – bopapa_1979 Jul 29 '13 at 15:18
1

You will have too look at the registry to get that information.

You can follow from:

HKEY_CLASSES_ROOT\.extension

and it usually leads to something like HKEY_CLASSES_ROOT\extfile\Shell\Open\Command

and you will come to the command to open the file type.

Depending on what you are doing, it may be ideal to just ask for forgiveness ( that is, just open the file and see)

manojlds
  • 290,304
  • 63
  • 469
  • 417
  • Are you sure that's enough? Visual Studio some sort of voodoo magic, where .sln files have a different icon based on the Visual Studio version that created them. This is spooky, and probably not stored where you said. – zmbq Mar 02 '12 at 20:51
  • @zmbq - If you see the `HKEY_CLASSES_ROOT`, you will see different defintion for a sln file, handled by different version of VS. The icon is defined under `DefaultIcon`. Basically, all the info is there. http://stackoverflow.com/questions/4693562/how-does-windows-know-what-version-of-visual-studio-a-sln-file-relates-to – manojlds Mar 02 '12 at 20:54
1

All of that information lives in the registry.. you could navigate to HKEY_CLASSES_ROOT, find the extension and go from there to find the default handler. But depending on the type of file and the associated handler(s) you'll need to wade into CLSIDs and whatnot... you're probably better off catching an exception instead.

kprobst
  • 16,165
  • 5
  • 32
  • 53
1

This information is in the registry. For example:

# Mount the HKCR drive in powershell
ps c:\> new-psdrive hkcr registry hkey_classes_root
ps c:\> cd hkcr:\.cs

# get default key for .cs
PS hkcr:\.cs> gp . ""
(default)    : VisualStudio.cs.10.0
...

# dereference the "open" verb
PS hkcr:\.cs> dir ..\VisualStudio.cs.10.0\shell\open

    Hive: hkey_classes_root\VisualStudio.cs.10.0\shell\open

Name                           Property
----                           --------
Command                        (default) : "C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe" /dde
ddeexec                        (default) : Open("%1")
x0n
  • 51,312
  • 7
  • 89
  • 111