I'm using AssocQueryString in order to get the executable associated to certain extensions.
It works good for extensions like .pdf
and .txt
. But I've noticed that it doesn't return anything for all image extensions I've tried (.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;
Further informations:
It also works with image extensions, but only if asking for the executable associated to 'edit' instead of 'open'.
Double-clicking on a .bmp file causes the file is opened with the default Windows 10 photo viewer.
Update
Currently, my code is:
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));
It shows "{4ED3A719-CEA8-4BD9-910D-E252F997AFC2}". How to have the same result seen on Windows 7? (A dll or an executable filename)
Furthermore, I noticed that after changing .bmp
into an non-existent (like '.abcde') return a similar result. For this I can't even know if there's an associated program.
As stated in comments, your machine's registrations for image file extensions are not using an application to open the files, they are using a DLL invoked by Rundll32 instead.
Per the ASSOCSTR
documentation:
ASSOCSTR_EXECUTABLE
An executable from a Shell verb command string. For example, this string is found as the (Default) value for a subkey such asHKEY_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.
Per the ASSOCF
documentation:
ASSOCF_REMAPRUNDLL
InstructsIQueryAssociations
methods to ignoreRundll.exe
and return information about its target. TypicallyIQueryAssociations
methods return information about the first.exe
or.dll
in a command string. If a command usesRundll.exe
, setting this flag tells the method to ignoreRundll.exe
and return information about its target.
Also, when calling AssocQueryString()
, try setting the pszExtra
parameter to NULL instead of a specific verb.
Also, pay attention to the documentation for the last parameter of 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.
You are setting your BufSize
variable to a byte count not a character count. Your code is assuming Sizeof(Char)
is 1, but that is only true in Delphi 2007 and earlier. In Delphi 2009 and later, Sizeof(Char)
is 2 instead.
And always check return values for errors.
Try this:
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;
Alternatively:
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;