Invoking UI from woker thread.

E

Erakis

Hi,

My application has to start a worker thread and the user may cancel the
process at any time. To do that I'm creating and starting a Thread object. In
this worker thread I update the UI using Control.Invoke(...) I noticed that
UI is not well responding, I mean the stop button click is not always
handled. Here is the minimum code listing to understand my problem.

/ **********************************************************
Main form code
***********************************************************/
public delegate void AddLogEntryDelegate(string entry);
public delegate void ThreadFinishedDelegate();

private void AddLogEntry(string entry)
{
// Append color text to the RichText box
int num = this.m_Txt_Log.TextLength;

// Add new entry
string leftInfo = DateTime.Now.ToString() + " -> ";
int leftInfoLength = leftInfo.Length;
this.m_Txt_Log.AppendText( DateTime.Now.ToString() + " -> " + entry +
Environment.NewLine);
this.m_Txt_Log.Select( num, num + leftInfoLength );
this.m_Txt_Log.SelectionColor = Color.FromArgb( 58, 83, 128 );
this.m_Txt_Log.Select( num + leftInfoLength, this.m_Txt_Log.TextLength -
1 );
this.m_Txt_Log.SelectionColor = Color.FromArgb( 0, 0, 0 );
this.m_Txt_Log.Focus();
}

private void m_Bt_Play_Click(object sender, System.EventArgs e)
{
// Reset events
m_EventStopThread.Reset();
m_EventThreadStopped.Reset();

// Start thread
m_ThreadScript = new Thread( new ThreadStart( this.ScriptThreadFunction
) );
m_ThreadScript.Start();

// Disable command that cannot be activated while in run mode
this.m_Bt_Play.Enabled = false;
this.m_Bt_Stop.Enabled = true;
}

private void m_Bt_Stop_Click(object sender, System.EventArgs e)
{
// Disable stop button
this.m_Bt_Stop.Enabled = false;

// Is thread is active
if ( m_ThreadScript != null && m_ThreadScript.IsAlive )
{
// Set event "Stop"
m_EventStopThread.Set();

// Wait when thread will stop or finish
while (m_ThreadScript.IsAlive)
{
if ( WaitHandle.WaitAll( (new ManualResetEvent[]
{m_EventThreadStopped}), 10, true) )
{
break;
}

// Process all windows messages
Application.DoEvents();
}
}

// Reset button state
this.m_Bt_Play.Enabled = true;
this.m_Bt_Stop.Enabled = false;
}

private void ScriptThreadFunction()
{
new ScriptProcess(m_EventStopThread, m_EventThreadStopped, this).Run();
}

/ **********************************************************
ScriptProcess code
***********************************************************/
public ScriptProcess(ManualResetEvent eventStop, ManualResetEvent
eventStopped, FormPrincipal form)
{
m_EventStop = eventStop;
m_EventStopped = eventStopped;
m_FormPrincipal = form;
}

public void Run()
{
bool bStopThread = false;
int iDelay = 20;
while ( !bStopThread )
{
// Watch for thread stopped event
// and wait (iDelay * 10) millisecons to slow thread
if (iDelay > 0)
{
if ( m_EventStop.WaitOne(10, true) )
{
bStopThread = true;
}

iDelay -= 1;

// Give application time to process it message
// Like the stop button or the exit button of the form
Application.DoEvents();
continue;
}

// Reset delay
iDelay = 20;

// Give application time to process it message
// Like the stop button or the exit button of the form
Application.DoEvents();

/********************************************
// Do script process here (many line cut to make code much easier to
read) !
// Main UI are updated many time by calling this function (Invoke)
// I also tried to use BeginInvoke but I got the same problem about
bad the UI responsivity.
m_FormPrincipal.Invoke(m_FormPrincipal.m_DelegateAddLogEntry, new
object[] {"Doing something..."});
********************************************/

// Give application time to process it message
// Like the stop button or the exit button of the form
Application.DoEvents();
}

// Make synchronous call to main form to inform it that thread finished
m_FormPrincipal.Invoke(m_FormPrincipal.m_ThreadFinishedDelegate, null);

// Thread stopped
m_EventStopped.Set();
}



Is there something wrong with that code ? Usually I'm coding using Visual
C++ and SendMessage and I never get any problem with UI responsivity. I
noticed that puttin alot of Application.DoEvents in the process help the
responsivity but I don't think it is a good solution because that doesn't
work perfectly.

