如何使用Delphi获取与文件扩展名相关联的程序名称?

9
我需要获取当前用户与文件扩展名相关联的程序名称。如果右键单击文件并选择属性,那么我需要的是“打开方式”行右侧的程序名称。
例如,对于“.xls”,我想获得答案“Microsoft Office Excel”,或者用户用作打开.xls文件的默认程序。
我已经确定这并不像进入HKEY_CLASSES_ROOT并挑选出来那么简单,因为它也可能在HKEY_LOCAL_MACHINE或HKEY_CURRENT_USER或HKEY_USERS中指定。
也许我所需要知道的只是Windows用于确定此类问题的排序以及如何访问每个位置。当然,使用Windows API调用完成此任务最理想。
这与以下问题类似: 如何使用Delphi从文件扩展名获取图标和描述? 但是该问题仅回答了如何获取扩展名的描述和关联程序的图标。我找不到一种方法来将其扩展为还可以获取关联程序的名称。
我正在使用Delphi 2009,并需要适用于Windows XP、Vista和7的解决方案。
谢谢你们所有人的答案。
看起来我的信念是,可执行文件的名称实际上并未记录在注册表中。在广泛查找可提供名称的Windows API之后,我找不到其中任何一款。
我认为Mef的答案是最好的。从程序可执行文件中获取可执行文件的名称。
后续:我发现David Hefferman对“如何使用默认文本编辑器打开文件?”的回答提供了一个很好的解决方案,可以使用不同扩展名的默认程序打开一个程序。
6个回答

11

当你需要的功能有对应的API函数时,不要在注册表中进行过度搜索。

在您的情况下,您需要使用AssocQueryString函数。您可以将文件名扩展名作为参数传入,并且它会告诉您处理该扩展名的程序(AssocStr_Executable)。如果您计划运行该程序以打开文档,则实际上需要命令字符串而不仅仅是可执行文件;AssocQueryString也可以提供给您(AssocStr_Command)。它还可以告诉您文档类型,例如Windows资源管理器中显示的“文本文档”或“Zip归档”(AssocStr_FriendlyDocName)。

该API函数是IQueryAssociations接口的一个包装器。如果您正在查找许多文件类型的程序,或者与单个类型相关的许多字符串,那么您可能希望实例化该接口并重复使用它,而不是一遍又一遍地调用API函数。


@Rob:我同意,我宁愿使用API函数而不是注册表。但我不是在寻找文件类型的友好名称。我正在寻找文件类型默认应用程序的友好名称。我希望能够告诉用户.xls的默认应用程序是“Microsoft Office Excel”,.txt的默认应用程序是“记事本”。我不知道是否有API可以获取应用程序名称。 - lkessler
1
这是正确的答案。传入ASSOCSTR_FRIENDLYAPPNAME将给出应用程序名称。 - Peter Ruderman

5

Delphi自带一个名为ShellApi.pas的单元,下面的示例代码中使用了它。文件必须存在。

以下是如何使用它:

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;

这是一个不错的选择,可用于获取可执行文件的路径,而不使用注册表。但它要求实际的文件被传递给它,而不仅仅是扩展名。然而,它仍然无法提供运行扩展名的程序名称。 - lkessler
是的,你说得对 - 正如我在我的原始帖子中所指出的那样,它需要一个实际的文件,而不仅仅是一个扩展名。如果我使用“Workbook1.xls”调用它,不确定你所说的“但它仍然没有给出运行扩展名的程序名称。”,它会返回:“C:\ Program Files(x86)\ Microsoft Office \ Office12 \ EXCEL.EXE”。 - RobertFrank
他不是在寻找excel.exe,他是在寻找Microsoft Office Excel。 - Leo

4

步骤1

获取指定文件扩展名的可执行文件,例如使用以下函数:

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;

第二步

现在,您可以从此可执行文件的版本信息中读取程序的名称!最简单的方法是使用TVersionInfo类,您可以通过Google找到它,例如这里

(请参见此处或marc_s对此问题的回答 :-)

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

但是,你必须知道有些程序使用"description"键(比如 RAD Studio 本身或 MS Excel),而其他程序则使用产品名称键...


