How do I START to debug this?

  • Thread starter Thread starter Iain
  • Start date Start date
I

Iain

I've an application I've been working on for some time which has a Windows
Forms User interface, a set of 'business object' classes and interacts with
a SQL Server database and some Legacy COM Objects.

All was working well until I added some Logging code.

The Logging code raises an Event from one of the buisiness objects to the
main form of the (MDI) Windows UI. This adds a row to a DataSet which is
the source for a DataGrid on a MDI Child window.

The problem is that with the Logging event active some VERY bizzare things
are happening. If this was C++ I would be looking for stack corruption, but
in c#?

Tracing is difficult. I do not necessarily get the same symptoms on each
run and where the code gets to depends on the data and exactly where these
logging events are raised.

In each case I get

n unhandled exception of type 'System.NullReferenceException' occurred in
system.windows.forms.dll
Additional information: Object reference not set to an instance of an object

Appearing at the Application.Run.

The stack frame has
UnsafeNativeMethods.DispatchMessageW

At its top.

If I trace through I might find that some Controls on one of the Windows
Forms (not the one which manages the log!) seem to be invalid with
'error:cannot obtain value' appearing when viewed in the watch window (as a
first pass it would appear that all references or properties cannot be
found, but value variables can).

If I modify my OnLog handler to NOT raise the event then these problems do
NOT occur and the program works fine.

I have commented out the code which creates and process the COM Object and
the same sort of things happen.

I don't know where to start, really, can anyone give some hints?

Thanks

Iain
 
Iain,

Are the business objects running in a thread other than the UI thread?
If this is the case, then you will want to call Invoke (passing in a
delegate and any required parameters) to make sure that the code that
modifies the UI (or any code that will ultimately modify the UI) will be
called on the UI thread.

Hope this helps.
 
Thanks, Nicholas for this response.

Since the first post, I've reverted to the old Binary Chop means of
debugging - deleting bits of code until it works / fails.

The whole thing is fine unless the DataTable I'm populating via my events is
bound to my DataGrid. When it is the UI fails in one way or another
otherwise it is OK (this applies even if I defer the binding until after
some events have occurred).

Now I'm gonna seem ignorant. I don't see how to use Invoke.

Firstly, you are right the events are being called from a separate thread.
And I can appreciate this may be the underlying cause.

Oddly, in this thread, I already have a different event being called which
links to a Custom Control I've produced. This has never apparently shown
any problems.

So we have Thread 1 which is the UI. This contains an event handler which
will receive a new log event. It (indirectly) adds the data in this Log
event into a new row in a DataTable which in turn is bound to a DataGrid.

When my underlying Worker object is created it adds this handler (in the Top
window) to the event source in the Worker object.

At some point a 'Run' method is called which starts the thread which calls
the delegates on the Event Source, which raises the event and so on.

(I hope this makes some kind of sense).

Now from reading the books, Invoke seems to be mainly intended for calling
Methods on a control from another thread and I can't really see how it
applies here. I would have assumed that MS would manage the cross thread
marshalling of Delegates automatically, but who knows!

Add to that, that I have got a functionally very similar custom event being
raised alread which does not seem to cause any problems.

I don't know if this makes any sense, but I'd appreciate any further
feedback!

Iain
 
Iain,

You are incorrect in assuming that MS would handle any cross-thread
delegate issues. To be honest, I am glad they did it the way they did, as
making assumptions like that can be very, very bad.

If you have a data table/view that is attached to a grid (which is a UI
element), then changing the underlying data source will cause the UI to be
changed. When you make the change, the event is fired on the thread that
the change occured, and then the event handler attempts to update the UI as
a result. This is why you are having the problems that you are having.

Now, to get around this, call the Invoke method. This will have the
effect of calling the delegate on the thread that the control has affinity
with (in this case, the main UI thread). Say you wanted to update a data
table that is bound to a table. You can write a method like this:

// Updates the data table.
public static void UpdateDataTable(DataTable table, string field, object
value)
{
// Set the value on the first row of the data table.
table.Rows[0][field] = value;
}

Now, define a delegate which matches the signature:

public delegate void UpdateDataTableCallback(DataTable table, string field,
object value);

Finally, from the worker thread, call Invoke on a control or form that
was created on the main UI thread:

// Assume that mobjForm is the main form.
mobjForm.Invoke(new UpdateDataTableCallback(UpdateDataTable), new
object[3]{pobjDataTable, pstrField, pobjValue});
 
Thankyou. That has fixed the problem.

I guess I should apply this to my other events which affect the UI, even though they *appear* to work.

And whilst I would have preffered a little more specific error message from our Friends in Seattle on this one (that is - 'please use the right thread', rather than *CRASH*!), it is easier that message passing to a hidden window and a damn sight les daunting than the Com marshalling alternatives (CoMarshalInterThreadInterfaceInStream if I remember correctly!).

Thank you again. This would have taken forever.

Iain
 
Iain,

It should be noted that it doesn't pass the message to a hidden window.
Rather, when you call Invoke, the message handler for the control/form you
call it on is the one receiving the message and processing it. If that
window is hidden, then yes, it is the case, but otherwise, no.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Thankyou. That has fixed the problem.

I guess I should apply this to my other events which affect the UI, even
though they *appear* to work.

And whilst I would have preffered a little more specific error message from
our Friends in Seattle on this one (that is - 'please use the right thread',
rather than *CRASH*!), it is easier that message passing to a hidden window
and a damn sight les daunting than the Com marshalling alternatives
(CoMarshalInterThreadInterfaceInStream if I remember correctly!).

Thank you again. This would have taken forever.

Iain
 
Back
Top