How to install a hook on one process?

private const int WH_KEYBOARD_LL = 13; private const int WM_KEYDOWN = 0x0100; private static LowLevelKeyboardProc _proc = HookCallback; private static IntPtr _hookID = IntPtr.Zero; private static Form1 _applicationForm; // Данный метод устанавливает хук на все процессы: private static IntPtr SetHook(LowLevelKeyboardProc proc) { using (Process curProcess = Process.GetCurrentProcess()) using (ProcessModule curModule = curProcess.MainModule) { return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0); } } private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam); private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) { return CallNextHookEx(_hookID, nCode, wParam, lParam); } 

How can I install only on a separate process?

  • The last parameter SetWindowsHookEx accepts the PID of the process. - D .Stark
  • Low-level hooks do not require removal to a separate library. You can set them directly in your application and pass NULL to the 3rd parameter. - D .Stark
  • @ D.Stark, In what sense is a separate library? So it can be installed only through the PID, I thought it was possible through the list of processes to somehow go through to find the right one and install it only on the necessary hook process. - Anonymous
  • " In what sense is a separate library? " - Otherwise, why are you passing the module descriptor to a function? Read the documentation. - D .Stark
  • @ D.Stark, I still do not understand what documentation you are talking about ?! What is wrong with the code? If you can give a direct answer what is wrong. - Anonymous

1 answer 1

There is no opportunity to install a low-level hook on a separate process. The low-level hook is therefore called the "low-level" because it is called before the input event reaches the target process (more precisely, the stream, since the message queues belong to the threads). Although the SetWindowsHookEx function allows you to pass the fourth parameter of the thread ID to set the hook only on events of this thread, for WH_KEYBOARD_LL this parameter does not work, it seems, precisely because of its particular nature.

However, it is not a secret to which process the message will be sent to: it is the process that owns the current active window. This means that it is quite simple to implement filtering of the hook events of a specific process using the GetWindowThreadProcessId and GetForegroundWindow functions. After reworking the example from here , we get the following code:

 using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; using System.Windows.Forms; using System.Diagnostics; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Process[] prs = Process.GetProcessesByName("notepad"); if (prs.Length == 0) throw new ApplicationException("Process not found!"); KeyLogger.SetHook((uint)prs[0].Id); } } class KeyLogger { const int WH_KEYBOARD_LL = 13; const int HC_ACTION = 0; const int WM_KEYDOWN = 0x0100; const uint VK_CAPITAL = 0x14; static uint hookProcessID; static IntPtr hookID; static KeyboardProc Callback = KeyboardHookCallback; public static void SetHook(uint pid) { hookProcessID = pid; hookID = SetWindowsHookEx(WH_KEYBOARD_LL, Callback, IntPtr.Zero, 0); if (hookID == IntPtr.Zero) throw new ApplicationException("Failed to install hook!"); Console.WriteLine("Started listening keyboard events..."); Application.Run(); } static IntPtr KeyboardHookCallback(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN) { int vkCode = Marshal.ReadInt32(lParam); uint pid = 0; GetWindowThreadProcessId(GetForegroundWindow(), out pid); if (pid == hookProcessID) { Console.Out.WriteLine("Key: " + ((Keys)vkCode).ToString() + ";"); } } return CallNextHookEx(hookID, nCode, wParam, lParam); } delegate IntPtr KeyboardProc(int nCode, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] static extern IntPtr SetWindowsHookEx(int idHook, KeyboardProc lpfn, IntPtr hInstance, int threadId); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool UnhookWindowsHookEx(IntPtr hhk); [DllImport("user32.dll")] static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint ProcessId); [DllImport("user32.dll")] static extern IntPtr GetForegroundWindow(); } } 
  • Is it possible to install a hook when installing a hook? There must be a message loop. - D .Stark
  • @ D.Stark It is - Application.Run(); . Console applications can also have a message loop (I added a link to System.Windows.Forms for this, but you can certainly do without it) - MSDN.WhiteKnight February
  • Those. chip in adding links to System.Windows.Forms? - D .Stark
  • @ D.Stark is not, the link itself does not solve anything. The trick is, in what you said, in the message loop. It can simply be launched in different ways, either by calling Application.Run from Windows Forms (or WPF), or by explicit calls to GetMessage - TranslateMessage - DispatchMessage. The type of project here does not really matter. - MSDN.WhiteKnight February