2009-11-28 868 views
10

Tôi đang cố gắng ghép ứng dụng của bên thứ 3 để tôi có thể vẽ lên màn hình của nó. Vẽ lên màn hình thật dễ dàng và tôi không cần trợ giúp với nó, nhưng dường như tôi đang gặp vấn đề với việc sử dụng SetWindowsHookEx để xử lý WH_GETMESSAGE. Tôi không thể tìm ra những gì để vượt qua cho 2 tham số cuối cùng.SetWindowsHookEx trong C#

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 

namespace WindowDrawer 
{ 
    public partial class Form1 : Form 
    { 
     private delegate int HookProc(int code, IntPtr wParam, IntPtr lParam); 
     static IntPtr hHook; 
     IntPtr windowHandle; 
     uint processHandle; 

     HookProc PaintHookProcedure;  

     [System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)] 
     static extern System.IntPtr FindWindowByCaption(int ZeroOnly, string lpWindowName); 

     [System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "SetWindowsHookEx", SetLastError = true)] 
     static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId); 

     [System.Runtime.InteropServices.DllImport("user32.dll")] 
     static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); 

     // When you don't want the ProcessId, use this overload and pass IntPtr.Zero for the second parameter 
     [System.Runtime.InteropServices.DllImport("user32.dll")] 
     static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); 

     [System.Runtime.InteropServices.DllImport("kernel32.dll", CharSet =System.Runtime.InteropServices.CharSet.Auto)] 
     public static extern IntPtr GetModuleHandle(string lpModuleName); 

     public Form1() 
     { 
      InitializeComponent(); 
     } 

     private void button1_Click(object sender, EventArgs e) 
     { 

      PaintHookProcedure = new HookProc(PaintHookProc); 
      windowHandle = FindWindowByCaption(0, "Untitled - Notepad"); 
      uint threadID = GetWindowThreadProcessId(windowHandle, out processHandle); 
      IntPtr hMod = System.Runtime.InteropServices.Marshal.GetHINSTANCE(typeof(Form1).Module); 

      // HERE IS THE PROBLEM. WHAT THE HECK DO I PASS INTO THE LAST 2 PARAMS? I get a null pointer 
      hHook = SetWindowsHookEx(WH_GETMESSAGE, PaintHookProcedure, hMod, threadID); 


     } 

     public int PaintHookProc(int nCode, IntPtr wParam, IntPtr lParam) 
     { 
      // Do some painting here. 
      return CallNextHookEx(hHook, nCode, wParam, lParam); 
     } 

     private const int WM_PAINT = 15; 
     private const int WH_GETMESSAGE = 3; 
    } 





} 

Trả lời

12

SetWindowsHookEx chi tiết cụ thể hai tham số cuối cùng thusly:

  • hMod

[trong] Xử lý đến DLL có chứa các thủ tục móc trỏ đến bởi lpfn tham số. Tham số hMod phải là được đặt thành NULL nếu tham số dwThreadId chỉ định một luồng được tạo theo quy trình hiện tại và nếu thủ tục móc nằm trong mã được kết hợp với quy trình hiện tại.

  • dwThreadId

[trong] Chỉ định định danh của thread mà thủ tục hook là có liên quan. Nếu tham số này bằng không, quy trình móc là được liên kết với tất cả các chủ đề hiện có chạy trên cùng một máy tính để bàn với chủ đề gọi là .

Tôi không chắc chắn bạn có thể sử dụng tệp .dll theo cách được yêu cầu, nhưng bạn chắc chắn có thể thử.

Lấy hMod qua Marshal.GetHINSTANCE(typeof(Form1).Module)dwThreadId qua Process.Threads. Ngoài ra, hãy đặt dwThreadId thành 0 nếu bạn muốn móc nối chung (ví dụ: móc cho tất cả các cuộc gọi GetMessage() trong màn hình hiện tại) nhưng hãy cẩn thận với các hình phạt về hiệu suất.

+0

Tôi đã sửa đổi mã ở trên để phản ánh ý tưởng của bạn Tôi vẫn nhận được SetWindowsHookEx (WH_GETMESSAGE, PaintHookProcedure, hMod, threadID) == 0; – Darthg8r

+1

Có khả năng khác biệt là bạn cần phải đóng gói PaintHookProcedure của bạn trong một DLL riêng biệt; ngăn cản việc sử dụng .NET. Lấy mã thông báo lỗi bằng GetLastError() và xem vấn đề là gì. –

+2

Và bởi GetLastError() tôi có nghĩa là Marshal.GetLastWin32Error(); p/gọi trực tiếp GetLastError() là không đáng tin cậy. –

2

Tôi tin rằng bạn cần phải P/Gọi GetModuleHandle và sử dụng tay cầm nó trả cho tham số thứ ba của SetWindowsHookEx. Tôi cũng tin rằng 0 là chính xác cho tham số thứ tư, vì bạn không muốn móc bất kỳ một chuỗi cụ thể nào trong ứng dụng của bên thứ ba.

Nếu cách này không hiệu quả với bạn, SetWindowsHookEx trên MSDN có thể hướng bạn đi đúng hướng.

+0

GetModuleHandle (string lpModuleName); Tôi phải nhập cái gì? Tên exe? – Darthg8r

+0

Vượt qua 'NULL' và nó sẽ kéo tay cầm từ quá trình gọi (thực thi của bạn) –

3

Tôi không biết nhưng nếu bạn đang sử dụng các giá trị tham số chỉ định bạn muốn, như API giúp nói "chèn một DLL vào một quy trình khác", thì tôi biết nó chỉ hoạt động nếu bạn viết một DLL không được quản lý để gọi nó.

2

Tôi biết rằng đây là câu hỏi cũ nhưng tôi hy vọng rằng vẫn có ai đó sẽ thấy điều này hữu ích. Tôi nghĩ rằng bạn đang trộn lên intIntPtr

[System.Runtime.InteropServices.DllImport("user32.dll")] 
static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); 
7

Sau đây gợi ý này sẽ không làm việc:

"móc toàn cầu không được hỗ trợ trong.Nền tảng NET. Ngoại trừ các WH_KEYBOARD_LL ở mức độ thấp móc và WH_MOUSE_LL ở mức độ thấp móc, bạn không thể thực hiện móc toàn cầu trong Microsoft.NET Framework."

Từ "How to set a Windows hook in Visual C# .NET"

-1

Công việc này cho tôi sử dụng 13 ...

private static IntPtr SetHook(LowLevelKeyboardProc proc) 
     { 
      using (Process curProcess = Process.GetCurrentProcess()) 
      using (ProcessModule curModule = curProcess.MainModule) 
      { 
       return SetWindowsHookEx(13, proc, 
       GetModuleHandle(curModule.ModuleName), 0); 
      } 
     }