none
フックによる、別プロセスから自作アプリのウィンドウへのメッセージの送信 RRS feed

  • 質問

  • お世話になっております。
    システムフックを用いて別プロセスのウィンドウのマウスイベントをキックにして、自作アプリのウィンドウにメッセージを送ろうとしています。
    ほとんどの場合はうまくいくのですが、ある特定のアプリについてのみうまくいかない場合があり、原因を解明したいと思っております。

    具体的にはこちらのフラッシュゲームをFireFoxで起動した場合です(IEではうまくいきます)。
    http://casual.hangame.co.jp/oekaki/#

    以下テストコード全体です。
    // HookTestDll.h
    #pragma once
    #include <Windows.h>

    #ifdef _WINDLL
    #    define DLL_EXPORT __declspec(dllexport)
    #else
    #    define DLL_EXPORT __declspec(dllimport)
    #endif

    DLL_EXPORT void SetHook( HWND hToolWnd );
    DLL_EXPORT void EndHook();

    enum
    {
        WM_DLLNOTIFY = WM_APP,
    };

    // HookTestDll.cpp
    #include "HookTestDll.h"

    #pragma data_seg( "MY_DATA" )
    static HHOOK g_hHook = 0;
    static HWND g_hToolWnd = 0;
    #pragma data_seg( "MY_DATA" )
    #pragma comment ( linker, "/section:MY_DATA,RWS" )

    static HINSTANCE g_hInstance;

    int PASCAL DllMain( HINSTANCE hInstance, DWORD dwReason, void* )
    {
        if( dwReason == DLL_PROCESS_ATTACH )
        {
            g_hInstance = hInstance;
        }
        return 1;
    }

    static long PASCAL MyHookProc( int nCode, WPARAM wparam, LPARAM lparam )
    {
        if( nCode < 0 )
        { return ::CallNextHookEx( g_hHook, nCode, wparam, lparam ); }

        const UINT message = wparam;

        switch( message )
        {
        case WM_RBUTTONDOWN:
            ::PostMessageW( g_hToolWnd, WM_DLLNOTIFY, 0, 0 );
            OutputDebugStringW( L"WM_RBUTTONDOWN\n" );
            return 1;
        case WM_RBUTTONUP:
            return 1;
        }

        return ::CallNextHookEx( g_hHook, nCode, wparam, lparam );
    }

    void SetHook( HWND hToolWnd )
    {
        g_hToolWnd = hToolWnd;
        g_hHook = ::SetWindowsHookExW( WH_MOUSE, MyHookProc, g_hInstance, 0 );
    }

    void EndHook()
    {
        ::UnhookWindowsHookEx( g_hHook );
    }

    // HookTestMain.cpp
    #include <Windows.h>
    #include "../HookTestDll/HookTestDll.h"
    #pragma comment ( lib, "../Debug/HookTestDll.lib" )

    long PASCAL WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
    {
        switch( message )
        {
        case WM_DESTROY:
            ::PostQuitMessage(0);
            return 0;
        case WM_CREATE:
            SetHook( hWnd );
            return 0;
        case WM_DLLNOTIFY:
            ::OutputDebugStringW( L"WM_DLLNOTIFY\n" );
            return 0;
        }
        return ::DefWindowProcW( hWnd, message, wParam, lParam );
    }

    int PASCAL wWinMain( HINSTANCE, HINSTANCE, wchar_t*, int )
    {
        WNDCLASSW wc = {};
        wc.lpfnWndProc = WndProc;
        wc.lpszClassName = L"Test";
        ::RegisterClass( &wc );

        const HWND hWnd = ::CreateWindowExW(
            WS_EX_TOPMOST,
            L"Test", L"Test", WS_SYSMENU,
            0, 0, 300, 200,
            0, 0, 0, 0 );

        ::ShowWindow( hWnd, SW_SHOWNORMAL );
        ::UpdateWindow( hWnd );

        MSG msg;
        for(;;)
        {
            int ret = ::GetMessageW( &msg, 0,0,0 );
            if( ret == 0 || ret == -1 ){ break; }
            ::TranslateMessage( &msg );
            ::DispatchMessageW( &msg );
        }
        return msg.wParam;
    }


    VCでFlashPlayerのプロセスにアタッチすることにより、出力ウィンドウにWM_RBUTTONDOWNが出力されるのは確認できますが、WM_DLLNOTIFYは上の例でのみ出力されません。
    ウィンドウハンドルがおかしいのかと、FindWindowで取得したハンドルでも試してみましたがうまくいきません。


    また、今回のケースとはちょっと話題がズレますが、MSDNにこのようなことがかいてありました。
    「ハンドルなどの Win32 のほとんどのデータ構造体やデータ値は、実際には単一プロセスのコンテキスト内だけで有効です。」
    http://msdn.microsoft.com/ja-jp/library/h90dkhs0%28v=vs.90%29.aspx

    フックハンドルやウィンドウハンドルをプロセス間で共有しても大丈夫なことは保障されているんでしょうか?
    以上よろしくお願いします。

    環境
    Win7 Pro 64
    VC2010 EXPRESS

    2012年10月18日 13:18

