忍者ブログ
Comments
[03/30 のん]
TrackBack
Counter
blogRank
忍者ブログ [PR]
http://lresult.blog.shinobi.jp/
Borland C++ Builderを用いたWindowsプログラミングメモブログ

×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

ポストメッセージの送信を試してみる

postmsgform.png

←こんな感じでフォームを作ってみた。

 

 

 



TEdit名    HandleEdit, MsgEdit, wParamEdit, lParamEdit
TButton名  PostMessageBtnとしました。

さっそく、メッセージのWM_CLOSE(0x0010)をアプリに送信して
そのメッセージが送信されたアプリを終了させてみる。

試しにメモ帳を起動します。
今回はメモ帳のハンドルを見つける為にツールを使用します。
VCに付いているツール、「spy++」や
BCBに付いてくる「WinSight32」
フリーだと「Winspector Spy」が有名らしいです。

あいにく、、BCBに付いている「WinSight32」は非常に使い難いやら重いやら。。

まぁなんだかんだで、メモ帳のハンドルが分かれば、その値をHandleEditにコピペします
次にMsgEditにWM_CLOSE、つまり「10」を入力
lParamとwParamは使わない為、「0」を入力しておきます。

PostMessageBtnを押すと、ポストメッセージが送信され
メモ帳が終了します。
最初に試したときは感動しましたよ、コレ。

補足:StrToInt("$" + StrHex)というように
   先頭に"$"を付けてStrToIntを使うと
   16進数表記の文字列を10進数のint型にキャスト出来ます。便利便利。

<Unit1.h>
//---------------------------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE 管理のコンポーネント
    TLabel *HandleLabel;
    TLabel *MsgLabel;
    TLabel *wParamLabel;
    TLabel *lParamLabel;
    TEdit *HandleEdit;
    TEdit *MsgEdit;
    TEdit *wParamEdit;
    TEdit *lParamEdit;
    TButton *PostMessageBtn;
    void __fastcall PostMessageBtnClick(TObject *Sender);
private: // ユーザー宣言
public:  // ユーザー宣言
    __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

<Unit1.cpp>
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::PostMessageBtnClick(TObject *Sender)
{
    // ハンドル, メッセージ, wPram, lPramをそれぞれ宣言してやります
    HWND hWnd;
    UINT msg, wParam;
    LONG lParam;

    // それぞれの値を設定します
    hWnd   = (HWND)StrToInt("$" + HandleEdit->Text);
    msg    = (UINT)StrToInt("$" + MsgEdit->Text);
    wParam = (UINT)StrToInt("$" + wParamEdit->Text);
    lParam = (LONG)StrToInt("$" + lParamEdit->Text);

    // ポストメッセージ送信
    PostMessage(hWnd, msg, wParam, lParam);
}
//---------------------------------------------------------------------------

PR

マウスのグローバルフックを試してみる
今回もDLLはVC6.0で作成
呼び出し側のアプリは全く同じものなので省略で。
キーボードのフックとほとんど同じですな。

・[新規作成] → [プロジェクト]タブ → Win32 Dynamic-Link Library を選んで
 プロジェクト名 "MouseHook"で作成してから...

<DLL側 MouseHook.cpp>
// MouseHook.cpp : DLL アプリケーション用のエントリ ポイントを定義します。
//

#include "stdafx.h"

#define DLLEXPORT extern "C" __declspec(dllexport)

// 宣言
// フック開始用

DLLEXPORT void HookStart(void);
// フック停止用
DLLEXPORT void HookStop(void);
// フック処理用 DLLにする事によってグローバルフックが出来るようになります
DLLEXPORT LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam);

//---------------------------------------------------------------------------
// 必ず初期化しないと動いてくれない
#pragma data_seg(".shared")
HHOOK g_hHook = NULL;  
// フックハンドル
#pragma data_seg()
#pragma comment(linker, "/section:.shared,rws")

HINSTANCE g_hInst;

//---------------------------------------------------------------------------
// DLLエントリポイント
BOOL APIENTRY DllMain(HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    if ( ul_reason_for_call == DLL_PROCESS_ATTACH ){
        g_hInst = (HINSTANCE)hModule;   // DLLモジュールのハンドル取得
    }
    return TRUE;
}
//---------------------------------------------------------------------------
// フック開始

