mark_c wrote: ↑Tue Nov 03, 2020 1:07 am
Some problems are of response speed
If speed is an issue, consider using the
Raw Input API.
mark_c wrote: ↑Tue Nov 03, 2020 1:07 am
if I press the keys with code: (90="Z",67="C")
Those are ASCII CHARACTER CODES, not KEYBOARD KEY CODES. But, it just happens that VIRTUAL KEY CODES for ASCII letters/numbers do match their ASCII character codes. But not all ASCII characters do. So be aware that there is a distinction you need to make.
mark_c wrote: ↑Tue Nov 03, 2020 1:07 am
together and holding them down I also press the key (88="X"), its pressure is not detected.
That is because your code is managing the hook incorrectly to begin with.
mark_c wrote: ↑Tue Nov 03, 2020 1:07 am
Ultimately what I want to do is capture the pressure of multiple keys pressed in any order and in any way.
First, you are hooking only your own app's main UI thread. There are easier ways to handle keyboard input for the main UI thread without using a keyboard hook at all. You can use the TApplication(Events)::OnMessage event, handling WM_KEY(DOWN|UP) and WM_CHAR messages as needed, eg:
Code: Select all
void __fastcall TForm1::TForm1(TComponent *Owner)
: TForm(Owner)
{
Application->OnMessage = &AppMessage;
}
void __fastcall TForm1::~TForm1()
{
Application->OnMessage = NULL;
}
void __fastcalll TForm1::AppMessage(MSG &Msg, bool &Handled)
{
switch (Msg.message)
{
case WM_KEYDOWN:
case WM_KEYUP:
// use Msg.wParam and Msg.lParam as needed...
// note that Msg.wParam is a VIRTUAL KEY CODE...
break;
case WM_CHAR:
// use Msg.wParam and Msg.lParam as needed...
// note that Msg.wParam is a TRANSLATED CHARACTER CODE...
break;
}
}
Or, you can simply set the Form's KeyPreview property to true, and then use the Form's OnKey(Down|Up|Press) events, eg:
Code: Select all
void __fastcall TForm1::TForm1(TComponent *Owner)
: TForm(Owner)
{
KeyPreview = true;
}
void __fastcalll TForm1::FormKeyDown(TObject* Sender, Word &Key, TShiftState Shift)
{
// use Key and Shift as needed...
// note that Key is a VIRTUAL KEY CODE...
}
void __fastcalll TForm1::FormKeyUp(TObject* Sender, Word &Key, TShiftState Shift)
{
// use Key and Shift as needed...
// note that Key is a VIRTUAL KEY CODE...
}
void __fastcalll TForm1::FormKeyDown(TObject* Sender, WideChar &Key)
{
// use Key as needed...
// note that Key is a TRANSLATED CHARACTER CODE...
}
Second, you are running your own manual message loop that is not a valid VCL message loop. If you must use your own loop, then at least use the TApplication::HandleMessage()/TApplication::ProcessMessages() method instead of using TranslateMessage()/DispatchMessage() directly, so that VCL activity is handled properly. However, do note that keyboard hooks don't use actual messages, the hook callback will be called inside of GetMessage() itself without returning a message to your code, so your loop will not handle the updated 'test' value in a timely manner, which is probably what is confusing you. Do your UI updates in the hook callback itself, eg:
Code: Select all
int test = 0;
bool bAbort = false;
HHOOK hKeyboardHook = NULL;
void __fastcall TForm1::Button1Click(TObject *Sender)
{
bAbort = false;
Label1->Caption = "Started.....";
hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD,
(HOOKPROC)KeyboardProc,
(HMODULE)GetModuleHandle(NULL),
GetCurrentThreadId() );
if (hKeyboardHook)
{
do
{
Application->HandleMessage();
}
while (!bAbort && !Application->Terminated);
UnhookWindowsHookEx(hKeyboardHook);
hKeyboardHook = NULL;
}
}
void __fastcall TForm1::Button2Click(TObject *Sender)
{
bAbort = true;
}
//---------------------------------------------------------------------------
// note that these are VIRTUAL KEY CODES...
static const unsigned char keycodes[] = {'Z', 'X', 'C', 'V', 'B', 'N', 'M', VK_OEM_COMMA};
static const int num_keycodes = sizeof(keycode)/sizeof(keycode[0]);
LRESULT CALLBACK KeyboardProc( int nCode, WPARAM wParam, LPARAM lParam )
{
if (nCode == HC_ACTION)
{
for(int i = 0; i < num_keycodes; ++i)
{
if (GetAsyncKeyState(keycodes[i]) < 0)
{
++test;
}
}
Form1->Caption = test;
}
return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
}
But better, just get rid of the manual loop altogether and let the normal VCL message loop in TAppliction::Run() handle everything for you. Just install the hook and then exit, letting execution return to the main message loop, eg:
Code: Select all
int test = 0;
HHOOK hKeyboardHook = NULL;
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Label1->Caption = "Started.....";
hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD,
(HOOKPROC)KeyboardProc,
(HMODULE)GetModuleHandle(NULL),
GetCurrentThreadId() );
}
void __fastcall TForm1::Button2Click(TObject *Sender)
{
if (hKeyboardHook)
{
UnhookWindowsHookEx(hKeyboardHook);
hKeyboardHook = NULL;
}
}
//---------------------------------------------------------------------------
// note that these are VIRTUAL KEY CODES...
static const unsigned char keycodes[] = {'Z', 'X', 'C', 'V', 'B', 'N', 'M', VK_OEM_COMMA};
static const int num_keycodes = sizeof(keycode)/sizeof(keycode[0]);
LRESULT CALLBACK KeyboardProc( int nCode, WPARAM wParam, LPARAM lParam )
{
if (nCode == HC_ACTION)
{
for(int i = 0; i < num_keycodes; ++i)
{
if (GetAsyncKeyState(keycodes[i]) < 0)
{
++test;
}
}
Form1->Caption = test;
}
return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
}
Third, is there a reason why you are using GetAsyncKeyState() rather than looking at the key data that the hook gives you in the wParam and lParam parameters?
Code: Select all
int test = 0;
HHOOK hKeyboardHook = NULL;
LRESULT CALLBACK KeyboardProc( int nCode, WPARAM wParam, LPARAM lParam )
{
if (nCode == HC_ACTION)
{
// note that wParam is a VIRTUAL KEY CODE...
switch (wParam)
{
case 'Z':
case 'X':
case 'C':
case 'V':
case 'B':
case 'N':
case 'M':
case VK_OEM_COMMA:
++test;
Form1->Caption = test;
break;
}
}
return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
}