VBからC++で書いたDLLを呼び出して、ウィンドウが作成されたときに自分自身(VB)にメッセージを送りたいのです。どうもDLLのソースが間違ってるぽいのですが...ご教授お願いします。
↓CPP
#include <windows.h>
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
LRESULT CALLBACK MsgHook(int nCnode, WPARAM wParam, LPARAM lParam)
{
struct HWND__ * aaa;
MSG *pmsg;
pmsg = (MSG *)lParam;
if(((CWPSTRUCT *)lParam)->message == WM_CREATE)
{
aaa = FindWindow("WindowTitle",NULL);
SendMessage(aaa,WM_COPYDATA,wParam,lParam);
}
return 0;
}
↓DEF
LIBRARY hook.DLL
EXPORTS
MsgHook
何がしたいのかわかりません。
もう少し、やりたいことを具体的に記述してください。
また、どううまくいかないのか、成功した場合はどんな結果を期待するのかも記述すべきです。
まぁ、推測するに、システムフックをかけてウィンドウの作成を呼び出し側に通知したいといったところでしょうか。
自アプリを監視したいだけなら、こんな DLL を作る必要はありません。
他アプリも監視したいなら…まぁ、このコードではいけませんね。
DLL を作っているところから、グローバルフックだろうとは思いますが。
以下、グローバルフックだとして話を進めると…
グローバルフックには DLL のインスタンスハンドルが必要です。これを DllMain で取得して、どこかに保存しておかなければなりません。
SetWindowsHookEx 関数は VB からは使えません。
呼び出すことは可能ですが、グローバルフックの場合はその戻り値を共有しなければならず、これは VB では不可能です。
そこで、SetWindowsHookEx、UnhookWindowsHookEx を呼び出すコードは DLL 内に必要です。ここで、先ほど保存しておいたインスタンスハンドルを使います。
また、MsgHook のかわりに、このフックをセット/解除する関数をエクスポートすべきです。
ついでに、フックプロシージャ内で毎回クライアントウィンドウを検索せずに、このフックをセットする関数で一度セットすればよりよいかと。
コールバック関数を使うという手もありますね。
フックプロシージャは、CallNextHookEx を呼び出さなければなりません。
ちゃんと nCode を判定して、適切な処理を行ってください。
def ファイルの LIBRARY 文には、拡張子は含めません。…たぶん。
EXPORTS 文は前述の通りに修正して、あとは…
フックハンドルの共有のために、共有セクションの定義も行う必要があります。
ざっと修正点を挙げてみました。こんな感じでどうでしょう?
ご指摘ありがとうございます。説明不足かつ勉強不足でしたね。
VBでグローバルフックが出来ないので、VCでDLLを作りたかったのです。おっしゃる通り、ウィンドウの作成をDLLからVBに通知したかったのです。シャノンさんの指摘をヒントによーく調べてみます。
http://hp.vector.co.jp/authors/VA016117/hook.html
↑ここを参考にしながらなんとか出来ました。ありがとうございました。
'--------------------------------------------------
VB側
'--------------------------------------------------
Private Sub Form_Load()
Call sethook
End Sub
Private Sub Form_Unload(Cancel As Integer)
Call freehook
End Sub
'--------------------------------------------------
DLL側
'--------------------------------------------------
#define STRICT
#include <windows.h>
#define DllExport __declspec( dllexport )
#define CLASS_NAME "ThunderFormDC"//開発ツール
//#define CLASS_NAME "ThunderRT6FormDC"//実行ファイル
#define TITLE_NAME "777"
#define MESSAGE "777"
DllExport void CALLBACK sethook(void);
DllExport void CALLBACK freehook(void);
HWND g_lng_aaa;
COPYDATASTRUCT g_udt_CDS;
//共有領域
#pragma data_seg(".sharedata")
/*フックプロシージャハンドル
共有領域のデータは初期化しないとうまく確保されない*/
HHOOK hHookWnd=0;
#pragma data_seg()
HINSTANCE hdll;
//BCでは『DllEntryPoint』
BOOL WINAPI DllMain (HINSTANCE hInstance, DWORD reason, LPVOID lpReserved)
{
g_lng_aaa = FindWindow(CLASS_NAME,TITLE_NAME);
(void)lpReserved;
if(reason==DLL_PROCESS_ATTACH)
{
hdll=hInstance;//DLLハンドル保存
}
return TRUE;
}
LRESULT CALLBACK CallWndProc(int nCode,WPARAM wParam,LPARAM lParam)
{
//メッセージを処理する必要があるか?
if(nCode == HC_ACTION){
if(((CWPSTRUCT *)lParam) -> message == WM_CREATE)
{
char str_aaa[512];
char str_bbb[256];
GetClassName(((CWPSTRUCT *)lParam) -> hwnd,str_aaa,256);
GetWindowText(((CWPSTRUCT *)lParam) -> hwnd,str_bbb,256);
strcat(str_aaa,"■");
strcat(str_aaa,str_bbb);
g_udt_CDS.dwData = 0;
g_udt_CDS.lpData = (void*)str_aaa;
g_udt_CDS.cbData = lstrlen(str_aaa) + 1;//終端のNULLも送信
SendMessage(g_lng_aaa,WM_COPYDATA,(WPARAM)NULL,(LPARAM)&g_udt_CDS);
}
}
return CallNextHookEx(hHookWnd, nCode, wParam, lParam);//次のフック
}
//フック設定
void CALLBACK sethook(void)
{
hHookWnd=SetWindowsHookEx(WH_CALLWNDPROC,CallWndProc, hdll, 0);
}
//フック解除
void CALLBACK freehook(void)
{
UnhookWindowsHookEx(hHookWnd);
}
ごくまれに、SendMessageが二重で送信されてしまう現象が出てしまいます。
フックの解除がうまく出来ていないのでしょうか?FreeLibraryを使うと解消出来るでしょうか?
>ごくまれに、SendMessageが二重で送信されてしまう現象が出てしまいます。
ごめんなさい、ちょっとこれ以上はわかりません。
ただし、一つ言えることは
>FreeLibraryを使うと解消出来るでしょうか?
FreeLibrary を使うべきところはありません。
あと、これは些細なことなんですが、
>#define CLASS_NAME "ThunderFormDC"//開発ツール
>//#define CLASS_NAME "ThunderRT6FormDC"//実行ファイル
>g_lng_aaa = FindWindow(CLASS_NAME,TITLE_NAME);
こんな面倒なことしないで
void CALLBACK sethook(HWND hwndForm)
{
g_lng_aaa = hwndForm
// SetWindowsHookEx とか。省略
}
でいいんじゃないでしょうか
そうですね。引数で渡せば簡単ですよね(W VCあんまりよくわかってなかったんで、頭一杯で...。二重送信はVB側でフラグかなんか立てて対応することにします。
度々のご指摘本当にありがとうございました。
便乗質問ですみません。
g_lng_aaa = FindWindow(CLASS_NAME,TITLE_NAME);を消して、
//フック設定
void CALLBACK sethook(HWND hwndForm)
{
g_lng_aaa = hwndForm;
hHookWnd=SetWindowsHookEx(WH_CALLWNDPROC,CallWndProc, hdll, 0);
}
とやってみたのですが、CallWndProcの中ではg_lng_aaaは0になってしまいます。VBの開始時と終了時にMessageBoxでg_lng_aaaを出力してみるとウインドウハンドルが格納されているのですが...。グローバル変数への代入はDllMainの中で行わないといけないのでしょうか?
...自己解決しました。
//共有領域
#pragma data_seg(".sharedata")
/*フックプロシージャハンドル
共有領域のデータは初期化しないとうまく確保されない*/
HHOOK hHookWnd=0;
HWND g_lng_aaa=0;
#pragma data_seg()
と、しなければいけないのですね。
>CallWndProcの中ではg_lng_aaaは0になってしまいます。
ありゃ、そうでしたか。これは失礼を…
>//共有領域
>#pragma data_seg(".sharedata")
>/*フックプロシージャハンドル
>共有領域のデータは初期化しないとうまく確保されない*/
>HHOOK hHookWnd=0;
>HWND g_lng_aaa=0;
>#pragma data_seg()
あー…なるほど。そんな気もしますね。
テキトーなこと言ってゴメンナサイでした。
いえいえ〜おかげでプログラムがスッキリして良かったです。
SendMessageが二重送信と書きましたが、二重受信かもしれません。
VBの開発環境で、実行ファイルの×ボタンから終了せずに、ツールバーや
メニューバーから終了すると、自分自身に受信用フックが残ってしまうみたです。参考まで。
ツイート | ![]() |