Problems of VB101SamplesBCL2 - UsingTheSerialPort. Pls help me :)

Hi everyone,

I am testing one of the example <UsingTheSerialPort> atVB101SamplesBCL2 But there are some error which i don't know how to edit.

I found a similar post that same with me having this error -VB101SamplesBCL2 Using The serial PORt..

From there, nobugz says need to add aInvoke method, but i don't understand how to add into it.

This is how i connect it & how the problem occurs:

  • I am using Visual Basic 2005 and i connect 2 computer with bluetooth, by using BlueSoleil. [Successfully connected]
  • Both computer have the program, and i run the program together and try to send some text to another computer by using the application -UsingTheSerialPort.
  • The sending part no problem, because i tested it by hyperterminal. [Hyperterminal can receive the text that i send from the other computer, by the application]
  • But when i send text from one application to another application. The receiving part came out with error.

It highlight one sentence >>textBox2.Text = SerialPort1.ReadLine() and says :

"InvalidOperationException was unhandled - Cross-thread operation not valid: Control 'textBox2' accessed from a thread other than the thread it was created on."

This is the program code ofUsingTheSerialPort:[The highlighted is the error]

===========================================================

Imports System

Imports System.Collections.Generic

Imports System.ComponentModel

Imports System.Data

Imports System.Drawing

Imports System.Text

Imports System.Windows.Forms

Imports System.IO.Ports

Class Form1

Inherits Form

PublicSubNew()

InitializeComponent()

EndSub'New

Private stopBits, parityAs Array' arrays to access the enumerations in System.IO.Ports

Private validStopBitsAsNew ArrayList()' ArrayLists hold the valid values for this machine

Private validParityAsNew ArrayList()

' Send the text in the text box to the serial port.

' The data should stay in the buffer until received by the event handler.

PrivateSub SendButton_Click(ByVal senderAsObject,ByVal eAs EventArgs)Handles Button1.Click

Try

' Write a line to the serial port

SerialPort1.WriteLine(textBox1.Text)

Catch exAs System.Exception

MessageBox.Show(ex.Message)

EndTry

EndSub'SendButton_Click

' Event handler for data reception

PrivateSub serialPort_DataReceived(ByVal senderAsObject,ByVal eAs SerialDataReceivedEventArgs)

' Read the buffer to the text box.

textBox2.Text = SerialPort1.ReadLine()

EndSub'serialPort_DataReceived

' Load the form and set up parameters from the default serial port.

' Open the port and prepare it for IO.

PrivateSub Form1_Load(ByVal senderAsObject,ByVal eAs EventArgs)HandlesMyBase.Load

Try

' Open the serial port

SerialPort1.Open()

' Set the event handler for data reception

AddHandler SerialPort1.DataReceived,AddressOf serialPort_DataReceived

Catch exAs System.Exception

MessageBox.Show(ex.Message)

EndTry

label11.Text = SerialPort1.PortName

label2.Text = SerialPort1.BaudRate.ToString()

label4.Text = SerialPort1.StopBits.ToString()

label6.Text = SerialPort1.Parity.ToString()

label8.Text = SerialPort1.DataBits.ToString()

label10.Text = SerialPort1.RtsEnable.ToString()

' Populate the stop bits box with all valid options.

' Note that the serial port must be open to accurately test the options.

comboBox1.SelectedText = SerialPort1.StopBits.ToString()

Dim currentStopBitSettingAs System.IO.Ports.StopBits = SerialPort1.StopBits

stopBits = [Enum].GetValues(GetType(System.IO.Ports.StopBits))

Dim sbtypeAs System.IO.Ports.StopBits

ForEach sbtypeIn stopBits

' test to make sure the machine supports the setting

Try

SerialPort1.StopBits = sbtype

validStopBits.Add(sbtype)

comboBox1.Items.Add(sbtype)

Catch' ignore this entry as invalid

EndTry

Next sbtype

SerialPort1.StopBits = currentStopBitSetting

' Populate the parity with valid options

comboBox2.SelectedText = SerialPort1.Parity.ToString()

Dim currentParitySettingAs System.IO.Ports.Parity = SerialPort1.Parity

