I'm trying to hook the creation of a windows in my C# app.
static IntPtr hhook = IntPtr.Zero;
static NativeMethods.HookProc hhookProc;
static void Main(string[] args)
{
// Dummy.exe is a form with a button that opens a MessageBox when clicking on it.
Process dummy = Process.Start(@"Dummy.exe");
try
{
hhookProc = new NativeMethods.HookProc(Hook);
IntPtr hwndMod = NativeMethods.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
hhook = NativeMethods.SetWindowsHookEx(HookType.WH_CBT, hhookProc, hwndMod, (uint)AppDomain.GetCurrentThreadId());
Console.WriteLine("hhook valid? {0}", hhook != IntPtr.Zero);
while (!dummy.HasExited)
dummy.WaitForExit(500);
}
finally
{
if(hhook != IntPtr.Zero)
NativeMethods.UnhookWindowsHookEx(hhook);
}
}
static int Hook(int nCode, IntPtr wParam, IntPtr lParam)
{
Console.WriteLine("Hook()");
return NativeMethods.CallNextHookEx(hhook, nCode, wParam, lParam);
}
Problem is, when clicking on my button (in Dummy.exe), I never enter in my Hook, what am I doing wrong?
Thanks.
EDIT
Program.cs
using System;
using System.Diagnostics;
namespace Hooker
{
class Program
{
static IntPtr hhook = IntPtr.Zero;
static NativeMethods.HookProc hhookProc;
static void Main(string[] args)
{
Process dummy = Process.Start(@"Dummy.exe");
try
{
hhookProc = new NativeMethods.HookProc(Hook);
hhook = NativeMethods.SetWindowsHookEx(HookType.WH_CBT, hhookProc, IntPtr.Zero, 0);
Console.WriteLine("hhook valid? {0}", hhook != IntPtr.Zero);
while (!dummy.HasExited)
dummy.WaitForExit(500);
}
finally
{
if(hhook != IntPtr.Zero)
NativeMethods.UnhookWindowsHookEx(hhook);
}
}
static int Hook(int nCode, IntPtr wParam, IntPtr lParam)
{
Console.WriteLine("Hook()");
return NativeMethods.CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam);
}
}
}
NativeMethods.cs
namespace Hooker
{
using System;
using System.Runtime.InteropServices;
internal static class NativeMethods
{
public delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(HookType hookType, HookProc lpfn, IntPtr hMod, int dwThreadId);
[DllImport("user32.dll")]
public static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", SetLastError = true)]
public static extern int GetWindowThreadProcessId(IntPtr hwnd, ref int pid);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
}
}
For dummy, do a new Form with :
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("CONTENT", "TITLE");
}
One issue with your code is that hhookProc
can be garbage collected while your native code still needs it. Use GC.KeepAlive
or put in in a static variable.
The hMod
param should probably be null, since you specified a thread in your own process:
hMod [in]
Type: HINSTANCE
A handle to the DLL containing the hook procedure pointed to by the lpfn parameter. The hMod parameter must be set to NULL if the dwThreadId parameter specifies a thread created by the current process and if the hook procedure is within the code associated with the current process.
But I think this only works for windows on the thread you specify.
In theory you can specify threads in other applications or even a global hook. The specified callback then gets called on the corresponding thread, even if that thread is in another process, in which case your dll gets injected into that process(that's the reason you need to specify the module handle in the first place).
But I believe that's not possible with .net code, because the mechanism for injecting into other processes and calling the hook method over there doesn't work with JIT compiled code.
Like most SetWindowsHookEx
hooks, WH_CBT
hooks require that the hook callback lives in a separate Win32 DLL that will get loaded into the target process. This essentially requires that the hook is written in C/C++, C# won't work here.
(Low-level mouse and keyboard hooks are two exceptions to this rule. It might also be possible to use other hooks in C# but only if you are hooking one of your own threads, so dwThreadId is the id of a thread in the current process, not 0. I haven't confirmed this, though. And you need to make sure you're using the Win32 threadid, so using GetCurrentThreadId is probably the best bet here.)
If you want to watch for new windows appearing from another app, an alternative C#-friendly approach is to use the SetWinEventHook API instead, specify WINEVENT_OUTOFCONTEXT (which is the magic flag that gets the events delivered to your own process, removing the need for a DLL and making C# usable here) and hook for the EVENT_OBJECT_CREATE
and EVENT_OBJECT_SHOW
events. You can listen to either your own process/thread's events, or to all processes/threads on the current desktop.
This will get you all sorts of "create" and show notifications, including those for child HWNDs within dialog, and even items within listboxes and similar; so you'll need to filter to extract only those for top-level HWNDs: eg. check that idObject==OBJID_WINDOW and idChild==0; that hwnd is visible (IsVisible()
) and is top-level.
Note that using WinEvents requires that the thread that calls SetWinEventHook is pumping messages - which is typically the case anyway if it's a thread with UI. If not, you may need to add a message loop (GetMessage/TranslateMessage) manually. And you'll also want to use GC.KeepAlive() with the callback here also to prevent it from getting collected until after you call UnhookWinEvents.
This won't work in C#
Scope: Thread
If the application installs a hook procedure for a thread of a different application, the procedure must be in a DLL.
(Documentation of
SetWindowsHookEx
)
Scope: Global
To install a global hook, a hook must have a native DLL export to inject itself in another process that requires a valid, consistent function to call into. This behavior requires a DLL export. The .NET Framework does not support DLL exports.
(Source)
I'm not familiar with the NativeMethod
class you are referencing, but I'll make some assumptions and try to make some ground.
My guess this has to do with what handle you are hooking. The
dummy.MainWindowHandle
represents the front most window's handle, which is usually what you are looking for. However, in this case, you are opening a MessageBox.Show() which likely has a different handle than the one you have hooked to.
I'd assume that
IntPtr hwndMod = NativeMethods.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
is probably going to return the same result as
dummy.Refresh();
IntPtr hwndMod = dummy.MainWindowHandle;
So I think it's safe to say that they might be giving you the handle you aren't looking for.
Perhaps try to make a test WinForm application. That way you can grab the proper handle. Just make sure to use
dummy.WaitForInputIdle();
dummy.Refresh();
before grabbing the handle to make sure you are grabbing the right handle at launch time.
I see it is a console application , so console application doesn't enter a windows messages loop.
simple solution is to include system.windows.forms
and simply type application.start() in your main and things will be fine :)
来源:https://stackoverflow.com/questions/9102814/how-to-hook-an-application