質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.82%

WM_POINTER*系のメッセージをグローバルフックしたい

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 2,045

tekka

score 376

 前提・実現したいこと

Windows10(64bit)の各種ブラウザ上でスタイラスペンの挙動(とくにWM_POINTERENTER、WM_POINTERLEAVE)を取得したい。

 発生している問題・エラーメッセージ

C++で作成したDLLにより、各種ブラウザのウィンドウにグローバルフックを設定し、WM_POINTER*のメッセージを取得しようとしましたが、出来ませんでした。

 該当のソースコード

DLL

#include "stdafx.h"


#pragma data_seg(".shareddata")
HWND g_hWnd = 0;
HHOOK hHook = 0;
#pragma data_seg()

HINSTANCE hInst;

EXPORT_API_ int SetHook(HWND hWnd, DWORD dwThreadId)
{

    hHook = SetWindowsHookEx(WH_GETMESSAGE, HookProc, hInst, dwThreadId);
    if (hHook == NULL) {
        return -1;
    }
    else {
        g_hWnd = hWnd;
    }
    return 0;
}

EXPORT_API_ int ResetHook()
{
    if (UnhookWindowsHookEx(hHook) != 0) {
        UnregisterPointerInputTarget(g_hWnd, PT_TOUCH);
    }
    return 0;
}

EXPORT_API_ LRESULT CALLBACK HookProc(int nCode, WPARAM wp, LPARAM lp)
{
    CWPSTRUCT *pcwp;

    if (nCode < 0) return CallNextHookEx(0, nCode, wp, lp);

    if(nCode == HC_ACTION ) {

        const MSG& msg = *(MSG*)lp;

        if (msg.message == WM_POINTERENTER) {
            SendMessage(g_hWnd, WM_INRANGE, 0, 0);
        }
        else if (msg.message == WM_POINTERLEAVE) {
            SendMessage(g_hWnd, WM_OUTRANGE, 0, 0);
        }
    }

    return CallNextHookEx(hHook, nCode, wp, lp);
}

// エントリポイント
BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        // アタッチ
        hInst = hModule;
        //bSetHook = FALSE;
        break;
    case DLL_PROCESS_DETACH:
        // デタッチ
        break;
    }
    return TRUE;
}


DLL呼び出し

// hook_exe.cpp: アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"
#include "hook_exe.h"

#define MAX_LOADSTRING 100

// グローバル変数:
HINSTANCE hInst;                                // 現在のインターフェイス
WCHAR szTitle[MAX_LOADSTRING];                  // タイトル バーのテキスト
WCHAR szWindowClass[MAX_LOADSTRING];            // メイン ウィンドウ クラス名

HMODULE hDll;

// このコード モジュールに含まれる関数の宣言を転送します:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);
int _is_hooked = 0;