parity = [Enum].GetValues(GetType(System.IO.Ports.Parity))

Dim ptypeAs System.IO.Ports.Parity

ForEach ptypeIn parity

' test to make sure the machine supports the setting

Try

SerialPort1.Parity = ptype

validParity.Add(ptype)

comboBox2.Items.Add(ptype)

Catch' ignore this entry

EndTry

Next ptype

EndSub'Form1_Load

' Respond to the form closing event by closing the SerialPort instance.

PrivateSub Form1_FormClosing(ByVal senderAsObject,ByVal eAs FormClosingEventArgs)

SerialPort1.Close()

EndSub'Form1_FormClosing

' The following methods demonstrate the ability to set parameters of the serial port

' through the SerialPort instance. The baud rate of the port will be set in response to

' the corresponding button clicks.

PrivateSub button3_Click(ByVal senderAsObject,ByVal eAs EventArgs)Handles Button3.Click

SerialPort1.BaudRate = 1200

label2.Text = SerialPort1.BaudRate.ToString()

EndSub'button3_Click

PrivateSub button4_Click(ByVal senderAsObject,ByVal eAs EventArgs)Handles Button4.Click

SerialPort1.BaudRate = 4800

label2.Text = SerialPort1.BaudRate.ToString()

EndSub'button4_Click

PrivateSub button5_Click(ByVal senderAsObject,ByVal eAs EventArgs)Handles Button5.Click

SerialPort1.BaudRate = 9600

label2.Text = SerialPort1.BaudRate.ToString()

EndSub'button5_Click

PrivateSub comboBox1_SelectedIndexChanged(ByVal senderAsObject,ByVal eAs EventArgs)Handles comboBox1.SelectedIndexChanged

SerialPort1.StopBits =CType(validStopBits(comboBox1.SelectedIndex), System.IO.Ports.StopBits)

label4.Text = SerialPort1.StopBits.ToString()

EndSub'comboBox1_SelectedIndexChanged

PrivateSub comboBox2_SelectedIndexChanged(ByVal senderAsObject,ByVal eAs EventArgs)Handles comboBox2.SelectedIndexChanged

SerialPort1.Parity =CType(validParity(comboBox2.SelectedIndex), System.IO.Ports.Parity)

label6.Text = SerialPort1.Parity.ToString()

EndSub'comboBox2_SelectedIndexChanged

EndClass'Form1

===================================================================================

Thank you.

[14057 byte] By [Wanxi] at [2008-1-10]
# 1

Nobugz is right. You need to use control.Invoke or control.BeginInvoke to marshal the call to the UI thread.

The problem is that the eventhandler for the DataReceived event runs on a thread pool thread, and no thread is allowed to call methods of a control generated on another thread - in this case the UI thread - except for the four thread safe methods - Invoke, BeginInvoke, EndInvoke and CreateGraphics. This illegal cross thread call is exactly what you do when you call:

textBox2.Text = SerialPort1.ReadLine()

It is a very long storry how to do it right, but you can find a complete description here: http://www.innovatic.dk/knowledg/SerialCOM/SerialCOM.htm , which also shows a small sample program.

I know that the description has grown to a size where it gives some beginners headache, but once you get through it you will have a pretty good understanding of how multithreading in .NET works.

CarstenKanstrup at 2007-10-3 > top of Msdn Tech,Visual Studio Express Editions,Visual Basic 2005 Express Edition...
# 2

Hi,

Thanks for your information. But i still duno how to write it..

I roughly understand that i need to use Invoke method, because there is a different level of thread, so the update of the textBox2 is not valid..

I know what i need to put, but i do not know what is variables and put it at where.

From book, i found all this, but i not sure i set it correctly anot.

  • Delegate Sub MyDelegate(ByVal sender As Object) // Set to 'sender'?
  • Dim theDelegate As MyDelegate
  • theDelegate = New MyDelegate(AddressOf __.__) // when i type in this "theDelegate" it says "declaration expected", but i though point 2 is the declaration, ist it? And what should i put for the Underline part?
  • theDelegate.Invoke (_) // Do i need this?
