为什么 AssocQueryString 找不到与图像扩展关联的可执行文件?

Why doesn't AssocQueryString find executable associated to image extensions?

我正在使用 AssocQueryString 以获取与某些扩展关联的可执行文件。

它适用于 .pdf.txt 等扩展。但我注意到,对于我尝试过的所有图像扩展(.bmp.png.jpg.ico),它都没有 return 任何东西。

uses
  ShLwApi, Windows, Dialogs;

const
    // ASSOCF enumerated values mapped to integer constants
    ASSOCF_INIT_NOREMAPCLSID = [=10=]000001;
    ASSOCF_INIT_BYEXENAME = [=10=]000002;
    ASSOCF_OPEN_BYEXENAME = [=10=]000002;
    ASSOCF_INIT_DEFAULTTOSTAR = [=10=]000004;
    ASSOCF_INIT_DEFAULTTOFOLDER = [=10=]000008;
    ASSOCF_NOUSERSETTINGS = [=10=]000010;
    ASSOCF_NOTRUNCATE = [=10=]000020;
    ASSOCF_VERIFY = [=10=]000040;
    ASSOCF_REMAPRUNDLL = [=10=]000080;
    ASSOCF_NOFIXUPS = [=10=]000100;
    ASSOCF_IGNOREBASECLASS = [=10=]000200;
    
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;

更多信息:

它也适用于图像扩展,但前提是要求与 'edit' 而不是 'open' 关联的可执行文件。

双击 .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));

它显示“{4ED3A719-CEA8-4BD9-910D-E252F997AFC2}”。如何在 Windows 7 上看到相同的结果? (dll 或可执行文件名)

此外,我注意到在将 .bmp 更改为不存在的(如“.abcde”)后 return 类似的结果。为此,我什至不知道是否有相关程序。

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

根据 ASSOCSTR 文档:

ASSOCSTR_EXECUTABLE
An executable from a Shell verb command string. For example, this string is found as the (Default) value for a subkey such as HKEY_CLASSES_ROOT\ApplicationName\shell\Open\command. If the command uses Rundll.exe, set the ASSOCF_REMAPRUNDLL flag in the flags parameter of IQueryAssociations::GetString to retrieve the target executable.

Caution
Not all app associations have executables. Do not assume that an executable will always be present.

根据 ASSOCF 文档:

ASSOCF_REMAPRUNDLL
Instructs IQueryAssociations methods to ignore Rundll.exe and return information about its target. Typically IQueryAssociations methods return information about the first .exe or .dll in a command string. If a command uses Rundll.exe, setting this flag tells the method to ignore Rundll.exe and return information about its target.

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

另外,注意AssocQueryString()最后一个参数的文档:

cchOut [in, out]
Type: DWORD*

A pointer to a value that, when calling the function, is set to the number of characters in the pszOut buffer. When the function returns successfully, the value is set to the number of characters actually placed in the buffer.

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

并始终检查 return 值是否有错误。

试试这个:

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;

或者:

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;