@Mef:这是一个有趣的想法——从可执行文件的版本信息中提取名称,考虑到你提到的注意事项,这可能会起作用。我不确定Windows是否在其“打开方式”对话框中使用它。...但我仍然认为应用程序名称本身深藏在注册表中,并且可以被获取。它有些复杂,例如:http://msdn.microsoft.com/en-us/library/ee872122(VS.85).aspx和http://msdn.microsoft.com/en-us/library/ee330739(v=VS.85).aspx - lkessler
@lkessler:当注册扩展名(如marc_s所描述的)时,您可以指定文件描述(以及扩展描述) - 我认为这就是Windows在“打开方式”对话框中使用的内容...但我可能错了。我思考了一下,并得出结论:不能有一个特定的地方存储应用程序名称的注册表,因为“打开方式”也适用于不将任何内容写入注册表中的exe文件,您可以使用简单的空白WinForm测试此功能;-)有趣的部分是显示的名称,我不知道=) - Leo
我认为它一定要使用注册表中的某些东西。否则,我不明白这个OpenWithAdd工具在这里能怎么运行:http://www.winhelponline.com/blog/openwithadd-12-supports-windows-vista/ - lkessler
@lkessler:我不理解你的意思。这个工具将你之前输入的名称写入到marc_s提到的注册表键中。那不是程序自己定义的内容... - Leo
.xlsx扩展名文件和Excel怎么样? - Kiquenet
要检查文件是否有关联,您可以使用本地函数FindExecutable,这基本上是Windows资源管理器内部使用的...如果没有关联,它会给出一个很好的错误代码(SE_ERR_NOASSOC)。成功后,它会给出相应可执行文件的路径。请参阅https://dev59.com/zWkw5IYBdhLWcg3w9vF-#9540278 - Kiquenet

4

我认为你需要结合Mef和Rob Kennedy的答案。

采用Rob Kennedy的答案,然后从Mef的答案中采用第2步。直接读取注册表不是好的做法,所以你应该放弃他的第1部分。

但我不是在寻找文件类型的友好名称。

AssocQueryString不仅返回文件类型的友好名称(ASSOCSTR_FRIENDLYDOCNAME),还可以返回打开文件的可执行文件名(ASSOCSTR_EXECUTABLE) - 这就是你需要的。

更重要的是:我不确定,但也许ASSOCSTR_FRIENDLYAPPNAME会符合你的需求。在这种情况下,你只需要使用Rob Kennedy的答案。

直接读取注册表的问题在于它可能返回错误的信息。这是因为你读取系统设置 - 应用程序注册的内容。但用户可能会覆盖这些设置。例如,他可能右键单击.xls文件并选择“打开方式...” -> “其他应用程序” -> “OpenOffice” -> “始终使用此应用程序”。.xls类型的注册信息不会被更改(用户偏好设置保存在单独的位置,因此应用程序无法干扰它们),因此你的代码(直接读取注册表)将继续生成“MS Excel”,即使用户双击文件 - 也会启动OpenOffice。


@Alexander。如果用户说“始终使用此应用程序”,那么这些信息必须被存储在某个地方,这就是我想要的。无论它是否存储在注册表中,我都需要知道该用户将使用哪个应用程序打开该文件扩展名。 - lkessler
那又怎样?这就是为什么我建议你使用API而不是手动搜索这些信息的原因。 - Alex

3

这篇文章怎么样:确定关联的应用程序

以 Excel 为例,你会在 HKEY_CLASSES_ROOT 中找到扩展名为 .xls 的条目——该条目的默认值为 Excel.Sheet.8

当你再次查找 HKEY_CLASSES_ROOT 中的 Excel.Sheet.8 条目时,你会发现一个默认值为 Microsoft Office Excel 97-2003 Worksheet 的条目——那可能就是最好的了。


谢谢你。我在搜索中没有看到那篇文章。但实际上有两个问题。(1)它只使用了HKEY_CLASSES_ROOT,这不一定是Windows将要使用的位置(请参见我在问题中提到的其他位置),(2)它给出了可执行程序的路径。但我实际上需要的是该程序的名称,而不是路径。 - lkessler
2
实际上,HKEY_CLASSES_ROOT 是 HKEY_CURRENT_USER\Software\Classes 和 HKEY_LOCAL_MACHINE\SOFTWARE\Classes(按照这个顺序)的合并视图,因此它Windows将使用的位置。 - Remko
@marc_s:你的想法是对的。但是你得到的值“Microsoft Office Excel 97-2003 Worksheet”仍然描述了文档。我需要程序名称。当我搜索时,我可以在“HKCR\Local Settings\Software\Microsoft\Windows\Shell\MuiCache”找到它,并且它与文件路径相关联的值为“Microsoft Office Excel”。在我的64位机器上,它也在“\HKCR\Wow6432Node\Local Settings\Software\Microsoft\Windows\SHell\MuiCache”中,还可能在HKCU和其他地方。问题是如何从“.xls”追踪到“Microsoft Office Excel”? - lkessler
链接已失效,存档万岁:https://web.archive.org/web/20080511193532/http://www.latiumsoftware.com/en/delphi/00005.php :) - WiredPrairie
对于 HKEY_CLASSES_ROOT\.xlsx\Excel.Sheet.12 下的 ShellNew 扩展,其 REG_SZ FileName 值为 _C:\Program Files\Microsoft Office 15\Root\VFS\Windows\ShellNew\excel12.xlsx_。 - Kiquenet
显示剩余3条评论

0

如果用户对 .xls 文件说“始终使用此应用程序”,则信息将存储在

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

该密钥具有一个条目“Application”,其中包含应用程序名称(例如“soffice.exe”)。它与HK_CR中的应用程序密钥相关联,例如HK_CR\Applications\soffice.exe\


我没有查看_Application_。HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.xlsxOpenWithListOpenWithProgids - Kiquenet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接