为什么AssocQueryString没有找到与图像扩展相关的可执行文件?

Fab*_*zio 2 delphi winapi file-type file-association windows-10

我正在使用AssocQueryString以获取与某些扩展相关联的可执行文件.它适用于".pdf"和".txt"之类的扩展,但我注意到它没有为我尝试的所有图像扩展返回任何内容(".bmp",".png",".jpg" ,".ic").

uses
  ShLwApi, Windows, Dialogs;

const
// ASSOCF enumerated values mapped to integer constants
ASSOCF_INIT_NOREMAPCLSID = $00000001;
ASSOCF_INIT_BYEXENAME = $00000002;
ASSOCF_OPEN_BYEXENAME = $00000002;
ASSOCF_INIT_DEFAULTTOSTAR = $00000004;
ASSOCF_INIT_DEFAULTTOFOLDER = $00000008;
ASSOCF_NOUSERSETTINGS = $00000010;
ASSOCF_NOTRUNCATE = $00000020;
ASSOCF_VERIFY = $00000040;
ASSOCF_REMAPRUNDLL = $00000080;
ASSOCF_NOFIXUPS = $00000100;
ASSOCF_IGNOREBASECLASS = $00000200;

var
  Buffer: array [0..1024] of char;
  BufSize: DWord;
begin
  BufSize := Sizeof(Buffer);
  Buffer[0] := #0;
  AssocQueryString(
    ASSOCF_NOTRUNCATE,
    ASSOCSTR_EXECUTABLE,
    '.bmp',
    'open',
    Buffer,
    @BufSize
  );
  ShowMessage(Buffer);
end;
Run Code Online (Sandbox Code Playgroud)

进一步的信息:

它也适用于图像扩展,但仅限于要求与"编辑"而不是"打开"相关联的可执行文件.

双击bmp文件会导致使用默认的Windows 10照片查看器打开文件.

更新

目前,我的代码是:

var
  Buffer: array [0..1024] of Char;
  BufSize: DWord;
  Res: HResult;
begin
  BufSize := Length(Buffer);
  Res := AssocQueryString(
    ASSOCF_REMAPRUNDLL or ASSOCF_NOTRUNCATE,
    (*ASSOCSTR_DELEGATEEXECUTE missing on Delphi 2007*) 18,
    '.bmp',
    nil,
    Buffer,
    @BufSize
  );
  If Res = S_OK then
    ShowMessage(Buffer)
  else
    ShowMessage('Error ' + IntToStr(Res) + sLineBreak + SysErrorMessage(Res));
Run Code Online (Sandbox Code Playgroud)

它显示"{4ED3A719-CEA8-4BD9-910D-E252F997AFC2}"..如何在Win7上看到相同的结果?(一个DLL或一个可执行文件名)此外,我注意到在将'.bmp'更改为一个不存在的(如'.abcde')后返回一个类似的结果..为此,我甚至不知道是否有一个相关的程序.

Rem*_*eau 5

如评论中所述,您的计算机的图像文件扩展名注册不使用应用程序打开文件,而是使用Rundll32调用的DLL.

根据ASSOCSTR文件:

ASSOCSTR_EXECUTABLE
Shell动词命令字符串中的可执行文件.例如,找到此字符串作为子键的(默认)值,例如HKEY_CLASSES_ROOT\ApplicationName\shell\Open\command.如果该命令使用Rundll.exe,请在IQueryAssociations :: GetString的flags参数中设置ASSOCF_REMAPRUNDLL标志以检索目标可执行文件.

注意
并非所有应用关联都具有可执行文件.不要假设可执行文件总是存在.

根据ASSOCF文件:

ASSOCF_REMAPRUNDLL
指示IQueryAssociations方法忽略Rundll.exe并返回有关其目标的信息.通常,IQueryAssociations方法返回有关第一个.exe.dll命令字符串的信息.如果命令使用Rundll.exe,则设置此标志会告诉方法忽略Rundll.exe并返回有关其目标的信息.

此外,在调用时AssocQueryString(),尝试将pszExtra参数设置为NULL而不是特定的动词.

另外,请注意以下参数的文档AssocQueryString():

cchOut [in,out]
类型:DWORD*

指向一个值的指针,该值在调用函数时设置为pszOut缓冲区中的字符数.当函数成功返回时,该值设置为实际放置在缓冲区中的字符数.

您将BufSize变量设置为字节数而不是字符数.您的代码假定Sizeof(Char)为1,但这仅适用于Delphi 2007及更早版本.在Delphi 2009及更高版本中,Sizeof(Char)是2而不是.

并始终检查错误的返回值.

试试这个:

var
  Buffer: array [0..1024] of Char;
  BufSize: DWord;
  Res: HResult;
begin
  BufSize := Length(Buffer);
  Res := AssocQueryString(
    ASSOCF_REMAPRUNDLL or ASSOCF_NOTRUNCATE,
    ASSOCSTR_EXECUTABLE,
    '.bmp',
    nil,
    Buffer,
    @BufSize
  );
  If Res = S_OK then
    ShowMessage(Buffer)
  else
    ShowMessage('Error ' + IntToStr(Res));
end;
Run Code Online (Sandbox Code Playgroud)

或者:

var
  Buffer: string;
  BufSize: DWord;
  Res: HResult;
begin
  BufSize := 0;
  Res := AssocQueryString(
    ASSOCF_REMAPRUNDLL or ASSOCF_NOTRUNCATE,
    ASSOCSTR_EXECUTABLE,
    '.bmp',
    nil,
    nil,
    @BufSize
  );
  if Res = S_FALSE then
  begin
    SetLength(Buffer, BufSize-1);
    Res := AssocQueryString(
      ASSOCF_REMAPRUNDLL or ASSOCF_NOTRUNCATE,
      ASSOCSTR_EXECUTABLE,
      '.bmp',
      nil,
      PChar(Buffer),
      @BufSize
    );
  end;
  If Res = S_OK then
    ShowMessage(Buffer)
  else
    ShowMessage('Error ' + IntToStr(Res));
end;
Run Code Online (Sandbox Code Playgroud)