这是正确使用DoEvents吗? -包括样本项目 [英] Is this a proper use of DoEvents? - Sample Project Included

查看:63
本文介绍了这是正确使用DoEvents吗? -包括样本项目的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用第一个代码块在单独的线程上执行低级鼠标挂钩.实际上,它是这样工作的(信不信由你),因为订阅的行为会初始化钩子.并且,通过钩子,我需要能够阻止调用事件的方法,以便我可以设置一个值来更改其执行过程.这就是为什么我不能简单地将事件处理程序卸载到另一个线程的原因.

I'm using the first code block to execute a low-level mouse hook on a separate thread. It actually works like this (believe it or not) because the act of subscribing initializes the hook. And with a hook I need to be able to block the method calling an event so that I can set a value to alter its course of execution. This is the reason why I can't simply off-load the event handler to another thread.

我的问题是,即使这可行,还有另一种方法可以避免DoEvents?

My problems is, even though this works, is there another way that can avoid DoEvents?

DoEvents是否可能仅适用于其自己线程上的事件,或者此调用会影响我的GUI线程?据我所知,它似乎根本不影响我的GUI.

Is it possible that DoEvents only applies to events on its own thread or does this call affect my GUI thread? It does not appear to affect my GUI at all as far as I can tell.

注意:如果不调用Sleep,则CPU将会显着增加.
注意:如果没有DoEvents,则挂钩消息会累积并迫使操作系统断开挂钩.

Note: Without the call to Sleep the CPU will increase significantly.
Note: Without DoEvents the hook messages build up and force the OS to disconnect the hook.

编辑:我创建了一个示例项目,以便你们可以进行测试.该应用程序将在单独的线程上启动鼠标钩,并捕获鼠标右键,并通过消息框告知您这样做.您可以使用以下链接获取该项目.

I created a sample project so you guys can test this. The app will start a mouse hook on a separate thread and capture a mouse right-click and let you know it did so via a message box. You can get that project using the below link.

该示例显示您可以阻止GUI线程,并且仍然可以处理该钩子而不会出现问题,这表明该钩子位于其自己的线程上.

The sample shows that you can block the GUI thread and still handle the hook without issues which confirms that the hook is on its own thread.

https://github.com/mzomparelli/Threaded-Low -Level-Mouse-Hook-Example

我现在开始认为这是DoEvents的有效用法,尽管许多人声称DoEvents总是.

I am now starting to think that this is a valid use of DoEvents despite the many claims that DoEvents is always bad.

private static bool blnStopMouseHook = false;
        public static void StartMouseHook()
        {
            if (MouseHook == null)
            {
                blnStopMouseHook = false;
                MouseHook = new Thread(new ThreadStart(() => { MouseHookThread(); }));
                MouseHook.SetApartmentState(ApartmentState.STA);
                MouseHook.Start();
            }
        }

        public static void StopMouseHook()
        {
            blnStopMouseHook = true;
            MouseHook.Join();
            MouseHook = null;
        }

        private static void MouseHookThread()
        {
            HookManager.MouseWheel += HookHandlers.HookManagerOnMouseWheel;
            HookManager.MouseClickExt += HookHandlers.HookManagerOnMouseClickExt;
            do
            {
                System.Threading.Thread.Sleep(1);
                Application.DoEvents();
            } while (blnStopMouseHook == false);

            HookManager.MouseWheel -= HookHandlers.HookManagerOnMouseWheel;
            HookManager.MouseClickExt -= HookHandlers.HookManagerOnMouseClickExt;

        }

下面是我的HookProc的片段,该片段创建了事件HookManagerOnMouseWheel

Below is a snippet of my HookProc which creates the event HookManagerOnMouseWheel

private static int MouseHookProc(int nCode, int wParam, IntPtr lParam)
        {
            if (nCode >= 0)
            {
                //Marshall the data from callback.
                MouseLLHookStruct mouseHookStruct = (MouseLLHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseLLHookStruct));

                switch (wParam)
                {
                    case WM_MOUSEWHEEL:
                        mouseDelta = (short)((mouseHookStruct.MouseData >> 16) & 0xffff);
                       break;
                }

                //generate event 
                MouseEventExtArgs e = new MouseEventExtArgs(
                                                   button,
                                                   clickCount,
                                                   mouseHookStruct.Point.X,
                                                   mouseHookStruct.Point.Y,
                                                   mouseDelta);



                //Wheel was moved
                if (s_MouseWheel!=null && mouseDelta!=0)
                {
                    s_MouseWheel.Invoke(null, e);
                }



                //If someone listens to move and there was a change in coordinates raise move event

                if (e.Handled)
                {
                    return -1;
                }
            }

            //call next hook
            return CallNextHookEx(s_MouseHookHandle, nCode, wParam, lParam);
        }

