S
Stefan Kiryazov
I'm developing a simple applications that extracts data from files,
that can eventually become large. I have logic that scans a particular
folder, and for each file in the folder instanciates a parser class
and processes the specified file.
I want to start a background thread with the main logic to not freeze
the UI. I am new to windows forms, so i had to try some scenarios and
I am wonder what is the best approach. Also, when i manipulate the UI
controls on the form from the worker thread it sometimes yields an
error, the cause of which i can't exactly understand. More detailed
explanation follows.
I do the following: On the click event of a specific button I start
the worker thread with:
ThreadPool.QueueUserWorkItem( new WaitCallback(ReadTree), this );
ReadTree is a static method that scans the directory and parses the
files found. While doing this, i want be showing percentage and a
progress bar. It used to work with no problem - the background thread
accesses and manipulates the controls of the form, that is passed as a
paremeter. The problem is not caused by the pool, because i tried to
start the thread also as follows:
Thread t = new Thread( new ThreadStart( ReadTree ) );
t.Start();
Then, I did some change in the logic and the UI and i found that the
application crashes, yielding a NullReferenceException in the
following code, that updates the percentage label and the progress bar
and adds a new record for the parsed file in a dataset, that is bound
to a grid in the main form:
((frmMain)state).progressBar.Increment( (Int32)fileSize );
if ( parsedBytes < ((frmMain)state).totalBytes ) parsedBytes +=
fileSize;
((frmMain)state).lblPercentage.Text = Math.Round( (
(double)parsedBytes/(double)((frmMain)state).totalBytes ) * 100
).ToString() + "%";
newRow = ((frmMain)state).dsFiles.Tables["Files"].NewRow();
newRow[0] = fileName;
((frmMain)state).dsFiles.Tables["Files"].Rows.Add( newRow );
However, if i simply run the method in the same thread there is no
problem. The error does not fire every time when i change properties
of form controls - it just happens on a particular file. No division
by zero or anything wrong with the numbers. And, as i see it, the
error is not actually taking place in the above code, but just happens
in the mean time, because if all this is in a try-catch block, the
exception is not caught. Also try-catch wrapping the starting of the
thread - also nothing. When I do the following:
try
{
Application.Run(new frmMain());
}
catch( Exception ex )
{
String txt = ex.Message;
}
I manage to catch the exception. Here are the details:
{"Object reference not set to an instance of an object."
} System.NullReferenceException
StackTrace " at System.Windows.Forms.UnsafeNativeMethods.PeekMessage(MSG&
msg, HandleRef hwnd, Int32 msgMin, Int32 msgMax, Int32 remove)
at System.Windows.Forms.ComponentManager.System.Windows.Forms.UnsafeNativeMethods+IMsoComponentManager.FPushMessageLoop(Int32
dwComponentID, Int32 reason, Int32 pvLoopData)
at System.Windows.Forms.ThreadContext.RunMessageLoopInner(Int32
reason, ApplicationContext context)
at System.Windows.Forms.ThreadContext.RunMessageLoop(Int32
reason, ApplicationContext context)
at System.Windows.Forms.Application.Run(Form mainForm)
at CadParser.frmMain.Main() in
e:\\projects\\mj\\immpilot\\parser\\cadparser\\main.cs:line
303" string
But curiously, when i comment the above code, no exception is being
thrown.
Another issue, that I find curious is that when i QuickView the state
object (that is, the main form) the viewer takes very long time to
load. Then, some properties are displayed correctly, while others show
"error: cannot obtain value", for example the Text property of the
label that I try to access.
I see that this is a problem caused by a background thread accessing
the forms elements, which doesn't seem to be the best approach, but
where can i read more about this problem, and what exactly happens
behind the stage.
And also, what is the best workaround concept for this. I intend to do
create a new class, that manages the scanning of the directory and the
calling of the parsing functions, which does not tuch any form
members. It will expose an event, for which the form will subscribe,
and will alter the UI appropriately, according to the EventArgs
passed. Is this the best approach? Any ideas where can i read more
about more complex situations concerning windows forms and
multithreading.
Thanks everybody.
that can eventually become large. I have logic that scans a particular
folder, and for each file in the folder instanciates a parser class
and processes the specified file.
I want to start a background thread with the main logic to not freeze
the UI. I am new to windows forms, so i had to try some scenarios and
I am wonder what is the best approach. Also, when i manipulate the UI
controls on the form from the worker thread it sometimes yields an
error, the cause of which i can't exactly understand. More detailed
explanation follows.
I do the following: On the click event of a specific button I start
the worker thread with:
ThreadPool.QueueUserWorkItem( new WaitCallback(ReadTree), this );
ReadTree is a static method that scans the directory and parses the
files found. While doing this, i want be showing percentage and a
progress bar. It used to work with no problem - the background thread
accesses and manipulates the controls of the form, that is passed as a
paremeter. The problem is not caused by the pool, because i tried to
start the thread also as follows:
Thread t = new Thread( new ThreadStart( ReadTree ) );
t.Start();
Then, I did some change in the logic and the UI and i found that the
application crashes, yielding a NullReferenceException in the
following code, that updates the percentage label and the progress bar
and adds a new record for the parsed file in a dataset, that is bound
to a grid in the main form:
((frmMain)state).progressBar.Increment( (Int32)fileSize );
if ( parsedBytes < ((frmMain)state).totalBytes ) parsedBytes +=
fileSize;
((frmMain)state).lblPercentage.Text = Math.Round( (
(double)parsedBytes/(double)((frmMain)state).totalBytes ) * 100
).ToString() + "%";
newRow = ((frmMain)state).dsFiles.Tables["Files"].NewRow();
newRow[0] = fileName;
((frmMain)state).dsFiles.Tables["Files"].Rows.Add( newRow );
However, if i simply run the method in the same thread there is no
problem. The error does not fire every time when i change properties
of form controls - it just happens on a particular file. No division
by zero or anything wrong with the numbers. And, as i see it, the
error is not actually taking place in the above code, but just happens
in the mean time, because if all this is in a try-catch block, the
exception is not caught. Also try-catch wrapping the starting of the
thread - also nothing. When I do the following:
try
{
Application.Run(new frmMain());
}
catch( Exception ex )
{
String txt = ex.Message;
}
I manage to catch the exception. Here are the details:
{"Object reference not set to an instance of an object."
} System.NullReferenceException
StackTrace " at System.Windows.Forms.UnsafeNativeMethods.PeekMessage(MSG&
msg, HandleRef hwnd, Int32 msgMin, Int32 msgMax, Int32 remove)
at System.Windows.Forms.ComponentManager.System.Windows.Forms.UnsafeNativeMethods+IMsoComponentManager.FPushMessageLoop(Int32
dwComponentID, Int32 reason, Int32 pvLoopData)
at System.Windows.Forms.ThreadContext.RunMessageLoopInner(Int32
reason, ApplicationContext context)
at System.Windows.Forms.ThreadContext.RunMessageLoop(Int32
reason, ApplicationContext context)
at System.Windows.Forms.Application.Run(Form mainForm)
at CadParser.frmMain.Main() in
e:\\projects\\mj\\immpilot\\parser\\cadparser\\main.cs:line
303" string
But curiously, when i comment the above code, no exception is being
thrown.
Another issue, that I find curious is that when i QuickView the state
object (that is, the main form) the viewer takes very long time to
load. Then, some properties are displayed correctly, while others show
"error: cannot obtain value", for example the Text property of the
label that I try to access.
I see that this is a problem caused by a background thread accessing
the forms elements, which doesn't seem to be the best approach, but
where can i read more about this problem, and what exactly happens
behind the stage.
And also, what is the best workaround concept for this. I intend to do
create a new class, that manages the scanning of the directory and the
calling of the parsing functions, which does not tuch any form
members. It will expose an event, for which the form will subscribe,
and will alter the UI appropriately, according to the EventArgs
passed. Is this the best approach? Any ideas where can i read more
about more complex situations concerning windows forms and
multithreading.
Thanks everybody.