B
Bruce Wood
I have some sort of a concurrency problem that has me stumped.
I have a form (actually several) that run background worker threads
from the OnLoad method. Sometimes... occasionally... I get a crash
dump from a user that indicates that I "cannot call Invoke or
InvokeAsync on a control until the window handle has been created."
Now, never mind that by the time OnLoad runs, the window handle should
already be created. I decided that, hey, if that's the problem then
I'll just patch it. My patched code is below.
As you can see, in the DoBackgroundWork method I added a test for
control.IsHandleCreated, and then if it's not I do a
control.CreateControl. However, even after this patch I had a crash
this morning with this same annoying message.
2007-03-01 06:36:54Z User mm_tboos on PRICTX99, running M:\program
files\StockCodeGenerator\StockCodeGenerator.exe (StockCodeGenerator
version 2.4.2615.30559) received the following exception:
2007-03-01 06:36:54Z System.InvalidOperationException: Cannot call
Invoke or InvokeAsync on a control until the window handle has been
created.
2007-03-01 06:36:54Z at
System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate
method, Object[] args, Boolean synchronous)
2007-03-01 06:36:54Z at
System.Windows.Forms.Control.Invoke(Delegate method, Object[] args)
2007-03-01 06:36:54Z at
Agama.Controls.BackgroundWorkParameters.DoBackgroundWork(Object
parameter)
Do I have misconceptions about when handle creation happens? Am I
missing something here?
Code follows.
public delegate void BackgroundWorkDelegate(params object[]
parameter);
public delegate void ControlEnableDelegate(bool enable);
private class BackgroundWorkParameters
{
private Control _callingControl;
private BackgroundWorkDelegate _backgroundJob;
private BackgroundWorkDelegate _finishJob;
private object[] _parameters;
private ControlEnableDelegate _enableControls;
private Exception _finishException;
public BackgroundWorkParameters(Control callingControl,
BackgroundWorkDelegate backgroundJob,
BackgroundWorkDelegate finishJob,
object[] parameters,
ControlEnableDelegate enableControls)
{
this._callingControl = callingControl;
this._backgroundJob = backgroundJob;
this._finishJob = finishJob;
this._parameters = parameters;
this._enableControls = enableControls;
this._finishException = null;
}
public void ScheduleBackgroundJob()
{
this._callingControl.Cursor = Cursors.WaitCursor;
if (this._enableControls != null)
{
this._enableControls(false);
}
ThreadPool.QueueUserWorkItem(
new WaitCallback(DoBackgroundWork), null);
}
private void DoBackgroundWork(object parameter)
{
try
{
this._backgroundJob(this._parameters);
}
finally
{
if (!this._callingControl.IsHandleCreated)
{
this._callingControl.CreateControl();
}
this._callingControl.Invoke(
new MethodInvoker(FinishWork), null);
if (this._finishException != null)
{
throw new ApplicationProgrammingErrorException(
"Exception trapped while finishing background
task.",
this._finishException);
}
}
}
private void FinishWork()
{
this._finishException = null;
try
{
if (this._finishJob != null)
{
try
{
this._finishJob(this._parameters);
}
catch (Exception ex)
{
this._finishException = ex;
}
}
}
finally
{
this._callingControl.Cursor = Cursors.Default;
if (this._enableControls != null)
{
try
{
this._enableControls(true);
}
catch (Exception ex)
{
this._finishException = ex;
}
}
}
}
}
public static void BackgroundWork(Control callingControl,
BackgroundWorkDelegate backgroundJob,
BackgroundWorkDelegate finishJob,
object[] parameters,
ControlEnableDelegate enableControls)
{
BackgroundWorkParameters p = new
BackgroundWorkParameters(callingControl, backgroundJob, finishJob,
parameters, enableControls);
p.ScheduleBackgroundJob();
}
I have a form (actually several) that run background worker threads
from the OnLoad method. Sometimes... occasionally... I get a crash
dump from a user that indicates that I "cannot call Invoke or
InvokeAsync on a control until the window handle has been created."
Now, never mind that by the time OnLoad runs, the window handle should
already be created. I decided that, hey, if that's the problem then
I'll just patch it. My patched code is below.
As you can see, in the DoBackgroundWork method I added a test for
control.IsHandleCreated, and then if it's not I do a
control.CreateControl. However, even after this patch I had a crash
this morning with this same annoying message.
2007-03-01 06:36:54Z User mm_tboos on PRICTX99, running M:\program
files\StockCodeGenerator\StockCodeGenerator.exe (StockCodeGenerator
version 2.4.2615.30559) received the following exception:
2007-03-01 06:36:54Z System.InvalidOperationException: Cannot call
Invoke or InvokeAsync on a control until the window handle has been
created.
2007-03-01 06:36:54Z at
System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate
method, Object[] args, Boolean synchronous)
2007-03-01 06:36:54Z at
System.Windows.Forms.Control.Invoke(Delegate method, Object[] args)
2007-03-01 06:36:54Z at
Agama.Controls.BackgroundWorkParameters.DoBackgroundWork(Object
parameter)
Do I have misconceptions about when handle creation happens? Am I
missing something here?
Code follows.
public delegate void BackgroundWorkDelegate(params object[]
parameter);
public delegate void ControlEnableDelegate(bool enable);
private class BackgroundWorkParameters
{
private Control _callingControl;
private BackgroundWorkDelegate _backgroundJob;
private BackgroundWorkDelegate _finishJob;
private object[] _parameters;
private ControlEnableDelegate _enableControls;
private Exception _finishException;
public BackgroundWorkParameters(Control callingControl,
BackgroundWorkDelegate backgroundJob,
BackgroundWorkDelegate finishJob,
object[] parameters,
ControlEnableDelegate enableControls)
{
this._callingControl = callingControl;
this._backgroundJob = backgroundJob;
this._finishJob = finishJob;
this._parameters = parameters;
this._enableControls = enableControls;
this._finishException = null;
}
public void ScheduleBackgroundJob()
{
this._callingControl.Cursor = Cursors.WaitCursor;
if (this._enableControls != null)
{
this._enableControls(false);
}
ThreadPool.QueueUserWorkItem(
new WaitCallback(DoBackgroundWork), null);
}
private void DoBackgroundWork(object parameter)
{
try
{
this._backgroundJob(this._parameters);
}
finally
{
if (!this._callingControl.IsHandleCreated)
{
this._callingControl.CreateControl();
}
this._callingControl.Invoke(
new MethodInvoker(FinishWork), null);
if (this._finishException != null)
{
throw new ApplicationProgrammingErrorException(
"Exception trapped while finishing background
task.",
this._finishException);
}
}
}
private void FinishWork()
{
this._finishException = null;
try
{
if (this._finishJob != null)
{
try
{
this._finishJob(this._parameters);
}
catch (Exception ex)
{
this._finishException = ex;
}
}
}
finally
{
this._callingControl.Cursor = Cursors.Default;
if (this._enableControls != null)
{
try
{
this._enableControls(true);
}
catch (Exception ex)
{
this._finishException = ex;
}
}
}
}
}
public static void BackgroundWork(Control callingControl,
BackgroundWorkDelegate backgroundJob,
BackgroundWorkDelegate finishJob,
object[] parameters,
ControlEnableDelegate enableControls)
{
BackgroundWorkParameters p = new
BackgroundWorkParameters(callingControl, backgroundJob, finishJob,
parameters, enableControls);
p.ScheduleBackgroundJob();
}