マウスフックをグローバルフックにするには

解決


ボビン  2003-11-03 05:04:59  No: 52386

スクリーン上でドラッグして指定した長方形中の画像を取り込み、
デスクトップや他のアプリの画像を変換して表示するプログラムを
作ろうと考えています。その際に作製したプログラムのウィンドウ
以外の領域もクリックしなければならないので、色々調べフックを
使う事にしました。

ところが、いざマウスのフックプロシージャをDLLに定義して、
グローバル(システム)フックにしてみても、ローカルフックの
ような動きしかしません。そのアプリ上のタイトルバー等でも
フックできているのですが、デスクトップ等の領域上では全く
メッセージがフックされていないようです。

ネットに落ちていたサンプルコードを元にMFCでのコードに書き
直したのですが(私、MFCしか知らないもので…すいません…)
主要な部分は変えてないつもりです。
だから、exe側でMFC AppWizardを使っているのがいけないのかな
とも考えているのですが、それ以外の可能性が全くつかめません。

お手数おかけしますが、よろしくお願いします。

ソースコードの主要な部分です。

・DLLのインスタンスを取得するための関数を定義しました。
HINSTANCE ThisDllInst = NULL;
HINSTANCE GetThisDllInst(){return ThisDllInst;}
・その関数のための変数をDllMainで設定しました。
extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
    ThisDllInst = hInstance;
・マウスフックの登録部分です。
MouseHook = SetWindowsHookEx
    (WH_MOUSE,(HOOKPROC)MouseProc,GetThisDllInst(),0);
・フックに関連する変数です。
#pragma data_seg(".ShareForHook")←.defのSECTIONSでREAD WRITE SHARED
HHOOK MouseHook = NULL;
#pragma data_seg()
・フックプロシージャです
LRESULT CALLBACK MouseProc(int nCode,WPARAM wParam,LPARAM lParam)
{
    // マウスメッセージでなければ、次のフックプロシージャを呼ぶ
    if(nCode != HC_ACTION)
        return CallNextHookEx(MouseHook,nCode,wParam,lParam);

    switch(wParam)
    {
    case WM_NCLBUTTONDOWN:
    case WM_NCLBUTTONUP:
    case WM_NCMOUSEMOVE:
        デスクトップ上等でマウスを操作しても、
        ここのラベルに処理が移らない。
        (略)
        return 1;
    // 他のメッセージはそのまま
    default:
        return CallNextHookEx(MouseHook,nCode,wParam,lParam);
    }
}


tetuo  2003-11-04 00:08:03  No: 52387

>    case WM_NCLBUTTONDOWN:
>    case WM_NCLBUTTONUP:
>    case WM_NCMOUSEMOVE:
>        デスクトップ上等でマウスを操作しても、
>        ここのラベルに処理が移らない。

デスクトップに限らずクライアント領域でのマウス操作では
そこに処理は行かないと思いますよ。

WM_NCxxxxxxxxx は非クライアント領域でのメッセージなので
WM_LBUTTONDOWNなどもチェックしましょう。


ボビン  2003-11-04 02:08:16  No: 52388

tetuo様、早速のレスありがとうございます。

早速、WM_LBTTONDOWNに変えて実行してみたのですが、
やはり、デスクトップ上等での操作は取得できない
ようです。

システムフックの場合、非クライアント領域のメッセージも
流れてくるのでNCを付けたほうがいいかな…。と思って付加
していたのですが、どちらでも駄目となると…う〜ん。


tetuo  2003-11-04 18:07:06  No: 52389

if(nCode != HC_ACTION)を
if(nCode >= 0 )にしてみてもだめですか?


fuku  2003-11-05 01:15:03  No: 52390

ちょっと試してみたんですが、私の技量ではマウスフックで非クライアント領域をロックする事はできませんでした。

代わりに
>スクリーン上でドラッグして指定した長方形中の
というボビンさんのやりたい操作と思われる操作を別の方法で作成できたので書いておきます。
確認環境は Win98SE VC++6.0 MFC ダイアログベース です。

左ドラッグで始点、終点を指定、右クリックでキャンセルするコード
関数は全てダイアログクラス所属です。

・この操作のために追加されたクラスメンバ
POINT start;//ドラッグの開始座標
POINT end;//ドラッグの終了座標
bool capflg;//マウスをキャプチャしているかどうか(ウィンドウ構築までにfalseで初期化しておく)

・開始関数
{
   capflg=true;
   SetCapture();
   mouse_event(MOUSEEVENTF_RIGHTDOWN,0,0,0,GetMessageExtraInfo());
}

・OnLButtonDown
{
   if(capflg==true){
      start=point;
      return;
   }
   CDialog::OnLButtonDown(nFlags, point);
}

・OnLButtonUp
{
   if(capflg==true){
      end=point;
      mouse_event(MOUSEEVENTF_RIGHTUP,0,0,0,GetMessageExtraInfo());
      ReleaseCapture();
      ClientToScreen(&start);
      ClientToScreen(&end);
      capflg=false;
      CString buf;//AfxMessageBox(buf)までは確認表示
      buf.Format("%d,%d - %d,%d",start.x,start.y,end.x,end.y);
      AfxMessageBox(buf);
      return;
   }
   CDialog::OnLButtonUp(nFlags, point);
}

・OnRButtonUp
{
   if(capflg==true){
      ReleaseCapture();
      capflg=false;
      AfxMessageBox("キャンセル");
      return;
   }
   CDialog::OnRButtonUp(nFlags, point);
}


ボビン  2003-11-06 01:57:58  No: 52391

tetuo様、fuku様、レスありがとうございます。
返信が遅くなって申し訳ありませんでした。

tetuo様
> if(nCode >= 0 )にしてみてもだめですか?

早速やってみたのですが、クライアント上の
マウスメッセージも含む全てのメッセージが
フックプロシージャを素通りしてしまって、
できませんでした。

fuku様
マニュアルのマウスキャプチャの項にクライアント領域外では
SetCaptureしても、マウスが押し続けられていないと、すぐに
キャプチャーが解けてしまうような事が載っていたので諦めて
いたのですが、まさか、こんな方法があるとは!(最初は何故
右ボタンを押し続けるのか分からなかったのですが…)確かに、
この方法だとマニュアル通りの方法になりますね。

当初考えていたフックではないですが、目的の動作を達成
できたので、これでプログラムを書いていこうと思います。

tetuo様、fuku様、お忙しい中ほんとうにありがとうございました。


tetuo  2003-11-06 03:47:09  No: 52392

> if(nCode >= 0 )にしてみてもだめですか?

ごめんなさい、nCodeが0以上の時に独自の処理をするようにってことですので
ボビンさんのコーディングだと
if(nCode < 0)にするべきでした。

まだ見ていらっしゃるといいのですが・・・


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

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






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