PS : I cannot use anything else then C# .NET 1.1.

Best regards,
Martin
 
N

not_a_commie

Your main UI thread is stuck in a WaitAll for 10ms at a time, and the
delay may be longer depending on the CPU cores available, etc. You
should have your play button enabled and your stop button disabled
from inside your thread. Just make the stop button set the stop event.
And I don't really understand your delay stuff in the thread or your
DoEvents calls there. I would think you would just check the stop
event state (aka, use a timeout of 0) each time you passed through
your loop. And as far as those events go, performance wise you are
much better off using the Interlocked class or a common lock
mechanism. The ManualResetEvent is three orders of magnitude slower
than the lock keyword (Monitor class).
 
N

not_a_commie

Your main UI thread is stuck in a WaitAll for 10ms at a time, and the
delay may be longer depending on the CPU cores available, etc. You
should have your play button enabled and your stop button disabled
from inside your thread. Just make the stop button set the stop event.
And I don't really understand your delay stuff in the thread or your
DoEvents calls there. I would think you would just check the stop
event state (aka, use a timeout of 0) each time you passed through
your loop. And as far as those events go, performance wise you are
much better off using the Interlocked class or a common lock
mechanism. The ManualResetEvent is three orders of magnitude slower
than the lock keyword (Monitor class).
 
E

Erakis

Hi,

Thanks for your answer.

The delay here is to slow down the process because the script process must
send command (one by one) to an automate. Perhaps a 200 millisecons should do
the trick.

So you advise me to make a WaitAll of 0 milliseconds and manage the time in
the while loop ? Using tick or something else ?

You tell me to make the stop button set the stop event. But it is what I do
actually ?
private void m_Bt_Stop_Click(object sender, System.EventArgs e)
{
...
m_EventStopThread.Set();
...
}

I don't understand why should I implement a lock mechanism here ? Could you
give me more details on that ?

Best regards,
Martin
 
E

Erakis

Hi,

Thanks for your answer.

The delay here is to slow down the process because the script process must
send command (one by one) to an automate. Perhaps a 200 millisecons should do
the trick.

So you advise me to make a WaitAll of 0 milliseconds and manage the time in
the while loop ? Using tick or something else ?

You tell me to make the stop button set the stop event. But it is what I do
actually ?
private void m_Bt_Stop_Click(object sender, System.EventArgs e)
{
...
m_EventStopThread.Set();
...
}

I don't understand why should I implement a lock mechanism here ? Could you
give me more details on that ?

Best regards,
Martin
 
E

Erakis

[...]
Is there something wrong with that code ? Usually I'm coding using Visual
C++ and SendMessage and I never get any problem with UI responsivity. I
noticed that puttin alot of Application.DoEvents in the process help the
responsivity but I don't think it is a good solution because that doesn't
work perfectly.

No, it doesn't.  You shouldn't be using Application.DoEvents() at all.  
Any time you do, you've necessarily got something that is going to hinder 
UI responsiveness.  I'm surprised that you say that "putting a lot of" the  
calls into the background thread help, because DoEvents() should only  
process messages (events) for the current thread, and presumably you don't  
have any GUI objects that are owned by the background thread where you put  
all those calls.

But regardless, your goal should be to get rid of all the calls to  
Application.DoEvents(), and to get rid of blocking loops that are executed  
on the main GUI thread.

You also probably don't need the ManualResetEvent instances.

Instead, use Control.Invoke() to handle the interaction between the main  
GUI thread and your background thread.  Break a method like the stop  
button event handler into two, where the first part signals to the  
background thread, and the second part is a method invoked with  
Control.Invoke() when the thread actually completes.

You're already using Control.Invoke() now, though you don't show enough  
code for anyone to know precisely how.  But the same techniques would  
apply to the other things.

For controlling the thread execution, instead of the ManualResetEvent you 
use now, just create a volatile bool variable that is the condition for  
continuing the thread's loop or not (e.g. this would replace your local  
"bStopThread" variable), and then use Thread.Sleep() to throttle the  
execution of the loop.