这是我的事件处理程序.

This is my event handler.

public static void HookManagerOnMouseWheel(object sender, MouseEventExtArgs mouseEventArgs)
        {

            int iHotkey;
            int iHotkey2;
            string keyCombination = CurrentModifiers();
            string keyCombination2 = CurrentModifiers();

            if (Window.Taskbar().IsMouseOver() || Window.Taskbar2().IsMouseOver())
            {
                //Create combination string

                if (mouseEventArgs.Delta < 0)
                {
                    keyCombination = keyCombination + "+MOUSE-TASKBAR-SCROLL-DOWN";
                    keyCombination2 = keyCombination2 + "+MOUSE-ANYWHERE-SCROLL-DOWN";
                }
                else
                {
                    keyCombination = keyCombination + "+MOUSE-TASKBAR-SCROLL-UP";
                    keyCombination2 = keyCombination2 + "+MOUSE-ANYWHERE-SCROLL-UP";
                }

                iHotkey = GLOBALS.hotkeys.FindIndex(l => l.HotkeyString() == keyCombination);
                iHotkey2 = GLOBALS.hotkeys.FindIndex(l => l.HotkeyString() == keyCombination2);
                if (iHotkey >= 0)
                {
                    ExecuteAction(iHotkey);
                    mouseEventArgs.Handled = true;
                    return;
                }
                else if (iHotkey2 >= 0)
                {
                    ExecuteAction(iHotkey2);
                    mouseEventArgs.Handled = true;
                    return;
                }
            }

            if (mouseEventArgs.Delta < 0)
            {
                keyCombination = keyCombination + "+MOUSE-ANYWHERE-SCROLL-DOWN";
            }
            else
            {
                keyCombination = keyCombination + "+MOUSE-ANYWHERE-SCROLL-UP";
            }

            iHotkey = GLOBALS.hotkeys.FindIndex(l => l.HotkeyString() == keyCombination);
            if (iHotkey >= 0)
            {
                ExecuteAction(iHotkey);
                mouseEventArgs.Handled = true;
                return;
            }


        }

推荐答案

多线程处理的麻烦在于,不能保证主线程发送的事件不会超过工作线程可以处理的事件,或者工作线程会保证无所事事而饿死".这就是为什么您的代码具有那些难看的SleepDoEvents调用.

The trouble with multithreading is that there is no guarantee that the main thread won't send more events than the worker thread can handle, or that the worker thread will be "starved" by the lack of anything to do. That is why your code has those ugly Sleep and DoEvents calls.

您需要的是一种同步机制.

What you need is a synchronization mechanism.

在这种情况下,我建议您遵循生产者-消费者模式,这需要一个队列.在这种情况下,我建议您使用 BlockingCollection 作为您的队列.

I would suggest in this case that you follow a producer-consumer pattern, which requires a queue. I'd recommend in this case you use a BlockingCollection for your queue.

阻塞集合将允许主线程向其添加事件,并提供允许工作线程从其获取事件的方法.如果没有事件,则集合将阻塞工作线程,直到有一个可用线程为止.

A blocking collection will allow the main thread to add events to it and provides methods that allow the worker thread to take events from it. If there are no events, the collection will block the worker thread until one is available.

因此,首先,声明一个用于保存事件的数据结构:

So first, declare a data structure for holding events:

struct Event 
{
    object sender;
    EventArgs e;
}

然后声明您的队列:

private BlockingCollection<Event> _queue = new BlockingCollection<Event>();

在主线程中,使用添加到队列中的普通事件处理程序来处理事件:

In your main thread, handle your events with a normal event handler that adds to the queue:

private OnMouseAction(object sender, EventArgs e)
{
    _queue.Add(new Event {sender = sender, e = e});
}

在您的工作线程中,只需读取队列并对其进行操作即可:

And in your worker thread, just read the queue and act on it:

private void MouseHookWorker(CancellationToken token)
{
    try
    {
        while (!token.IsCancellationRequested)
        {
            var event = _queue.Take(token);
            ProcessEvent(event.sender, event.e);
        }
    }
    catch (OperationCanceledException ex)
    {
    }
}     

并在ProcessEvent中实现实际工作(无论是什么).

And implement the real work (whatever that is) in ProcessEvent.

要停止工作线程,您可以发出取消令牌的信号,或者使用_queue.CompleteAdding();

To stop the worker thread, you can either signal the cancellation token, or simple stop the queue with _queue.CompleteAdding();

CancellationToken是可选的,但可能是一个好主意.如果您不知道如何使用它,请参阅此问题.

The CancellationToken is sort of optional, but probably a good idea. If you don't know how to use it, see this question.

这篇关于这是正确使用DoEvents吗? -包括样本项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