//共有セグメント
#pragma data_seg(".shareddata")
HHOOK hKeyHook = 0;
HWND g_hWnd = 0;        //キーコードの送り先のウインドウハンドル
#pragma data_seg()

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: ここにコードを挿入してください。

    // グローバル文字列を初期化しています。
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_HOOKEXE, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // アプリケーションの初期化を実行します:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_HOOKEXE));

    MSG msg;

    // メイン メッセージ ループ:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}


ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_HOOKEXE));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_HOOKEXE);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // グローバル変数にインスタンス処理を格納します。

   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

   if (!hWnd)
   {
      return FALSE;
   }

   RegisterTouchWindow(hWnd, 0);

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_TEST:

        _is_hooked = 2;
        InvalidateRect(hWnd, NULL, TRUE);

        break;
    case WM_INRANGE:

        _is_hooked = 3;
        InvalidateRect(hWnd, NULL, TRUE);

        break;
    case WM_OUTRANGE:

        _is_hooked = 4;
        InvalidateRect(hWnd, NULL, TRUE);

        break;
    case WM_HOOKSTART:

        hook_start(hWnd);

        _is_hooked = 1;
        InvalidateRect(hWnd, NULL, TRUE);

        break;
    case WM_HOOKEND:

        //TODO:フック終了

        hook_end();

        _is_hooked = 0;
        InvalidateRect(hWnd, NULL, TRUE);

        break;
    case WM_COMMAND:
        {

            int wmId = LOWORD(wParam);
            // 選択されたメニューの解析:
            switch (wmId)
            {
            case IDM_TEST:

                //テスト用フック開始メニューを受け取る

                SendMessage(hWnd, WM_HOOKSTART, 0, 0);

                break;
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_CREATE:

        //DLL読み込み
        hDll = LoadLibrary(_T("hook.dll"));

        InvalidateRect(hWnd, NULL, TRUE);
        break;
    case WM_DESTROY:

        //DLL解放

        hook_end();

        FreeLibrary(hDll);

        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// バージョン情報ボックスのメッセージ ハンドラーです。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

std::vector<int> hook_start(HWND hWnd) {

    int ret = 0;
    std::vector<int> ret_ary;

    FUNC_SetHook lpFunc = (FUNC_SetHook)GetProcAddress(hDll, "SetHook");

    DWORD process_id_ary[1000] = {};

    FindProcessId("firefox.exe", process_id_ary);

    for (int ii = 0; ii < 1000; ii++) {

        DWORD process_id = process_id_ary[ii];

        if (process_id == NULL) {
            break;
        }

        HWND hwnd_ary[1000] = {};

        GetWindowHandle(process_id, hwnd_ary);

        for (int jj = 0; jj < 1000; jj++) {

            HWND hwnd = hwnd_ary[jj];

            if (hwnd == NULL) {
                break;
            }

            DWORD dwPid = 0;
            DWORD thread_id = GetWindowThreadProcessId(hwnd, &dwPid);

            ret = lpFunc(hWnd, thread_id);
            ret_ary.push_back(ret);
        }
    }
    return ret_ary;
}

int hook_end() {

    //TODO:unhook

    FUNC_ResetHook lpFunc = (FUNC_ResetHook)GetProcAddress(hDll, "ResetHook");

    return 0;
}

void FindProcessId(const char *processname, DWORD * process_id_ary)
{
    HANDLE hProcessSnap;
    PROCESSENTRY32 pe32;
    //DWORD result_ary[1000];
    int idx = 0;

    // Take a snapshot of all processes in the system.
    hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (INVALID_HANDLE_VALUE == hProcessSnap) {
        //return(FALSE);
        return;
    }

    pe32.dwSize = sizeof(PROCESSENTRY32); // <----- IMPORTANT

                                          // Retrieve information about the first process,
                                          // and exit if unsuccessful
    if (!Process32First(hProcessSnap, &pe32))
    {
        CloseHandle(hProcessSnap);          // clean the snapshot object
        return;
    }

    do
    {
        if (0 == strcmp(processname, _bstr_t (pe32.szExeFile)))
        {
            process_id_ary[idx] = pe32.th32ProcessID;
            idx++;
        }
    } while (Process32Next(hProcessSnap, &pe32));

    process_id_ary[idx] = NULL;

    CloseHandle(hProcessSnap);

}

void GetWindowHandle(const DWORD TargetID, HWND * hwnd_ary)
{
    int idx = 0;
    HWND hWnd = GetTopWindow(NULL);

    do {
        //if (GetWindowLong(hWnd, GWL_HWNDPARENT) != 0 || !IsWindowVisible(hWnd))
            //continue;
        DWORD ProcessID;
        GetWindowThreadProcessId(hWnd, &ProcessID);
        if (TargetID == ProcessID) {

            hwnd_ary[idx] = hWnd;
            idx++;
            //return hWnd;
        }
    } while ((hWnd = GetNextWindow(hWnd, GW_HWNDNEXT)) != NULL);

    hwnd_ary[idx] = NULL;
}

 試したこと

  • Spyxxで確認しましたが、スタイラスペンで各種ブラウザ(Edge,Firefox)のウィンドウにタッチしてもWM_POINTER系メッセージは取得できませんでした。
    VisualStudioのウィンドウ上では、WM_POINTER系のメッセージが取得できました。
  • ブラウザ側からJavaScriptでペンの挙動を取得することも考えましたが、得られる挙動の情報が少ないため、断念しました。

 補足情報(FW/ツールのバージョンなど)

ウィンドウハンドルの取得方法、グローバルフックの設定方法も、正直怪しいですが、よろしくお願いします。

 追記

  • Spyxxでメッセージが取得できない原因は、64bitのプロセスに対して32bitのSpyxxで監視しようとしていたからでした。
    Spyxx(amd64)によって監視したところ、ブラウザのウィンドウでWM_POINTERのメッセージが確認できました。
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+1

SetWindowsHookExに対して別プロセスのdwThreadIdを指定しているのでエラーになっているんじゃないでしょうか?
SetWindowsHookExが成功しているか確認してみてください。

SetWindowsHookEx

An error may occur if the hMod parameter is NULL and the dwThreadId parameter is zero or specifies the identifier of a thread created by another process.

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/08/26 20:41

    ご回答ありがとうございます。
    SetWindowsHookExの戻り値は、正常でした。
    確認して頂いたのに、自己解決しまして、お手数をお掛けしまして申し訳ありません。
    またよろしくお願い致します。

    キャンセル

check解決した方法

0

プロジェクトを再作成したところ、正常にフックが設定できました。
プロジェクト構成を弄ってどこかおかしくなっていたんだと思います。
お騒がせしました。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.82%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る