C# 这是正确使用DoEvents吗?-样本项目包括
我使用第一个代码块在一个单独的线程上执行一个低级鼠标钩子。它实际上是这样工作的(信不信由你),因为订阅的行为初始化了钩子。通过一个钩子,我需要能够阻止调用事件的方法,这样我就可以设置一个值来改变它的执行过程。这就是为什么我不能简单地将事件处理程序卸载到另一个线程的原因 我的问题是,即使这样做有效,是否有其他方法可以避免C# 这是正确使用DoEvents吗?-样本项目包括,c#,multithreading,C#,Multithreading,我使用第一个代码块在一个单独的线程上执行一个低级鼠标钩子。它实际上是这样工作的(信不信由你),因为订阅的行为初始化了钩子。通过一个钩子,我需要能够阻止调用事件的方法,这样我就可以设置一个值来改变它的执行过程。这就是为什么我不能简单地将事件处理程序卸载到另一个线程的原因 我的问题是,即使这样做有效,是否有其他方法可以避免DoEvents DoEvents是否可能只应用于其自身线程上的事件,或者此调用是否会影响我的GUI线程?据我所知,它似乎根本不会影响我的GUI 注意:如果不调用睡眠CPU将显著增
DoEvents
DoEvents
是否可能只应用于其自身线程上的事件,或者此调用是否会影响我的GUI线程?据我所知,它似乎根本不会影响我的GUI
注意:如果不调用睡眠
CPU将显著增加。注意:没有
DoEvents
时,钩子消息会累积并强制操作系统断开钩子
编辑:我创建了一个示例项目,所以你们可以测试一下。该应用程序将在一个单独的线程上启动一个鼠标钩子,并捕获鼠标右键单击,并通过一个消息框让您知道它是这样做的。您可以使用下面的链接获取该项目
该示例显示,您可以阻止GUI线程,但仍然可以处理钩子,而不会出现问题,从而确认钩子位于自己的线程上
我现在开始认为这是对DoEvents
的有效使用,尽管许多人声称DoEvents
总是不好
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
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);
}
这是我的事件处理程序
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;
}
}
publicstaticvoid hookmanageronmouseweelwheel(对象发送器,MouseEventExtArgs mouseEventArgs)
{
int iHotkey;
int iHotkey2;
字符串键组合=CurrentModifiers();
string keyCombination2=CurrentModifiers();
if(Window.task().IsMouseOver()| | Window.Taskbar2().IsMouseOver())
{
//创建组合字符串
if(mouseEventArgs.Delta<0)
{
按键组合=按键组合+“+鼠标-任务栏-向下滚动”;
keyCombination2=keyCombination2+“+鼠标-任意位置-向下滚动”;
}
其他的
{
按键组合=按键组合+“+鼠标-任务栏-向上滚动”;
keyCombination2=keyCombination2+“+鼠标-任意位置-向上滚动”;
}
iHotkey=GLOBALS.hotkeys.FindIndex(l=>l.HotkeyString()==keyCombination);
iHotkey2=GLOBALS.hotkeys.FindIndex(l=>l.HotkeyString()==keyCombination2);
如果(iHotkey>=0)
{
执行(iHotkey);
mouseEventArgs.Handled=true;
返回;
}
否则如果(iHotkey2>=0)
{
执行(iHotkey2);
mouseEventArgs.Handled=true;
返回;
}
}
if(mouseEventArgs.Delta<0)
{
keyCombination=keyCombination+“+MOUSE-ANYWHERE-SCROLL-DOWN”;
}
其他的
{
keyCombination=keyCombination+“+MOUSE-ANYWHERE-SCROLL-UP”;
}
iHotkey=GLOBALS.hotkeys.FindIndex(l=>l.HotkeyString()==keyCombination);
如果(iHotkey>=0)
{
执行(iHotkey);
mouseEventArgs.Handled=true;
返回;
}
}
多线程的问题在于,无法保证主线程发送的事件不会超过工作线程所能处理的,或者工作线程会因为没有任何事情可做而“饿死”。这就是为什么你的代码有那些丑陋的Sleep
和DoEvents
调用
您需要的是一个同步机制
在这种情况下,我建议您遵循生产者-消费者模式,这需要一个队列。在这种情况下,我建议您在队列中使用
阻塞集合将允许主线程向其中添加事件,并提供允许工作线程从中获取事件的方法。如果没有事件,集合将阻止工作线程,直到有一个可用
首先,声明用于保存事件的数据结构:
struct Event
{
object sender;
EventArgs e;
}
然后声明您的队列:
private BlockingCollection<Event> _queue = new BlockingCollection<Event>();
在工作线程中,只需读取队列并对其执行操作:
private void MouseHookWorker(CancellationToken token)
{
try
{
while (!token.IsCancellationRequested)
{
var event = _queue.Take(token);
ProcessEvent(event.sender, event.e);
}
}
catch (OperationCanceledException ex)
{
}
}
并在ProcessEvent
中实现真正的工作(无论是什么)
要停止工作线程,您可以向取消令牌发送信号,或者使用\u queue.completedadding()简单地停止队列代码>
CancellationToken有点可选,但可能是个好主意。如果您不知道如何使用它,请参阅。使用现有代码将事件附加到新创建的线程上,但这并不意味着新线程将处理事件。事实上,事实并非如此。导致引发事件的任何线程都将继续处理该事件。这就是为什么您需要在代码中调用DoEvents
您需要一种在引发事件后将事件封送到后台线程的方法
如果我是你,我会使用微软的反应式框架(Rx)来实现这一点。主位只需NuGet“System.Reactive”,WinForms位只需NuGet“System.Reactive.Windows.Forms”,WPF位只需NuGet“System.Reactive.Windows.Threading”
然后你可以这样做:
IObservable<EventPattern<MouseEventArgs>> mouseMoves =
Observable
.FromEventPattern<MouseEventHandler, MouseEventArgs>(
h => this.MouseMove += h,
h => this.MouseMove -= h);
IDisposable subscription =
mouseMoves
.ObserveOn(Scheduler.Default)
.Do(ep =>
{
Console.WriteLine("Th" + Thread.CurrentThread.ManagedThreadId);
})
.ObserveOn(this)
.Subscribe(ep =>
{
Console.WriteLine("UI" + Thread.CurrentThread.ManagedThreadId);
});
Console.WriteLine("!" + Thread.CurrentThread.ManagedThreadId);
!1
Th4
Th4
UI1
UI1
Th4
UI1
Th4
UI1
Th4
UI1
Th4
UI1
应该相当清楚地看到,这段代码正确地将调用推送到后台线程,然后再推回到UI
没有阻塞发生
要分离事件处理程序,只需调用subscription.Dispose()代码>
在我的代码中
!1
Th4
Th4
UI1
UI1
Th4
UI1
Th4
UI1
Th4
UI1
Th4
UI1
using System.Reactive;
using System.Reactive.Linq;
using System.Reactive.Concurrency;
using System.Threading;
IDisposable subscription =
(
from mm in mouseMoves
let direction = mm.EventArgs.Delta < 0 ? "DOWN" : "UP"
let keyCombination = CurrentModifiers() + "+MOUSE-ANYWHERE-SCROLL-" + direction
let iHotkey = GLOBALS.hotkeys.FindIndex(l => l.HotkeyString() == keyCombination)
where iHotkey >= 0
select new { mm.EventArgs, iHotkey }
)
.Do(x => x.EventArgs.Handled = true)
.ObserveOn(Scheduler.Default)
.Subscribe(x => ExecuteAction(x.iHotkey));