キーフックについて、再度質問させていただきます。
やりたいことは、グローバルキーフックを行い、例えばメモ帳で何かキーが押されたら「あいうえお」と入力させる、などをしたいのです。
そこで、それを実現するため次のようなコードを書いてみました。
雛形として以前ここで質問にお答え頂いたMr.XRAYさんのコードを元とさせて
いただいていますm(_)m
------------------------------DLLのコード----------------------------
library KeyHook;
uses
Windows,SysUtils,
Messages;
var
hHookCallWndProc:integer;
MainFormHandle: THandle;
//===================================================================
// フックのコールバック関数
// このDLLを使用したアプリにキーコードを送る。
// メッセージIDはWM_APP+100を使用。
//===================================================================
function KeyWndProc(nCode:integer;wParam:integer;lParam:integer):
integer; stdcall;
begin
if nCode < 0 then begin
Result := CallNextHookEx(hHookCallWndProc, nCode, wParam, lParam);
end else begin;
Result := CallNextHookEx(hHookCallWndProc, nCode, wParam, lParam);
if nCode=HC_ACTION then begin
Result:=-1;
MainFormHandle := FindWindow('TForm1',nil); //(念のため)
if lParam > 0 then //キーを押したとき
PostMessage(MainFormHandle,WM_APP+100,Wparam,0);
end;
end;
end;
//===================================================================
// フック関数の登録
// 登録するフック関数はKeyBoardProc
//===================================================================
function StartKeyHook(AppHandle:THandle): Boolean; stdcall;
var
Ret:Integer;
begin
MainFormHandle := AppHandle;
Result:=False;
Ret:=SetWindowsHookEx(WH_KEYBOARD,@KeyWndProc,HInstance,0);
if Ret=0 then Exit else hHookCallWndProc:=ret;
Result:=True;
end;
//===================================================================
// フックの解除
//===================================================================
procedure StopKeyHook; stdcall;
begin
UnhookWindowsHookEx(hHookCallWndProc);
end;
//===================================================================
// 外部からDLL内のメソッドを利用可能にするためのオマジナイ
//===================================================================
exports
StartKeyHook,
StopKeyHook;
begin
end.
--------------------------プログラムのコード-------------------------
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private 宣言 }
protected
procedure WndProc(var msg: TMessage);override;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
function StartKeyHook(Wnd: HWND): Boolean; stdcall; external 'KeyHook.dll';
procedure StopKeyHook; stdcall; external 'KeyHook.dll';
var
hHookLib : THANDLE;
HookFlag : Boolean;
{$R *.DFM}
//===================================================================
// 本Form破棄の時にはフックを解除
//===================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
StopKeyHook;
FreeLibrary(hHookLib);
end;
//===================================================================
// Application側でメッセージ通知を受取る
// ここではフォームのWndProcメソッドを使用
// キーを感知したらアクティブウィンドウに「あいうえお」を送る。
//===================================================================
procedure TForm1.WndProc(var msg: TMessage);
begin
if (msg.Msg=WM_APP+100) then begin
//★★★★★
StopKeyHook;
Keybd_event(Byte('A'),0,0,0);
Keybd_event(Byte('A'),0,KEYEVENTF_KEYUP,0);
Keybd_event(Byte('I'),0,0,0);
Keybd_event(Byte('I'),0,KEYEVENTF_KEYUP,0);
Keybd_event(Byte('U'),0,0,0);
Keybd_event(Byte('U'),0,KEYEVENTF_KEYUP,0);
Keybd_event(Byte('E'),0,0,0);
Keybd_event(Byte('E'),0,KEYEVENTF_KEYUP,0);
Keybd_event(Byte('O'),0,0,0);
Keybd_event(Byte('O'),0,KEYEVENTF_KEYUP,0);
StartKeyHook(Form1.Handle);
//★★★★★
end;
inherited WndProc(msg);
end;
//===================================================================
// キーフックを有効にする
//===================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
HookFlag :=False;
try
if hHookLib=0 then hHookLib := LoadLibrary('TabKeyHook.dll');
StartKeyHook(Form1.Handle);
except
end;
end;
//===================================================================// キーフックを無効にする
//===================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
StopKeyHook;
end;
end.
以上、これで実行しメモ帳でキーを押すとどうもprocedure TForm1.WndProc(var msg: TMessage);
がループされているかのようにキャレットが固まってフリーズするような感じに
なってしまいます。
★★★で囲まれた部分を、beepとかshowmessageに変えてやれば、キーボードを押した時に
普通に一回それらが出て終わりなので、その部分以外は特に問題無いようです。
また、★の中で、StartKeyHook(Form1.Handle);を除いてそこで止めてしまえば
一回きりですが変換は上手く行くようです。
色々考えたのですが、どうしても分からずここで手が詰まっています。。
どなたかお分かりの方、宜しくお願いします。
すいません、一部改行が変になってしまいました。
環境はDelphi6Pro+WindowsXP(sp2)です。
下記を対策するだけで最終的に実現したいことができるかどうかわかりませんが、
SetWindowsHookExでグローバルフックを仕掛ける場合、戻り値 hHookCallWndProc をフック関数 KeyWndProc に飛び込んでくる各プロセスで参照できるように工夫しなければなりません。
C++でしたら共有データセクション(#pragma data_seg)に置く方法が定番のようですが、Delphiではメモリマップドファイルを使う方法くらいしかないようです。
「Delphi メモリマップドファイル」などでgoogleすると参考になる情報が検索できます。
>SHIMAPEE様
回答ありがとうございます。メモリマップドファイルというものを
初めて知りました。
それでコードを書き直してみたのですが、やはり同じ箇所で上手く
行かないようです。とりあえず回避策として、間隔1のtimerを貼り付けて、
それを噛ませれば固まることは無くなったのですが、早く動かすと若干
挙動が変になるときもあります。
うーん・・なかなか難しいようで。。
少し試してみました。
全てのキーをフックすることには成功していませんが、例えばTABキーを
'aiueo'にすりかえることには成功しました。
なお、SetWindowsHookExとUnhookWindowsHookExを繰り返すのはオーバ
ヘッドが大きいのではないかと思い、フラグを使ってみました。
【DLL側】
・共有データにフラグを新設。
・フラグをTrueにするprocedureを新設してexportもしておく。
・フックを登録したらフラグをTrue。
・フック関数内でフラグTrueなら処理。処理を開始したらフラグをFalse。
【プログラム側】
・フラグをTrueにするprocedureをexternal宣言。
・StopKeyHookとStartKeyHookは削除。'aiueo'を入力したらフラグをTrue。
Windows XP SP2 + Delphi2007 + メモ帳と秀丸エディタで確認しました。
TABキーの取りこぼしはなさそうです。
ありがとうございます。
そのようにしたところ、上記の問題は解決しました。
フラグを使うのは良い方法みたいですね。
丁寧な説明で大変分かりやすかったです。
元スレ
フックしたキーコードをAppで受け取るには?
https://www.petitmonte.com/bbs/answers?question_id=4807
か、このスレか
どちらに書こうか非常に迷いましたがとりあえず、新しい方のこちらに書きます。
私も追随して勉強させていただいたのでようやく理解できました。
私にとって、キーフックに関して役立ったURLを書いておきます。
先のスレにも登場していましたが
Gen's 不定期ローテク講座 〜 using Hook
file:///C:/MyFolder/MyData/Programing/Delphi/MyDelphiFolder/OpenSource/★要チェックDelphiコード/フック/Gen's/Gen's%20不定期ローテク講座%20〜%20using%20Hook.html
ここは、Delphi初期のページですが、確かにばっちり書かれています。
しかし、微妙に脱線して読みにくかったりで、私は数年来理解できていませんでした。今日、はじめて理解できたよ、この難解なページが。かなりうれしい。
で、その理解の助けになったのが
DelWiki - Tips/フックとかやってみる
http://delwiki.info/?Tips%2F%A5%D5%A5%C3%A5%AF%A4%C8%A4%AB%A4%E4%A4%C3%A4%C6%A4%DF%A4%EB
もっと、理解の助けになったのが、文章はほとんどないけど
正確なサンプルソースを提供していただけているここ。
TechnoCity TIPS
http://hp.vector.co.jp/authors/VA003525/tips0.htm
応用はXRAYさんのところを利用するとよいでしょう。
サンプルプログラム集 フック関数の種類
http://homepage2.nifty.com/Mr_XRAY/Delphi/plSamples/KindOfHook.htm
よいページと情報を提供してくれている、みなさんに感謝。
おっと、リンク先間違った
Gen's 不定期ローテク講座 〜 using Hook
http://www2.biglobe.ne.jp/~sakai/usehook.htm
こちらですね。
たびたび、蛇足なんだけど
お世話になっている、この掲示板のあるこちらのフックサンプル
http://madia.world.coocan.jp/delphi/Tokusen/hook.htm
http://madia.world.coocan.jp/delphi/Tokusen/hook.lzh
こちらのソースは、メモリマップドファイルが使われていないので
不完全なコードになります。安定動作してくれない。
運良ければ動くだろうけど(Win9x系なら動くのか?)
俺の環境じゃ動きませんでした。(Win2K、D2006)
以上、ご参考までに。
あ・・久々に見たら書き込みが^^;
Fusaさん、多くのURLありがとうございます。
フック関連も、がんばって探せば参考になる良いページは色々あるようですね。
ちなみに、一番↑の私のコードですが、フラグを使わずとも、WH_KEYBOARD_LL
で書き換えてやったら、思い通りの動作をしました。
理由は良く分からないのですが、LLのと普通のとでは、こういった違いが起こるようです。
でも本当に勉強になりました。
皆さんのおかげで、多くの問題が解決して感謝です。
ツイート | ![]() |