2012-02-01 46 views
1

我想鉤在我的C#應用​​程序中創建一個窗口。如何掛鉤應用程序?

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); 
} 

問題是,當點擊我的按鈕(在Dummy.exe)時,我從來沒有進入我的鉤子,我做錯了什麼?

謝謝。


編輯

的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); 
    } 
} 

對於虛擬,做一個新的形式:

public Form1() 
    { 
     InitializeComponent(); 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     MessageBox.Show("CONTENT", "TITLE"); 
    } 
+0

爲什麼要使用一個鍵盤鉤子?我想檢測何時exe(即沒有GUI)調用另一個exe(有一個GUI)。什麼是鍵盤的鏈接? – 2012-02-01 21:35:13

+0

我誤解了WH_CBT的含義。 – CodesInChaos 2012-02-01 21:37:55

+0

你爲什麼用'hwnd'作爲模塊句柄的前綴?這不是窗戶把手。 – CodesInChaos 2012-02-01 21:40:21

回答

2

你的代碼有一個問題,hhookProc可以被垃圾收集,而你的本地代碼仍然需要它。使用GC.KeepAlive或放入一個靜態變量。

hMod PARAM也許應該是空的,因爲你在你自己的進程中指定線程:

HMOD [中]

類型:HINSTANCE

句柄包含的DLL掛鉤過程由lpfn參數指向。如果dwThreadId參數指定由當前進程創建的線程,並且鉤子過程位於與當前進程關聯的代碼中,則hMod參數必須設置爲NULL。


但我認爲這隻能用於指定的線程窗口。

理論上,您可以在其他應用程序中指定線程,甚至可以指定全局掛鉤。然後在相應的線程上調用指定的回調,即使該線程在另一個進程中,在這種情況下,您的dll將被注入到該進程中(這就是您需要首先指定模塊句柄的原因)。

但我相信這是不可能的.net代碼,因爲注入到其他進程和調用那裏的鉤子方法的機制不適用於JIT編譯代碼。

+0

已經是這樣了,我發佈了所有的代碼,看看我的編輯。 – 2012-02-01 21:54:45

+0

@Arnaud您的新代碼仍然被破壞。您需要在自己的進程中指定一個'0' hModule和一個線程。在這種情況下,您只能在自己的過程中觀察窗口。或者你指定'0'或一個外部線程和一個非空'hModule'。但正如我寫的,第二種方法在.net中可能不可能。 – CodesInChaos 2012-02-01 21:56:42

+0

實際上,我不知道應該將哪個值傳遞給SetWindowsHookEx來掛接我從當前應用程序內部啓動的應用程序... – 2012-02-01 22:02:27

0

我不熟悉你所引用的NativeMethod類,但我會做一些假設並嘗試做一些理由。 我的猜測與你掛鉤的手柄有關。該

dummy.MainWindowHandle

代表了最前端的窗口的句柄,這通常是你在找什麼。但是,在這種情況下,您將打開一個MessageBox.Show(),它可能具有與您所連接的句柄不同的句柄。

我認爲

IntPtr hwndMod = NativeMethods.GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName); 

很可能將返回相同的結果

dummy.Refresh(); 
IntPtr hwndMod = dummy.MainWindowHandle; 

所以我認爲這是肯定地說,他們可能給你,你不是活得手柄尋找。

也許試着做一個測試WinForm應用程序。這樣你可以抓住合適的手柄。只要確保抓住手柄,以確保您在啓動時抓住右手柄使用前

dummy.WaitForInputIdle(); 
dummy.Refresh(); 

+0

我修改了代碼,但沒有改變,無法鉤住任何東西... – 2012-02-01 21:36:00

+0

@ArnaudF。那麼我需要更多關於NativeMethod的東西或者項目的一個副本來進一步測試。我沒有足夠的工作。 – Corylulu 2012-02-01 21:38:24

+0

已添加全部代碼 – 2012-02-01 21:51:41

7

與大多數SetWindowsHookEx鉤子一樣,WH_CBT鉤子要求鉤子回調存在於一個單獨的Win32 DLL中,該DLL將被加載到目標進程中。這基本上要求鉤子用C/C++編寫,C#在這裏不起作用。 (低級別的鼠標和鍵盤鉤子是這個規則的兩個例外,也可能在C#中使用其他鉤子,但只有當你鉤住你自己的線程時,所以dwThreadId是線程的標識符在目前的過程中,不是0.我還沒有證實這一點,但你需要確保你使用Win32的threadid,所以使用GetCurrentThreadId可能是最好的選擇。)

如果你想觀察從另一個應用程序出現的新窗口,另一種C#友好的方法是使用SetWinEventHook API,而不是指定WINEVENT_OUTOFCONTEXT(這是魔術標誌,它將事件傳遞到您自己的進程,無需DLL並使C#在這裏可以使用)並鉤上EVENT_OBJECT_CREATEEVENT_OBJECT_SHOW事件。您可以偵聽您自己的進程/線程事件,也可以偵聽當前桌面上的所有進程/線程。

這將爲您提供各種「創建」和顯示通知,包括對話框中的子HWND,甚至列表框及類似內容中的項目。因此您需要進行過濾以僅提取那些用於頂級HWND的:例如。檢查idObject == OBJID_WINDOW和idChild == 0;該hwnd是可見的(IsVisible())並且是頂層。

請注意,使用WinEvents需要調用SetWinEventHook的線程正在抽取消息 - 無論如何,如果它是帶UI的線程,通常情況下都是如此。如果沒有,您可能需要手動添加消息循環(GetMessage/TranslateMessage)。而且,您還需要在回調函數中使用GC.KeepAlive(),以防止在調用UnhookWinEvents之前收集它。

1

這將無法工作在C#

範圍:線程

如果應用程序安裝的不同應用程序的線程一個鉤子程序,該程序必須是在一個DLL

(的SetWindowsHookEx文檔)

範圍:全球

要安裝全局鉤子,鉤子必須有一個本機DLL導出在另一個進程中注入自身需要一個有效的,一致的函數來調用。此行爲需要DLL導出。 .NET Framework不支持DLL導出。

Source

+0

在C#中實現的唯一可能的窗口鉤子是_global低級別hooks_和_local線程hooks_。 – ordag 2012-02-01 22:30:16

0

我看到它是一個控制檯應用程序,所以控制檯應用程序不會進入窗口消息循環。

簡單的方案是包括System.Windows.Forms的

,只需鍵入application.start()在主 ,事情都會好起來的:)