DLLEXPORT void HookStart()
{
    g_hHook = SetWindowsHookEx(WH_MOUSE, (HOOKPROC)MouseProc, g_hInst, 0);

    if ( g_hHook == NULL ){
        MessageBox(NULL, "フック開始は失敗しました", "HookStart", MB_OK);
        return;
    }
    MessageBox(NULL, "フック開始は成功しました", "HookStart", MB_OK);
}
//---------------------------------------------------------------------------
// フック処理

DLLEXPORT LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam)
{
    char sMsg[100];     // メッセージ表示用

    // この構造体から位置,ハンドル,ヒットテストコード,追加情報を取得出来ます
    MOUSEHOOKSTRUCT *mmsg; 

    // lParamをMOUSEHOOKSTRUCT型でキャストし
    // 構造体のメンバを指定出来るようにします

    mmsg = (MOUSEHOOKSTRUCT *)lParam;

    if ( code < 0 ){
        return CallNextHookEx(g_hHook, code, wParam, lParam);
    }

    // 今回は右クリックと位置を確認します
    if ( wParam == WM_RBUTTONDOWN ){
        wsprintf(sMsg,
                 "位置 X : %d, Y : %d で\r\n右クリックされました!"
                 , mmsg->pt.x, mmsg->pt.y);

        MessageBox(mmsg->hwnd, sMsg, "MouseProc", MB_OK);
    }

    // 最大化ボタン上かどうかも確認してみます
    // マウスカーソルを最大化ボタン上に持っていくとメッセージが表示されます
    if ( mmsg->wHitTestCode == HTMAXBUTTON ){
        MessageBox(mmsg->hwnd, "最大化ボタン!", "MouseProc", MB_OK);
    }

    // 最後は次のフックへ渡します
    return CallNextHookEx(g_hHook, code, wParam, lParam);
}
//---------------------------------------------------------------------------
// フック停止
DLLEXPORT void HookStop(void)
{
    BOOL bResult;

    if ( g_hHook == NULL ) return;

    bResult = UnhookWindowsHookEx(g_hHook);
    if ( bResult == 0 ){
        MessageBox(NULL, "フック解除は失敗しました", "HookStop", MB_OK);
        return;
    }

    MessageBox(NULL, "フック解除は成功しました", "HookStop", MB_OK);
}
//---------------------------------------------------------------------------

と、まぁどうすかね。
SetWindowsHookExの第1引数をWH_MOUSEに変更してある点、
MOUSEHOOKSTRUCT型による、マウス座標やヒットテストコードの取得
この辺りが、キーボードフックと異なる点です。

ここでひとつ。
MessageBoxで確認をしていますが、コードの書き方がマズイと
大量のMessageBoxが出現し、どうにもならなくなってしまう場合がありますので
その辺、上手くやるなりなんなりで注意してください。

・参考
typedef struct tagMOUSEHOOKSTRUCT { 
    POINT pt;              // pt.x  pt.y  マウスのスクリーン座標
    HWND  hwnd;            // メッセージを受け取ったウィンドウのハンドル
    UINT  wHitTestCode;    // ヒットテストコード
    DWORD dwExtraInfo;     // 追加情報
} MOUSEHOOKSTRUCT;

ヒットテストコードにはマウスカーソルがどの位置にあるのか等の
情報が入ります。(タイトルバー、クライアント領域、閉じるボタン上など)
MSDN「WM_NCHITTES」にて説明があります。

キーボードのグローバルフックの続き。
DLL側はVC6で作成したので、今度は呼び出し(アプリ)側をBCBで。
今回は、コンストラクタでDLLを読み込み、フォームを閉じる前に解放するコード

少しややこしいのは、関数ポインタの宣言とか、
GetProcAddressにて関数のアドレスをゲットするとことか。


・ローカルフックと同様に、Buttonコンポーネントの"HookStartBtn", "HookStopBtn"を
 設置し、作成したDLLを同じフォルダに置いてから...

<Unit1.h>
//---------------------------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE 管理のコンポーネント
    TButton *HookStartBtn;
    TButton *HookStopBtn;
    void __fastcall HookStartBtnClick(TObject *Sender);
    void __fastcall HookStopBtnClick(TObject *Sender);
    void __fastcall FormClose(TObject *Sender, TCloseAction &Action);

private: // ユーザー宣言

    // DLLのインスタンスハンドル
    HINSTANCE m_hInst;

    // DLL関数呼び出し用の関数ポインタ宣言
    __declspec(dllexport)void (*HookStart)(void);   // フック開始呼び出し用
    __declspec(dllexport)void (*HookStop)(void);    // フック停止呼び出し用

public:  // ユーザー宣言
    __fastcall TForm1(TComponent* Owner);

};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

