Using Windows Stock Icons in Delphi

Posted by on in Blogs
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.
About
Gold User, Rank: 9, Points: 364
Crazy about Delphi Programming!
Comments are not available for public users. Please login first to view / add comments.