×
INTELLIGENT WORK FORUMS
FOR COMPUTER PROFESSIONALS

Log In

Come Join Us!

Are you a
Computer / IT professional?
Join Tek-Tips Forums!
  • Talk With Other Members
  • Be Notified Of Responses
    To Your Posts
  • Keyword Search
  • One-Click Access To Your
    Favorite Forums
  • Automated Signatures
    On Your Posts
  • Best Of All, It's Free!
  • Students Click Here

*Tek-Tips's functionality depends on members receiving e-mail. By joining you are opting in to receive e-mail.

Posting Guidelines

Promoting, selling, recruiting, coursework and thesis posting is forbidden.

Students Click Here

Catching OnHelp in Delphi 7 - Event never fires
2

Catching OnHelp in Delphi 7 - Event never fires

Catching OnHelp in Delphi 7 - Event never fires

(OP)
I have been trying to detect when the user is trying to open the help for something. Switching a form to "KeyPreview:= True" and catching the "OnKeyDown" event works, but does not cut it. Plus, this project has hundreds of forms, and I'm not going to go do this on each and every form, and try to remember later to add it to any new forms, etc.

Therefore, I have been seeking a global application-level event handler for catching the help. In the "Application" object, there is in fact an event "OnHelp" which I tried to use, but for some reason this event is never triggered. The goal is to provide 2 help resources for the application: A) regular HTML help (.chm files) which is already there and working, and B) online help to open a corresponding webpage instead of the local help file. In order to do so, I have to catch any possible event of trying to open the help (not just F1), catch the HelpContext ID, cancel the local help file from opening, then open the web page in its place.

Here's my code related to what I'm trying to do...

CODE

function TForm1.AppHelp(Command: Word; Data: Longint; var CallHelp: Boolean): Boolean;
begin
  //Breakpoint here is never reached
  if WebHelp then begin //Check if user's option to use Web Help is enabled
    CallHelp:= False; //Cancel local help from opening
    DoHelp(Data); //Open web help instead
    //is "Data" supposed to be the HelpContext ID?
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Application.OnHelp:= AppHelp; //assigning function to event
end;

procedure TForm1.DoHelp(const HelpContext: Integer);
const
  WebRoot = 'http://mycompanywebsiteroot.com/Help.htm?id=';
begin
  //Help.htm is a page that redirects to proper page based on 'id' parameter
  ShellExecute(WindowHandle, 'open', PChar(WebRoot+IntToStr(HelpContext)), nil, nil, SW_SHOWNORMAL); //launch web page in default browser
end;
 

JD Solutions

RE: Catching OnHelp in Delphi 7 - Event never fires

Only a guess, but check the value of Keypreview.

If it's not true, then the F1 key will not be seen by the form, but by the active control.

RE: Catching OnHelp in Delphi 7 - Event never fires

(OP)
No, this is definitely not the problem. Like I said, I don't have any difficulties using the form's OnKeyDown event, and KeyPreview is in fact enabled. But this is NOT the method I want to use - I have hundreds of forms and I will not put this code on all hundred+ forms. The point is I need an application level event handler - which has nothing to do with catching a key press.

JD Solutions

RE: Catching OnHelp in Delphi 7 - Event never fires

Then you'll want to handle the WM_HELP message in your application.  That way, you won't have to handle it on each of your forms.

CODE

  TForm1 = class(TObject)
  ...
  private
    { Private declarations }
    procedure WMHELP(var Msg: TWMHelp); message WM_HELP;
  public
   ...
  end;

procedure TForm1.WMHELP(var Msg: TWMHelp);
begin
   //handle help message here.
end;

RE: Catching OnHelp in Delphi 7 - Event never fires

(OP)
Thank you, that half way answers my question. Now the problem is 2 things: A) How do I get the HelpContext ID which corresponds with the control which was focused, and B) How do I cancel the local help file from opening in this case?

