Win32 Timers created with SetTimer normally only execute when the message queue is empty. Is there any way to manually force an execution in the case the GUI thread is very busy and thus not empty?
(Edit) As discussed below, in particular this is about having low priority messages (in this case indirectly to show a tool tip) continue to work when saturating the UI thread (but not blocking it). Here's some code:
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();
}
}
}
If this is "the wrong way to do it"...happy to hear the "right way to do it."
Success! As Raymond Chen suggests above, the solution is to use PeekMessage. Here is the completed code:
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();
}
}
}