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

Posted

技术标签:

【中文标题】为啥 AssocQueryString 找不到与图像扩展关联的可执行文件?【英文标题】:Why doesn't AssocQueryString find executable associated to image extensions?为什么 AssocQueryString 找不到与图像扩展关联的可执行文件? 【发布时间】:2017-01-15 00:04:33 【问题描述】:

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

它适用于像 .pdf.txt 这样的扩展。但我注意到它不会为我尝试过的所有图像扩展返回任何内容(.bmp.png.jpg.ico)。

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;

更多信息:

它也适用于图像扩展,但前提是要求与“编辑”而不是“打开”关联的可执行文件。

双击 .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')后,会返回类似的结果。为此我什至不知道是否有相关的程序。

【问题讨论】:

FindExecutable 为您系统上的图像文件返回什么? @IInspectable: 结果为 31(SE_ERR_NOASSOC -> “指定文件类型与可执行文件没有关联。” -> msdn.microsoft.com/en-us/library/windows/desktop/…)。 如果您将pszExtra 参数留空(传递nil 而不是'open'),您将看到.bmp 扩展名实际上与PhotoViewer.dll 相关联,而不是可执行文件(至少在 Win7 64 上 - 我怀疑它在 Win10 上是相似的)。命令提示符下的assoc .bmp 表示这是一个Paint.Picture,根据ftype Paint.Picturerundll32 PhotoViewer.dll 相关联。 @KenWhite:我确认“assoc .bmp”在 Windows 10 上也显示“Paint.Picture”。我尝试将 nil 作为 pszExtra 参数传递,但它仍然产生一个空字符串。 你想用这些信息做什么? 【参考方案1】:

如 cmets 中所述,您机器的图像文件扩展名注册不是使用应用程序打开文件,而是使用 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 [输入,输出] 类型:双字*

指向一个值的指针,在调用该函数时,该值设置为 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;

或者:

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;

【讨论】:

非常感谢您的建议和文档报价。我已经尝试过您的解决方案,但在这两种情况下,AssocQueryString 在我的系统上都返回 -2147023741。 你是对的。也许我误解了一些东西,但是您的两个代码示例都设置了 ASSOCF_REMAPRUNDLL 标志。如果我双击“.bmp”文件,则它会使用 Windows 10 照片查看器打开。为什么 AssocQueryString 仍然返回 ERROR_NO_ASSOCIATION? @ExDev:代码在 Win7 上运行良好,它按预期返回 PhotoViewer.dll。当我在 Win10 上运行完全相同的代码时,它失败了。我注意到 Win10 上的 .bmp 没有 open 动词(只有 editprintto)。就像文档说的那样,不能保证关联具有可执行文件。但是,ASSOCSTR_DELEGATEEXECUTE 在 Win10 上适用于 .bmp(尽管它的动词没有明确的 DelegateExecute 值),并且返回的 CLSID 属于“关联启动执行命令”COM 对象,因此行为上的差异可能有与使用 IExecuteCommand 与 command-ilne 有关。 我通过添加我当前的代码更新了我的问题。它返回“4ED3A719-CEA8-4BD9-910D-E252F997AFC2”。代码有问题吗?我的意思是,我能获得在 Win7 上看到的相同结果吗? ('PhotoViewer.dll') @ExDev 我还想知道:你想对关联信息做什么?

以上是关于为啥 AssocQueryString 找不到与图像扩展关联的可执行文件?的主要内容,如果未能解决你的问题,请参考以下文章

总是提示找不到符号为啥

java为啥每次都是找不到文件。找不到文件?

为啥 GetProperty 找不到它?

为啥我找不到 ProgressiveMediaSource?

为啥找不到这些符号?

ASP.NET为啥找不到服务器?