Trap ctrl/alt function key at the application level

Started by Dwight Scoles, February 04, 2008, 12:14:14 AM

Previous topic - Next topic

Dwight Scoles

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



TechSupport

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!






Rudolf Fürstauer

Hi Paul,

thanks for the great support!

This is a very, very useful sample to me.   :)


Rudolf Fürstauer

Dwight Scoles

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

Dwight Scoles

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

TechSupport

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




Dwight Scoles

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