Communication between threads?

G

Guest

Hi,

I'm having trouble letting my background thread tell my main thread what to
do.

I'm trying to tell my main thread to open a form, but when my background
thread ends, the form that I thought my main thread had opened disappears.
Obviously there's something that I don't understand here.

The background thread is run in another class on another form. How do I,
from there, get my main thread to do something? This is my scenario:

StartForm (my main form)
CommForm (handles my serial communication)
FormA (I want StartForm to open this form)

When data is available on my serialport, the sub below is run as a separate
thread in my CommForm. When all data is read, the thread ends.

Private Shared Sub ReceiveEvent(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles SerialPort.DataAvailable
'here I want to tell my main thread (StartForm) to open FormA

'this is how I try to do that
Dim del As Start.MyDelegate
del = New Start.MyDelegate(AddressOf Start.OpenFormA)
del.Invoke()

End Sub

How should I solve this? Is an event more appropriate to use instead of a
delegate?
Iv'e been searching a bit on delegates and events, but I can't figure out
how to get them working between threads. All examples I've found are within
the same thread.

Many thanks in advance for any help on this.
I've been trying for days now...
/Daniel
 
B

Brian Gideon

Daniel,

The way your code works right now is that the OpenFormA method is
executed on the same thread as the SerialData.DataAvailable event
handler. To run code on the main GUI thread you must use
Control.Invoke. Modify your code so that it looks like the following.


Private Shared Sub ReceiveEvent(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles SerialPort.DataAvailable
'here I want to tell my main thread (StartForm) to open FormA

'this is how I try to do that
Dim del As Start.MyDelegate
del = New Start.MyDelegate(AddressOf Start.OpenFormA)

' This executes the delegate on the current thread.
'del.Invoke()

' This executes the delegate on the main GUI thread.
Me.Invoke(del)

End Sub

Brian
 
G

Guest

Hi Brian,

Thank you for the reply! Finally a precise answer to my questions. Much
appreciated.
But unfortunately I can't get your suggestion to work in this case, since
the sub is shared.

'Me' is valid only within an instance method.

I think I need to have the sub shared, since it handles an event of a
control that is shared. I think that control (my .NET framework component
that handles serial communication) needs to be shared since I want to be able
to send messages from all parts of my program.

Or could one figure out another solution? Maybe do all transmits from a
single thread, and let the other parts of my code send messages to that
thread?

Any suggestions or comments?

Best regards
/Daniel Strigard
 
G

Guest

Hi again.

I have been able to make the sub and component "unshared".

This way the Me.Invoke(del) gets accepted.
But I still get the same crash as soon as the thread ends. It doesn't seem
like it is run on another thread...?

Could it be that Me points at my class/form Communication, in which the
RecieveEvent sub is placed, and my delegate is placed in my class/form Start?
But my class Start is opening the Communication form in its load event...

Best regards
/Daniel
 
B

Brian Gideon

Daniel,

Since the Communication form is opened in the load event of the Start
form it too should be running on the main GUI thread. That's good.
That's how it should be. Since they are both running on the main GUI
thread it doesn't matter whose Invoke method you use. They will both
do exactly the same thing. That is they will post a message to the GUI
thread's message pump instructing it to run the specified delegate and
wait for it to complete.

I'm not sure what's going wrong. Can you post the code inside the
OpenFormA method?

Brian
 
G

Guest

This is the code in my main class:

Public Class Start

Public Delegate Sub MyDelegate()

...

Public Shared Sub openFormA()
frmA = New FormA
frmA.Show()
End Sub

...

End Class

This is the code in my Communication class:

Public Class Communication

...

Private Sub ReceiveEvent(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles SerialPort.DataAvailable

...

Dim del As Start.MyDelegate
del = New Start.MyDelegate(AddressOf Start.openFormA)
Dim x As Object = Me.Handle 'just to get the handle created. not used.
Me.Invoke(del)

End Sub

...

End Class


I cant figure out this. I don't know if the code above will make it any
easyer for you to find the problem either. I'll be happy to post more code if
you think that will help.

Lots of thanks in advance.
/Daniel
 
B

Brian Gideon

Daniel,

The code looks fine to me. You're saying that FormA closes prematurely
right? When does it close? Do you have code somewhere that explicitly
closes FormA? Did you write the SerialPort class or are you using the
one built into v2 of the .NET Framework?

Brian
 
G

Guest

FormA does not close. It hangs as soon as it is started. The "background" of
FormA is displayed, but all textboxed, labels and controls in the form are
transparent, so that you see the underlying window through my FormA window.
(The form doesn't respond to any mouse or keyboard commands.)

I dont have any code that closes FormA, except the code in my exit button of
FormA.

The serialport class I use is shareware from www.sax.net. From them I have
been told that the recieve event is run on its own thread.

Quote from SAX.NET:
"The results you are getting are do to threading. By loading the form in the
event, the form has to run in that events thread.
[..]
To solve this you can work it in to your code so that the form get created
on its own thread."

Thank you for trying to help me with this.
/Daniel
 
B

Brian Gideon

Can you post as much code as possible? I must be missing something
obvious. Are the other forms responsive after you've attempted to
display FormA? If they are then my best guess is that FormA isn't
running on the GUI thread. If they aren't then there's an infinite
loop or blocking methoding during the showing of FormA.

The quote from sax.net is a little peculiar. You can't just create
another thread to display a form. It's not that simple.
 
G

Guest

Hi Brian,

It’s just FormA that crashes. My other forms are still working, so I have
also come to the conclusion that FormA must be run on some other thread.

I have several thousands of lines of code, and I won’t /can’t post all of
it, but I’ll post some that I think might be helpful in understanding what
could be causing this.

You’ve seen the general structure of my code before:

Start – my main class/form
Communication – handles communication over the comport
FormA – displays the data that I receive on the comport

FormA crashes when it is finished with all initation-code I have added.

Do you have some tips on how to figure this out in debug mode? Can one see
the status of all threads? I can’t find any errormessage. FormA just freezes
and lets all other classes continue.

I previously used another third-party component for the communication from
www.hardandsoftware.com. I had to switch to www.sax.net when the
communication speed of the other component wasn’t enough anymore. I haven’t
made any changes in FormA since the switch. Everything worked ok before the
switch.

/Daniel

My code:
-----------

Public Class Start

Public Delegate Sub MyDelegate()

#Region " Windows Form Designer generated code "
'...
#End Region


Private Sub StartProcedure(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
'...
Dim frmCommunication As New Communication
frmCommunication.ShowInTaskbar = False
frmCommunication.Visible = False
frmCommunication.Show()
'...
End Sub

Public Shared Sub showFormA()
frmA = New FormA
frmA.Show()
End Sub

End Class


Public Class Communication

#Region " Windows Form Designer generated code "

'...

'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.

Private components As System.ComponentModel.IContainer
Public WithEvents SP As Sax.Communications.SerialConnection
Private Sub InitializeComponent()
'...
End Sub

Public Shared Sub Main()
Application.Run(New Communication)
End Sub

#End Region

Private Sub Communication_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
'Initiates the serial port, etc.
'...
End Sub

Private Sub ReceiveEvent(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles SP.DataAvailable
'Reads the incoming bytes and places them in a buffer
'...
'If a certain combination of bytes is detected, the incoming
'message is completed and FormA should be opened
PrepareFormA()
End Sub

Private Sub PrepareFormA()
'Does some control on the received data and sets a few
'global variables in my MainModule
'...
Dim del As Start.MyDelegate
del = New Start.MyDelegate(AddressOf Start.showFormA)
Dim x As Object = Me.Handle 'just to get the handle created. not
used.
Me.Invoke(del)
End Sub

End Class


Public Class FormA

#Region " Windows Form Designer generated code "
'...
#End Region

Private Sub FormA_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
'This sub contains some hundred lines of initiations and checks.
'It also does calls to other subs in FormA which draws graphs based
'on the received data (thousands of lines of codes in all).
'All this is done without the program crashing
'...
'But the form crashes when everything that is initiated on startup
'of FormA is completed
End Sub 'On this line it crashes !!!

End Class
 
G

Guest

Hi again,

I just realized what mabe could be the cause of this!

I'm declaring frmA in my MainModule, as a part of my solution to prevent
several FormA to be opened at the same time. With this solution I can also
reach the controls of FormA from other forms.

Module MainModule
'...
Public frmA as FormA
'...
End Module

Maybe I should try to figure out some other way to achieve this. I'll give
it a go and I'll let you know how it goes and if it solved the thread problem.

Best regards and lots of thanks
/Daniel
 
G

Guest

Buggerit!

That wasn't it. It still doesn't work.
I now declare FormA inside the sub showFormA, but still no success...

What else could it be then? Was my code to any help?

Best regards and huge amount of thanks in advance to any help on this.

/Daniel - starting to get frustrated
 
G

Guest

Hi,

No, I haven't got it working yet.

I'm making a new application as a sort of mirror of my real application, to
try to see what it is that's causing the crash. I first just made the few
forms (start, communication and formA) with the same "interfaces" as my real
program. But here it worked without crashing.

So I'm using "cut and paste" to insert my real forms one by one into this
mirror-application. I still havent got things to crash, but I have a few
forms left before the mirror-application is identical to my real application.
And if I haven't got things to crash then, I just let the mirror-application
become my real application.

But it would be nice to know what causes this. I'd hate to find out that it
is a bug in VS.NET or something...

I'll post the result.
/Daniel
 
G

Guest

*Sigh*

I have sort of located the problem now. But I don't know why it is a problem.

The crash has something to do with my database. I cannot understand why the
database should have anything to do with the SAX component and the creating
of a new thread...?!?
I'm using MSDE to store my measurements.

When I started making a copy of my program to see what caused the crash,
everything worked fine until I got to the sub that creates my database.
In my start form I check if the database file exists, and if it doesn't I
try to create the database and my tables.

When I try to implement the SAX communication in my real program it crasches.
When I try to implement the SAX communication in a new program, without a
database, it works, until I try to create a database in that program. Then it
also crashes.

I get errors telling me that the database already exists, which it of course
does, since I just create my program in another folder on the same computer.
The database file isn't there, but the database itself exists on the server...
And If I try to do communication after that error message, the form crasches.
So ok, I'm maybe not handling the database in a correct manner, but what
does the threading care about that?

Any suggestions on how to try to sort this out? I guess I have to do some
changes in the way I create my database...?
And I should also try to do this on another computer, without any previous
database installed.

rgds
/Daniel
 
G

Guest

Ok,
now I have pin-pointed this down a bit further.

It isn't my database. It is in my startup procedure. The one that handles
MyBase.Load.

If I, for instance, display a simple MsgBox("") from the startup procedure,
the communication later crashes. If I do some simple changes in that
procedure, so that I don't display any messages, everything works fine. My
splashscreen is for instance one of the things that makes the crash occur.

(The reason I was led to believe it was my database was in fact due to that
it produced error messages, which triggered this strange crash.)

I can still show messages outside the startup procedures, but as soon as I
display something from the startup, it later crashes.

This gives me the creeps...
I have downloaded the latest service pack for the .NET Framework, to make
sure it isn't something like that. No change though.

I'm gonna see if I can produce this error in the example program that
SAX.NET offers, and send it to them. I'll post my results.

If you happen to figure this out, please let me know.

Best regards
/Daniel
 
G

Guest

I'm gonna see if I can produce this error in the example program that
SAX.NET offers, and send it to them. I'll post my results.

Hmm, well it worked in that program.
Then it probably isn't a fault in the component.
At least not such an obvious one...

rgds
/Daniel
 
B

Brian Gideon

Daniel,

That's it. The message box is modal and will thus block the completion
of the startup procedure. Since the startup procedure was marshaled
onto the GUI thread by calling Control.Invoke the thread that initiated
it all is blocked as well. I presume this thread is controlled by the
serial communications component you are using. If that thread gets
blocked long enough then it's possible that the component will have
problems reading the serial port. Instead of using Control.Invoke try
Control.BeginInvoke. The Invoke method will block until the delegate
has finished executing. The BeginInvoke method is non-blocking. I'm
glad you figured it out.

Brian
 
G

Guest

Ahh, thats it!
It finally works!
Me.BeginInvoke did the trick!
:)

Thank you so immensely much Brian for helping me with this!

I know that a msgbox causes the system to hold until it is closed, but I
didn't consider that this occurs at the same time as the initiation and
control of my communication is done.

Loads of thanks for your help!
Best regards

/Daniel Strigard
 

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