キー入力をグローバルフックしようとDLLを作ってみたのですが、フォーカスがなくなるとうまくフックできなくなってしまいます。いろんなサイトを見て回ったのですが原因がわりません。何がいけないのかご指摘お願いします。以下が主要部分です。
Class CKeyHook {
...
}
#pragma data_seg( ".CKHookData" )
HHOOK CKeyHook::hHook = NULL; //フックハンドルを共有領域置く
#pragma data_seg()
BOOL CKeyHook::HookOn()
{
hHook = ::SetWindowsHookEx(WH_KEYBOARD, this->KeyboardProc, ::__hInstance, 0);
if(hHook == NULL){
ErrorMsg("キーフックプロシージャのインストールに失敗");
return false;
}
return On_or_Off = true;
}
すいません。間違って送信してしまいました。以下が続きです。
LRESULT CALLBACK CKeyHook::KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if(nCode < 0)
return ::CallNextHookEx(hHook, nCode, wParam, lParam);
if(TargetWnd != NULL)
//TargetWndはユーザー定義メッセージを送るウインドウのハンドル
//HM_KEYHOOKが定義したメッセージ
::SendMessage(TargetWnd, HM_KEYHOOK, wParam, lParam);
return ::CallNextHookEx(hHook, nCode, wParam, lParam);
}
肝となる部分はこんな感じかと思います。よろしくお願いします
過去に似たような話題がありました。
多分、TargetWnd も共有セクション(#pragma data_seg)の中に置くと、上手くいくんじゃないでしょうか?
> 共有セクション
共有セグメント、かな?
シャノンさん、レスありがとうございます。言われたとおりにやってみましたが、やっぱりダメでした。なんなんですかね?
なんなんですかねぇ…
「うまくフックできない」とは、具体的にどんな感じなんですか?
KeyboardProc が呼ばれないとか、SendMessage が上手くいかないとか、SendMessage は成功してるけどメッセージが届かないとか…
どこでコケてるのかわかれば、対処法も見えてくると思いますが…
あとは、何点か、念のための確認ですが
・__hInstance はどうやって取得してますか?
・TargetWnd はどうやって取得してますか?
>「うまくフックできない」とは、具体的にどんな感じなんですか?
え〜と、つまりはキーロガーを作りたいわけですが、このプログラムを実行するとキー入力をフックして、なんのキーが押されたのかをクライアント領域に表示するようになっています。そしてここからが問題点なのですが、このプログラムがフォーカスを持ているときはちゃんと「このキーが押されましたよー」って表示するのですが、例えばメモ帳を開いてなんか入力してみても自作プログラムの方はまったく反応しません。spy++でチェックしてみたところ、メモ帳に向けて入力したときはCKeyHook::KeyboardProcが呼ばれていないようです。
>あとは、何点か、念のための確認ですが
>・__hInstance はどうやって取得してますか?
>・TargetWnd はどうやって取得してますか?
__hInstanceはDllMainがDLL_PROCESS_ATTACHを受け取ったときに受け取っています。
TargetWndは専用の関数を用意してデータメンバとして保持しています。
これがそのコードです。
void CKeyHook::SetTargetWnd(HWND hWnd)
{
TargetWnd = hWnd;
return ;
}
MSDN ライブラリ - 2003には、
dwThreadId
フックプロシージャを関連付けるべきスレッドの識別子を指定します。0 を指定すると、フックプロシージャは、呼び出し側スレッドと同じデスクトップ内で動作している既存のすべてのスレッドに関連付けられます。
とあります。
フックをインストールした後に、メモ帳とかを立ち上げたのでは、インストールした時点で既存のスレッドではないため、反応しないのではないでしょうか?
的外れでしたら流してください。
NowNowさん、返信遅れてすみません。メモ帳→自作プログラムの順番でやってみたのですがだめでした。
いろいろと試行錯誤を繰り返すうちに問題点が見えてきたので報告します。まず、2005/02/08(火)の私の発言に”メモ帳に向けて入力したときはCKeyHook::KeyboardProcが呼ばれていないようです”といいましたがごめんなさい、これ間違いです。ちゃんと呼ばれていました。ので、フックプロシージャはちゃんとセットできてます。問題点は,フォーカスを失うとなぜかメッセージの送信先のウインドウハンドルが0になってしまっていることでした。TargetWndを共有セグメントにおいても、置かなくてもです。
理由がわからないのですが、これはなぜ何でしょう?よろしくお願いします。
関係があるかどうかわかりませんがTargetWndに値がセットされるタイミングを書いておきます。
・NULLで初期化
・コンストラクタでNULLを代入
・OnCreate内でCKeyHook::SetTargetWndを呼び出し引数であるm_hWndを代入
あと、言い訳ではないんですがこの程度の原因に気づくのに遅れたわけがあります。DLL作成中にコンパイル->デバッグをするとどのプログラムを使ってDLLを呼び出すか聞かれるじゃないですか?そのときに、同時進行中の自作プログラムを選択していたんですけど、あれってだたF5押したときにそのとき選択したプログラムを呼び出すだけなんですね。てっきりVCがプログラムとデバッグ後のDLLとリンクするように裏でがんばってるのかと思ってました。
そんなこんなで、DLLデバッグ->F5押す->しかし自作プログラムフォルダ下のdebugフォルダには古いDLLが入ったまま->実行結果変わらず->なんで〜!ってなことを幾度となく繰り返していました。なんか悲しくなってきな(泣)
俺が恥晒してる過去スレ
http://madia.world.coocan.jp/cgi-bin/Vcbbs/wwwlng.cgi?print+200403/04030012.txt
これって、クラス化するメリットがあまり無いテーマのような気がします。
一度、関数ベースで書き直してみるとか…
解決しました。
共有セグメントに変数置く場合
#pragma data_seg(.share)
HHOOK hHook = NULL;
HWND TargetWnd;
#pragma data_seg()
とし、次にリンカに対してセクションの属性を変更するように
指示してやらなければならないようです。つまり
#pragma comment(linker, "/SECTION:.shared,RWS")
を追加してやります。
あと、セクション名は8文字以内の文字で指定しなければならないようです
。
これはリンカが長い名前をサポートしていないからで、
もし8文字を超えることがあれば、共有されない可能性があります。(参考までに)
>HWND TargetWnd;
HWND TargetWnd = NULL;
> #pragma comment(linker, "/SECTION:.shared,RWS")
> を追加してやります。
個人的には、def ファイルに書くほうがお勧めです。
ツイート | ![]() |