Wanxi at 2007-10-3 > top of Msdn Tech,Visual Studio Express Editions,Visual Basic 2005 Express Edition...
# 3

So my description gave you headache or you didn't bother to read it. You just want the final solution no matter if you understand what you are doing .

OK. It is really not so difficult. You want to transfer a string, so the first thing is to build a delegate, which is able to do that.

Public Delegate Sub StringSubPointer(ByVal Buffer As String)

Where have you got "ByVal sender As Object" from? It is certainly not from my decription. How do you expect to be able to program when you don't know what you are doing? "ByVal sender As Object" is a part of the signature for the EventHandler delegate. This is also described in details in the description. Maybe it is a good idea to read it anyway. Perhaps you don't bother to use maybe two days trying to understand it, but I have spend several weeks writing it!

Then you need to make a display routine on the UI thread:

Private Sub Display(ByVal Buffer As String)

TextBox2.Text = Buffer

End Sub

At last, replace your illegal statement with

Me.BeginInvoke(New StringSubPointer(AddressOf Display), SerialPort1.ReadLine())

PS. Why use:

AddHandler SerialPort1.DataReceived, AddressOf serialPort_DataReceived

instead of just:

Private Sub serialPort_DataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived

?

CarstenKanstrup at 2007-10-3 > top of Msdn Tech,Visual Studio Express Editions,Visual Basic 2005 Express Edition...
# 4

Hi,

I did read the website that you gave me Crying and try to compare it with other examples to make me understand more..

Seriously, i really really want the solutions, but i will go and understand it even i have the answer. This is not my final project that i need to do.. Is something just related to it.. So in the end i still will meet this kind of question again..

This is my old assignment >> Please Take a Look even there are people straight away give me the answer, but i will still find out the meaning of each line.

Your description is good, seriously. Because i understand the explaination that you wrote on it. But i totally do not have this kind of background of doing Invoke, Delegate.. So i will be confused by another question..

Example:

NameOfDelegate.Invoke ' or just NameOfDelegate.
ReturnValue = NameOfDelegate.Invoke

// I will wonder NameOfDelegate is stand for what? Is it something like my textBox2? Or "ReturnValue" is the textBox2?

Private Sub Display(ByVal Buffer As String)

TextBox2.Text = Buffer

// And why i need to put Buffer? textBox2.Text = Buffer?

Maybe you did write on the description. But i don't understand.

By the way, because of your words, i study it carefully again. And i found this!!! Smile

You wrote : "It is also necessary to specify any argument(s) to transfer and whether these should be transferred by value (ByVal) or by reference (ByRef). If we for example want to transfer a string by value to a subroutine, the whole definition becomes: Public Delegate Sub NameOfDelegate(ByVal Buffer As String)"

Although i don't know what is buffer.. Need to read carefully again? Hmmm..

And i don't think i need the two input right?

Ist the whole program will not add Delegate Function.. Just add Delegate Sub right?

For this:

Me.BeginInvoke(New StringSubPointer(AddressOf Display), SerialPort1.ReadLine())

why we need Me.BeginInvoke?

I thought is something like this.. (i got it from a book)

Public Class SimpleClass

Public Sub CallMe(ByVal content As String)

MsgBox(content)

End Sub

End Class

Delegate Sub MyDelegate(ByVal s As String)

Private Sub Form1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Click

' Get an instance of the destination class.

Dim destClass As New SimpleClass()

Dim theDelegate As MyDelegate

' Connect the delegate to its target, the CallMe method.

theDelegate = New MyDelegate(AddressOf destClass.CallMe)

' Make the call.

theDelegate.Invoke("Display this!")

End Sub

I saw the Delegate Sub MyDelegate(ByVal s As String)

For my program is ByVal sender As Object, so i though i need to change it to object.(I dun really know what is that for use)

But that time i never notice you had already wrote : "If we for example want to transfer a string by value to a subroutine, the whole definition becomes: Public Delegate Sub NameOfDelegate(ByVal Buffer As String)"

Sorry about this Crying

By the way, your method it works Smile

And i still got question to ask you. For my real project. Do you mind?

