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

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

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

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

Win32 API

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

C++

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

.NET Framework

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

Q&A

解決済

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

koiru
koiru

総合スコア4

C#

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

Win32 API

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

C++

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

.NET Framework

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

1回答

0グッド

0クリップ

3591閲覧

投稿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

以下のような質問にはグッドを送りましょう

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

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

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

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

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

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

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

適切な質問に修正を依頼しましょう。

回答1

2

ベストアンサー

こちらではそのコードを、ほぼコピペしてちゃんとフックされましたよ。(メモ帳に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

総合スコア1427

koiru, yohhoy👍を押しています

良いと思った回答にはグッドを送りましょう。
グッドが多くついた回答ほどページの上位に表示されるので、他の人が素晴らしい回答を見つけやすくなります。

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

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

このような回答には修正を依頼しましょう。

回答へのコメント

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#側に通信し、出力する方法にしたいと思います。 ありがとうございました!

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

ただいまの回答率
86.02%

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

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

質問する

関連した質問

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

C#

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

Win32 API

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

C++

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

.NET Framework

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