考虑多个事件

时间:2017-06-18 21:22:09

标签: c#

我有一个C#应用程序,它使用由各种事件触发的多个事件处理程序。举个例子:

  1. 全球鼠标悬挂活动
  2. 过滤的全局密钥挂钩事件(处理程序仅触发某些按键)
  3. 活动窗口更改全局挂钩
  4. 结构更改的应用程序挂钩(这是一个UIAAutomation事件,当每个应用程序窗口成为活动窗口时,为每个应用程序窗口创建不同的结构更改事件/处理程序(例如,如果应用程序是Internet Explorer然后向下浏览器,单击另一个网站链接是Internet Explorer应用程序实例上结构更改的示例)
  5. 所有这些事件(在后台线程上运行,例如MTA)都会更新我的应用程序的UI窗口。他们都按照自己的意愿工作。

    我的问题是,在某些情况下,会同时触发多个事件。因此,例如,在1秒内触发以下事件是可行的(如果我点击不同的活动窗口,则会出现这种情况):

    1. 活动窗口的全局更改
    2. Global Mouse Hook
    3. 应用程序结构已更改。
    4. 在不同的情况下(例如,不同的活动应用程序),一个事件优先于另一个事件作为由一组条件(布尔值)规则的定义事件。我不想在特定的时间段内对超过1个事件采取行动。

      考虑事件触发和决定的最佳编程机制是什么,通过一系列条件,哪一个可以采取行动?当然,这一切都必须在非常快的时间内完成(例如一秒或更短)。触发的事件都将在这段时间内发生。

      我希望这是有道理的,如果没有,请要求澄清。顺便提一下,我想通过某个定义事件更新我的应用程序UI的原因是我的UI呈现的信息将根据触发的事件略有不同(主要是由于不同事件的时间略有不同)触发)。顺便说一下,触发特定事件所花费的时间将根据所采取的动作而变化(例如,点击不同的活动窗口)。有时一种事件类型比另一种更快,但在不同的情况下,不同的事件类型可能是最快的事件(或触发的最慢事件)

      非常感谢下面的答案。我将首先检查System.Reactive库,因为它听起来是专门为该任务构建的。

3 个答案:

答案 0 :(得分:1)

微软的Reactive Framework(NuGet“System.Reactive”)可以非常有效地完成这种操作。你可以非常简单地做一些事情,但有些问题 - 特别是那些处理时间的问题 - 可能会非常复杂。

以下是您可能会做的一些示例:

var event1 = new Subject<int>();
var event2 = new Subject<int>();

var query =
    event1.Merge(event2).Buffer(TimeSpan.FromSeconds(1.0));

query.Subscribe(xs => Console.WriteLine($"\"{String.Join(", ", xs)}\""));

event1.OnNext(42);
Thread.Sleep(3000);
event2.OnNext(43);
Thread.Sleep(500);
event1.OnNext(44);

输出:

"42"
""
""
"43, 44"
""
""

请注意,即使事件相隔500毫秒,它也会同时产生"43, 44"

答案 1 :(得分:0)

我一直在使用下面的代码来防止事件处理中的冲突:

   class Program
    {
        enum EVENTS
        {
            EVENT1,
            EVENT2,
            EVENT3,
            EVENT4,

        }
        static void Main(string[] args)
        {

        }
        static void EventHandler(EVENTS myEvent)
        {
                Object thisLock = new Object();

                lock (thisLock)
                {
                    switch (myEvent)
                    {
                        case EVENTS.EVENT1 :
                            break;
                        case EVENTS.EVENT2:
                            break;
                        case EVENTS.EVENT3:
                            break;
                        case EVENTS.EVENT4:
                            break;
                    }
                }
        }

    }

答案 2 :(得分:0)