Thanks alot (*^^*)V

Regard

Wanxi

Wanxi at 2007-10-3 > top of Msdn Tech,Visual Studio Express Editions,Visual Basic 2005 Express Edition...
# 5

Now that your application works, you can relax a little. Let us take it step by step. My comments are in black, yours in color.

NameOfDelegate.Invoke ' or just NameOfDelegate.
ReturnValue = NameOfDelegate.Invoke

// I will wonder NameOfDelegate is stand for what? Is it something like my textBox2? Or "ReturnValue" is the textBox2?

NameOfDelegate just stands for the name you want to give the delegate. You can call it MyDelegate as you have done or StringSubPointer as I have done. It doesn't matter, but of course delegates, subroutines, functions, textboxes etc. must all have a unique name. You have a name too.

If the delegate points to a method, which is a function, this function will of course return a value. This is what the second line shows.

-

Private Sub Display(ByVal Buffer As String)

TextBox2.Text = Buffer

// And why i need to put Buffer? textBox2.Text = Buffer?

Buffer is the name of the input to the subroutine. When the subroutine is called, Buffer is loaded with the received text string.

Sorry, but it seems to me that you don't understand what a subroutine and a function is. This is so basic knowledge that you need to be absolutely sure about this before you can expect to understand the help files or a description like mine.

-

You wrote : "It is also necessary to specify any argument(s) to transfer and whether these should be transferred by value (ByVal) or by reference (ByRef). If we for example want to transfer a string by value to a subroutine, the whole definition becomes: Public Delegate Sub NameOfDelegate(ByVal Buffer As String)"

Although i don't know what is buffer.. Need to read carefully again? Hmmm..

And i don't think i need the two input right?

Ist the whole program will not add Delegate Function.. Just add Delegate Sub right?

What two input? The delegate has only one argument with the name Buffer. It is a string, which is transferred by value, that is, a copy of the original string is made and placed in the delegate. It is the same as when you declare a variable like "Dim MyText As String."

Me.BeginInvoke(New StringSubPointer(AddressOf Display), SerialPort1.ReadLine())

why we need Me.BeginInvoke?

I thought is something like this.. (i got it from a book)

Public Class SimpleClass

Public Sub CallMe(ByVal content As String)

MsgBox(content)

End Sub

End Class

Delegate Sub MyDelegate(ByVal s As String)

Private Sub Form1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Click

' Get an instance of the destination class.

Dim destClass As New SimpleClass()

Dim theDelegate As MyDelegate

' Connect the delegate to its target, the CallMe method.

theDelegate = New MyDelegate(AddressOf destClass.CallMe)

' Make the call.

theDelegate.Invoke("Display this!")

End Sub

I saw the Delegate Sub MyDelegate(ByVal s As String)

For my program is ByVal sender As Object, so i though i need to change it to object.(I dun really know what is that for use)

But that time i never notice you had already wrote : "If we for example want to transfer a string by value to a subroutine, the whole definition becomes: Public Delegate Sub NameOfDelegate(ByVal Buffer As String)"

Forget that book and read my description instead. The call "theDelegate.Invoke("Display this!")" just execute the method in the delegate "theDelegate" on the present thread and this is not what you need. You need to put a message on the message queue so you must use control.Invoke or control.BeginInvoke instead of delegate.Invoke. This message must include a delegate, which point to your display routine. There are to invoke methods - Invoke and BeginInvoke. BeginInvoke is the most appropriate in this case. Again this is a very long storry. If you want to understand what you are doing, I will recommend you to do the following:

1) Read about subroutines, functions, data types and other basic knowledge in your book. You seem to be confused about very basic things, and my description does not cover these - maybe it should?

2) Read my description step by step. If there is something you don't understand just ask me, but don't step to the next chapter before you fully understand the present one. I know that my description goes into details like the message queue, which your book probably don't cover, but it is my true belief that you will not be able to do reliable programming unless you have some basic knowledge about what goes on behind the scenes. For example, it is impossible to understand how Invoke and BeginInvoke works without knowledge about the message queue, and it is impossible to decide whether to use Invoke or BeginInvoke if you don't know how events are handled in SerialPort.