Or alternatively, keep one of the ManualResetEvents and use that for  
signaling to stop the thread, so that you can continue to combine that  
behavior and the throttling behavior in the same operation (allowing you  
to get the thread to terminate immediately instead of having to wait out  
its throttling timer).

Pete

Hi Pete,

I've made a little test, (based on what you propose me) and I noticed
that I got exactly the same problem. Here is the code :
********************************************************************************************
/ **********************************************************
Main form code
***********************************************************/
public delegate void AddLogEntryDelegate(string entry);
public delegate void ThreadFinishedDelegate();

public volatile bool bStopThread = false;

private void AddLogEntry(string entry)
{
// Append color text to the RichText box
int num = this.m_Txt_Log.TextLength;

// Add new entry to RichText box
string leftInfo = DateTime.Now.ToString() + " -> ";
int leftInfoLength = leftInfo.Length;
this.m_Txt_Log.AppendText( DateTime.Now.ToString() + " -> " +
entry + Environment.NewLine);
this.m_Txt_Log.Select( num, num + leftInfoLength );
this.m_Txt_Log.SelectionColor = Color.FromArgb( 58, 83, 128 );
this.m_Txt_Log.Select( num + leftInfoLength,
this.m_Txt_Log.TextLength - 1 );
this.m_Txt_Log.SelectionColor = Color.FromArgb( 0, 0, 0 );
this.m_Txt_Log.Focus();
}

private void m_Bt_Play_Click(object sender, System.EventArgs e)
{
// Start script process thread
m_ThreadScript = new Thread( new ThreadStart
( this.ScriptThreadFunction ) );
m_ThreadScript.Start();
}

private void m_Bt_Stop_Click(object sender, System.EventArgs e)
{
// Stop script process thread
scriptProcessor.bStopThread = true;

// Here I should wait for thred completition
// but I will not post the code for now
}

