Search code examples
delphiwinapiimagelist

How do I invoke SHGetImageList properly? Is it possible to retrieve HIMAGELIST with it?


I was trying to examine shell's shared image list, followed seemingly straightforward (but actually not very clear) PSDK documentation including the remark what I'm free to cast between HIMAGELIST and IImageList types (the first thing I found suspicious later). So, I wrote the following code:

procedure TForm1.FormCreate(Sender: TObject);
const
  IID_IImageList: TGUID = '{46EB5926-582E-4017-9FDF-E8998DAA0950}';
  IID_IImageList2: TGUID = '{192B9D83-50FC-457B-90A0-2B82A8B5DAE1}';
var
  himl: HIMAGELIST;
begin
  CoInitialize(nil);

  OleCheck(SHGetImageList(SHIL_LARGE, IID_IImageList, Pointer(himl)));
  ImageList1.Handle := himl;
end;

But it work very poorly: API call returns HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER) and then crashes with multiple access violations, suggesting some binary incoherence between application and library. However, the stock Delphi import of function I've used looks correct:

function SHGetImageList(iImageList: Integer; const riid: TGUID; 
  var ppvObj: Pointer): HResult;
function SHGetImageList; external shell32 name 'SHGetImageList' delayed;

The second this I found confusing in documentation is what riid parameter description list only one possible value - IID_IImageListwhich might not match my intention to retrieve a handle type.

So, how do I invoke SHGetImageList properly? And is it possible to retrieve HIMAGELIST with it?


Solution

  • The problem is with Delphi's stock declaration in the ShellAPI unit. It is missing the stdcall calling convention. To work around that, you can declare the function manually in your own code:

    function SHGetImageList(iImageList: Integer; const riid: TGUID;
      var ppvObj: Pointer): HResult; stdcall; external shell32;
    
    procedure TTestForm.Button1Click(Sender: TObject);
    const
      IID_IImageList: TGUID = '{46EB5926-582E-4017-9FDF-E8998DAA0950}';
    var
      himl: HIMAGELIST;
    begin
      //CoInitialize(nil);
      OleCheck(SHGetImageList(SHIL_LARGE, IID_IImageList, Pointer(himl)));
      ImageList1.Handle := himl;
    end;
    

    Note: VCL applications already call CoInitialize if you don't remove the Application.Initialize call in the project source.