-
История следующая, есть кейборд хук, перехватывает нажатие клавиш. Когда он активен, у людей с неангл. языком, например португальским, отрубаются символы вроде ç ã, ó. Помогите плиз разобраться, в чем дело, я же не меняю wParam, lParam..
...
KeyboardHook := SetWindowsHookEx(WH_KEYBOARD, @KeyboardProc, HInstance, 0);
...
function KeyboardProc(Code : Integer; wParam : Word; lParam : Longint): Longint;
stdcall;
const
SysKeys: set of Byte = [8, 9, 13, 27];
var
KeyDown: Boolean;
KeyChar: Char;
rslt: LongBool;
begin
if Code = HC_ACTION then
begin
KeyDown := (lParam and (1 shl 31)) = 0;
if KeyDown then
begin
GetKeyboardState(Keyboard);
SourceHandle := GetForegroundWindow;
if not (wParam in SysKeys) and
(ToAscii(WParam, LParam, Keyboard, @KeyChar, 0) > 0) then
rslt := PostMessage(DestHandle, WM_KEYHOOK, SourceHandle, Ord(KeyChar))
else
rslt := PostMessage(DestHandle, WM_EXKEYHOOK, SourceHandle, lParam);
if not rslt then
WriteLogToMyDocs('PostMessage failed, getlasterror='+IntToStr(GetLastError));
end;
end;
Result := CallNextHookEx(MouseHook, Code, wParam, lParam);
end;
-
Удалено модератором
-
Не вызываешь предыдущий хук
-
сорри, тут Result := CallNextHookEx(MouseHook, Code, wParam, lParam); ошибка, должно ж быть CallNextHookEx(KeyboardHook.... в этом ошибка? про предыдущий хук не видел в примерах, видел только про CallNextHookEx который обычно вызывают в начале обработчика, как здесь http://www.swissdelphicenter.ch/torry/showcode.php?id=1722и почему бага касается только спец символов?
-
Виноват, ошибка в объявлении функции
вместо
> function KeyboardProc(Code : Integer; wParam : Word; lParam > : Longint): Longint; > stdcall;
следует писать
function KeyboardProc(Code : Integer; wParam : WPARAM; lParam : LPARAM): LRESULT; stdcall;
-
кстати, на рсдн ответили следующее: "У меня такое было. Оказалось, что GetKeyboardState или ToAscii (точно не помню какая из двух) имеет некое внутреннее состояние и будучи вызванной в контексте клавиатурного хука, это состояние портит. "
но подробностей нет) может, тут кого наведет на решение...
-
> guest12 (26.08.14 23:09) [5]
> вызванной в контексте клавиатурного хука, это состояние > портит. "но подробностей нет
Не знаю, кто портит, но ToAscii может вернуть хоть 2 (для диакритических символов). Т.е., KeyChar, например, д.б. массивом.
Кроме того, непонятно что есть Keyboard, а также валиден ли MouseHook (лучше ли он нуля).
-- Regards, LVT.
-
тут вот говорят, давно известный косяк ToAscii и надо его 2 раза вызывать.. будем пробовать)
-
-
двойной вызов не помог, мне некритично не получать спец символы в перехвате, но критично, что это влияет на систему - почему это происходит??
-
переход на ToUnicode тоже не помог, проблема с '' вместо ç ã, ó осталась...:
function KeyboardProc(Code : Integer; wParam : WPARAM; lParam : LPARAM): LRESULT; stdcall;
const
SysKeys: set of Byte = [8, 9, 13, 27];
var
KeyDown: Boolean;
KeyChar: Char;
rslt: LongBool;
ta: integer;
us: widestring;
UResult : Integer;
function VKeytoWideString (Key : Word) : WideString;
var
WBuff : array [0..255] of WideChar;
KeyboardState : TKeyboardState;
begin
Result := '';
GetKeyBoardState (KeyboardState);
ZeroMemory(@WBuff[0], SizeOf(WBuff));
UResult := ToUnicode(key, MapVirtualKey(key, 0), KeyboardState, WBuff, Length(WBuff), 0);
if UResult > 0 then
SetString(Result, WBuff, UResult)
else if UResult = -1 then
Result := WBuff;
end;
begin
if Code = HC_ACTION then
begin
KeyDown := (lParam and (1 shl 31)) = 0;
if KeyDown then
begin
WriteLogToMyDocs('Enter: Code = ' + inttostr(Code) + ' wParam = ' + inttostr(wParam) + ' lParam = ' + inttostr(lParam));
SourceHandle := GetForegroundWindow;
WriteLogToMyDocs('before VKeytoWideString');
try
us := VKeytoWideString(WParam);
WriteLogToMyDocs('us = ' + us);
except
on e: exception do
WriteLogToMyDocs(e.message);
end;
WriteLogToMyDocs('after VKeytoWideString');
try
KeyChar := AnsiChar(us[1]);
except
on e: exception do
WriteLogToMyDocs(e.message);
end;
WriteLogToMyDocs('us = ' + us + ' KeyChar = ' + KeyChar);
if not (wParam in SysKeys) and (UResult > 0) then
rslt := PostMessage(DestHandle, WM_KEYHOOK, SourceHandle, Ord(KeyChar))
else
rslt := PostMessage(DestHandle, WM_EXKEYHOOK, SourceHandle, lParam);
if not rslt then
WriteLogToMyDocs('PostMessage failed, getlasterror='+IntToStr(GetLastError));
WriteLogToMyDocs('Exit: Code = ' + inttostr(Code) + ' wParam = ' + inttostr(wParam) + ' lParam = ' + inttostr(lParam));
end;
end;
Result := CallNextHookEx(KeyboardHook, Code, wParam, lParam);
end;
-
эксперименты показывают, что к примеру добавление вызова VKeytoWideString(75) сразу порождает баг в системе, если же его убрать - всё ок)
при этом, вызов GetKeyboardState(Keyboard) не влияет на данную багу...
что делать?)
-
> guest12 (17.02.15 01:02) [11]
> что делать?)
But twice?
-- Regards, LVT.
-
> But twice?
двойной вызов ToUnicode (либо двойной ToAscii) позволяет при наборе 'e вместо '' выдать системе e без диакритического знака, что также является багой..
-
покурил статьи... http://www.siao2.com/2006/04/06/569632.aspxhttp://www.siao2.com/2005/01/19/355870.aspxThere are two ways to work around this:
1) You can keep calling ToUnicode with the same info until it is cleared out and then call it one more time to put the state back where it was if you had never typed anything, or
2) You can load all of the keyboard info ahead of time and then when they type information you can look up in your own info cache what the keystrokes mean, without having to call APIs later.первый солюшен не совсем ясен, при каких условиях и значениях ToUnicode\ToAscii делать повторные вызовы?
-
под делфи есть такое?)
======== some code:
LRESULT CALLBACK MyKeyboardProc(int ccode, WPARAM wParam, LPARAM lParam)
;
GetKeyboardState(dbKbdState);
dwhkl = GetKeyboardLayout(GetWindowThreadProcessId(GetForegroundWindow(), NULL));
if(ToAsciiEx(pkbdllhook->vkCode, pkbdllhook->scanCode, dbKbdState, (LPWORD)szCharBuf, 0, dwhkl) == -1)
else
s buffer.
ToAsciiEx(lastState.vkCode, lastState.scanCode, dbKbdState, (LPWORD)szCharBuf, 0, dwhkl);
lastState.vkCode = 0;
}
}
}
return (CallNextHookEx(hHook, ccode, wParam, lParam));
}
============= end code
-
перевел в Delphi, следующим образом:
const
SysKeys: set of Byte = [8, 9, 13, 27];
var
KeyDown: Boolean;
KeyChar: Char;
rslt: LongBool;
Keyboard, dbKbdState: TKeyboardState;
ta: integer;
dwhkl: HKL;
lastState: TMessage;
begin
GetKeyboardState(dbKbdState);
dwhkl := GetKeyboardLayout(GetWindowThreadProcessId(GetForegroundWindow()));
if(ToAsciiEx(Msg.WParam, Msg.LParam, dbKbdState, @KeyChar, 0, dwhkl) = -1) then
begin
lastState := Msg;
//Clear out the buffer to return to the previous state - wait for ToAsciiEx to return a value other than -1 by passing the same key again. It should happen after 1 call.
while(ToAsciiEx(Msg.WParam, Msg.LParam, dbKbdState, @KeyChar, 0, dwhkl) <0) do
;
end
else
begin
//Do something with szCharBuf here since this will overwrite it...
//If we have a saved vkCode from last call, it was a dead key we need to place back in the buffer.
if(lastState.WParam <> 0) then
begin
//Safest to just clear this.
FillChar(dbKbdState, 0, 256);
//Put the old vkCode back into the locale's buffer.
ToAsciiEx(lastState.WParam, lastState.LParam, dbKbdState, @KeyChar, 0, dwhkl);
lastState.WParam := 0;
end
end;
end;
в итоге проблема осталась - диакритические знаки не отображаются (выдает просто а вместо ' над a)
-
предлагают еще такое решение, но не пойму где использовать такой цикл в хуке..?
The problem probably lies with ToAsciiEx.
It takes keystrokes and converts it to (ascii) character codes.
When using ToAsciiEx in a global hook proc, this messes up the handling of dead key characters in the application that is being hooked ..
The reason is that ToAsciiEx stores (remembers) the dead key when pressed and does not return a character.
When a 2nd key is pressed, ToAsciiEx takes the stored dead key, combines that with the new key and returns the combined character.
If a hook proc calls ToAsciiEx, the hooked application will also call ToAsciiEx (Actually TranslateMessage does this in the applications main message loop).
The fact that ToAsciiEx is called twice to process the same keystrokes messes up the dead key character combination, because the first ToAsciiEx that is called will eat the dead key that was stored and the 2nd ToAsciiEx will no longer find a dead key character stored and will therefore produce an incorrect ascii character ...
We basically confuse ToAsciiEx by calling it twice to process the same keystrokes: once in the global hook and once in the hooked application .
Ofcourse, in English, these special characters like "à" do not exist and this problem does not occur there. But in most European languages it does.
I have found a number of attempts on the internet to work around this problem, but none of them works as it should.
A method that works is to simply trap the WM_CHAR message in a global hook.
A WM_CHAR message carries complete characters like é, ñ, etc.
A WM_CHAR message is generated by the API function TranslateMessage that takes the keystrokes (WM_KEYDOWN etc.)
and translates the virtual key codes into an ascii character (most likely by calling ToAsciiEx internally).
TranslateMessage is called in an applications main message loop like this:
Code:
DO WHILE GetMessage(Msg, %NULL, 0, 0)
TranslateMessage Msg
DispatchMessage Msg
LOOP
Strangely enough, I have read that there are applications that do NOT call TranslateMessage, and use a message loop like:
Code:
DO WHILE GetMessage(Msg, %NULL, 0, 0)
DispatchMessage Msg
LOOP
Like this, no WM_CHAR messages are generated and a global hook can not trap the characters ...
However, keystroke messages like WM_KEYDOWN are still generated ..
I read that Borland Delphi shows (or showed) this behaviour, though I myself have never encountered a program behaving like this.
Apart from one I wrote myself to test.
Hope this helps (a bit)...
Kind regards
-
> guest12 (17.02.15 10:31) [13]
Неспешно копаясь в msdn внезапно читаем: As ToUnicodeEx translates the virtual-key code, it also changes the state of the kernel-mode keyboard buffer. This state-change affects dead keys, ligatures, alt+numpad key entry, and so on. It might also cause undesired side-effects if used in conjunction with TranslateMessage (which also changes the state of the kernel-mode keyboard buffer). https://msdn.microsoft.com/ru-ru/library/windows/desktop/ms646322(v=vs.85).aspx Что, видимо, заказывает пользование оных функций в глобальном хуке. Т.е., интепретировать ввод следует собс-ручно. -- Regards, LVT.
|