这是我的代码的简化版本,其中包含几个全局挂钩,这些挂钩由活动窗口的更改和鼠标左键单击触发。如果您左键单击其他活动窗口,则将触发鼠标单击事件和活动窗口事件的更改。如果您可以向我展示使用Reactive命名空间来处理2个事件的示例代码,当它们在毫秒之内被触发时,我将非常感激。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Testing_multiple_events
{
    public partial class Form1 : Form
    {
        int activeWindowCount = 1;
        int activeMouseClickCount = 1;
        public Form1()
        {                
            InitializeComponent();
            // set up the global hook event for change of active window
            GlobalEventHook.Start();               
            GlobalEventHook.WinEventActive += new EventHandler(GlobalEventHook_WinEventActive);                        

            // Setup global mouse hook to react to mouse clicks under certain conditions, see event handler
            MouseHook.Start();
            MouseHook.MouseAction += new EventHandler(MouseHook_MouseAction);
        }

        private void GlobalEventHook_WinEventActive(object sender, EventArgs e)
        {
            richTextBox1.AppendText("Active Window Change Global Hook Triggered: " + activeWindowCount + "\r\n");
            activeWindowCount++;
        }    

        private void MouseHook_MouseAction(object sender, EventArgs e)
        {
            richTextBox1.AppendText("Global MouseHook Triggered: " + activeMouseClickCount + "\r\n");
            activeMouseClickCount++;
        }
    }
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace Testing_multiple_events
{
    public static class GlobalEventHook
    {
        [DllImport("user32.dll")]
        internal static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc,
            WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);

        [DllImport("user32.dll")]
        internal static extern bool UnhookWinEvent(IntPtr hWinEventHook);

        public static event EventHandler WinEventActive = delegate { };
        public static event EventHandler WinEventContentScrolled = delegate { };

        public delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject,
            int idChild, uint dwEventThread, uint dwmsEventTime);

        private static WinEventDelegate dele = null;
        private static IntPtr _hookID = IntPtr.Zero;



        public static void Start()
        {
            dele = new WinEventDelegate(WinEventProc);
            _hookID = SetWinEventHook(Win32API.EVENT_SYSTEM_FOREGROUND, Win32API.EVENT_OBJECT_CONTENTSCROLLED, IntPtr.Zero, dele, 0, 0, Win32API.WINEVENT_OUTOFCONTEXT);
        }

        public static void stop()
        {
            UnhookWinEvent(_hookID);
        }

        public static void restart()
        {

            _hookID = SetWinEventHook(Win32API.EVENT_SYSTEM_FOREGROUND, Win32API.EVENT_OBJECT_CONTENTSCROLLED, IntPtr.Zero, dele, 0, 0, Win32API.WINEVENT_OUTOFCONTEXT);
        }

        public static void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
        {

                if (eventType == Win32API.EVENT_SYSTEM_FOREGROUND)
                {                       
                    WinEventActive(null, new EventArgs());
                }
        }
}
}

    using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace Testing_multiple_events
{
    public static class MouseHook
    {
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook,
          LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
          IntPtr wParam, IntPtr lParam);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);

        public static event EventHandler MouseAction = delegate { };

        private const int WH_MOUSE_LL = 14;

        private enum MouseMessages
        {
            WM_LBUTTONDOWN = 0x0201,
            WM_LBUTTONUP = 0x0202,
            WM_MOUSEMOVE = 0x0200,
            WM_MOUSEWHEEL = 0x020A,
            WM_RBUTTONDOWN = 0x0204,
            WM_RBUTTONUP = 0x0205
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct POINT
        {
            public int x;
            public int y;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct MSLLHOOKSTRUCT
        {
            public POINT pt;
            public uint mouseData;
            public uint flags;
            public uint time;
            public IntPtr dwExtraInfo;
        }

        public static void Start()
        {
            _hookID = SetHook(_proc);

        }
        public static void stop()
        {
            UnhookWindowsHookEx(_hookID);
        }

        private static LowLevelMouseProc _proc = HookCallback;
        private static IntPtr _hookID = IntPtr.Zero;

        private static IntPtr SetHook(LowLevelMouseProc proc)
        {
            using (Process curProcess = Process.GetCurrentProcess())
            using (ProcessModule curModule = curProcess.MainModule)
            {
                IntPtr hook = SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle("user32"), 0);
                if (hook == IntPtr.Zero) throw new System.ComponentModel.Win32Exception();
                return hook;
            }
        }

        private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);

        private static IntPtr HookCallback(
          int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0 && (MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam || MouseMessages.WM_RBUTTONDOWN == (MouseMessages)wParam || 
                MouseMessages.WM_MOUSEWHEEL == (MouseMessages)wParam))
            {
                MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
                MouseAction(null, new EventArgs());
            }
            return CallNextHookEx(_hookID, nCode, wParam, lParam);
        }

    }
}