Windows 8.1Comments Off on WinHlp32 for Windows 8.1
Dec202015
Microsoft dropped support for the old WinHelp (*.hlp) file format in Windows 7 (or was it in Vista?). They provided a download that added the missing WinHlp32.exe (and probably quite a few other files) back so we could display .hlp files again. Today I had the need to do that on Windows 8.1. Unfortunately I run into several issues:
The download is a .msu file which doesn’t install if your windows does not have one of the supported language packs. If you get the error “The update does not apply to your system”, install the English-US language pack (I didn’t have that, I used English-GB.)
The installer package takes forever to prepare, then asks you whether you want to install KB917607. After a while it looks as if it is finished. I tried to open a .hlp file and it still didn’t work. Looking closer revealed yet another window open requiring me to acknowledge the license terms. So I did that and lo and behold: I could finally view .hlp files again.
Why does Microsoft make everything so complicated?
Since Delphi 2005 the IDE provides the SplashScreenServices interface that is available even before the other interfaces that make up the ToolsAPI. It allows adding an icon to the splash screen quite easily:
uses
ToolsAPI;
// [...]
procedure AddPluginToSplashScreen(_Icon: HBITMAP; const _Title: string; const _Version: string);
{$IFDEF GX_VER170_up}
// Only Delphi 2005 and up support the splash screen services
begin
if Assigned(SplashScreenServices) then
SplashScreenServices.AddPluginBitmap(_Title,
_Icon, False, _Version);
end;
{$ELSE GX_VER170_up}
begin
end;
So, I could now stop writing this article if it weren’t for the case that GExperts supports Delphi 6 and 7 as well and I am stubborn enough to want that functionality for these as well. The SplashScreenServices didn’t exist back then, but it is still possible to do the same even though not as easily as the above.
It’s still no rocket science. Basically, it’s the following steps:
Find the splash screen form.
Decide where to place the icon and text.
Add the necessary controls.
For getting the splash screen, we use the global Screen object which is available to all IDE plugins. It’s just a matter of enumerating all existing forms for which the class provides the Forms and FormCount properties:
for i := 0 to Screen.FormCount - 1 do begin
frm := Screen.Forms[i];
if (frm.Name = 'SplashScreen') and frm.ClassNameIs('TForm') then begin
// we've got it
end;
(Of course it took me some digging to find out what the form is called and what class it is, but it isn’t rocket science either. (Why do I come back to rocket science all the time?))
Now, where do we place the additional information?
There is space for about 4 lines with 32×32 bitmaps and two lines of text to the right of the 6 or 7 respectively. Some experimenting gave me the pixel position: X=140, Y=150.
So, all we need to do is add three controls, one TImage and two labels, to the form, fill in the necessary properties and be done.
All controls use the form as the owner, so they will be freed automatically. I chose to use unique names for the controls, it would have been easier to just set the name to empty but we’ll come to that in a second. Assigning a HBitmap to a TImage is done by assigning it to the Picture.Bitmap.Handle property. I set the font color to white because black didn’t look too nice. Also I made the title bold. Oh, and of course I made the labels transparent, so they don’t look too ugly.
Now, why use unique names for the controls? Because I was thinking about reuse. I need it for GExperts now, but why not make it usable for other plugins as well? Like my Delphi IDE explorer Expert for which there is also a Delphi 6 and 7 version now.
As said above, there is space for 4 such entries. We will assume that we never need more than that for now. đ
By using unique control names it is easy to find out how many of the slots are already taken and add another one:
PluginIdx := 0;
while frm.FindComponent(PluginLogoStr + IntToStr(PluginIdx)) <> nil do
Inc(PluginIdx);
Putting it all together gives us a reusable procedure which we can add to any plugin without even thinking about Delphi versions:
procedure AddPluginToSplashScreen(_Icon: HBITMAP; const _Title: string; const _Version: string);
{$IFDEF GX_VER170_up}
// Only Delphi 2005 and up support the splash screen services
begin
if Assigned(SplashScreenServices) then
SplashScreenServices.AddPluginBitmap(_Title,
_Icon, False, _Version);
end;
{$ELSE GX_VER170_up}
const
XPOS = 140;
YPOS = 150;
PluginLogoStr = 'PluginLogo';
var
imgLogo: TImage;
lblTitle: TLabel;
lblVersion: TLabel;
i: integer;
PluginIdx: integer;
frm: TCustomForm;
begin
for i := 0 to Screen.FormCount - 1 do begin
frm := Screen.Forms[i];
if (frm.Name = 'SplashScreen') and frm.ClassNameIs('TForm') then begin
PluginIdx := 0;
while frm.FindComponent(PluginLogoStr + IntToStr(PluginIdx)) <> nil do
Inc(PluginIdx);
imgLogo := TImage.Create(frm);
imgLogo.Name := PluginLogoStr + IntToStr(PluginIdx);
imgLogo.Parent := frm;
imgLogo.AutoSize := True;
imgLogo.Picture.Bitmap.Handle := _Icon;
imgLogo.Left := XPOS;
imgLogo.Top := YPOS + 32 * PluginIdx;
lblTitle := TLabel.Create(frm);
lblTitle.Name := 'PluginTitle' + IntToStr(PluginIdx);
lblTitle.Parent := frm;
lblTitle.Caption := _Title;
lblTitle.Top := imgLogo.Top;
lblTitle.Left := imgLogo.Left + 32 + 8;
lblTitle.Transparent := True;
lblTitle.Font.Color := clWhite;
lblTitle.Font.Style := [fsbold];
lblVersion := TLabel.Create(frm);
lblVersion.Name := 'PluginVersion' + IntToStr(PluginIdx);
lblVersion.Parent := frm;
lblVersion.Caption := _Version;
lblVersion.Top := imgLogo.Top + lblTitle.Height;
lblVersion.Left := imgLogo.Left + 32 + 20;
lblVersion.Transparent := True;
lblVersion.Font.Color := clWhite;
end;
end;
end;
{$ENDIF GX_VER170_up}