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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

Q&A

解決済

1回答

6789閲覧

SetWindowsHookExで特定スレッドのメッセージをフックしたいが上手くいかない

koiru

総合スコア4

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

0グッド

0クリップ

投稿2021/06/12 01:11

編集2021/06/16 10:09

前提・実現したいこと

SetWindowsHookEx関数で、特定スレッドのメッセージをフックしたいです。

フックの種類は「WH_CALLWNDPROC」で、C++でフックプロシージャのDLLを作成し、C#から呼び出しています。
DLL、メインのアプリケーション、フック対象のソフト、すべて32bitです(OSは64bit)。

発生している問題

SetWindowsHookEx関数の引数4(dwThreadId)にスレッドIDを指定すると、一切メッセージがフックされません。
0 を指定してグローバルフックにすると、フックされるのですが……

該当のソースコード

メモ帳を起動し、その起動したメモ帳に対してフックします。

DLL側(dllmain.cpp)

※「HookProc.dll」という名前で生成しています

C++

1// dllmain.cpp : DLL アプリケーションのエントリ ポイントを定義します。 2#include "pch.h" 3#include <iostream> 4 5// エクスポート 6#define DllExport __declspec(dllexport) BOOL __stdcall 7extern "C" { 8 DllExport SetHook(DWORD threadId); 9 DllExport UnHook(void); 10} 11 12#pragma data_seg(".shared") // 何のため? 13HHOOK hHook = NULL; 14#pragma data_seg() 15 16HINSTANCE hInstance; 17 18 19BOOL APIENTRY DllMain( HMODULE hModule, 20 DWORD ul_reason_for_call, 21 LPVOID lpReserved 22 ) 23{ 24 switch (ul_reason_for_call) 25 { 26 case DLL_PROCESS_ATTACH: 27 hInstance = hModule; 28 break; 29 } 30 return TRUE; 31} 32 33LRESULT CALLBACK CallWndProc(int nCode, WPARAM wParam, LPARAM lParam) 34{ 35 if (nCode == HC_ACTION) 36 { 37 char buffer[256] = ""; 38 const LPCWPSTRUCT lpcwprs = (LPCWPSTRUCT)lParam; 39 sprintf_s(buffer, 255, "HC_ACTION message: %X, lParam: %d, wParam: %d, hwnd: %d", int(lpcwprs->message), int(lpcwprs->lParam), int(lpcwprs->wParam), int(lpcwprs->hwnd)); 40 std::cout << buffer << std::endl; 41 } 42 return CallNextHookEx(hHook, nCode, wParam, lParam); 43} 44 45BOOL APIENTRY SetHook(DWORD threadId) 46{ 47 hHook = SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, hInstance, threadId); 48 49 if (hHook == NULL) 50 { 51 return false; 52 } 53 else 54 { 55 return true; 56 } 57} 58 59BOOL APIENTRY UnHook(void) 60{ 61 bool ret = UnhookWindowsHookEx(hHook); 62 hHook = NULL; 63 return ret; 64} 65

呼び出す側(Form1.cs)

C#