CarstenKanstrup at 2007-10-3 > top of Msdn Tech,Visual Studio Express Editions,Visual Basic 2005 Express Edition...
# 6

Ermm.. Okay.. I understand all the things now..

It is something like i set buffer as a string, and display it by using the previous loaded Buffer that already have my text inside and put it into the textBox2. Readline will take the answer from buffer which is under display

Public Delegate Sub StringSubPointer(ByVal Buffer As String)

Private Sub Display(ByVal Buffer As String)

textBox2.Text = Buffer

End Sub

Me.BeginInvoke(New StringSubPointer(AddressOf Display), SerialPort1.ReadLine())

Wanxi at 2007-10-3 > top of Msdn Tech,Visual Studio Express Editions,Visual Basic 2005 Express Edition...
# 7

No, not quite.

The delegate you define has storage area for the method(s) you want to call and three methods (Invoke and BeginInvoke/EndInvoke) to call string methods. Note that this is the definition in the same way as a Byte is defined as an 8-bit data storage. You have just defined a new data type, but not used it yet.

Then comes the BeginInvoke statement. Let us break it down into pieces.

"New StringSubPointer" makes an instance of the class StringSubPointer in the same way as Dim MyString As String makes a new instance of the string class.

(AddressOf Display) fills the method field of the delegate you have just made with the address of the method you want to call - in this case "Display". Note the difference between definition and declaration. The definition is just the blueprint (drawing) of the house. It shows how the house shall be build, but it is not build yet. The declaration gives you the house. "Public Delegate Sub ..." is the definition, "New StringSubPointer" is the declaration.

SerialPort1.ReadLine reads the input from the serial port and when the line is received, the result is saved together with your delegate (in a TME object, but forget that), which must have the same signature. This is why you need to define a delegate, which can call a string method. If you instead used ReadByte, it would be necessary with a delegate, which can call byte methods like this: "Public Delegate Sub ByteSubPointer(ByVal InputByte As Byte)". The delegate and the method to call must always have the same arguments (called the signature) - (ByVal Buffer As String) in this case. The name (Buffer) need not be the same, but the data type shall (String).

Now we have a delegate with the method field and the inputdata.

Me.BeginInvoke then makes a message and send this to the message queue of the UI thread. The message consist of the Window, which shall receive the message - in this case "Me", and the delegate plus input data. In practice, it is even more complicated with TME object etc., but for the moment this is a good approximation. You can find the full storry in my description when you get sufficient knowledge to understand this (not recommended for beginners).

When the message pump comes to your message (there may be more messages before that), it sends it to the right window. The window then executes the method in the delegate with the supplied data - your text string. Because the message pump runs on the UI thread, Display will do the same so it can access methods of controls generated on the UI thread without problems - in this case the method Text of the control TextBox2. In this way, the received string, which was stored together with the delegate, is transferred to the textBox.

EDIT

I have just improved the description of Delegates. Please read it again and let me know if there is still something you don't understand. Your input is very helpfull because it makes it possible for me to see what I need to improve.

CarstenKanstrup at 2007-10-3 > top of Msdn Tech,Visual Studio Express Editions,Visual Basic 2005 Express Edition...
# 8

Thanks for the information.

I discuss with my friend what you have wrote.. i not very understand the whole things.. but i roughly understand part of it.

It is somethings like i understand the program although not good in program it.

Thanks

Wanxi at 2007-10-3 > top of Msdn Tech,Visual Studio Express Editions,Visual Basic 2005 Express Edition...
# 9

mauchaha here the 3 listed u need to do:

1st- must always listen to me

2nd- dun be lazy

3rd- must always love me

FufuBigBro-.-'(dardar) at 2007-10-3 > top of Msdn Tech,Visual Studio Express Editions,Visual Basic 2005 Express Edition...
# 10

=.='' +*wordless*+

Later they report abuse you =D

you should in VB6.. here VB5..

Take Care.

p/s: Muacks

Wanxi at 2007-10-3 > top of Msdn Tech,Visual Studio Express Editions,Visual Basic 2005 Express Edition...