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

質問
-
お世話になっております。
システムフックを用いて別プロセスのウィンドウのマウスイベントをキックにして、自作アプリのウィンドウにメッセージを送ろうとしています。
ほとんどの場合はうまくいくのですが、ある特定のアプリについてのみうまくいかない場合があり、原因を解明したいと思っております。
具体的にはこちらのフラッシュゲームを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
すべての返信
-
フックハンドルについては、CallNextHookEx()の第1引数に指定する時点でプロセス間共有が必須です。当然ながら認められていると考えてもよいでしょう。
蛇足ですが、CallNextHookEx()のドキュメントはずいぶん前に更新されていて、第1引数は無視される、つまり、フックハンドルを指定する必要はありません。ウィンドウハンドルもFindWindow()で検索できる時点で状況証拠としては共有できると言えます。ただどちらにしても直接的なドキュメントは見つけられませんでした。正確には、Window Station内で共有可能ではありますが。(ユーザー切り替えでの別ユーザーのウィンドウやリモートデスクトップ接続されているウィンドウは無理。)
-
お返事が遅くなって申し訳ありません。
ユーザーインターフェイス特権の分離なのかどうか、確認をしてみました。
MyHookProc内のPostMessageWの下にGetLastErrorをいれたのですが、
戻り値は1400(Invalid window handle )になっていました。
ユーザーインターフェイスの特権の分離が原因の場合GetLastError()は5(Access Denied)を返すとかかれています。
ウィンドウハンドルの値のプロセス間共有についてですが、XP上では同一のウィンドウに対して異なるウィンドウハンドルの値になるケースがあったと記憶しております。
同一プロセス内でもウィンドウプロシージャの引数のhWndの値とFindWindowの戻り値が異なっていました。
ただ、私が今使っている7のマシンでは、いずれの方法でも同じ値になっているので確認できません。
CallNextHookExの第一引数は無視されるんですね。なるべく英語のドキュメントを読もうと思っているんですが、検索すると日本語のページが先にでてくるのでついついそっちを読んでしまいます。
余談ですが、日本語のMSDNにはCreateWindowのhInstanceは無視されるとかかれていますが、これに正しい値を入れない場合GetWindowLong( hWnd, GWL_HINSTANCE ) は適切な値を返しません。英語版は無視するとはかいてありませんね。