有没有办法强制 Win32 计时器在不空闲时执行?
Is there a way to force a Win32 Timer to execute when not idle?
使用 SetTimer 创建的 Win32 计时器通常只在消息队列为空时执行。在 GUI 线程非常繁忙因此不为空的情况下,有什么方法可以手动强制执行吗?
(编辑)
正如下面所讨论的,这尤其是关于让低优先级消息(在本例中间接显示工具提示)在 UI 线程饱和时继续工作(但不阻止它)。这是一些代码:
using System;
using System.Threading;
using System.Windows.Forms;
namespace ToolTipTesting
{
public partial class Form1 : Form
{
Thread _thread = null;
bool _run = false;
bool _exit = false;
public Form1()
{
var tsbStart = new ToolStripButton();
tsbStart.Text = "Start";
tsbStart.Click += (s,e) => _run = true;
var tsbStop = new ToolStripButton();
tsbStop.Text = "Stop";
tsbStop.Click += (s,e) => _run = false;
var tslValue = new ToolStripLabel();
var ts = new ToolStrip();
ts.Items.Add(tsbStart);
ts.Items.Add(tsbStop);
ts.Items.Add(tslValue);
Controls.Add(ts);
_thread = new Thread(() =>
{
int i = 0;
while (!_exit)
{
if(_run)
{
var result = BeginInvoke(new Action(() => { tslValue.Text = (i++).ToString(); ts.Update(); } ));
while(!_exit && !result.IsCompleted)
result.AsyncWaitHandle.WaitOne(10);
}
else
{
Thread.Sleep(100);
}
}
});
FormClosing += (s,e) =>
{
_exit = true;
_thread.Join();
};
_thread.Start();
}
}
}
如果这是 "the wrong way to do it"...很高兴听到 "right way to do it."
成功!正如 Raymond Chen 上面所建议的,解决方案是使用 PeekMessage。这是完整的代码:
using System;
using System.Threading;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace ToolTipTesting
{
[StructLayout(LayoutKind.Sequential)]
public struct NativeMessage
{
public IntPtr handle;
public uint msg;
public IntPtr wParam;
public IntPtr lParam;
public uint time;
public System.Drawing.Point p;
}
public partial class Form1 : Form
{
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool PeekMessage(ref NativeMessage lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg);
const int QS_TIMER = 0x0010;
const int WM_TIMER = 0x0113;
Thread _thread = null;
bool _run = false;
bool _exit = false;
public Form1()
{
var tsbStart = new ToolStripButton();
tsbStart.Text = "Start";
tsbStart.Click += (s,e) => _run = true;
var tsbStop = new ToolStripButton();
tsbStop.Text = "Stop";
tsbStop.Click += (s,e) => _run = false;
var tslValue = new ToolStripLabel();
var ts = new ToolStrip();
ts.Items.Add(tsbStart);
ts.Items.Add(tsbStop);
ts.Items.Add(tslValue);
Controls.Add(ts);
_thread = new Thread(() =>
{
int i = 0;
NativeMessage nativeMessage = new NativeMessage();
while (!_exit)
{
if(_run)
{
var result =
BeginInvoke
(
new Action
(
() =>
{
tslValue.Text = (i++).ToString();
PeekMessage
(
ref nativeMessage,
IntPtr.Zero,
WM_TIMER,
WM_TIMER,
QS_TIMER << 16
);
ts.Update();
}
)
);
while(!_exit && !result.IsCompleted)
result.AsyncWaitHandle.WaitOne(10);
}
else
{
Thread.Sleep(100);
}
}
});
FormClosing += (s,e) =>
{
_exit = true;
_thread.Join();
};
_thread.Start();
}
}
}
使用 SetTimer 创建的 Win32 计时器通常只在消息队列为空时执行。在 GUI 线程非常繁忙因此不为空的情况下,有什么方法可以手动强制执行吗?
(编辑) 正如下面所讨论的,这尤其是关于让低优先级消息(在本例中间接显示工具提示)在 UI 线程饱和时继续工作(但不阻止它)。这是一些代码:
using System;
using System.Threading;
using System.Windows.Forms;
namespace ToolTipTesting
{
public partial class Form1 : Form
{
Thread _thread = null;
bool _run = false;
bool _exit = false;
public Form1()
{
var tsbStart = new ToolStripButton();
tsbStart.Text = "Start";
tsbStart.Click += (s,e) => _run = true;
var tsbStop = new ToolStripButton();
tsbStop.Text = "Stop";
tsbStop.Click += (s,e) => _run = false;
var tslValue = new ToolStripLabel();
var ts = new ToolStrip();
ts.Items.Add(tsbStart);
ts.Items.Add(tsbStop);
ts.Items.Add(tslValue);
Controls.Add(ts);
_thread = new Thread(() =>
{
int i = 0;
while (!_exit)
{
if(_run)
{
var result = BeginInvoke(new Action(() => { tslValue.Text = (i++).ToString(); ts.Update(); } ));
while(!_exit && !result.IsCompleted)
result.AsyncWaitHandle.WaitOne(10);
}
else
{
Thread.Sleep(100);
}
}
});
FormClosing += (s,e) =>
{
_exit = true;
_thread.Join();
};
_thread.Start();
}
}
}
如果这是 "the wrong way to do it"...很高兴听到 "right way to do it."
成功!正如 Raymond Chen 上面所建议的,解决方案是使用 PeekMessage。这是完整的代码:
using System;
using System.Threading;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace ToolTipTesting
{
[StructLayout(LayoutKind.Sequential)]
public struct NativeMessage
{
public IntPtr handle;
public uint msg;
public IntPtr wParam;
public IntPtr lParam;
public uint time;
public System.Drawing.Point p;
}
public partial class Form1 : Form
{
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool PeekMessage(ref NativeMessage lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg);
const int QS_TIMER = 0x0010;
const int WM_TIMER = 0x0113;
Thread _thread = null;
bool _run = false;
bool _exit = false;
public Form1()
{
var tsbStart = new ToolStripButton();
tsbStart.Text = "Start";
tsbStart.Click += (s,e) => _run = true;
var tsbStop = new ToolStripButton();
tsbStop.Text = "Stop";
tsbStop.Click += (s,e) => _run = false;
var tslValue = new ToolStripLabel();
var ts = new ToolStrip();
ts.Items.Add(tsbStart);
ts.Items.Add(tsbStop);
ts.Items.Add(tslValue);
Controls.Add(ts);
_thread = new Thread(() =>
{
int i = 0;
NativeMessage nativeMessage = new NativeMessage();
while (!_exit)
{
if(_run)
{
var result =
BeginInvoke
(
new Action
(
() =>
{
tslValue.Text = (i++).ToString();
PeekMessage
(
ref nativeMessage,
IntPtr.Zero,
WM_TIMER,
WM_TIMER,
QS_TIMER << 16
);
ts.Update();
}
)
);
while(!_exit && !result.IsCompleted)
result.AsyncWaitHandle.WaitOne(10);
}
else
{
Thread.Sleep(100);
}
}
});
FormClosing += (s,e) =>
{
_exit = true;
_thread.Join();
};
_thread.Start();
}
}
}