Reputation: 193
I have this custom message box class :
public class AutoCloseMsb
{
readonly System.Threading.Timer _timeoutTimer;
readonly string _caption;
private AutoCloseMsb(string text, string caption, int timeout)
{
_caption = caption;
_timeoutTimer = new System.Threading.Timer(OnTimerElapsed,
null, timeout, System.Threading.Timeout.Infinite);
MessageBox.Show(text, caption);
}
public static void Show(string text, string caption, int timeout)
{
new AutoCloseMsb(text, caption, timeout);
}
private void OnTimerElapsed(object state)
{
IntPtr mbWnd = FindWindow("#32770", _caption);
if (mbWnd != IntPtr.Zero)
SendMessage(mbWnd, WmClose, IntPtr.Zero, IntPtr.Zero);
_timeoutTimer.Dispose();
}
private const int WmClose = 0x0010;
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
}
And I'm calling it in a few places one after another :
AutoCloseMsb.Show("Bot 1 Turn", "Turns", ThinkTime);
AutoCloseMsb.Show("Bot 2 Turn", "Turns", ThinkTime);
AutoCloseMsb.Show("Bot 3 Turn", "Turns", ThinkTime);
AutoCloseMsb.Show("Bot 4 Turn", "Turns", ThinkTime);
AutoCloseMsb.Show("Bot 5 Turn", "Turns", ThinkTime);
And the variable ThinkTime is getting value out of the resources that's where i actually change it. However if i put 3000 milliseconds for example as a display time it will display the first one for 3 secs than the others wont be shown for this duration they will close in something like 100-200 ms ( they just show up and close instantly) why is this happening ? Should i reset the value of the variable after each message box is shown ?
Upvotes: 0
Views: 323
Reputation: 42453
If you're clicking the OK button in the MessageBox you're not getting rid of the Timer and as such it will still fire after the messagebox has already been closed. Due to the non-uniqueness of the captions the event handler in OnTimerElapsed will find a messagebox and then closes it. That leads to the subsequent closing of messageboxes because there is always a Timer that still needs to fire.
The quick fix for your bug is to move the code to get rid of the timer event directly after the MessageBox instead of the OnTimerElapsed event:
private AutoCloseMsb(string text, string caption, int timeout)
{
_caption = caption;
_timeoutTimer = new System.Threading.Timer(OnTimerElapsed,
null, timeout, System.Threading.Timeout.Infinite);
// the next call blocks, until either the user
// or the timer closes the the messagbox
MessageBox.Show(text, caption);
// now we can stop this timer
_timeoutTimer.Change(Timeout.Infinite, Timeout.Infinite);
// and dispose it
_timeoutTimer.Dispose();
}
The TimerElapsedEvent only signals that the cleanup can start:
private void OnTimerElapsed(object state)
{
Debug.WriteLine("on timer");
IntPtr mbWnd = FindWindow("#32770", _caption);
if (mbWnd != IntPtr.Zero)
SendMessage(mbWnd, WmClose, IntPtr.Zero, IntPtr.Zero);
// don't touch the state here, only signal to continue
}
These changes achieve what you want and keeps the flow of the code clear.
Upvotes: 3