0

I have some code that calls SHGetFileInfo to retrieve the list of system icons:

FileIcons: TImageList;

procedure LoadFileIcons;
var
  listHandle: Integer;
  shFileInfo: TSHFileInfo;
begin
  listHandle:= SHGetFileInfo('', 0, shFileInfo, Sizeof(shFileInfo),
                             SHGFI_SYSICONINDEX or SHGFI_LARGEICON);

  if (listHandle <> 0) then
  begin
    FileIcons.Handle:=listHandle;
    FileIcons.ShareImages:=true;
  end;
end

On Delphi 10.4 this worked fine, and I was able to load icons and display files with proper icon. On Delphi 11.3, it raises the following exception:

Project Project.exe raised exception class ERangeError with message 'Range check error at address 00000000035C7A99'.

Has anything changed between Delphi 10.4 and Delphi 11.3? How can I fix it?

Federico Pessina
  • 199
  • 1
  • 4
  • 15
  • 1
    SHGetFileInfo returns a Cardinal, not an Integer. Define listHandle as Cardinal. – Matej Jun 14 '23 at 11:21
  • @Matej It still raises the same exception also defining it as Cardinal – Federico Pessina Jun 14 '23 at 11:39
  • 1
    Defining it as NativeUInt solved the problem. Thanks for the suggestion. – Federico Pessina Jun 14 '23 at 11:41
  • 5
    What does the declaration for `SHGetFileInfo` say is returned? The Windows header files say `DWORD_PTR`. Always better to avoid guesswork. – David Heffernan Jun 14 '23 at 12:44
  • @DavidHeffernan In Delphi the return type is NativeUInt. Should I use something different rather than that? – Federico Pessina Jun 15 '23 at 07:05
  • 1
    Check again. In my `Winapi.ShellAPI.pas` the function is declared as such: `function SHGetFileInfo(pszPath: LPCWSTR; dwFileAttributes: DWORD; var psfi: TSHFileInfo; cbFileInfo, uFlags: UINT): DWORD_PTR; stdcall;` so the return type is `DWORD_PTR`. Where to you get `NativeUInt` from? Of course `DWORD_PTR` is an alias to `NativeUInt`, but `DWORD_PTR` is categorically the right type to use. Do you know how to find Delphi library source code and, equally importantly, the Windows API documentation? – David Heffernan Jun 15 '23 at 08:17
  • 2
    @DavidHeffernan: Almost certainly the OP is getting `NativeUInt` from [Code Insight](https://i.stack.imgur.com/s78c9.png) in its Ctrl+Space flavour. This "following of type links" is arguably not a helpful behaviour of Code Insight. (I know you know this, but for the record.) – Andreas Rejbrand Jun 15 '23 at 08:23
  • 1
    @Frederico: Every time you use a new function in some API, you reads its documentation. Of course, you know this, because it is obvious. So obviously you read [SHGetFileInfo](https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shgetfileinfoa) carefully before you wrote this code. And there you saw that the return type is `DWORD_PTR`, no? – Andreas Rejbrand Jun 15 '23 at 08:30
  • @AndreasRejbrand I did not write that code, I am just converting from D104 to D113 and that code did not compile. As you wrote, checking code insight I saw NativeUInt as return type and it seems to work. Also, the property "Handle" of TImageList appears to be NativeUInt so I thought it was the right type to use here. I will use DWORD_PTR then. – Federico Pessina Jun 15 '23 at 08:37
  • 1
    Yes, currently `DWORD_PTR` is exactly the same thing as `NativeUInt`, so Code Insight is not technically wrong. (And I'd be somewhat surprised if this ever changed, but it is not impossible.) In any case, `DWORD_PTR` is semantically the right type, and that will (hopefully) never change. – Andreas Rejbrand Jun 15 '23 at 08:39
  • @DavidHeffernan On Code Insight it says NativeUInt, and by Ctrl+click on SHGetFileInfo it got me to this : `function SHGetFileInfo; external shell32 name 'SHGetFileInfoW'; `. Now I've found the declaration you are referring to. I Will chage to DWORD_PTR, thanks. – Federico Pessina Jun 15 '23 at 08:40
  • If Code Insight's display of `NativeInt` is the culprit then I wonder why OP had to ask this question to begin with, since then he could have changed the type right away without asking and forcefully ignoring any documentation. – AmigoJack Jun 15 '23 at 09:02
  • @AmigoJack As I wrote in the question, I am converting from D104 to D112. Also on D104 Code Insight display `NativeUInt`, and the documentation of SHGetFileInfo is always that, but on D104 it worked without raising exceptions. I Thought it would've result in something more than just let it work fixing the type. – Federico Pessina Jun 15 '23 at 09:22
  • "_on D104 it worked without raising exceptions_" - more likely because it was compiled for 32 bit then and now you want 64 bit - a detail you nowhere mentioned/confirmed. Moreover 19 hours ago I linked to an answer explaining that `DWORD_PTR` is **not** `^DWORD`, so I should be the one stating "_As I wrote_". – AmigoJack Jun 15 '23 at 11:08
  • @AmigoJack No, it is 64bit on both D104 and D113. – Federico Pessina Jun 16 '23 at 05:20
  • The main point to take away is the importance of reading documentation. Programming requires attention to detail as a baseline. – David Heffernan Jun 16 '23 at 07:15

1 Answers1

-2

Solved declaring listHandle as DWORD_PTR:

FileIcons: TImageList;

procedure LoadFileIcons;
var
  listHandle: DWORD_PTR;
  shFileInfo: TSHFileInfo;
begin
  listHandle:= SHGetFileInfo('', 0, shFileInfo, Sizeof(shFileInfo),
                             SHGFI_SYSICONINDEX or SHGFI_LARGEICON);

  if (listHandle <> 0) then
  begin
    FileIcons.Handle:=listHandle;
    FileIcons.ShareImages:=true;
  end;
end
Federico Pessina
  • 199
  • 1
  • 4
  • 15
  • That's wrong - look up the [docs](https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shgetfileinfoa) and use the appropriate type: `DWORD_PTR`, which is surely defined already. Also see [this](https://stackoverflow.com/a/25842658/4299358) why it differs as per platform. – AmigoJack Jun 14 '23 at 15:08
  • @AmigoJack SHGetFileInfo in Delphi refers to SHGetFileInfoW (Which still return DWORD_PTR), not SHGetFileInfoA, and returns a NativeUInt. Defining it as a pointer to a DWORD, will result in a compile error. I could at most use DWORD, but on Win64 SHGetFileInfo returns an UInt64 and won't fit into a DWORD. Anyway, declaring it a DWORD will raise the exception again. The only way to let it work seems to declare it as NativeUInt to me, which let it compile and work properly. Do you have any suggestion? – Federico Pessina Jun 15 '23 at 07:25
  • I suggest you start by not confusing `DWORD` and `DWORD_PTR`. Surely you can see that there is a difference. – David Heffernan Jun 15 '23 at 08:17