Periodic problem in InitializeComponent of modal form with a forms.timer

S

Stephen Rice

Hi,

I have a periodic problem which I am having a real time trying to sort.

Background:
An MDI VB app with a DB on SQL 2000.
I have wrapped all the DB access into an object which spawns a thread to
access the database and then displays a modal dialog which allows the user
to cancel the task, if it is taking longer than they want, and shows them a
display of how long the query has been running so far.

I have been very careful (I think!) to make sure that the UI is only ever
updated from the main UI thread. I pass status info from the SQL therad to
the UI thread through a System.Threading.AutoResetEvent.
A system.windows.forms.timer runs in the Modal form to update a textbox with
the amount of time it has been there and to check the 'SQL finished' event
above.

Everything work fine 99.9% of the time BUT, very occasionally, I get an odd
situation in InitializeComponent in the modal form.
As far as I can ascertain (sorry, that should be, totally guess!), when the
timer is enabled, it does not 'see' the containing forms message loop and so
it instantiates one itself (?). This new message loop 'sees' messages in the
MDI's queue including the pending message currently being processed which
caused this whole event to occur (stay with me here!)

I think that this makes much more sense in the context of a real stack
trace:

Stack Trace: at System.Data.SqlClient.SqlCommand.ValidateCommand(String
method, Boolean executing)
at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior
cmdBehavior, RunBehavior runBehavior, Boolean returnStream)
at System.Data.SqlClient.SqlCommand.ExecuteReader()
at Ideal.clsDBThread.SQLFetchThread()
Current Stack: at System.Environment.GetStackTrace(Exception e)
at System.Environment.GetStackTrace(Exception e)
at System.Environment.get_StackTrace()
at Ideal.basUtility.LogError(Exception e, String strUserMsg)
at Ideal.clsDatabase.SQLFetch(String strCommand)
at Ideal.clsDatabase.TryEditLock(Int32 iDealID)
5 at Ideal.frmDeal.cmdEdit_Click(Object sender, EventArgs e)
at System.Windows.Forms.Control.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons
button, Int32 clicks)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ButtonBase.WndProc(Message& m)
at System.Windows.Forms.Button.WndProc(Message& m)
at System.Windows.Forms.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg,
IntPtr wparam, IntPtr lparam)
at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
at
System.Windows.Forms.ComponentManager.System.Windows.Forms.UnsafeNativeMetho
ds+IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason,
Int32 pvLoopData)
at System.Windows.Forms.ThreadContext.RunMessageLoopInner(Int32 reason,
ApplicationContext context)
4 at System.Windows.Forms.ThreadContext.RunMessageLoop(Int32 reason,
ApplicationContext context)
3 at System.Windows.Forms.Timer.set_Enabled(Boolean value)
at System.Windows.Forms.Timer.set_Interval(Int32 value)
2 at Ideal.frmDBExec.InitializeComponent()
at Ideal.frmDBExec..ctor()
at Ideal.clsDatabase.SQLFetch(String strCommand)
at Ideal.clsDatabase.TryEditLock(Int32 iDealID)
1 at Ideal.frmDeal.cmdEdit_Click(Object sender, EventArgs e)
at System.Windows.Forms.Control.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons
button, Int32 clicks)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ButtonBase.WndProc(Message& m)
at System.Windows.Forms.Button.WndProc(Message& m)
at System.Windows.Forms.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg,
IntPtr wparam, IntPtr lparam)
at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
at
System.Windows.Forms.ComponentManager.System.Windows.Forms.UnsafeNativeMetho
ds+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 Ideal.basMain.Main()

From the bottom.
Normal appication stack (I think) with a mouse click being processed
At point (1) the routine which handles the button click is called
This calls some SQL which causes the spawned thread/Modal dialog combination
and the modal dialog (Initialisation call can be seen at point 2)
Within this initialisation the timer interval gets set which in turn enables
the timer (point 3). All normal so far
Then:
At point (4), within the timer enable, there is a call to RunMessageLoop
which (I think) hooks onto the MDI's context and picks up the same mouse
click that we are currently processing and recalls the button click routine!
(point 5)

Any help or pointers to help would be VERY VERY apprieciated

Thanks in advance

Stephen
 
C

Cor Ligthert

Stephen,

In my mind do you need with a problem as yours all the source before your
eyes.

However the first answer on your question is simple. For multithreaded
applications should the forms timer not be used.

There is the threading timer (for me a terrible thing comparing to that easy
forms timer because it is difficult to stop). However it is made specialy
for threading, therefore I would in your place first look for that.

http://msdn.microsoft.com/library/d.../html/frlrfSystemThreadingTimerClassTopic.asp

I hope this helps?

Cor
 
S

Stephen Rice

Hi Cor,

Thanks very much for you suggestion.