すべての返信

  • http://www.skt-products.com/contents/moaimawashi.html

    こちらのフラッシュゲームでも同じ現象が確認できます。(会員登録等は不要です)

    2012年10月18日 13:32
  • Windows Vistaから導入されたユーザー インターフェイス特権の分離でしょうか。Webブラウザーは特にセキュリティを考慮してあえて自身を下位ILに変更している可能性があります。
    • 回答の候補に設定 佐伯玲 2012年10月24日 5:11
    • 回答としてマーク 佐伯玲 2012年10月29日 7:00
    • 回答としてマークされていない サウロ 2012年11月28日 17:09
    • 回答の候補の設定解除 サウロ 2012年11月28日 17:09
    2012年10月18日 14:18
  • 佐祐理様
    毎度ありがとうございます。
    なるほど、そう考えれば納得できますね。
    今忙しいので少し時間がかかるかもしれませんが、確認してみます。

    後の質問についてですが、
    ウィンドウハンドルやフックハンドルの共有は一般に認められているんでしょうか?
    探してみましたが、MSDNにはそれらしい記事は見当たりませんでした。
    2012年10月20日 9:37
  • フックハンドルについては、CallNextHookEx()の第1引数に指定する時点でプロセス間共有が必須です。当然ながら認められていると考えてもよいでしょう。
    蛇足ですが、CallNextHookEx()のドキュメントはずいぶん前に更新されていて、第1引数は無視される、つまり、フックハンドルを指定する必要はありません。

    ウィンドウハンドルもFindWindow()で検索できる時点で状況証拠としては共有できると言えます。ただどちらにしても直接的なドキュメントは見つけられませんでした。正確には、Window Station内で共有可能ではありますが。(ユーザー切り替えでの別ユーザーのウィンドウやリモートデスクトップ接続されているウィンドウは無理。)

    • 回答の候補に設定 佐伯玲 2012年10月24日 5:11
    • 回答としてマーク 佐伯玲 2012年10月29日 7:00
    • 回答としてマークされていない サウロ 2012年11月28日 17:09
    • 回答の候補の設定解除 サウロ 2012年11月28日 17:09
    2012年10月20日 23:01
  • こんにちは、サウロ さん
    フォーラムオペレータの佐伯 玲 です。

    その後の状況はいかがでしょうか?
    佐祐理 さんから追加の情報を含め参考になると思われましたので「回答としてマーク」を付けさせていただきました。
    疑問点や不明点などありましたら引き続きこちらのスレッドへご返信くださいませ。

    宜しくお願い致します。
    __________________________
    日本マイクロソフト株式会社 フォーラム オペレータ 佐伯 玲

    2012年10月29日 7:00
  • お返事が遅くなって申し訳ありません。

    ユーザーインターフェイス特権の分離なのかどうか、確認をしてみました。
    MyHookProc内のPostMessageWの下にGetLastErrorをいれたのですが、
    戻り値は1400(Invalid window handle )になっていました。
    ユーザーインターフェイスの特権の分離が原因の場合GetLastError()は5(Access Denied)を返すとかかれています。

    ウィンドウハンドルの値のプロセス間共有についてですが、XP上では同一のウィンドウに対して異なるウィンドウハンドルの値になるケースがあったと記憶しております。
    同一プロセス内でもウィンドウプロシージャの引数のhWndの値とFindWindowの戻り値が異なっていました。
    ただ、私が今使っている7のマシンでは、いずれの方法でも同じ値になっているので確認できません。

    CallNextHookExの第一引数は無視されるんですね。なるべく英語のドキュメントを読もうと思っているんですが、検索すると日本語のページが先にでてくるのでついついそっちを読んでしまいます。
    余談ですが、日本語のMSDNにはCreateWindowのhInstanceは無視されるとかかれていますが、これに正しい値を入れない場合GetWindowLong( hWnd, GWL_HINSTANCE ) は適切な値を返しません。英語版は無視するとはかいてありませんね。
    2012年11月11日 9:06
  • 同一プロセス内でもウィンドウプロシージャの引数のhWndの値とFindWindowの戻り値が異なっていました。

    それは単に処理のどこかが間違っているだけでは? 酷い言いがかりレベルです。例えばCreateWindow完了前にFindWindowを呼び出したとか、別のウィンドウ向けのメッセージのhWndを見たとか。

    実はそこまでコードを読んでいませんでした。(読みたくない)

    CreateWindowExW()の戻り値の方のhWndを使ってSetHook(hWnd)を呼び出してはどうでしょうか?

    2012年11月11日 23:21
  • >それは単に処理のどこかが間違っているだけでは?
    んーそうかもしれません。何年も前のことですが一応きちんと調べたつもりだったんですけどね。何かの勘違いだった気もしています。ですが今となっては確認のしようがありません。

    >CreateWindowExW()の戻り値の方のhWndを使ってSetHook(hWnd)を呼び出してはどうでしょうか?
    いえ、CreateWindowExW()の戻り値も別プロセスのFindWindowの戻り値も、ウィンドウプロシージャの引数も現在使用しているマシンではすべて同じでした。

    2012年11月15日 17:33