And also, ok I can do this on a form, but do I have to do this on all forms? Can I just do it on the main form and the rest will work?

JD Solutions

RE: Catching OnHelp in Delphi 7 - Event never fires

No, as a message handler, just include it in the main form, it should work for all your forms.  

You should be able to get the helpcontext from the active control:

CODE

procedure TForm1.WMHELP(var Msg: TWMHelp);
var
   ActiveCtrlID: Integer;
begin
   ActiveCtrlID:= Screen.ActiveControl.HelpContext;
   //handle help message here.
end;

RE: Catching OnHelp in Delphi 7 - Event never fires

As far as cancelling the local help, I'd have to see how it's triggered now.

Maybe a flag can be set within the message handler that is checked before the local help is opened.  If the flag is set, don't open help, and turn off the flag, else open the local help.

RE: Catching OnHelp in Delphi 7 - Event never fires

(OP)
After digging and digging into this, I found that this message also for some reason is missing some info. The help context ID (Msg.HelpInfo.dwContextId) always returns 0. I think Msg.Result may be able to cancel the action, don't know yet. But I absolutely need to figure out a way to get this context id. Remember, I have hundreds of forms in this project so I need something on the global application level, not on the form level.

JD Solutions

RE: Catching OnHelp in Delphi 7 - Event never fires

(OP)
* clarifying what I mean - I'm sure I'm not the first person who's done this. There has to be some source already done exactly for this. Any tips would be great.

JD Solutions

RE: Catching OnHelp in Delphi 7 - Event never fires

I neglected to add the call to the inherited WMHelp message from within the form's message handler.

CODE

procedure TForm1.WMHELP(var Msg: TWMHelp);
begin
   inherited;
   ...
end;

See if that sets the help context ID.
 

RE: Catching OnHelp in Delphi 7 - Event never fires

A few item's I've found looking into this, hopefully one of them helps.

This from Quality Central

CODE

The context-sensitive help isn't shown as a hint when using the bordericon Help in a Form, but it's shown as normal helpcontext.This is due to the following function from the RTL Unit HelpIntfs:

function THelpManager.Hook(Handle: Longint; HelpFile: String; Command: Word; Data: Longint): Boolean;
begin
  if HelpFile <> '' then FHelpFile := HelpFile;
  case Command of
    HELP_CONTEXT:
    begin
     ShowContextHelp(Data, HelpFile);
    end;
    { note -- the following subtly turns HELP_CONTEXTPOPUP into HELP_CONTEXT.
      This is consistent with D5 behavior but may not be ideal. }
    HELP_CONTEXTPOPUP:
    begin
     ShowContextHelp(Data, HelpFile);
    end;
    HELP_QUIT:
    begin
     DoSoftShutDown;
    end;
    HELP_CONTENTS:
    begin
      DoTableOfContents;
    end;
  else
    CallSpecialWinHelp(Handle, HelpFile, Command, Data);
  end;
  Result := true;
end;

The case HELP_CONTEXTPOPUP is treated the same as HELP_CONTEXT, which causes the display of the normal helpcontext instead of a hint.
To solve this you have to clear the case HELP_CONTEXTPOPUP, then the procedure CallSpecialWinHelp will be called, which does show the context-sensitive help as a hint
If you don't want to temper with the RTL, it is of course also possible to hook the WM_HELP message yourself and write something like this:

procedure TForm1.WMHelp(var Message: TWMHelp);
var
  Control: TWinControl;
begin
  Control := FindControl(Message.HelpInfo^.hItemHandle);
  WinHelp(Screen.ActiveForm.Handle,pChar(Application.HelpFile),HELP_CONTEXTPOPUP, Control.HelpContext);
end;

This from experts-exchange

CODE

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    Edit1: TEdit;
    Label1: TLabel;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    procedure WMHELP(var Msg: TWMHelp); message WM_HELP;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.WMHELP(var Msg: TWMHelp);