private void ScriptThreadFunction()
{
Invoke(m_DelegateAddLogEntry, new object[] {"Work is
starting !"});

// Thread loop
int iDelay = 200;
while ( !bStopThread )
{
// Make a delay of 200 millisecond between each work batch
if (iDelay > 0)
{
iDelay -= 1;
Thread.Sleep(1);
continue;
}

// Reset delay
iDelay = 200;

// Simulate a little part of work
Invoke(m_DelegateAddLogEntry, new object[] {"Work in
progress..."});
Thread.Sleep(50);
}

Invoke(m_DelegateAddLogEntry, new object[] {"Work has been
done !"});
}
********************************************************************************************

So sometime I click on the stop button and the thread stopped
sucessfully. However, other times it simply does not work on the fist
click time.

I really don't understand why it does not work, I usally work with MFC
and this way of work is well working.

Do I'm doing something wrong ?

Best regards,
 
E

Erakis

[...]
Is there something wrong with that code ? Usually I'm coding using Visual
C++ and SendMessage and I never get any problem with UI responsivity. I
noticed that puttin alot of Application.DoEvents in the process help the
responsivity but I don't think it is a good solution because that doesn't
work perfectly.

No, it doesn't.  You shouldn't be using Application.DoEvents() at all.  
Any time you do, you've necessarily got something that is going to hinder 
UI responsiveness.  I'm surprised that you say that "putting a lot of" the  
calls into the background thread help, because DoEvents() should only  
process messages (events) for the current thread, and presumably you don't  
have any GUI objects that are owned by the background thread where you put  
all those calls.

But regardless, your goal should be to get rid of all the calls to  
Application.DoEvents(), and to get rid of blocking loops that are executed  
on the main GUI thread.

You also probably don't need the ManualResetEvent instances.

Instead, use Control.Invoke() to handle the interaction between the main  
GUI thread and your background thread.  Break a method like the stop  
button event handler into two, where the first part signals to the  
background thread, and the second part is a method invoked with  
Control.Invoke() when the thread actually completes.

You're already using Control.Invoke() now, though you don't show enough  
code for anyone to know precisely how.  But the same techniques would  
apply to the other things.

For controlling the thread execution, instead of the ManualResetEvent you 
use now, just create a volatile bool variable that is the condition for  
continuing the thread's loop or not (e.g. this would replace your local  
"bStopThread" variable), and then use Thread.Sleep() to throttle the  
execution of the loop.

Or alternatively, keep one of the ManualResetEvents and use that for  
signaling to stop the thread, so that you can continue to combine that  
behavior and the throttling behavior in the same operation (allowing you  
to get the thread to terminate immediately instead of having to wait out  
its throttling timer).

Pete

Hi Pete,

I've made a little test, (based on what you propose me) and I noticed
that I got exactly the same problem. Here is the code :
********************************************************************************************
/ **********************************************************
Main form code
***********************************************************/
public delegate void AddLogEntryDelegate(string entry);
public delegate void ThreadFinishedDelegate();

public volatile bool bStopThread = false;

private void AddLogEntry(string entry)
{
// Append color text to the RichText box
int num = this.m_Txt_Log.TextLength;

// Add new entry to RichText box
string leftInfo = DateTime.Now.ToString() + " -> ";
int leftInfoLength = leftInfo.Length;
this.m_Txt_Log.AppendText( DateTime.Now.ToString() + " -> " +
entry + Environment.NewLine);
this.m_Txt_Log.Select( num, num + leftInfoLength );
this.m_Txt_Log.SelectionColor = Color.FromArgb( 58, 83, 128 );
this.m_Txt_Log.Select( num + leftInfoLength,
this.m_Txt_Log.TextLength - 1 );
this.m_Txt_Log.SelectionColor = Color.FromArgb( 0, 0, 0 );
this.m_Txt_Log.Focus();
}

private void m_Bt_Play_Click(object sender, System.EventArgs e)
{
// Start script process thread
m_ThreadScript = new Thread( new ThreadStart
( this.ScriptThreadFunction ) );
m_ThreadScript.Start();
}

private void m_Bt_Stop_Click(object sender, System.EventArgs e)
{
// Stop script process thread
scriptProcessor.bStopThread = true;

// Here I should wait for thred completition
// but I will not post the code for now
}

private void ScriptThreadFunction()
{
Invoke(m_DelegateAddLogEntry, new object[] {"Work is
starting !"});

// Thread loop
int iDelay = 200;
while ( !bStopThread )
{
// Make a delay of 200 millisecond between each work batch
if (iDelay > 0)
{
iDelay -= 1;
Thread.Sleep(1);
continue;
}

// Reset delay
iDelay = 200;

// Simulate a little part of work
Invoke(m_DelegateAddLogEntry, new object[] {"Work in
progress..."});
Thread.Sleep(50);
}

Invoke(m_DelegateAddLogEntry, new object[] {"Work has been
done !"});
}
********************************************************************************************

So sometime I click on the stop button and the thread stopped
sucessfully. However, other times it simply does not work on the fist
click time.

I really don't understand why it does not work, I usally work with MFC
and this way of work is well working.

Do I'm doing something wrong ?

Best regards,
 
E

Erakis

I've also tried to use BeginInvoke but I got the same problem.

To reproduce the bug you could set the delay to 1 millisecond and this way
the stop button event is almost never fired.

I've noticed also that if a remove the Invoke call on my thread then the
stop button click event is always never fired.
 
E

Erakis

I've also tried to use BeginInvoke but I got the same problem.

To reproduce the bug you could set the delay to 1 millisecond and this way
the stop button event is almost never fired.

I've noticed also that if a remove the Invoke call on my thread then the
stop button click event is always never fired.
 
E

Erakis

*********************************************
public class ScriptProcess
{
// Stop thread signal
private volatile bool m_bStopThread = false;

// Script process thread
private Thread m_ThreadScript = null;

// Reference to main form used to make sync/async user interface calls:
private FormPrincipal m_FormPrincipal;

// Locking object
private object m_LockingObjectScriptProcessThread = new object();

// Default constructor
public ScriptProcess(FormPrincipal form)
{
// Get a reference on the main form
m_FormPrincipal = form;
}

// Start the script process thread
public bool Start()
{
// Lock script process operation
bool bRet;
lock(m_LockingObjectScriptProcessThread);
{
// Is thread is already running ?
if (m_ThreadScript != null && m_ThreadScript.IsAlive)
{
bRet = false;
}
else
{
// Initialize script process thread properties
m_ThreadScript = new Thread( new ThreadStart( this.ScriptThreadFunction
) );
m_ThreadScript.Name = "ScriptThreadFunction";
m_ThreadScript.IsBackground = true;

// Reset stop flag
m_bStopThread = false;

// Start thread
m_ThreadScript.Start();

// Thread started
bRet = true;
}
}
return bRet;
}

// Stop the script process thread
public bool Stop()
{
// Lock script process operation
bool bRet;
lock(m_LockingObjectScriptProcessThread)
{
if (m_ThreadScript == null || !m_ThreadScript.IsAlive)
{
bRet = false;
}
m_bStopThread = true;
bRet = true;
}
return bRet;
}

// Returns a value indicating if the script process thread is alive
public bool IsRunning()
{
return m_ThreadScript != null && m_ThreadScript.IsAlive;
}


/// <summary>
/// Function runs in script process thread
/// </summary>
private void ScriptThreadFunction()
{
int iDelay = 1;
while ( !m_bStopThread )
{
// Setup a delay between each process script command
// ** Command are sent to an automate at interval of 200 miliseconds **
// The send operation is not blocking
if (iDelay > 0)
{
// Watch for thread stopped flag
if ( m_bStopThread )
{
continue;
}

iDelay -= 1;
Thread.Sleep(1);
continue;
}

// Reset delay
iDelay = 1;

// Now I read a script file and I send a single command
// sLine contain the command to send to the automate
m_FormPrincipal.Invoke(m_FormPrincipal.m_SendCommandDelegate, new
object[] {sLine} );
// This SendCommand operation is very quickly < 1 ms

// Once I read a command to send, I send it and I notified the UI from that
m_FormPrincipal.Invoke(m_FormPrincipal.m_DelegateAddLogEntry, new
object[] { string.Format( "Command {0} sent !", sLine) , Color.Gray});
}

// Make asynchronous call to main form to inform it that thread has
finished it works
m_FormPrincipal.BeginInvoke(m_FormPrincipal.m_ThreadFinishedDelegate, null);
}
}
*********************************************

public class FormPrincipal : System.Windows.Forms.Form
{
// Delegates declaration
public delegate void AddLogEntryDelegate(string entry, System.Drawing.Color
color);
public delegate void ThreadFinishedDelegate();
public delegate void SendCommandDelegate(string cmd);

// Delegate members
public AddLogEntryDelegate m_DelegateAddLogEntry;
public ThreadFinishedDelegate m_ThreadFinishedDelegate;
public SendCommandDelegate m_SendCommandDelegate;

// Script process engine
private ScriptProcess m_ScriptProcess = null;

// Flag that indicate if application should be close
// when thread has complete it work
private bool m_bCloseApplicationOnThreadFinish = false;

// FormPrincipal constructor
public FormPrincipal( string[] args )
{
// Initialize components
InitializeComponent();

// Initialize delegates
m_DelegateAddLogEntry = new AddLogEntryDelegate(this.AddLogEntry);
m_ThreadFinishedDelegate = new ThreadFinishedDelegate(this.ThreadFinished);
m_SendCommandDelegate = new SendCommandDelegate(this.SendCommand);

// Initialize script process object
m_ScriptProcess = new ScriptProcess(this);
}

// Load event of the FormPrincial
private void FormPrincipal_Load(object sender, System.EventArgs e)
{
// No script file choose yet, disabled the play button
this.m_Bt_Play.Enabled = false;
}

// Form closing event
private void FormPrincipal_Closing(object sender,
System.ComponentModel.CancelEventArgs e)
{
// Stop script engine
if ( m_ScriptProcess.Stop() )
{
// Signal to thread to stop
m_bCloseApplicationOnThreadFinish = true;

// Cancel from closing until the thread stop
// Once we will received the TreadFinished event we will
// close it if the "m_bCloseApplicationOnThreadFinish" flag is set
e.Cancel = true;
}
}

// On play button click event
private void m_Bt_Play_Click(object sender, System.EventArgs e)
{
// Clear log
this.m_Txt_Log.Clear();
this.m_Txt_Log.Focus();

// Start script process
if ( m_ScriptProcess.Start() )
{
// Disable command that canno tbe activated while in play mode
this.m_Bt_Play.Enabled = false;
this.m_Bt_Stop.Enabled = true;
}
}

// Stop button click event
private void m_Bt_Stop_Click(object sender, System.EventArgs e)
{
StopScriptProcess();
}

// Stop script process thread if it is running.
private void StopScriptProcess()
{
// Disable stop button
m_Bt_Stop.Enabled = false;

// Stop script process thread
m_ScriptProcess.Stop();
}

// Called when script process thread has finished
private void ThreadFinished()
{
this.m_Bt_Play.Enabled = true;
this.m_Bt_Stop.Enabled = false;

// Close form if the user was waiting for
if (m_bCloseApplicationOnThreadFinish)
{
Close();
}
}

// Add new log entry to the RichTextBox control
private void AddLogEntry(string entry, System.Drawing.Color color)
{
// Append color text to the RichText box
int num = this.m_Txt_Log.TextLength;
string leftInfo = DateTime.Now.ToString() + " -> ";
int leftInfoLength = leftInfo.Length;
this.m_Txt_Log.AppendText( DateTime.Now.ToString() + " -> " + entry +
Environment.NewLine);
this.m_Txt_Log.Select( num, num + leftInfoLength );
this.m_Txt_Log.SelectionColor = Color.FromArgb( 58, 83, 128 );
this.m_Txt_Log.Select( num + leftInfoLength, this.m_Txt_Log.TextLength - 1
);
this.m_Txt_Log.SelectionColor = color;
this.m_Txt_Log.Focus();
}
}
*********************************************

Now you have the entire code and this is exactly the one I'm using to test
the problem. The only thing you don't have is the content of the
"SendCommand" function but it doesn't matter because the problem is still
there even if this fuction has only a single "return" line inside.

I take in consideration you advise about using the WaitHandle for my
StopThread signal but I don't think that using Sleep could prevent
"m_Bt_Stop_Click" event to be called when user click on the stop button.

Best regards,
Martin
 
E

Erakis

*********************************************
public class ScriptProcess
{
// Stop thread signal
private volatile bool m_bStopThread = false;

// Script process thread
private Thread m_ThreadScript = null;

// Reference to main form used to make sync/async user interface calls:
private FormPrincipal m_FormPrincipal;

// Locking object
private object m_LockingObjectScriptProcessThread = new object();

// Default constructor
public ScriptProcess(FormPrincipal form)
{
// Get a reference on the main form
m_FormPrincipal = form;
}

// Start the script process thread
public bool Start()
{
// Lock script process operation
bool bRet;
lock(m_LockingObjectScriptProcessThread);
{
// Is thread is already running ?
if (m_ThreadScript != null && m_ThreadScript.IsAlive)
{
bRet = false;
}
else
{
// Initialize script process thread properties
m_ThreadScript = new Thread( new ThreadStart( this.ScriptThreadFunction
) );
m_ThreadScript.Name = "ScriptThreadFunction";
m_ThreadScript.IsBackground = true;

// Reset stop flag
m_bStopThread = false;

// Start thread
m_ThreadScript.Start();

// Thread started
bRet = true;
}
}
return bRet;
}

// Stop the script process thread
public bool Stop()
{
// Lock script process operation
bool bRet;
lock(m_LockingObjectScriptProcessThread)
{
if (m_ThreadScript == null || !m_ThreadScript.IsAlive)
{
bRet = false;
}
m_bStopThread = true;
bRet = true;
}
return bRet;
}

// Returns a value indicating if the script process thread is alive
public bool IsRunning()
{
return m_ThreadScript != null && m_ThreadScript.IsAlive;
}


/// <summary>
/// Function runs in script process thread
/// </summary>
private void ScriptThreadFunction()
{
int iDelay = 1;
while ( !m_bStopThread )
{
// Setup a delay between each process script command
// ** Command are sent to an automate at interval of 200 miliseconds **
// The send operation is not blocking
if (iDelay > 0)
{
// Watch for thread stopped flag
if ( m_bStopThread )
{
continue;
}

iDelay -= 1;
Thread.Sleep(1);
continue;
}

// Reset delay
iDelay = 1;

// Now I read a script file and I send a single command
// sLine contain the command to send to the automate
m_FormPrincipal.Invoke(m_FormPrincipal.m_SendCommandDelegate, new
object[] {sLine} );
// This SendCommand operation is very quickly < 1 ms

// Once I read a command to send, I send it and I notified the UI from that
m_FormPrincipal.Invoke(m_FormPrincipal.m_DelegateAddLogEntry, new
object[] { string.Format( "Command {0} sent !", sLine) , Color.Gray});
}

// Make asynchronous call to main form to inform it that thread has
finished it works
m_FormPrincipal.BeginInvoke(m_FormPrincipal.m_ThreadFinishedDelegate, null);
}
}
*********************************************

public class FormPrincipal : System.Windows.Forms.Form
{
// Delegates declaration
public delegate void AddLogEntryDelegate(string entry, System.Drawing.Color
color);
public delegate void ThreadFinishedDelegate();
public delegate void SendCommandDelegate(string cmd);

// Delegate members
public AddLogEntryDelegate m_DelegateAddLogEntry;
public ThreadFinishedDelegate m_ThreadFinishedDelegate;
public SendCommandDelegate m_SendCommandDelegate;

// Script process engine
private ScriptProcess m_ScriptProcess = null;

// Flag that indicate if application should be close
// when thread has complete it work
private bool m_bCloseApplicationOnThreadFinish = false;

// FormPrincipal constructor
public FormPrincipal( string[] args )
{
// Initialize components
InitializeComponent();

// Initialize delegates
m_DelegateAddLogEntry = new AddLogEntryDelegate(this.AddLogEntry);
m_ThreadFinishedDelegate = new ThreadFinishedDelegate(this.ThreadFinished);
m_SendCommandDelegate = new SendCommandDelegate(this.SendCommand);

// Initialize script process object
m_ScriptProcess = new ScriptProcess(this);
}

// Load event of the FormPrincial
private void FormPrincipal_Load(object sender, System.EventArgs e)
{
// No script file choose yet, disabled the play button
this.m_Bt_Play.Enabled = false;
}

// Form closing event
private void FormPrincipal_Closing(object sender,
System.ComponentModel.CancelEventArgs e)
{
// Stop script engine
if ( m_ScriptProcess.Stop() )
{
// Signal to thread to stop
m_bCloseApplicationOnThreadFinish = true;

// Cancel from closing until the thread stop
// Once we will received the TreadFinished event we will
// close it if the "m_bCloseApplicationOnThreadFinish" flag is set
e.Cancel = true;
}
}

// On play button click event
private void m_Bt_Play_Click(object sender, System.EventArgs e)
{
// Clear log
this.m_Txt_Log.Clear();
this.m_Txt_Log.Focus();

// Start script process
if ( m_ScriptProcess.Start() )
{
// Disable command that canno tbe activated while in play mode
this.m_Bt_Play.Enabled = false;
this.m_Bt_Stop.Enabled = true;
}
}

// Stop button click event
private void m_Bt_Stop_Click(object sender, System.EventArgs e)
{
StopScriptProcess();
}

// Stop script process thread if it is running.
private void StopScriptProcess()
{
// Disable stop button
m_Bt_Stop.Enabled = false;

// Stop script process thread
m_ScriptProcess.Stop();
}

// Called when script process thread has finished
private void ThreadFinished()
{
this.m_Bt_Play.Enabled = true;
this.m_Bt_Stop.Enabled = false;

// Close form if the user was waiting for
if (m_bCloseApplicationOnThreadFinish)
{
Close();
}
}

// Add new log entry to the RichTextBox control
private void AddLogEntry(string entry, System.Drawing.Color color)
{
// Append color text to the RichText box
int num = this.m_Txt_Log.TextLength;
string leftInfo = DateTime.Now.ToString() + " -> ";
int leftInfoLength = leftInfo.Length;
this.m_Txt_Log.AppendText( DateTime.Now.ToString() + " -> " + entry +
Environment.NewLine);
this.m_Txt_Log.Select( num, num + leftInfoLength );
this.m_Txt_Log.SelectionColor = Color.FromArgb( 58, 83, 128 );
this.m_Txt_Log.Select( num + leftInfoLength, this.m_Txt_Log.TextLength - 1
);
this.m_Txt_Log.SelectionColor = color;
this.m_Txt_Log.Focus();
}
}
*********************************************

Now you have the entire code and this is exactly the one I'm using to test
the problem. The only thing you don't have is the content of the
"SendCommand" function but it doesn't matter because the problem is still
there even if this fuction has only a single "return" line inside.

I take in consideration you advise about using the WaitHandle for my
StopThread signal but I don't think that using Sleep could prevent
"m_Bt_Stop_Click" event to be called when user click on the stop button.

Best regards,
Martin
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Top