Unfortunately, I think that using a system.Threading.Timer might cause more
problems rather than solve them? In the MSDN docs they say (About the
TimerCallback) 'The method does not execute in the thread that created the
timer; it executes in a thread pool thread supplied by the system'
The reason for the timer is to update the UI to show how long the task has
been running and so needs to run in the UI's own thread.

Also, although the windows.forms.timer is not thread safe, it is never
accessed from any other thread than the UI one (I hope) and so I don't think
that this will be an issue (as far as I understand the way that the timer is
working?)

I am sure you are right that it is a threading issue, because of the
periodic nature, but I can't for the life of me see where the 'unsafe'
access is going on.

Hmmm.

One other problem I have is that this happens so infrequently that I havn't
been able to recreate the problem myself in a debug environment. All I have
to work from are logs that I have got all the clients to create.

Stephen
 
C

Cor Ligthert

Stephen,

You mean that what you are doing is

Starting a modal form with a timer as a clock in it and a button
Start in that modal form a thread to read something
Abort that thread when the user pushes the button (when it is not ready)

Cor
 
S

Stephen Rice

Hi Cor,

Yes (but with the following very slight 'frills')

In the main app, start a thread to run the SQL command
Do a 'WaitOne' on a 'SQL thread finished' event with a timeout of three
seconds
If the event did not fire within the three seconds, display a modal dialog
with a 'clock' and a cancel button (I wait for up to three seconds before
doing this so that the, majority, of requests which take less than three
seconds do not have the overhead of creating the dialog and if any user
wants to cancel the SQL task within three seconds they should be shot for
being so impatient!)

The timer tick event first checks for the SQL thread having finished by
doing a very short 'WaitOne' on the event above and then either closes the
dialog with a me.Close if it has or updates a 'clock' label.

If the Cancel button is clicked, it cancels the SqlCommand and then does a
Thread.Join to wait for the DB thread finishing before doing a me.Close.

The main app which created modal dialog does a .Dispose on the dialog form
object when it returns to make sure that the timer is cleaned up.

Thanks again.

Stephen
 
C

Cor Ligthert

Stephen

And when you do it as I wrote, what does that mean. The reference too a
dataset is the most simple thing there can be (assuming you are using a
dataset).

Cor
 
S

Stephen Rice

Hi Cor,

I'm really sorry. I am not quite sure what you mean.
Do you mean your suggestion to use the Threading.Timer?
 
C

Cor Ligthert

Stephen,

I mean to start all dataretrival actions from your model form (in that
thread)

Not that I ever tried it, however when I saw you problem I thought why not.

What you want is to get a dataset (I still assume) and in the meantime the
user may (after a shortwhile) only do one thing and that is stop it, what
can be done in that modal form.

Cor
 
S

Stephen Rice

Cor,

Right. (Sorry) I see totally what you mean.

Actually, this is how I had things set up for a very short while when I was
developing it.
The only thing I have against this is that it means the modal form will pop
up for every SQL access which means you can get quite a large number of
popup/disappearing dialogs. Which is A) fairly ugly and B) slows the UI down
a fair amount.

Anoyingly, because I, so far, havn't found a way of forcing the error to
occur, I havn't got an easy way of testing if this does alleviate the
problem.

I think that I will spend a bit of time writing a test prog to fire requests
to the DB one after another and see if I can get the error to occur myself
and then try you suggestion to see if it gets rid of it.

If you have any other thoughts in the mean time. Please give me a shout!

Thanks

Stephen
 
Joined
Jan 23, 2007
Messages
1
Reaction score
0
Windows.Forms.Timer problem

Hi,

I have a Windows.Forms.Timer problem that seems to be like Stephen problem. I'm developping a .NET application with Delphi 2005 that has timer (System.Windows.Forms.Timer) in the main form. This timer controls the start and stop of a flash player .NET component (AxShockwaveFlash), that I believe runs at another thread.
From time to time, the application raise an unhandle exception that says:

************** Exception Text **************
System.ArgumentNullException: Value cannot be null.
at System.Windows.Forms.Timer.set_Interval(Int32 value)
at Visualizador.TVisualizador.Finalizar()
at Visualizador.TVisualizador.TimerFinalizar_Tick(Object sender, EventArgs e)
at System.Windows.Forms.Timer.OnTick(EventArgs e)
at System.Windows.Forms.Timer.Callback(IntPtr hWnd, Int32 msg, IntPtr idEvent, IntPtr dwTime)
But, as Stephen, other problem I have is that this happens so infrequently that I haven't been able to recreate the problem myself in a debug environment. What I garantee is the procedure Visualizador.TVisualizador.Finalizar() sets the timer interval with a constant value, not null, what seems to me to be a memory invasion problem.

Cor or anyone else can help me?

Thanks in advance,
Sandra
 

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

Top