| Subject |
Re: Grid search |
| From |
Mervyn Bick <invalid@invalid.invalid> |
| Date |
Tue, 29 Sep 2020 12:29:26 +0200 |
| Newsgroups |
dbase.getting-started |
| Attachment(s) |
trap_ctrlV_ctrlX.wfm |
On 2020/09/28 14:35, Robbie Nott wrote:
> Hi Akshat
> Would love to see Mervyn's detailed explanation, perhaps - between the 2
> of us we could "translate" it into something understandable by us little
> people.
>
> If we could hook into other programs with this it'd be awesome !
>
> Regards
> Robbie
We're not talking about hooking into a separate program.
Installing a Windows hook allows one to intercept messages, keystrokes
or mouse actions from Windows to the specific form or control on that
form. The type of hook determines exactly what is intercepted.
An entryfield's onKey event is not triggered for all keystrokes. If,
for instance you want to know if Cut or Paste has altered the value in
an entryfield dBASE has no built-in way of determining this as it
happens. Adding a keyboard hook makes this possible. An example, with
comments, is attached.
In the case of the form Akshat referred to, he wanted to use the numeric
keypad as hot keys to launch specific actions. This was made possible
by using a keyboard hook.
Internally dBASE doesn't differentiate between the left and right shift
keys, the left and right control keys and between the numeric key pad
and the number keys in the top row of the keyboard. To dBASE shift is
shift no matter which shift key was pressed. A keyboard hook enables on
to determine exactly which key has been pressed.
Mervyn.
| clear
if __version__ < 8
msgbox("This form will only run in dBase Pluse 8 or higher.","Error")
return
endif
/* An entryfield's onKey event is not triggered by all keystokes. Using a keyboard hook can
address this as ALL keystokes can be examined.
If, for instance, a programmer requires an action to take place if the value in an entryfield
is changed the normal course would be to use the entryfield's onKey event handler.
This works when data in typed in but, unfortunately, an entryfield's onKey event doesn't fire
for ALL keystrokes. The event doesn't fire, for instance, if characters are cut from the field
using ctrl-X or are pasted in using ctrl-V. The onChange event can't be used here as it only
fires when the entryfield loses focus.
Adding a keyboard hook allows the programmer to specifically test for ctrl-X and ctrl-V and
then take the necessaryaction.
In this example a keyboard hook is installed for the form but monitoring for ctrl-X and ctrl-V
only ocurrs when an entryfield has focus. An option is, however, to create a custom entryfield
which incorporates an individual keyboard hook.
*/
#include winuser.h
#define VK_V 0x56 //not defined in winuser.h
#define VK_X 0x58 //not defined in winuser.h
** END HEADER -- do not remove this line
//
// Generated on 2020-09-27
//
parameter bModal
local f
f = new trap_ctrlV_ctrlXForm()
if (bModal)
f.mdi = false // ensure not MDI
f.readModal()
else
f.open()
endif
class trap_ctrlV_ctrlXForm of FORM
with (this)
onOpen = class::FORM_ONOPEN
onClose = class::FORM_ONCLOSE
height = 17.0
left = 20.1429
top = 0.6364
width = 42.1429
text = ""
endwith
this.ENTRYFIELD1 = new ENTRYFIELD(this)
with (this.ENTRYFIELD1)
onChange = class::ENTRYFIELD1_ONCHANGE
onKey = class::ENTRYFIELD1_ONKEY
height = 1.0
left = 6.7143
top = 2.4545
width = 20.2857
value = "Entryfield1"
endwith
this.ENTRYFIELD2 = new ENTRYFIELD(this)
with (this.ENTRYFIELD2)
onChange = class::ENTRYFIELD2_ONCHANGE
onKey = class::ENTRYFIELD2_ONKEY
height = 1.0
left = 6.7143
top = 5.2273
width = 20.2857
value = "Entryfield2"
endwith
this.ENTRYFIELD3 = new ENTRYFIELD(this)
with (this.ENTRYFIELD3)
onChange = class::ENTRYFIELD3_ONCHANGE
onKey = class::ENTRYFIELD3_ONKEY
height = 1.0
left = 6.7143
top = 8.4091
width = 20.2857
value = "Entryfield3"
endwith
this.TEXT1 = new TEXT(this)
with (this.TEXT1)
height = 5.2273
left = 4.0
top = 10.5
width = 33.0
l0 = "<p>An entryfield's onkey and key events are not triggered by ctrl-X and ctrl-V. This program uses ctrl-X and ctrl-V, which do change the contents of the entryfield, to trigger the onKey event.</p><p>Ctrl-C is ignored as it does not change the contents of "
l0 += "the entryfield. </p>"
text = l0
endwith
function form_onOpen
//The following create prototypes for various functions in WIndows. Once
//a function has been EXTERN'd dBASE can use the function as if it is actually
//part of dBASE. To create the prototype the function name is case sensitive.
//Once dBASE has accepted the function it is no longer case sensitive.
if type("GetCurrentThreadId") # "FP"
extern CULONG GetCurrentThreadId() kernel32 from "GetCurrentThreadId"
endif
if type("GetAsyncKeyState") # "FP"
extern CSHORT GetAsyncKeyState(CINT) user32
endif
if type("GetWindowLong") # "FP"
extern CLONG GetWindowLong(CHANDLE, CINT) user32 from "GetWindowLongA"
endif
if type("SetWindowsHookEx") # "FP"
extern CLONG SetWindowsHookEx(CINT, CPTR, CHANDLE, CULONG) user32 from "SetWindowsHookExA"
endif
if type("UnhookWindowsHookEx") # "FP"
extern CLOGICAL UnhookWindowsHookEx(CHANDLE) user32
endif
if type("CallNextHookEx") # "FP"
extern CLONG CallNextHookEx(CHANDLE, CINT, CUINT, CUINT) user32
endif
CALLBACK CLONG hookWndProc(CINT, CUINT, CUINT) OBJECT this
//CALLBACK is a dBASE command telling dBASE we are about to change
//a pointer in in Windows to execute our procedure (hookWndProc) rather
//than the native Windows procedure. It sets up the prototype for the
//function in dBASE much as FUNCTION does for user-defined functions
//or EXTERN for external functions.
this.hookProc = GetCallAddress(class::HOOKWNDPROC)
//GetCallAddress is a dBASE function to get the address of our replacement
//function in memory.
hInst = GetWindowLong(this.hwnd, GWL_HINSTANCE)
//GWL_HINSTANCE is a Windows constant containing a LONG value (64-bits)
// which points it to the memory location where the handle assigned to the form
// is kept. GetWIndowsLong() is a Windows function which retrieves LONG values.
this.hook = SetWindowsHookEx(WH_KEYBOARD, this.hookProc, hInst, GetCurrentThreadId())
//A hook allows a programmer to divert messages from Windows to a user program.
//SetWindowsHook is a Windows function which set a hook, in this case a keyboard hook and
//this.hookProc is the address of our funtion. hInst is the pointer to this form and
//GetCurrentThreadId is information that Windows needs.
form.entryfield1.setFocus()
return
function hookWndproc(hCode, wParam, lParam)
//All keyboard messages to the form are redirected here.
t1 = wParam // virtual key code.
t2 = lParam // bit 31 = 0 keydown, bit 31 = 1 keyup
if hCode = 0 and bitset(t2,31) = true //keyup
//Each keystoke is passed twice. Once on key down and again on key up
//Each keystroke is, in turn, passed twice. Once with hCode = 0 and then with hCode = 3
//The test above makes sure we only react once instead of four times.
if "ENTRYFIELD"$form.activecontrol.name and not (t1=VK_TAB or t1=VK_RETURN or (t1>=VK_PRIOR and t1<=VK_DOWN))
//For this form we only want to trap keystokes to entryfields so we check for "ENTRYFIELD" in the
//active control's name.
//Exclude tab, enter, arrow keys, pgup, pgdwn, home and end keys as these do trigger the onKey event.
//Deal with these keys in the relevant onKey event handler
if GetAsyncKeyState(VK_CONTROL) <> 0 and (t1 = VK_V or t1 = VK_X)
//v or x ASCII values. Defined in form's header
//ctrl-c excluded so as not to trigger onKey event
//GetAsyncKeyState is a Windows function which reurns a value if a key's virtual
// value is passed to it. If the value returned is not 0 it means the key is down.
form.activecontrol.onKey(t1,0,false,true)
//if ctrl-V or ctrl-X was pressed pass the value for the key and the control key state
//to the control's onkey event handler and execute it. The program now knows that
//the contents of the control has changed and can deal with it.
endif
endif
endif
return CallNextHookEx(this.hook, hCode, wParam, lParam)
//CallNextHookEx is a Windows function which passes the parameter
//on to the next process in Windows. If we return 1 instead of
//executing CallNextHook() the parameters don't get passed on.
//This effectively "swallows" the keystroke. To "swallow" a keystroke
// 1 must be returned for all 4 messages generated by a single keystroke.
function ENTRYFIELD1_onChange()
?'ef1 onChange'
return
function ENTRYFIELD1_onKey(nChar, nPosition,bShift,bControl)
?'ef1 onKey '+iif(bControl = true,'Ctrl+','')+chr(nChar)
return
function ENTRYFIELD2_onChange()
?'ef2 onChange'
return
function ENTRYFIELD2_onKey(nChar, nPosition,bShift,bControl)
?'ef2 onKey '+iif(bControl = true,'Ctrl+','')+chr(nChar)
return
function ENTRYFIELD3_onChange()
?'ef3 onChange'
return
function ENTRYFIELD3_onKey(nChar, nPosition,bShift,bControl)
?'ef3 onKey '+iif(bControl = true,'Ctrl+','')+chr(nChar)
return
function form_onClose
UnhookWindowsHookEx(this.hook)
release callback hookWndProc object this
//House keeping. Put things back the way you found them.
return
endclass
|
|