Skip to content

Using Windows Stock Icons in Delphi

it is not a big discovery that all applications are running in the environment provided by the underlying operating system. Delphi is well-known for its rapid application development capabilities. The latest version of Delphi - Delphi 2010 - is the first release to support Windows 7, with all its latest and hottest additions including touch, multi-touch, gesturing and Direct2D. The Windows operating system is implemented in native C++ code and Delphi 2010 can directly leverage all its features without introducing additional layers of redirection, which is necessary in .NET. Microsoft has provided .NET developers with Windows API Code Pack for .NET, which contains managed wrappers for p-invoked functionality available from native Windows API layer. If you want to code for Windows you have the choice of using any of the .NET programming languages, including C#, Delphi Prism and others, or resort to native programming, like Visual C++, Delphi and C++Builder. Even though many .NET programmers believe that .NET is the only and the best way to code for Windows, the native code is still recommended where performance matters, like in the case of new Windows 7 subsystems like Direct2D or Windows Web Service API. I like Kenny Kerr saying in "Introducting Direct2D" MSDN article, that "if you want to develop high-performance and high-quality commercial applications, you’ll still look to C++ and native code to deliver that power." Another great comment from this author is on the new native Windows 7 Web Services API in another MSDN article "Windows Web Services": "C++ developers no longer have to think of themselves as second-class citizens in the world of Web Services. WWS is designed from the ground up to be a completely native-code implementation of SOAP, including support for many of the WS-* protocols."

Delphi 2010 is gaining traction again, and many Delphi programmers that moved away to Java and .NET in the dark era of late Borland, are looking at Delphi again, because it continues to be the relevant choice for your just another new project and it combines native performance with the the power of visual RAD programming. You get the best of two worlds: native, high performance code and elegant Visual Component Library. Delphi also means great, very responsive IDE and standalone executables with no dependencies that will run on just any version of Windows, does not matter which version of .NET is installed or not:-)

I was just reading through recent Dr.Bob’s excellent article on using new Windows 7 TaskBar in Delphi code (good stuff!) and though it would be cool to revisit just another area also covered by Windows API Code Pack: Windows Stock Icons.

Delphi 2010 comes with "ShellAPI" unit that simplifies Windows Shell programming. It is not a new unit, but it works great with new Windows 7 APIs! In order to retrieve Windows Stock Icons in your Delphi code, you need to call "SHGetStockIconInfo" function defined in "ShellAPI" exported from shell32 Windows DLL. The "SHGetStockIconInfo" function takes an integer "id" of the icon we want to retrieve, "flags" argument that can be used to specify if we want normal or large size icons, and returns HResult and "TSHStockIconInfo" record as "out" parameter.

I have put together a small utility class named "TStockIcon" that wraps calls to "SHGetStockIconInfo" and simplifies access to Windows Stock Icons.


unit StockIconUtil;

interface

uses
  Windows, Graphics;

type
  TStockIconSize = (sisLarge, sisSmall, sisShellSize);

  TStockIcon = class
    class function GetIcon(id: integer): HICON; overload;
    class function GetIcon(id: integer; size: TStockIconSize;
      overlay, selected: boolean): HICON; overload;
    class function GetBmp(id: integer): Graphics.TBitmap; overload; // conflicts with "Windows" unit
    class function GetBmp(id: integer; size: TStockIconSize;
      overlay, selected: boolean): Graphics.TBitmap; overload;
  end;

implementation

uses
  SysUtils, ShellAPI;

{ TStockIcon }

class function TStockIcon.GetIcon(id: integer; size: TStockIconSize;
  overlay, selected: boolean): HICON;
var
  flags: cardinal;
  SSII: TSHStockIconInfo;
  ResCode: HResult;
begin
  // you always want to get an icon so SHGSI_ICON is here in all choices
  case size of
    sisLarge: flags := SHGSI_ICON or SHGSI_LARGEICON;
    sisSmall: flags := SHGSI_ICON or SHGSI_SMALLICON;
    sisShellSize: flags := SHGSI_ICON or SHGSI_SHELLICONSIZE;
  end;

  if selected then
     flags := flags OR SHGSI_SELECTED;
  if overlay then
     flags := flags OR SHGSI_LINKOVERLAY;

  SSII.cbSize := SizeOf(SSII);
  ResCode := SHGetStockIconInfo(id, flags, SSII);

  if ResCode <> S_OK then
  begin
    if ResCode = E_INVALIDARG then
      raise Exception.Create(
        'The stock icon identifier [' + IntToStr(id) + '] is invalid')
    else
      Result := 0;
  end
  else
    Result := SSII.hIcon;
end;

class function TStockIcon.GetIcon(id: integer): HICON;
begin
  Result := GetIcon(id, sisLarge, false, false);
end;

class function TStockIcon.GetBmp(id: integer): TBitmap;
begin
  Result := GetBmp(id, sisLarge, false, false);
end;

class function TStockIcon.GetBmp(id: integer; size: TStockIconSize; overlay,
  selected: boolean): TBitmap;
var aIcon: HICON; tempIcon: TIcon;
begin
  aIcon := GetIcon(id, size, overlay, selected);
  tempIcon := TIcon.Create;
  try
    tempIcon.Handle := aIcon;
    Result := Graphics.TBitmap.Create;
    tempIcon.AssignTo(Result);
  finally
    tempIcon.Free;
  end;
end;

end.

What is new to Delphi 2010 is the possibility to easily convert icons to bitmaps with new: "TIcon.AssignTo(Dest: TPersistent); override;" method that can take a TBitmap param as "Dest".

Here is the output of my test Delphi 2010 application running on Windows 7 and displaying all Stock Icons as bitmaps in a Delphi VCL Form:

The source code for "StockIconUtil" Delphi unit and a test application can be downloaded from the EDN Code Central.

{ 5 } Comments

  1. Chris | May 31, 2010 at 11:16 am | Permalink

    There’s no need to fully qualify TBitmap, since you put Graphics after Windows in the uses clause (as is normal).

  2. Pawel Glowacki | May 31, 2010 at 11:19 am | Permalink

    Thx Chris, you are right. I just like to be explicit:-)

  3. Alister Christie | June 1, 2010 at 12:27 am | Permalink

    It’s interesting to see a few old 16 colour icons disbursed amongst the 32 bit icons.

  4. Keld R. Hansen | June 3, 2010 at 5:36 am | Permalink

    What if one doesn’t have Delphi 2010, but only - say - 2007?

    How would you then go about converting a TIcon to a TBitMap?

  5. Pawel Glowacki | August 9, 2010 at 2:04 pm | Permalink

    @Keld,
    that’s a good question:-)

Post a Comment

Your email is never published nor shared. Required fields are marked *

Bad Behavior has blocked 2 access attempts in the last 7 days.

Close