1using System; 2using System.Runtime.InteropServices; 3using System.Windows.Forms; 4 5namespace MessageHook 6{ 7 public partial class Form1 : Form 8 { 9 public Form1() 10 { 11 InitializeComponent(); 12 } 13 14 15 [DllImport("HookProc.dll")] 16 extern static bool SetHook(uint threadId); 17 [DllImport("HookProc.dll")] 18 extern static bool UnHook(); 19 20 [DllImport("user32.dll", SetLastError = true)] 21 static extern IntPtr FindWindow(string lpClassName, string lpWindowName); 22 [DllImport("user32.dll")] 23 static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId); 24 25 private void Form1_Load(object sender, EventArgs e) 26 { 27 // メモ帳(32bit)を対象にフックする 28 System.Diagnostics.Process p = System.Diagnostics.Process.Start("notepad.exe"); 29 System.Threading.Thread.Sleep(100); 30 IntPtr notepadWndHandle = FindWindow("Notepad", null); 31 uint threadId = GetWindowThreadProcessId(notepadWndHandle, IntPtr.Zero); 32 if (threadId == 0) 33 { 34 Console.WriteLine("スレッドIDの取得に失敗"); 35 Close(); 36 } 37 else 38 { 39 bool ret = SetHook(threadId); 40 Console.WriteLine("SetHook: " + ret); 41 } 42 } 43 44 private void Form1_FormClosed(object sender, FormClosedEventArgs e) 45 { 46 bool ret = UnHook(); 47 Console.WriteLine("UnHook: " + ret); 48 } 49 } 50} 51

試したこと

  • Microsoftのドキュメントを読みましたが、関係することは書かれていませんでした。

SetWindowsHookExA function (winuser.h) - Win32 apps | Microsoft Docs

どこを直せば実現できるのでしょうか?
回答よろしくお願いします。

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

  • Visual Studio 2019

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

こちらではそのコードを、ほぼコピペしてちゃんとフックされましたよ。(メモ帳にstd::coutの出力結果は出ないので適当なファイルをfopenしてfprintfするコードに変更した程度)

hHookを.sharedにおくのは、c#側exeのHookProc.dllのhHook (SetWindowsHookEx呼んだ結果)をnotepad.exeのHookProc.dllと共有してCallNextHookExに有効なhHookを渡せるようにする意図ですかね。ただ、CallNextHookExでは「HHOOK hhk : This parameter is ignored.」なので必要は無いですが。

追記
まず、CallWndProcはnotepad.exeのプロセス側で動いているんですが、それは大丈夫ですか?
notepad.exeのcoutと、自プロセス(C#exe)のcoutは別で、フックしたくらいで混ざったりしません。
表示するだけなら、下記のようにnotepad側にコンソールを割り当てるとかAttachConsoleする方法が楽でしょう。

cpp

1if( GetStdHandle(STD_OUTPUT_HANDLE) == 0 ) { 2 AllocConsole(); 3 freopen( "CONOUT$", "w", stdout); 4}

投稿2021/06/12 10:34

編集2021/06/13 09:05
matukeso

総合スコア1590

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

koiru

2021/06/13 05:15 編集

std::coutをやめ、fopenとfprintfでファイルに出力してみたところ、しっかりフックできていることが確認できました……! きっかけを与えていただき、本当にありがとうございました! また、「0 を指定してグローバルフックにすると、フックされる(std::coutで、Visual Studioの出力ウィンドウに出力される)」と書いたのですが、どうやら自ウィンドウのメッセージしか、std::coutで出力されていなかったみたいでした。 つまり、フックの確認にstd::coutを使用しており、(なぜそうなるのかは分かりませんが)自ウィンドウ以外のメッセージの場合に、そのstd::coutが機能しておらず、フックが失敗したと自分が勘違いしていたのが原因でした。 まさか出力部分に問題があるとは、思いもしませんでした……。 追加で質問になるのですが、std::coutが使えないとなると、Visual Studioの出力ウィンドウへ出力するには、なにを使えばいいのでしょうか? ファイルへの出力では、確認が面倒ですし、スマートではないので……。 ちなみに、#pragma data_seg(".shared") について。 ソース内の疑問まで拾っていただき、ありがとうございます。 改めて調べてみたところ、同一のDLLを使用しているプロセス同士で変数の共有ができ、これを応用して、別のプロセスでフック中かの確認などができるみたいです。 また、使用するには、.defファイルを追加し、下記のように記述する必要があったみたいです。 SECTIONS .shared READ WRITE SHARED
koiru

2021/06/14 09:33

>CallWndProcはnotepad.exeのプロセス側で動いている なんと、そうだったのですか! coutは、動いているプロセスのコンソールに出力するのですね。 ご提示いただいたコードを、CallWndProc内のcoutの直前に追加したのですが、コンソールが別ウィンドウで開くのですね……。 できれば、Visual Studioの「出力ウィンドウ」へ出力したいのですが、可能でしょうか? AttachConsoleを使うのだと思い、AllocConsole(); の部分を AttachConsole(VSのPID※1); に変更してみたのですが、AttachConsoleが失敗してしまいます……。 GetLastError関数では、エラーコード6が返され、「指定されたプロセスにコンソールがない」のが理由みたいです。 ※1「VSのPID」は、Visual StudioのメインウィンドウのPIDのことです(VSに属している「コンソール ウィンドウ ホスト」のPIDも指定してみたのですが、同じように失敗しました) どのプロセスのPIDを指定するのでしょうか?
matukeso

2021/06/14 13:11

c++では、コンソールとデバッグウインドウは別物のハズです。ま、自分でお好きなipcを使ってnotepad.exeのdllからc#側に通信すればいいんじゃないですかね。socket/pipe/共有メモリなどなど。ま、dllでOutputDebugString使っておいて、notepad側にも別のvisual studioをアタッチしてもいいですが。
koiru

2021/06/15 08:19

なるほど。C#側に通信し、出力する方法にしたいと思います。 ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問