キー入力をグローバルフックするには?

解決


MEV  2005-02-08 11:00:33  No: 56322

キー入力をグローバルフックしようと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;
}


MEV  2005-02-08 11:05:51  No: 56323

すいません。間違って送信してしまいました。以下が続きです。

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);
}

肝となる部分はこんな感じかと思います。よろしくお願いします


シャノン  2005-02-08 18:45:55  No: 56324

過去に似たような話題がありました。
多分、TargetWnd も共有セクション(#pragma data_seg)の中に置くと、上手くいくんじゃないでしょうか?


シャノン  2005-02-08 18:46:27  No: 56325

> 共有セクション
共有セグメント、かな?


MEV  2005-02-08 19:33:52  No: 56326

シャノンさん、レスありがとうございます。言われたとおりにやってみましたが、やっぱりダメでした。なんなんですかね?


シャノン  2005-02-08 20:59:49  No: 56327

なんなんですかねぇ…

「うまくフックできない」とは、具体的にどんな感じなんですか?
KeyboardProc が呼ばれないとか、SendMessage が上手くいかないとか、SendMessage は成功してるけどメッセージが届かないとか…
どこでコケてるのかわかれば、対処法も見えてくると思いますが…

あとは、何点か、念のための確認ですが
・__hInstance はどうやって取得してますか?
・TargetWnd はどうやって取得してますか?


MEV  2005-02-08 21:22:03  No: 56328

>「うまくフックできない」とは、具体的にどんな感じなんですか?
え〜と、つまりはキーロガーを作りたいわけですが、このプログラムを実行するとキー入力をフックして、なんのキーが押されたのかをクライアント領域に表示するようになっています。そしてここからが問題点なのですが、このプログラムがフォーカスを持ているときはちゃんと「このキーが押されましたよー」って表示するのですが、例えばメモ帳を開いてなんか入力してみても自作プログラムの方はまったく反応しません。spy++でチェックしてみたところ、メモ帳に向けて入力したときはCKeyHook::KeyboardProcが呼ばれていないようです。

>あとは、何点か、念のための確認ですが
>・__hInstance はどうやって取得してますか?
>・TargetWnd はどうやって取得してますか?
__hInstanceはDllMainがDLL_PROCESS_ATTACHを受け取ったときに受け取っています。
TargetWndは専用の関数を用意してデータメンバとして保持しています。
これがそのコードです。
void CKeyHook::SetTargetWnd(HWND hWnd)
{
  TargetWnd = hWnd;
  return ;
}


NowNow  2005-02-09 20:36:09  No: 56329

MSDN ライブラリ - 2003には、

dwThreadId 
フックプロシージャを関連付けるべきスレッドの識別子を指定します。0 を指定すると、フックプロシージャは、呼び出し側スレッドと同じデスクトップ内で動作している既存のすべてのスレッドに関連付けられます。 

とあります。
フックをインストールした後に、メモ帳とかを立ち上げたのでは、インストールした時点で既存のスレッドではないため、反応しないのではないでしょうか?

的外れでしたら流してください。


MEV  2005-02-10 10:50:19  No: 56330

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が入ったまま->実行結果変わらず->なんで〜!ってなことを幾度となく繰り返していました。なんか悲しくなってきな(泣)


シャノン  2005-02-10 16:04:47  No: 56331

俺が恥晒してる過去スレ
http://madia.world.coocan.jp/cgi-bin/Vcbbs/wwwlng.cgi?print+200403/04030012.txt

これって、クラス化するメリットがあまり無いテーマのような気がします。
一度、関数ベースで書き直してみるとか…


MEV  2005-02-12 22:43:39  No: 56332

解決しました。

共有セグメントに変数置く場合
#pragma data_seg(.share)
HHOOK hHook = NULL;
HWND TargetWnd;
#pragma data_seg()
とし、次にリンカに対してセクションの属性を変更するように
指示してやらなければならないようです。つまり
#pragma comment(linker, "/SECTION:.shared,RWS")
を追加してやります。

あと、セクション名は8文字以内の文字で指定しなければならないようです

これはリンカが長い名前をサポートしていないからで、
もし8文字を超えることがあれば、共有されない可能性があります。(参考までに)


MEV  2005-02-12 22:44:30  No: 56333

>HWND TargetWnd;
HWND TargetWnd = NULL;


シャノン  2005-02-13 02:24:09  No: 56334

> #pragma comment(linker, "/SECTION:.shared,RWS")
> を追加してやります。

個人的には、def ファイルに書くほうがお勧めです。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加