begin
  Edit1.Text := IntToStr(Msg.HelpInfo.hItemHandle);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Label1.Caption := IntToStr(Memo1.Handle);
end;

RE: Catching OnHelp in Delphi 7 - Event never fires

(OP)
I got very close with it... but it's still on a form-by-form basis. Using Screen.ActiveControl.HelpContext works, but isn't very sufficient, because if I have for example the [?] showing in the top-right next to [X] (Close) (which turns the pointer into a help pointer, click a control to get help on it) - that doesn't work. The control doesn't get the focus when clicked, so it's not getting the proper help context.

I'll take a look into those posts you put up there. Thank you.

JD Solutions

RE: Catching OnHelp in Delphi 7 - Event never fires

(OP)
Getting the item handle looks to be promising, as in this second example of yours. I did in fact get the correct handle - but now what? How to get the HelpContext ID of the corresponding TWinControl of this handle? I guess what I mean is how to use this handle to get an instance of this control?
 

JD Solutions

RE: Catching OnHelp in Delphi 7 - Event never fires

Try

CODE

var
  Control: TWinControl;
begin
  with Msg.HelpInfo^ do
  begin
    Control := FindControl(hItemHandle);
    Edit1.Text := intToStr(Control.HelpContext);

Another link with some useful information - Not Delphi specific, but shows how to handle Help initiated from various events..

http://www.helpfulsolutions.com/ctx_sens_handout.html

RE: Catching OnHelp in Delphi 7 - Event never fires

(OP)
Thanks - I'm not used to handling handles :P

While I'm doing my testing for this in Delphi 7, the real project where this will come into play is in Delphi 2010. The Application.OnHelp event does work in D2010, but for some reason fires 3 times in a row every time. I certainly don't want to open the web page 3 times in a row.

Now we are also looking into upgrading entirely to Delphi XE2 (specifically Firemonkey). We have a demo of this, and upon testing, the Application.OnHelp event works perfectly. Only one weird thing here is "Integer" has now become "NativeInt" which is very strange. I've never used NativeInt, but somehow it is supported in earlier versions of Delphi (just not used anywhere). This is due to the new 64 bit compiling capabilities of XE2.

So I may have a different approach in the end for this. Thanks for all the help on the help.

JD Solutions

RE: Catching OnHelp in Delphi 7 - Event never fires

(OP)
(finishing my last post) - My 2 computers only have Delphi 7 - due to licenses and what-not only the core developers have D2010. I use D7 personally all the time, I think it's the most stable version (and I trust Borland much more than Embarcadero). Today I worked with the devs and did the testing on D2010 and DXE2 and got those results. It looks as if this help functionality was somehow broken in D7 (while probably worked fine in earlier Delphi versions) - then in D2010 they tried to make it work but was somehow buggy - then finally in XE2 (or maybe even just XE) they got it working again. Of course using the message method as you recommend should presumably be supported in all versions - so long as I finish my code to be compatible. Once I get it 100% working, maybe I'll post an FAQ here on "Adding Online Help to Delphi Projects".

JD Solutions

RE: Catching OnHelp in Delphi 7 - Event never fires

