The thread binding procedure is no longer called after pressing Tab several times. What for?

I set up a thread-specific window binding to track messages sent to WndProc. At first he worked. However, after I pressed Tab about 19 times to move the focus around the form, my hook callback is called nolonger. This caused concern about whether I pressed Tab quickly or slowly. Can anyone explain what is really happening?

Below is the code I wrote. I tested it on a 64-bit version of Windows 7.

using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; using System.Runtime.InteropServices; namespace HookTest { static class Program { private const int WH_CALLWNDPROC = 4; private delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam); private class MainForm : Form { private Button button1; private TextBox textBox1; public MainForm() { this.button1 = new System.Windows.Forms.Button(); this.textBox1 = new System.Windows.Forms.TextBox(); this.SuspendLayout(); // // button1 // this.button1.Location = new System.Drawing.Point(12, 38); this.button1.Name = "button1"; this.button1.Size = new System.Drawing.Size(75, 23); this.button1.TabIndex = 0; this.button1.Text = "Button 1"; this.button1.UseVisualStyleBackColor = true; // // textBox1 // this.textBox1.Location = new System.Drawing.Point(12, 12); this.textBox1.Name = "textBox1"; this.textBox1.Size = new System.Drawing.Size(100, 20); this.textBox1.TabIndex = 1; // // MainForm // this.Controls.Add(this.textBox1); this.Controls.Add(this.button1); this.Name = "MainForm"; this.Text = "Main Form"; this.ResumeLayout(false); this.PerformLayout(); } } private static IntPtr hWndProcHook = IntPtr.Zero; private static int messageCount = 0; [DllImport("Kernel32.dll", CharSet = CharSet.Auto)] public static extern IntPtr GetModuleHandle(string lpModuleName); [DllImport("Kernel32.dll", CharSet = CharSet.Auto)] public static extern uint GetCurrentThreadId(); [DllImport("User32.dll", CharSet = CharSet.Auto)] private static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId); [DllImport("User32.dll", CharSet = CharSet.Auto)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool UnhookWindowsHookEx(IntPtr hhk); [DllImport("User32.dll", CharSet = CharSet.Auto)] private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); InstallHook(); Application.Run(new MainForm()); UninstallHook(); } private static void InstallHook() { if (Program.hWndProcHook == IntPtr.Zero) { Console.WriteLine("Hooking..."); Program.hWndProcHook = SetWindowsHookEx( WH_CALLWNDPROC, WndProcHookCallback, GetModuleHandle(null), GetCurrentThreadId()); if(Program.hWndProcHook != IntPtr.Zero) Console.WriteLine("Hooked successfully."); else Console.WriteLine("Failed to hook."); } } private static void UninstallHook() { if (Program.hWndProcHook != IntPtr.Zero) { Console.WriteLine("Unhooking..."); if (UnhookWindowsHookEx(Program.hWndProcHook)) Console.WriteLine("Unhooked successfully."); else Console.WriteLine("Failed to unhook."); Program.hWndProcHook = IntPtr.Zero; } } private static IntPtr WndProcHookCallback(int nCode, IntPtr wParam, IntPtr lParam) { Console.WriteLine("WndProcHookCallback {0}", Program.messageCount++); return CallNextHookEx(Program.hWndProcHook, nCode, wParam, lParam); } } } 
+4
source share
2 answers

When testing your program, I received the following error:

CallbackOnCollectedDelegate detected
Message: The callback was made to the garbage collector collected by the delegate of the "Sandbox" form! Sandbox_Form.Program + HOOKPROC :: Invoke ". This can cause application crashes, corruption and data loss. When delegates are transferred to unmanaged code, they must be supported by the managed application until it is guaranteed that they will never be called.

I believe the problem is that a delegate that is implicitly created to be passed to SetWindowsHookEx receives a garbage collection for the callback. InstallHook creating a variable for the delegate and keeping it in scope, I think it will fix your problem, when I changed InstallHook to the next, I could no longer re-create the error.

 private static HookProc hookProcDelegate; private static void InstallHook() { if (Program.hWndProcHook == IntPtr.Zero) { Console.WriteLine("Hooking..."); hookProcDelegate = new HookProc(WndProcHookCallback); Program.hWndProcHook = SetWindowsHookEx( WH_CALLWNDPROC, hookProcDelegate, GetModuleHandle(null), GetCurrentThreadId()); if (Program.hWndProcHook != IntPtr.Zero) Console.WriteLine("Hooked successfully."); else Console.WriteLine("Failed to hook."); } } 
+3
source

What could cause Windows to cancel the low-level (global) click of the keyboard? . This situation may occur if the hook procedure ends. The timeout value is set using the HKEY_CURRENT_USER\Control Panel\Desktop key with the value LowLevelHooksTimeout (although this value was not on my system).

From MSDN (there’s also some good info at the bottom of this page):

If the hook procedure ends, the system passes the message to the next hook. However, in Windows 7 and later, it is deleted without a call.

From global interceptors get lost in windows

In Windows 7, we need to make sure that the hook callback function can return less than LowLevelHooksTimeout, which is 300 ms. And we allow the application to process the hook callback message 10 times. If it expires for the 11th time, Windows will untie the application from the hook chain. This is a design feature, and it was added in Win7 RTM.

This page also offers Raw input instead.

Edit: here's a tutorial on designing code using Raw Input in C #.

0
source

Source: https://habr.com/ru/post/1439597/


All Articles