<Unit1.cpp>
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
// コンストラクタ

__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
    // DLLの読み込み
    m_hInst = LoadLibrary("KeybdHook.dll");
    // エラー処理
    if ( m_hInst == NULL ){
        ShowMessage("KeybdHook.dll:読込失敗しました");
        return;
    }

    // DLL関数のアドレスを取得
    HookStart = (void (*)(void))GetProcAddress(m_hInst, "HookStart");
    HookStop  = (void (*)(void))GetProcAddress(m_hInst, "HookStop" );
    // エラー処理
    if ( HookStart == NULL || HookStop == NULL ){
        ShowMessage("関数のアドレス取得に失敗しました");
        FreeLibrary(m_hInst);   // 読み込んだDLLを解放
        m_hInst = NULL;
        return;
    }
}
//---------------------------------------------------------------------------
// APP側フック開始

void __fastcall TForm1::HookStartBtnClick(TObject *Sender)
{
    HookStart();
}
//---------------------------------------------------------------------------
// APP側フック停止

void __fastcall TForm1::HookStopBtnClick(TObject *Sender)
{
    HookStop();
}
//---------------------------------------------------------------------------
// フォームを閉じた

void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
    // DLLの解放
    if ( m_hInst != NULL ){
        FreeLibrary(m_hInst);   // 読み込んだDLLを解放
        m_hInst = NULL;
    }
}
//---------------------------------------------------------------------------

と、まぁどんなもんでしょうか。
フック処理は全てDLLで実施している為、アプリ側では呼び出しているだけ。

ここまで敢えて"HookStopBtn"でフック解除を行ってきたが
普通はデストラクタやClose()イベントで解除するもんだと思う。

また、実行してみると分かるが、メッセージボックスが2回表示されますな。
これは、KeyDownとKeyUpで2回呼ばれている為、lParamからどちらなのか判断します。

if ( (lParam & 0x80000000) == 0 )       // KeyUp
else if ( (lParam & 0x80000000) == 1 )  // KeyDown

こんな感じで。

今度は、キーボードのグローバルフックを試してみる
グローバルフックするにはDLLにてフックプロシージャを実行する必要がある
ただ・・問題が1つ。。

プロセス間で使えるグローバル変数は共有セグメントってのに確保せねばならんらしい。
ただ、BCBで実現しようとするとアセンブラやメモリマップト・ファイル(メモリマップド・ファイル?)等が
出てきて、いろいろ面倒らしい上に調べてもサッパリHITせず..。

というわけで、仕方なくVisual C++ 6.0を使用してDLLを作成。
#pragma data_seg("hoge") ~ #pragma data_seg()の間で
利用するグローバル変数を宣言し、初期化しなければならない。

・[新規作成] → [プロジェクト]タブ → Win32 Dynamic-Link Library を選んで
 プロジェクト名 "KeybdHook"で作成してから...

<DLL側 KeybdHook.cpp>
//---------------------------------------------------------------------------
// KeybdHook.cpp : DLL アプリケーション用のエントリ ポイントを定義します。
//

#include "stdafx.h"

#define DLLEXPORT extern "C" __declspec(dllexport)

// 宣言
// フック開始用

DLLEXPORT void HookStart(void);
// フック停止用
DLLEXPORT void HookStop(void);
// フック処理用 DLLにする事によってグローバルフックが出来るようになります
DLLEXPORT LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam);

//---------------------------------------------------------------------------
// 必ず初期化しないと動いてくれない

#pragma data_seg(".shared")
HHOOK g_hHook = NULL;   // フックハンドル
#pragma data_seg()
#pragma comment(linker, "/section:.shared,rws")

HINSTANCE g_hInst;
//---------------------------------------------------------------------------


//---------------------------------------------------------------------------
// DLLエントリポイント

BOOL APIENTRY DllMain(HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    if ( ul_reason_for_call == DLL_PROCESS_ATTACH ){
        g_hInst = (HINSTANCE)hModule;  
// DLLモジュールのハンドル取得
    }
    return TRUE;
}
//---------------------------------------------------------------------------
// フック開始

DLLEXPORT void HookStart()
{
    g_hHook = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)KeyboardProc, g_hInst, 0);

    if ( g_hHook == NULL ){
        MessageBox(NULL, "フック開始は失敗しました", "HookStart", MB_OK);
        return;
    }
    MessageBox(NULL, "フック開始は成功しました", "HookStart", MB_OK);
}
//---------------------------------------------------------------------------
// フック処理

