views:

462

answers:

6

I need to get the name of the program currently associated with a file extension for the current user. If you right-click on a file and select properties, then what I need is the program name that is to the right of the "Opens with" line.

e.g. For ".xls", I want to be able to get the answer "Microsoft Office Excel", or whatever program the user has as their default program to open .xls files.

I have determined it's not as easy as just going into HKEY_CLASSES_ROOT and picking it out, since it may also be specified in HKEY_LOCAL_MACHINE or HKEY_CURRENT_USER or HKEY_USERS.

Maybe all I need to know is the pecking order used by Windows to determine this and how to get to each of the locations. Of course, a Windows API call to do this would be ideal.

This is a similar question to: How to get icon and description from file extension using Delphi? but that question only answered how to get the description of the extension and the icon of the associated program. I couldn't find a way to extend that to also get the name of the associated program.

I'm using Delphi 2009 and need a solution that works on Windows XP, Vista and 7.


Thank you all for your answers.

It appears my belief that the name of the executable is not in the Registry after all. And after looking around extensively for a Windows API that will give the name, I could not find one.

I think Mef's answer then is the best. To get the name of the executable from the information included in the program's executable.

+3  A: 

How about this article here: Determining the associated application

In the concrete case of Excel, you will find the .xls extension under HKEY_CLASSES_ROOT - the default value of that entry is Excel.Sheet.8.

When you go search for Excel.Sheet.8 again in HKEY_CLASSES_ROOT, you'll find an entry with a default value of Microsoft Office Excel 97-2003 Worksheet - that's probably as good as it gets.

marc_s
Thanks for that. I hadn't seen that article in my search. But there's actually 2 problems with it. (1) It only uses HKEY_CLASSES_ROOT and that won't necessarily be the location that Windows will use (see the other locations I mention in my question), and (2) It gives the path to the executable program. But I actually need the name of that program, not the path to it.
lkessler
Actually HKEY_CLASSES_ROOT is a merged view of HKEY_CURRENT_USER\Software\Classes and HKEY_LOCAL_MACHINE\SOFTWARE\Classes (in that order) so it *IS* the location Windows will use.
Remko
With HKCU having precedence.
Nat
@marc_s: You've got the right idea. But the value you've got of "Microsoft Office Excel 97-2003 Worksheet" still describes the document. I need the program name. When I search, I can find it at: "HKCR\Local Settings\Software\Microsoft\Windows\Shell\MuiCache" and it has the value of "Microsoft Office Excel" associated with the file path. It's also at "\HKCR\Wow6432Node\Local Settings\Software\Microsoft\Windows\SHell\MuiCache" on my 64-bit machine, and it's also in HKCU and maybe elsewhere. The question is how do I trace my way from ".xls" to "Microsoft Office Excel"?
lkessler
+3  A: 

Step 1

Get the executable which is assigned to a file extension, for instance with the following function:

uses Registry, Windows, SysUtils;

function GetAssociation(const DocFileName: string): string;
var
  FileClass: string;
  Reg: TRegistry;
begin
  Result := '';
  Reg := TRegistry.Create(KEY_EXECUTE);
  Reg.RootKey := HKEY_CLASSES_ROOT;
  FileClass := '';
  if Reg.OpenKeyReadOnly(ExtractFileExt(DocFileName)) then
  begin
    FileClass := Reg.ReadString('');
    Reg.CloseKey;
  end;
  if FileClass <> '' then begin
    if Reg.OpenKeyReadOnly(FileClass + '\Shell\Open\Command') then
    begin
      Result := Reg.ReadString('');
      Reg.CloseKey;
    end;
  end;
  Reg.Free;
end;

(See here, or marc_s' anwser to this question :-)

Step 2

Now you can read out the name of the program from the version information of this executable! The easiest way is using the TVersionInfo class you can find via Google, for instance here.

var VersionInfo: TVersionInfo;  
VersionInfo := TVersionInfo.Create('PathToExe\name.exe');  
s := VersionInfo.KeyValue['Description'];

However, you have to be aware that some programs use the description key therefore (like RAD Studio itself or MS Excel), while others use the product name key...

Mef
@Mef: That's an interesting idea - to pull the name out of the version info in the executable itself, and that could work given the caveats you mention. I'm not sure if Windows uses that in its "Open With" dialog or not. ... But deep down I still think the application name itself is buried somewhere in the Registry and can be got at. It is somewhat complex, e.g.: http://msdn.microsoft.com/en-us/library/ee872122(VS.85).aspx and http://msdn.microsoft.com/en-us/library/ee330739(v=VS.85).aspx
lkessler
@lkessler: You can specify a file description (and an ext description) when registering an extension (what marc_s described) - I thought **that** is what Windows uses in the Open With Dialog... But I may be wrong. I thought about it, and I came to the conclusion that there can't be a specific place in the Registry where Application Names are stored - because "Open With" works with exes that don't write anything to Registry semselfs as well, you can test this with a simple blank WinForm ;-) The interesting part is what name is displayed, I don't know =)
Mef
@Mef: I think it must use something in the Registry. Otherwise, I don't see how the OpenWithAdd tool at: http://www.winhelponline.com/blog/openwithadd-12-supports-windows-vista/ can work.
lkessler
@lkessler: I don't understand what you mean. This tool writes the name you entered before to the registry key marc_s mentioned. That's nothing the programs themselfs define...
Mef
+2  A: 

