且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

pressing标签几次后挂线的过程不再被调用。为什么?

更新时间:2023-09-28 15:38:16

在测试你的节目,我得到了以下错误

  

CallbackOnCollectedDelegate检测
  消息:回调作出   在类型的垃圾回收委托沙盒   表格!Sandbox_Form.Program + HOOKPROC ::调用。这可能会导致   应用程序崩溃,损坏和数据丢失。当传递代表   非托管code,它们必须保持活由托管的应用程序   直到它被保证它们不会被调用

我相信这个问题是隐含在创建要传递给 SetWindowsHookEx函数回调越来越垃圾回收的委托。通过明确地创造了代表一个变量,并保持它的范围,我认为它会让你的问题消失,当我修改 InstallHook 下面我再也无法重新创建该错误。

 私有静态HOOKPROC hookProcDelegate;

私有静态无效InstallHook()
{
    如果(Program.hWndProcHook == IntPtr.Zero)
    {
        Console.WriteLine(挂钩......);

        hookProcDelegate =新HOOKPROC(WndProcHookCallback);

        Program.hWndProcHook =调用SetWindowsHookEx(
            WH_CALLWNDPROC,
            hookProcDelegate,
            的GetModuleHandle(空)
            GetCurrentThreadId());

        如果(Program.hWndProcHook!= IntPtr.Zero)
            Console.WriteLine(上钩成功。);
        其他
            Console.WriteLine(无法挂钩。);
    }
}
 

I installed a thread-specific windows hook to monitor messages sent to WndProc. It worked at first. However, after I pressed Tab about 19 times to move focus around a form, my hook callback is nolonger called. This happened regaless of whether I pressed Tab quickly or slowly. Can anybody explain what is really going on?

Below is the code I wrote. I tested it on Windows 7 64 bit.

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

While testing your program I got the following error

CallbackOnCollectedDelegate was detected
Message: A callback was made on a garbage collected delegate of type 'Sandbox Form!Sandbox_Form.Program+HookProc::Invoke'. This may cause application crashes, corruption and data loss. When passing delegates to unmanaged code, they must be kept alive by the managed application until it is guaranteed that they will never be called.

I believe the problem is the delegate that is implicitly created to be passed in to SetWindowsHookEx for the callback is getting garbage collected. By explicitly creating a variable for the delegate and keeping it in scope I think it will make your problem go away, when I modified InstallHook to the following 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.");
    }
}