DLLEXPORT LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam)
{
    if ( code < 0 ){
        return CallNextHookEx(g_hHook, code, wParam, lParam);
    }

    // 今回も試しにキーボードの[0]と[F2]の押下を確認してみます
    if ( wParam == 0x30 ){
        MessageBox(NULL, "0押した!", "KeyboardProc", MB_OK);
    }
    else if ( wParam == VK_F2 ){
        MessageBox(NULL, "F2押した!", "KeyboardProc", MB_OK);
    }

    return CallNextHookEx(g_hHook, code, wParam, lParam);
}
//---------------------------------------------------------------------------
// フック停止

DLLEXPORT void HookStop(void)
{
    BOOL bResult;

    if ( g_hHook == NULL ) return;

    bResult = UnhookWindowsHookEx(g_hHook);
    if ( bResult == 0 ){
        MessageBox(NULL, "フック解除は失敗しました", "HookStop", MB_OK);
        return;
    }

    MessageBox(NULL, "フック解除は成功しました", "HookStop", MB_OK);
}
//---------------------------------------------------------------------------


と、まぁこんな感じで。
DLLという事と、最初の共有セグメントでグローバル変数を宣言すれば
フックの処理の部分はローカルフックとそれほど違いはなさげ。

また、BCBでの実装方法をご存知の方がいらっしゃれば教えてください。お願いします、なにとぞ。

キーボードのローカルフックを試してみる
HookStartBtnを押し、フックが成功したら[0]キーか[F2]キーを押して
メッセージを受け取れていることを確認するコード
その後、きちんとHookStopBtnを押してフックを解除してやります。

・Form上にButtonコンポーネントの"HookStartBtn"と"HookStopBtn"を配置し
 表示用のEditコンポーネントも配置してから..

<Unit1.cpp>
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

// 宣言
HHOOK g_hHook;  // フックハンドル
// フックプロシージャ: この関数内でパラメータを処理する

LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam);

//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
 : TForm(Owner)
{
}
//---------------------------------------------------------------------------
// フックを開始する

void __fastcall TForm1::HookStartBtnClick(TObject *Sender)
{
    HINSTANCE hInst;

    // アプリケーションのインスタンスハンドルを取得
    hInst = (HINSTANCE)GetWindowLong(Handle, GWL_HINSTANCE);

    // フックをインストールします
    g_hHook = SetWindowsHookEx(WH_KEYBOARD,             // 今回はKeyboard
                               (HOOKPROC)KeyboardProc,  // 処理するための関数
                               hInst,                   // インスタンスのハンドル
                               0);                      // スレッドのID
    if ( g_hHook == NULL ){
        ShowMessage("フック開始は失敗しました");
    }
    else{
        ShowMessage("フック開始は成功しました");
    }
}
//---------------------------------------------------------------------------
// フックを処理する

LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam)
{
    // MSDNには...
    // > codeパラメータの値が0未満の場合、フックプロシージャは、
    // > それ以上メッセージを処理せず、CallNextHookEx関数にメッセージを渡し、
    // > その関数の戻り値を返さなければなりません。
    // とあるので返してやります。
    if ( code < 0 ){
        return CallNextHookEx(g_hHook, code, wParam, lParam);
    }

    // 今回は試しにキーボードの[0]と[F2]の押下を確認してみます
    if ( wParam == 0x30 ){
        Form1->Edit1->Text = "0押した!";
    }
    else if ( wParam == VK_F2 ){
        Form1->Edit1->Text = "F2押した!";
    }

    // MSDNには...
    // > codeパラメータの値が0以上かつ、フックプロシージャで
    // > メッセージを処理しなかったときは、CallNextHookEx関数を呼び出し、
    // > その関数の戻り値を返すことを強く推奨します
    // と、、あるので素直に返してやります。

    return CallNextHookEx(g_hHook, code, wParam, lParam);
}
//---------------------------------------------------------------------------
// フックを停止する

void __fastcall TForm1::HookStopBtnClick(TObject *Sender)
{
    BOOL bResult;
    bResult = UnhookWindowsHookEx(g_hHook);
    if ( bResult != 0 ){
        ShowMessage("フック解除は成功しました");
    }
    else{
        ShowMessage("フック解除は失敗しました");
    }
}
//---------------------------------------------------------------------------

と、まぁこんな感じで。
ローカルフックなので、ほっとんど意味無し。
OnKeyPressイベントでイイんじゃないか、と。

"LRESULT" WROTE ALL ARTICLES.
PRODUCED BY SHINOBI.JP @ SAMURAI FACTORY INC.