Looking forward to your FAQ post... interesting note about how help (doesn't) work in 2010.  I have D7, D2010 and I just purchased XE2, but have yet to install it, I'll have to test it when I get all three environments up and running.

RE: Catching OnHelp in Delphi 7 - Event never fires

Quote:

I got very close with it... but it's still on a form-by-form basis.

I read this and I'm not sure precisely what the issues are, but regarding the above quote, why not just tie into the Application level event ([link=http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/delphivclwin32/Forms_TApplication_OnHelp.html]TApplication.OnHelp[/link]) and be done with it from there?  That's what I've been doing with this little HLP->CHM unit I've been trying to work out off and on...

It is not possible for anyone to acknowledge truth when their salary depends on them not doing it.

RE: Catching OnHelp in Delphi 7 - Event never fires

(OP)
Glenn,

This is the entire problem of mine. In Delphi 7, Application.OnHelp is never triggered. I did make an event handler for this and assigned it, but no cigaro. It must be a bug in Delphi 7, because I do get success in later versions of Delphi.

JD Solutions

RE: Catching OnHelp in Delphi 7 - Event never fires

Okay...I've just been setting TApplication.HelpFile and it's been working for me just by including the unit.  I did look and found this, which indicates a bug fix for  > Delphi 6 and < Delphi 2009 for OnHelp:

http://helpware.net/downloads/index.htm#D6OnHelpFix

Look for Example12.zip and download that to study the D6OnHelpFix.pas file.  Hopefully that will point you in the right direction...

 

It is not possible for anyone to acknowledge truth when their salary depends on them not doing it.

RE: Catching OnHelp in Delphi 7 - Event never fires

(OP)
Wow, that works beautifully =D Thanks

JD Solutions

RE: Catching OnHelp in Delphi 7 - Event never fires

Basically I've been trying to learn how to use HtmlHelp and try to figure out all the things I could ever want to know, as well as get it working with my things.  One of the things I've run into is the following code.  What it does is that it puts a text window on top of a specified form with the text you want in the position you want it in.  Found it interesting anyway.

CODE

function HtmlHelp(hwndCaller: THandle; pszFile: PChar; uCommand: cardinal; dwData: longint): THandle; stdcall;

implementation

function HtmlHelp; external hhctrllib name 'HtmlHelpA';

function ShowPopupString1(Parent: TForm; Text: string; Position: TPoint): HWnd;
var
  popupstruct: TPopupStruct;
begin
  with popupstruct do
    begin
      cbStruct := sizeof(popupstruct);
      hinst := 0; // instance handle if str res
      idString := 0; // zero, resourceid or topicid
      pszText := PChar(Text); // text to display if idString = 0
      pt := Position;
      clrForeground := COLORREF(-1); // default RGB value
      clrBackground := COLORREF(-1); // default RGB value
      rcMargins := Rect(-1, -1, -1, -1);
      pszFont := 'MS Sans Serif, 10, , BOLD';
   end;
  Result := HtmlHelp(Parent.Handle, nil, HH_DISPLAY_TEXT_POPUP,
            DWord(@popupstruct));
end;

It is not possible for anyone to acknowledge truth when their salary depends on them not doing it.

RE: Catching OnHelp in Delphi 7 - Event never fires

Some pieces I forgot in the previous post:

CODE

const
  hhctrllib = 'hhctrl.ocx';

type
TPopupStruct = packed record
    cbStruct:      Integer;     // sizeof this structure
    hinst:         HINST;       // instance handle
   // string resource id, or text id if pszFile is specified
    idString:      cardinal;
    pszText:       PChar;       // used if idString is zero
    pt:            TPOINT;      // top center of popup window
    clrForeground: COLORREF;    // use -1 for default
    clrBackground: COLORREF;    // use -1 for default
// amount of space between edges of window and text,
// -1 for each member to ignore
    rcMargins:     TRect;       
   // facename, point size, char set, BOLD ITALIC UNDERLINE
    pszFont:       PChar;    
  end;

It is not possible for anyone to acknowledge truth when their salary depends on them not doing it.

Red Flag This Post

Please let us know here why this post is inappropriate. Reasons such as off-topic, duplicates, flames, illegal, vulgar, or students posting their homework.

Red Flag Submitted

Thank you for helping keep Tek-Tips Forums free from inappropriate posts.
The Tek-Tips staff will check this out and take appropriate action.

Reply To This Thread

Posting in the Tek-Tips forums is a member-only feature.

Click Here to join Tek-Tips and talk with other members! Already a Member? Login

Close Box

Join Tek-Tips® Today!

Join your peers on the Internet's largest technical computer professional community.
It's easy to join and it's free.

Here's Why Members Love Tek-Tips Forums:

Register now while it's still free!

Already a member? Close this window and log in.

Join Us             Close