Delphi comes with a unit ShellApi.pas that is used in the sample code below. The file has to exist.

Here's how to use it:

function MyShellFindExecutable(const aFileName: string): string;
var
  Buffer: array[0..WINDOWS.MAX_PATH] of Char;
begin
  Result := '';
  FillChar(Buffer, SizeOf(Buffer), #0);
  if (SHELLAPI.FindExecutable(PChar(aFileName), nil, Buffer) > 32) then
    Result := Buffer;
end;
Tom1952
That's a good alternative to get the path of the executable as opposed to using the Registry. But it does require that an actual file be passed to it rather than just the extension. However it still does not give the name of the program to run the extension.
lkessler
Yes, you're correct - as noted in my OP, it requires an actual file, not just an extension.Not sure what you mean by "However it still does not give the name of the program to run the extension." if I call it with "Workbook1.xls" it returns: "C:\Program Files (x86)\Microsoft Office\Office12\EXCEL.EXE"
Tom1952
He is not looking for excel.exe, he is looking for Microsoft Office Excel.
Mef
+2  A: 

Don't go spelunking in the registry when there are API functions designed to do what you need.

In your case, you want AssocQueryString. You can give it the file-name extension, and it will tell your the program registered to handle that extension (AssocStr_Executable). If you're planning on running that program to open a document, then you'll really want the command string instead of just the executable; AssocQueryString can give you that, too (AssocStr_Command). It can also tell you the document type like what's displayed in Windows Explorer, like "Text Document" or "Zip Archive" (AssocStr_FriendlyDocName).

That API function is a wrapper for the IQueryAssociations interface. If you're looking for programs from many file types, or lots of strings associated with a single type, you may wish to instantiate that interface and re-use it instead of calling the API function over and over.

Rob Kennedy
@Rob: I agree I'd sooner use an API function than the registry. But I'm not looking for the friendly name of the file type. I'm looking for the friendly name of the default application for the file type. I want to be able to tell the user that the default application for .xls is "Microsoft Office Excel" or for .txt is "Notepad". I don't know that there's an API to get the application name.
lkessler
+1  A: 

I think that you need to combine Mef's and Rob Kennedy's answers.

Take Rob Kennedy's answer and take step 2 from Mef's answer. Reading registry directly isn't good thing to do, so you should throw away his part 1.

But I'm not looking for the friendly name of the file type.

AssocQueryString returns not only friendly name for file type (ASSOCSTR_FRIENDLYDOCNAME), but also it can return the name of executable to open file (ASSOCSTR_EXECUTABLE) - that is what you need.

Even more than that: I'm not sure, but may be ASSOCSTR_FRIENDLYAPPNAME will match your needs. In that case, you may use only Rob Kennedy's answer.

The problem with reading registry directly is that it may return wrong info. That's because you read system settings - that is what application registered. But user may override this. For example, he may right click on .xls and select "Open with..." -> "Other app." -> "OpenOffice" -> "Use this app always". Registration info for .xls type will not be altered (user preferences are saved in separate place, so apps can't mess with them), so your code (which reads registry directly) will continue to produce "MS Excel", even though when user double-clicks on file - OpenOffice will be launched.

Alexander
@Alexander. If the user says "Use this app always", then that info has to be stored somewhere, and that's what I want. Whether or not it is stored in the Registry, I need to know what application this user will open this file extension with.
lkessler
Ummm... so? That's why I suggested you to use API and not hunt for this info manually.
Alexander
A: 

If the user says "use this app always" for .xls-files the info ist stored in

HK_CU\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.xls

The key has an entry "Application" containing the Application name (e.g. "soffice.exe"). It's correlated to an Applcication key in HK_CR, e.g. HK_CR\Applications\soffice.exe\

Tontyna

related questions