2009-11-28 25 views
10

Próbuję podłączyć aplikację innej firmy, aby móc rysować na jej ekranie. Rysowanie na ekranie jest łatwe i nie potrzebuję pomocy, ale wydaje mi się, że mam problemy z używaniem SetWindowsHookEx do obsługi WH_GETMESSAGE. Nie mogę wymyślić, co przekazać dla ostatnich 2 parametrów.SetWindowsHookEx w języku 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; 
    } 





} 

Odpowiedz

12

SetWindowsHookEx specyfika ostatnie dwa parametry wygląda następująco:

  • hMod

[w] Uchwyt do DLL zawierającej procedura hak wskazywany przez lpfn parametru. Parametr hMod musi zerowanego jeśli parametr dwThreadId określa gwint utworzony obecnym procesie gdy procedura hak jest kodem związane z bieżącym procesem.

  • dwThreadId

[w] Określa identyfikator gwintu z którym procedura haka ma być związany. Jeśli ten parametr ma wartość zero, procedura haka jest powiązana ze wszystkimi istniejącymi wątkami działającymi na tym samym pulpicie co wątek wywołujący .

Nie jestem pewien, czy można użyć biblioteki .NET w sposób wymagany, ale na pewno można spróbować.

Pobierz hMod przez Marshal.GetHINSTANCE(typeof(Form1).Module) i dwThreadId przez Process.Threads. Alternatywnie, ustaw dwThreadId na 0, jeśli chcesz uzyskać globalny hak (czyli hak dla wszystkich wywołań GetMessage() na bieżącym pulpicie), ale uważaj na kary za wydajność.

+0

i modded powyższy kod w celu odzwierciedlenia Twój pomysł: nadal otrzymuję SetWindowsHookEx (WH_GETMESSAGE, PaintHookProcedure, hMod, threadID) == 0; – Darthg8r

+1

Istnieje wyraźna możliwość, że musisz spakować PaintHookProcedure w oddzielną bibliotekę DLL; co wyklucza użycie .NET. Chwyć kod komunikatu o błędzie za pomocą GetLastError() i sprawdź, jaki jest problem. –

+2

I przez GetLastError() Mam na myśli Marshal.GetLastWin32Error(); p/wywołanie metody GetLastError() bezpośrednio jest niewiarygodne. –

2

wierzę trzeba P/Invoke GetModuleHandle i używać uchwyt powraca po raz trzeci parametr SetWindowsHookEx. Wierzę również, że 0 jest poprawny dla czwartego parametru, ponieważ nie chcesz przechwytywać żadnego konkretnego wątku w aplikacji innej firmy.

Jeśli to nie działa, SetWindowsHookEx w witrynie MSDN może wskazywać właściwy kierunek.

+0

GetModuleHandle (lpModuleName string);? Co mam przekazać do niego nazwa exe – Darthg8r

+0

Przełęcz 'NULL' i będzie to pociągnąć za uchwyt z procesu wywołującego (. plik wykonywalny) –

3

Nie wiem, ale jeśli używasz wartości parametrów, które określają, jak chce API, "wstrzyknij bibliotekę DLL do innego procesu", to dla wszystkich wiem, że może działać tylko wtedy, gdy napisz niezarządzaną bibliotekę DLL, z której można to nazwać.

2

Wiem, że to stare pytanie, ale mam nadzieję, że nadal jest ktoś, kto uzna to za przydatne. Myślę, że jesteś mieszania int i IntPtr

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

Poniższa sugeruje, to nie będzie działać:

„Global haki nie są obsługiwane w.NET Framework. Oprócz haka niskopoziomowego WH_KEYBOARD_LL i hak niskiego poziomu WH_MOUSE_LL, nie można wdrożyć globalne haki Microsoft .NET Framework.”

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

-1

To praca dla mnie użyć 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); 
      } 
     }