I have a form I want to show the user but only if they know the "special" keycode combination provided in a tech-support session. That means I need to capture a ctrl-alt-F10 (or some such keystroke combination).
I'm not sure any more about the background in getting my F1 help key to work, but it works no matter what form or control is active. I think the concepts were taken from a posting by Roger Garstang. (Thanks Roger).
Here is a code snippet from my main form custom function. You can see the additional case statements that I'm using to attempt to trap the Key Up condition. Nothing except the F1 key gets through here. :(
My main form contains two tab controls. When I put the wMsg case statement in the active form's custom function in the first tab, I also get no wMsg activity. When I put the wMsg case statement into the first text box custom function in that form I get the folowing codes for presses of F2, F3, F5, F6, F7, F8, F9, F10 (from of my log):
TMRkeyUp..Key Press: 257 113 -1069809663 <- "113" is the result of wParam and -1069809663 is lParam
TMRkeyUp..Key Press: 257 114 -1069744127
TMRkeyUp..Key Press: 257 116 -1069613055
TMRkeyUp..Key Press: 257 117 -1069547519
TMRkeyUp..Key Press: 257 118 -1069481983
TMRkeyUp..Key Press: 257 119 -1069416447
TMRkeyUp..Key Press: 257 120 -1069350911
First: How do I get these codes to "roll up" to the CUSTOM function of FRMMAIN?
Second: How do I trap for the control and the alt keys?
Any help would be much appreciated. Thanks, Dwight Scoles
Function FRMMAIN_CUSTOM ( _
hWndForm As Dword, _ ' handle of Form
wMsg As Long, _ ' type of message
wParam As Dword, _ ' first message parameter
lParam As Long _ ' second message parameter
) As Long
Local HelpType As String
Local TestVal As String
Local CtrlID As String
Local HlpMsg As String
Local iHelp As HELPINFO
Local hlpinfo As HELPINFO Ptr 'Pointer for Context Help Structure
Local CurrTab As Long
HelpType = "Table Listing"
Select Case wMsg
Case %WM_HELP
hlpinfo = lParam
iHelp = @hlpinfo
CtrlID = FF_Control_GetTag (iHelp.hItemHandle)
If CtrlID <> "" Or CurrTable = "" Then
CurrTab = FF_TabControl_GetSelectedTab (HWND_FRMMAIN_TABENTRY)
Select Case CurrTab
Case %KTI_Tab
If CtrlID = "" Then
CtrlID = FF_Control_GetTag(HWND_FRMKTIENTRY_TXTREFNBR)
End If
HlpMsg = " First KTI Field"+CtrlID +" help ID"+Str$(iHelp.iCtrlID) +_
" handle:"+Str$(iHelp.hItemHandle)
Case Else
HlpMsg = "Help Custom button pressed:"+CTRLID+" "+Str$(iHelp.iCtrlID) +_
" handle:"+Str$(iHelp.hItemHandle)
End Select
MsgBox hlpmsg
Else
' List Tab is active
CurrTab = FF_TabControl_GetSelectedTab (HWND_FRMMAIN_TABTABLES)
TestVal = UCase$(CurrTable)
Select Case TestVal
Case "RULE"
HelpType = "Rule Listing"
Case "KTI"
HelpType = "KTI Listing"
Case "ROUTINE"
HelpType = "Routine Listing"
Case "STRING"
HelpType = "String Listing"
Case "CHAIN"
HelpType = "Chain Listing"
Case "CODE"
HelpType = "Codes Listing"
Case "DOMAIN"
HelpType = "Domain Listing"
Case "WHOUSE"
HelpType = "WhoUse Listing"
Case "STUCKTO"
HelpType = "Stuck-To Listing"
Case "RULESTUCKTO"
HelpType = "RULE Stuck-To Listing"
Case "RULEKTI"
HelpType = "RULE KTI Listing"
Case "RULEWHOUSE"
HelpType = "RULE WHOUSE Listing"
Case "RULEROUTINE"
HelpType = "RULE ROUTINE Listing"
Case "RULECHAIN"
HelpType = "RULE CHAIN Listing"
Case "RULESTRING"
HelpType = "RULE String Listing"
Case "TMKTI"
HelpType = "TM KTI String Listing"
Case "TMRULES"
HelpType = "TM RULES String Listing"
Case "TMPRACTICES"
HelpType = "TM PRACTICES String Listing"
Case "TMTEMPLATES"
HelpType = "TM TEMPLATES String Listing"
Case "TMOTHER"
HelpType = "TM OTHER String Listing"
Case Else
HelpType = "?? Listing"
End Select
MsgBox "Help Custom button pressed"+Str$(iHelp.hItemHandle)+Str$(iHelp.iCtrlID) +" "+HelpType
End If
Function= %TRUE
Case %WM_KEYUP
writelog "","Key Press:"+Str$(wParam)+" "+Str$(lParam)
Case 1
WRITELOG "",Str$(%WM_KEYUP)+" "+Str$(wParam)+" "+Str$(lParam)
End Select
Hi Dwight,
The easiest way to handle this is to set up your own Accelerator Table to catch the keypress. That way you don't have to screw around with WM_KEYUP or WM_KEYDOWN, etc...
Here is an example based on the main form named as FORM1
Global ghAccel As Long
%IDM_SPECIALCODE = %WM_USER + 100
'------------------------------------------------------------------------------------------------------------------------
Function FORM1_WM_CREATE ( _
hWndForm As Dword, _ ' handle of Form
ByVal UserData As Long _ 'optional user defined Long value
) As Long
Dim a(1 To 1) As ACCELAPI
a(1).fVirt = %FNOINVERT Or %FCONTROL Or %FALT Or %FVIRTKEY
a(1).key = %VK_F10
a(1).cmd = %IDM_SPECIALCODE
ghAccel = CreateAcceleratorTable(a(1), 1)
End Function
'------------------------------------------------------------------------------------------------------------------------
Function FORM1_WM_COMMAND ( _
hWndForm As Dword, _ ' handle of Form
hWndControl As Dword, _ ' handle of Control
wNotifyCode As Long, _ ' notification code
wID As Long _ ' item, control, or accelerator identifer
) As Long
Select Case wID
Case %IDM_SPECIALCODE
MsgBox "Special tech support key press..."
End Select
End Function
'------------------------------------------------------------------------------------------------------------------------
Function FORM1_WM_DESTROY ( _
hWndForm As Dword _ ' handle of Form
) As Long
' Destroy the accelerator table
If ghAccel Then DestroyAcceleratorTable ghAccel
End Function
You also need to modify the FF_PumpHook FireFly function that found under the "Special Functions" tree branch in the Project Explorer. Add the following line to the end of the function:
If IsTrue TranslateAccelerator( HWND_FORM1, ghAccel, Msg) Then Function = %TRUE
The FF_PumpHook should now look something like this:
Function FF_PUMPHOOK(Msg As tagMsg) As Long
'If this function returns FALSE (zero) then the processing of the regular
'FireFly message pump will continue. Returning TRUE (non-zero) will bypass
'the regular message pump.
Function = %FALSE 'return %TRUE if you need to bypass the FireFly message pump
'If you are dealing with a 'normal' OCX control then the following code will
'allow the message to be forwarded to the OCX. Some OCX's have child windows
'so it may be necessary to modify the GetFocus() to properly reflect the correct window.
'If you are not using any OCX's then you can simply comment or delete this line.
Function = SendMessage( GetFocus(), &H37F, 0, VarPtr(Msg))
If IsTrue TranslateAccelerator( HWND_FORM1, ghAccel, Msg) Then Function = %TRUE
End Function
I ran my sample program and it worked perfectly. You may need to change the %IDM_SPECIALCODE value if you have already used "%WM_USER + 100" for some other purpose.
Hope this works for you!
Hi Paul,
thanks for the great support!
This is a very, very useful sample to me. :)
Rudolf Fürstauer
Paul,
As always, you have provided a simple method to apply to a confounding issue! You are the best! I'll apply this update tonight as soon as I get back to my system. I saw your previous post about the FF_Pumphook function but could not figure out how to make it work for my situation. This is a great expansion on the theme.
Thanks for all the great support! (I can hardly wait for FF3 ;D)
Dwight
Thank you Paul! This works beautifully 8)
If I can ask two questions (anybody?) about what is going on with the F1 Help key...
1) I do not recall doing anything special to make the F1 key press "visible". Why does the F1 key stroke pass through into the CUSTOM function in my main form, but no other "virtual" function keys do? (Perhaps I did code something but simply do not remember? I have 43 forms in this application and you have helped me out with a number of issues...)
2) And, why is the %IDM_SPECIALCODE trapped in WM_COMMAND for the main form, and not in CUSTOM? (Dummy me... I initially put the code in CUSTOM and of course nothing happened in response to alt-ctrl-F10)
This was so clean, especially in comparison to what I found doing searches on F1 and Function Keys etc. in the main language forum. I realize it is like comparing Apples and Peanut Butter, but still... The best part is that your recommendation worked!
Again, thank you for such a quick response with a perfect solution!
Dwight
To answer the second question, you trap the %IDM_SPECIALCODE in WM_COMMAND because the TranslateAccelerator call translates the WM_KEYDOWN message to a WM_COMMAND message.
If you redefine your F1 key as part of the new Accelerator Table (in the same manner as your %IDM_SPECIALCODE) then you should be able to capture it as well in %WM_COMMAND.
Here is what the WinAPI help file has to say about the special "F" keys:
Quote
If an application defines an accelerator that is also defined in the system accelerator table, the application-defined accelerator overrides the system accelerator, but only within the context of the application. Avoid this practice, however, because it prevents the system accelerator from performing its standard role in the Windows user interface. The system-wide accelerators are described in the following list:
Accelerator Description
ALT+ESC Switches to the next application.
ALT+F4 Closes an application or a window.
ALT+HYPHEN Opens the System menu for a document window.
ALT+PRINT SCREEN Copies an image in the active window onto the clipboard.
ALT+SPACEBAR Opens the System menu for an application window.
ALT+TAB Switches to the next application.
CTRL+ESC Switches to Windows Task List.
CTRL+F4 Closes the active group or document window.
F1 Starts Help if the application has Help.
PRINT SCREEN Copies an image on the screen onto the clipboard.
SHIFT+ALT+TAB Switches to the previous application. The user must press and hold down ALT+SHIFT while pressing TAB.
Here is how you would modify my last example to capture the F1 key:
Global ghAccel As Long
%IDM_SPECIALCODE = %WM_USER + 100
%IDM_F1KEY = %WM_USER + 101
Function FORM1_WM_CREATE ( _
hWndForm As Dword, _ ' handle of Form
ByVal UserData As Long _ 'optional user defined Long value
) As Long
Dim a(1 To 2) As ACCELAPI
a(1).fVirt = %FNOINVERT Or %FCONTROL Or %FALT Or %FVIRTKEY
a(1).key = %VK_F10
a(1).cmd = %IDM_SPECIALCODE
a(2).fVirt = %FNOINVERT Or %FVIRTKEY
a(2).key = %VK_F1
a(2).cmd = %IDM_F1KEY
ghAccel = CreateAcceleratorTable(a(1), 2)
End Function
Function FORM1_WM_COMMAND ( _
hWndForm As Dword, _ ' handle of Form
hWndControl As Dword, _ ' handle of Control
wNotifyCode As Long, _ ' notification code
wID As Long _ ' item, control, or accelerator identifer
) As Long
Select Case wID
Case %IDM_SPECIALCODE
MsgBox "Special tech support key press..."
Case %IDM_F1KEY
MsgBox "F1 Pressed"
End Select
End Function
Function FORM1_WM_DESTROY ( _
hWndForm As Dword _ ' handle of Form
) As Long
' Destroy the accelerator table
If ghAccel Then DestroyAcceleratorTable ghAccel
End Function
Paul,
Thank you for your kind response. I appreciate the little teaching session. I'm an old programmer from the CRT Terminal world, and the guts of the whole WINDOWS/GUI operations seem like a form of dark arts to me. :)
Dwight