- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I have constructed a QuickWin application in Visual Fortran. The application uses its own menu system, but retains many of the standard features of QuickWin (e.g., status bar).
I am trying to write my own shutdown for the app. I cannot use the WINEXIT call or the standard close icon on the caption bar because it will mean auxiliary files are not closed properly. I am using the WINSTATE function to Pause/Resume execution. I have noted that using this function activates Ctrl-S/Ctrl-Q for the menu item in theapp.
1) How might I attach Ctrl-C (or others) to my menu commands? In the Windows API example of "Creating a Run-Time Accelerator Table", the example always has a handle for the existing accelerator table which is revised and then (automatically) reloaded. How can I obtain the handle to the accelerator table of QuickWin?
2) How do I capture the mouse event click of theclose icon on the caption bar for a safe shutdown?
Slippery
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
JIMB@GEMINISI.COM:
1) How might I attach Ctrl-C (or others) to my menu commands? In the Windows API example of "Creating a Run-Time Accelerator Table", the example always has a handle for the existing accelerator table which is revised and then (automatically) reloaded. How can I obtain the handle to the accelerator table of QuickWin?
Yes, classic accelerators are out in QuickWin, as you don't have a place to put TranslateAccelerator into. Instead, use a keyboard hook, and just call appropriate menu handler routine when you detect the appropriate key combination. See this old thread for a sample.
To indicate the shortcut in the menu, separate it with a tab ("t") character in menu title. For example:
"&Open Ctrl+O"
JIMB@GEMINISI.COM:
2) How do I capture the mouse event click of theclose icon on the caption bar for a safe shutdown?
You have to subclass the frame window (SetWindowLong(GWL_WNDPROC)) and catch the WM_CLOSE. See this thread with a sample.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thank you for the info. I am still having some trouble capturing keyboard events. Consider the attached code. When run, I get a 'valid' keyboard hook value, and yet the KeyboardProc produces no output.
SUBROUTINE
CREATE_ACCELERATOR_TABLE()USE kernel32INTEGER (KIND = HANDLE) ABOUTMENU_HANDLE, FILEMENU_HANDLE, MENU_HANDLE, RESTARTMENU_HANDLE! TYPE (T_ACCEL), DIMENSION(3) :: ACCEL_ITEM
! Find the handle to the frame window menu bar
WIN_HANDLE = GETHWNDQQ(QWIN$FRAMEWINDOW)
CHILD_HANDLE = GETHWNDQQ(0_4)
MENU_HANDLE = GETMENU(WIN_HANDLE)
FILEMENU_HANDLE = GETSUBMENU(MENU_HANDLE, 1_SINT)
IDSTOP = GETMENUITEMID(FILEMENU_HANDLE, 1_SINT)
! ACCEL_ITEM(1)%fVirt = FCONTROL
! ACCEL_ITEM(1)%key = IACHAR('C')
! ACCEL_ITEM(1)%cmd = IDSTOP
RESTARTMENU_HANDLE = GETSUBMENU(MENU_HANDLE, 3_SINT)
IDAPPEND = GETMENUITEMID(RESTARTMENU_HANDLE, 0_SINT)
! ACCEL_ITEM(2)%fVirt = FCONTROL
! ACCEL_ITEM(2)%key = IACHAR('A')
! ACCEL_ITEM(2)%cmd = IDAPPEND
IDOVERWRITE = GETMENUITEMID(RESTARTMENU_HANDLE, 1_SINT)
! ACCEL_ITEM(3)%fVirt = FCONTROL
! ACCEL_ITEM(3)%key = IACHAR('O')
! ACCEL_ITEM(3)%cmd = IDOVERWRITE
ABOUTMENU_HANDLE = GETSUBMENU(MENU_HANDLE, 4_SINT)
WRITE
(UNIT = 6, FMT = '(A,1X,Z8.8)') 'Window =', WIN_HANDLE, 'Menu =', MENU_HANDLE, &'File =', FILEMENU_HANDLE, 'Restart =', RESTARTMENU_HANDLE, 'About =', ABOUTMENU_HANDLE, &
'Child =', CHILD_HANDLE
WRITE
(UNIT = 6, FMT = '(A,1X,Z8.8)') 'STOP =', IDSTOP, 'APPEND =', IDAPPEND, 'OVERWRITE=', IDOVERWRITE! ACCEL_TABLE_HANDLE = CREATEACCELERATORTABLE(ACCEL_ITEM(1), 3_SINT)
!WRITE(UNIT = 6, FMT = '(A,1X,Z8.8)') 'ACCEL_TABLE_HANDLE =', ACCEL_TABLE_HANDLE
!Of course, explicit interface for KeyboardProc here.
hKbHook = SetWindowsHookEx(WH_KEYBOARD,
LOC(KeyboardProc), NULL, GetCurrentThreadId())WRITE
(UNIT = 6, FMT = '(A,1X,Z8.8)') 'Handle to the hook procedure =', hKbHookEND SUBROUTINE
CREATE_ACCELERATOR_TABLE!==============================================================
INTEGER
(KIND = 4) FUNCTION KeyboardProc(nCode, wParam, lParam)!DEC$ATTRIBUTES STDCALL :: KeyboardProc
INTEGER (KIND = SINT), INTENT(IN) :: nCodeINTEGER (KIND = UINT_ PTR), INTENT(IN) :: wParamINTEGER (KIND = LONG_PTR), INTENT(IN) :: lParamINTEGER (KIND = HANDLE) hFocusINTEGER iEvent, nKeyINTEGER (KIND = HANDLE) :: hFrame, hChildhFrame = WIN_HANDLE
hChild = CHILD_HANDLE
WRITE
(UNIT = 6, FMT = '(A,1X,Z8.8)') 'nCode =', nCodeIF (nCode >= 0) THEN!Bit 31 specifies transition state of the key
nKey = wParam
IF (IAND(lParam, ISHFT(1, 31)) /= 0) THENiEvent = WM_KEYUP
ELSEiEvent = WM_KEYDOWN
END IFhFocus = GetFocus()
WRITE
(UNIT = 6, FMT = '(A,1X,Z8.8)') 'hFocus =', hFocusIF ((hFocus == hFrame .OR. hFocus == hChild) .AND. iEvent == WM_KEYUP) THENSELECT CASE (wParam)CASE DEFAULTWRITE
(UNIT = 6, FMT = '(''KeyboardProc wParam = '',Z8.8)') nKeyEND SELECTEND IFEND IFKeyboardProc = CallNextHookEx(hKbHook, nCode, wParam, lParam)
END FUNCTION
KeyboardProc
If I run the code with the call
hKbHook = SetWindowsHookEx(WH_KEYBOARD, LOC(KeyboardProc), NULL, 0)
then hKbHook is returned as NULL. Note that all variables undeclared here (including hKbHook)are declared in a MODULE and that 'USE user32' is declared in the module.
Slippery
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
If I'm right (you can verify that by temporarily moving the call to SetWindowsHookEx into any menu callback and then just check if it works), the cure is to subclass the frame window and call SWHE from there. Then, just define your own message and send it from the PROGRAM to the main window, like:
INTEGER, PARAMETER:: WM_INSTALLHOOK = WM_APP+1
lpOldWndProc = SetWindowLong(hFrame, &
GWL_WNDPROC, LOC(MainWndProc))
...
iRet = SendMessage(hFrame, WM_INSTALLHOOK, 0, 0)
...
INTEGER FUNCTION MainWndProc(...)
...
SELECT CASE(Msg)
CASE (WM_INSTALLHOOK)
hKbHook = SetWindowsHookEx(WH_KEYBOARD, LOC(KeyboardProc), NULL, 0)
MainWndProc = 0
CASE(...)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I am a little confused as to where each of the calls should lie.
At present, I have a PROGRAM which calls a MODULE which has all the windows processing. Ultimately, the PROGRAM will become a SUBROUTINE for a larger PROGRAM.
In an effort to keep the Windows processing separate from the PROGRAM, I have tried to make all explicit Windows calls a part of the MODULE. Thus, I built FUNCTION MainWndProc andKeyboardProc as part of the MODULE. I also created a SUBROUTINE called SENDMESSAGETOFRAME which does the SendMessage FUNCTION call to MainWndProc toinitialize the Windows hook.
When this is done, the program hangs on the SendMessage FUNCTION. I am assuming this means that the MainWndProc is not properly handling the message loop.
My MainWndProc only does explicit processing of the WM_INSTALLHOOK message, in which case it returns 0. In the case of any other message, it returns DefWindowProc. Should it instead be returning the call to lpOldWndProc? In any case, why should the SendMessage FUNCTION cause the system to hang?
I am getting thoroughly confused.
Slippery
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
JIMB@GEMINISI.COM:
My MainWndProc only does explicit processing of the WM_INSTALLHOOK message, in which case it returns 0. In the case of any other message, it returns DefWindowProc. Should it instead be returning the call to lpOldWndProc? In any case, why should the SendMessage FUNCTION cause the system to hang?
Yes, it should return CallWindowProc(lpOldWndProc...) indeed. But, are you sure it is ever entered? Place a breakpoint in CASE(WM_INSTALLHOOK) and check it out. Are you sure it doesn't hang in the hook function instead? I can't tell offhand, but I can imagine that it can screw up the application badly; make sure you properly call CallNextHookEx from the hook function. (and don't forget !DEC$ATTRIBUTES STDCALL for the wndproc and the hookproc)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Yes, I forgot the DEC$ATTRIBUTES STDCALL for the MainWndProc.Now theprogram no longer hangs.
However, I am now back to the SAME problem I had earlier. Here is the code for the MainWndProc.
INTEGER
(KIND = LRESULT) FUNCTION MainWndProc(hwnd, uMsg, wParam, lParam)!DEC$ATTRIBUTES STDCALL :: MainWndProc
INTEGER (KIND = HANDLE), INTENT(IN) :: hwndINTEGER (KIND = UINT), INTENT(IN) :: uMsgINTEGER (KIND = UINT_PTR), INTENT(IN) :: wParamINTEGER (KIND = LONG_PTR), INTENT(IN) :: lParamWRITE
(UNIT = 6, FMT = '(''MainWndProc entered '',I0)') uMsgSELECT CASE (uMsg)CASE (WM_INSTALLHOOK)WRITE
(UNIT = 6, FMT = '(''CALL SetWindowsHookEx'')')hKbHook = SetWindowsHookEx(WH_KEYBOARD,
LOC(KeyboardProc), NULL, 0_DWORD)WRITE
(UNIT = 6, FMT = '(''Keyboard hook '',Z8.8)') hKbHookMainWndProc = 0_LRESULT
CASE DEFAULTMainWndProc = CallWindowProc(lpOldWndProc, hwnd, uMsg, wParam, lParam)
END SELECTEND FUNCTION
MainWndProcAnd here is the resulting output when this routine is called with the message WM_INSTALLHOOK:
MainWndProc entered 32769
CALL SetWindowsHookEx
Keyboard hook 00000000So the call is not working.
Slippery
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thank you for the help. I am now capturing Ctrl+* keyboard events.
I had not realized that SetWindowsHookEx was only capable of capturing Ctrl+* keyboard events. This is what I need to do, so it is not a problem.
I have also written a shutdown function which unhooks the Window using a call to UnhookWindowsHookEx.
Right now, my App hangs on completion of the code. That is, it runs to completion, posts a message boxthat 'Program Terminated with exit code 0 Exit Window?' but the message box entry has been disabled. If I comment out the code to UnhookWindowsHookEx, it does not matter. The program still hangs.
Slippery
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Yes, that has been done. The App is now hanging without the message box.
Slippery
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
JIMB@GEMINISI.COM:
I had not realized that SetWindowsHookEx was only capable of capturing Ctrl+* keyboard events. This is what I need to do, so it is not a problem.
Um, it isn't supposed to be so. You should get all the keyboard messages (actually, all the WM_KEY** messages before they reach the message loop).
Now, about the hanging: don't forget to call CallNextHookEx from your KeyboardProc (see the Return Value section); I kind of recall the problems when I forgot to do it (although it was some 5-6 years ago).The other issue is that you come to get the "Program Terminated with exit code..." message at all. Normally, a QuickWin PROGRAM should end in an infinite loop, like:
DO
CALL SLEEPQQ(0)
END DO
In this way, (and thanks to the two-threaded structure), the program will sit idle in the loop and all the events will happen in the "primary" thread (the one driving menus, your WndProc, KeyboardProc etc.), and the program can be closed only through the
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
As far as getting keyboard messages, you are correct. I do get all input from the keyboard. So how might I capture Ctrl+C as opposed to C. Do I need to do something like the following:
INTEGER, SAVE :: iControl = 0
SELECT CASE (wParam)
CASE (VK_CONTROL)
IF (iEvent == WM_KEYDOWN) THEN
iControl = 1
ELSE
iControl = 0
END IF
CASE (IACHAR('C'))
IF (iControl == 1) THEN
! Do what you would do for Ctrl+C
END IF
! And similarly for other control keys
END SELECT
Now, as far as the hanging at the end goes, yes, I have been calling CallNextHookEx at the end of my KeyboardProc. And, no, it does not appear to be hanging today. I am still getting some strange response from the interface though. The App is closing itself down. I am guessing it is taking 5-10 seconds while it pauses, blinks, and then finally closes. Prior to using the hooks, it would close quickly.
In the larger scheme of things, the existing App we have closes itself nicely, without hanging, infinite loops, or other items. The real problem with the existing App lies in the X button and the Ctrl+C, both of which close the App without tidying up. Without the tidying up, the App produces unusable output.
Slippery
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
CASE (WM_CLOSE)
SELECT CASE(MessageBox(hFrame,"Save changes to Foo?"C, &
"Close"C,MB_YESNOCANCEL))
CASE (IDYES)
CALL SaveDocument()
FrameWndProc=CallWindowProc(lpfnOldFrameProc,hWnd,Msg,wParam,lParam)
CASE (IDNO)
FrameWndProc=CallWindowProc(lpfnOldFrameProc,hWnd,Msg,wParam,lParam)
CASE (IDCANCEL)
FrameWndProc=0
END SELECTThis works fine for both Ctrl+C and
I still don't know what went wrong with the hook, but you can use it only to emulate "other" accelerators.
JIMB@GEMINISI.COM:
As far as getting keyboard messages, you are correct. I do get all input from the keyboard. So how might I capture Ctrl+C as opposed to C.
if (GetKeyState(VK_CONTROL) < 0) then
will tell you if the Ctrl is pressed.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I have eliminated the Hook code and replaced it with message pump code for my child window, which is the one evidently which begins with keyboard focus. Here is the code:
INTEGER (KIND = LRESULT) FUNCTION ChildWndProc(hwnd, uMsg, wParam, lParam)
!DEC$ATTRIBUTES STDCALL :: ChildWndProc
INTEGER (KIND = HANDLE), INTENT(IN) :: hwnd
INTEGER (KIND = UINT), INTENT(IN) :: uMsg
INTEGER (KIND = UINT_PTR), INTENT(IN) :: wParam
INTEGER (KIND = LONG_PTR), INTENT(IN) :: lParam
INTEGER (KIND = SHORT) iAlt, iControl, iShift
Frame_Window_Message: SELECT CASE (uMsg)
CASE (WM_CHAR)
iControl = GetKeyState(VK_CONTROL)
iShift = GetKeyState(VK_SHIFT)
iAlt = GetKeyState(VK_MENU)
IF (IAND(iControl, KF_UP) == KF_UP .AND. IAND(iAlt, KF_UP) /= KF_UP .AND. IAND(iShift, KF_UP) /= KF_UP) THEN
! Control key is down
Character_Key: SELECT CASE (wParam)
CASE (Z'0001') ! Ctrl+A
CALL APPRESTART(.FALSE.)
CASE (Z'0003') ! Ctrl+C
CALL MERLINSTOP(.TRUE.)
CASE (Z'000F') ! Ctrl+O
CALL OVRRESTART(.FALSE.)
END SELECT Character_Key
END IF
CASE (WM_CLOSE)
CALL CLEAN_UP_FILES(...)
END SELECT Frame_Window_Message
ChildWndProc = CallWindowProc(lpOldChildWndProc, hwnd, uMsg, wParam, lParam)
END FUNCTION ChildWndProc
Ifany WM_CHAR messages get handled by this WindowProc, the application hangs. Also, the WM_CLOSE message isnever posted to this window. Should I have the WM_CLOSE message logic in a WindowProc for the Frame? How is the message pump interrupted by the WM_CHAR message handlers? Do I need to process all WM_CHAR messages here orwould the CallWindowProc call at the end handle some?
Slippery
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
One other item to note in the application hanging. On rare occasions, which I have not been able to reliably duplicate, the running of my App will yield an Intel Fortran error message: "unresolved contention for Intel Fortran RTL global resource". What might be causing this?
Slippery

- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page