Mikey0727

I am writing a custom Ref Class with C++ 2005 to control a measurement instrument via Serial Port.

My class inherets System::IO::Ports::SerialPort.

I also define events inside my class to generate action on specific RS232 messaages.

My problem is as follows: Inside the datareceived event handler, I raise events based on messages reveived. However, when my class is instantiated on a windows form, if the event handler defined on the form changes properties of a textbox control, the sytem generates "System.InvalidOperation Exception" and goes on to describe that a different thread tried to access the control.

I can solve the problem by setting the CheckForIllegalCrossThreadCalls property of the Windows Form to false. I do not like this solution, because it seems unsafe, and if someone other than myself was using my class, it might produce unexpected results in their program.

I can also solve the problem using the invoke method of the form / control with delegates to produce a thread safe call as described here:

http://msdn.microsoft.com/msdnmag/issues/04/02/TimersinNET/default.aspx

I do not like this solution, as it puts too much burden on the Application programmer using my class, defeating the point of object encapsulation. If my class is in a published Assembly, I will have to explain to the programmer using it to define delegate / use the form->invoke method.

My class also has a System::Timers::Timer ,and the Elapsed event had the same issue, until I set the SynchronizingObject property of the timer to the Windows Form which instantiated my class.

I like this solution, because it doesnt require anycode on the containing form, but the serialport class does not have a SynchronizingObject property, so I fail to see how to produce the same effect.

1 - Can I have my class inheret something so that it has a SychronizingObject Property

2 - Can I use any object System.Threading to invoke the event on the thread of the containing form This is acceptable to me as long as the code required can be part of my class, and not the form on which the class is instantiated.

3 - Any other possible solution I am missing

Thank You



Re: .NET Base Class Library Cross Thread Problem with SerialPort DataReceived Event

pat capozzi

The fix on this is relatively simple I believe

first set up a delegate

// This delegate enables asynchronous calls for setting

// the text property on a TextBox control.

delegate void SetTextCallback(string text);

inside your DataRecieved event put this code

string textIn = serialPort1.ReadExisting();

SetText(textIn);

Then build a method called SetText as follows

private void SetText(string text)

{

// InvokeRequired required compares the thread ID of the

// calling thread to the thread ID of the creating thread.

// If these threads are different, it returns true.

if (this.txtDataIn.InvokeRequired)

{

SetTextCallback d = new SetTextCallback(SetText);

this.Invoke(d, new object[] { text });

}

else

{

this.txtDataIn.Text = this.txtDataIn.Text + text;

}

}

This code sends the data from the serial port to a textbox but you can sub whatever control that you want to recieve the data

Hope this helps,

PatC





Re: .NET Base Class Library Cross Thread Problem with SerialPort DataReceived Event

Peter Ritchie

If you're using .NET 2.0, you could use a SynchronizationContext object to do what you want. For example, if you have a class that raises and event that you want to ensure is handled on the same thread used to create your object, you could do the following (pardon the C#, I'm not at my development computer; if it's not clear, let me know and I can translate to C++/CLI):

    public class MyClass

    {

        private SynchronizationContext synchronizationContext = WindowsFormsSynchronizationContext.Current;

 

        public void SomethingToCauseEvent ( )

        {

            OnMyEvent(EventArgs.Empty);

        }

 

        public event EventHandler MyEvent;

        protected void OnMyEvent(EventArgs e)

        {

            if (MyEvent != null)

            {

                synchronizationContext.Send(new SendOrPostCallback(OnMyEventInternal), e);

            }

        }

        private void OnMyEventInternal ( object state )

        {

            MyEvent(this, state as EventArgs);

        }

    }

In this example, the call to Send is synchronous.  If you needed something asynchronous (e.g. something that didn't depend on the client code being performant), you could use Post instead.






Re: .NET Base Class Library Cross Thread Problem with SerialPort DataReceived Event

Mikey0727

This looks like what I need. I am not that good with C#, but I am farmiliar enough that I think I can make it work. If you have time Id still like to see it properly in C++, since I am only just starting to use delegates / events.

I do have a question though -

private SynchronizationContext synchronizationContext = WindowsFormsSynchronizationContext.Current;

I get the point of this, as it syncs to the current form thread... What will happen here if someone used my class in a console app Should I be somehow checking the current context to make sure there is a form, and if not, raising the event differently

Great Post -

Thanks





Re: .NET Base Class Library Cross Thread Problem with SerialPort DataReceived Event

Peter Ritchie

Mikey0727 wrote:

This looks like what I need. I am not that good with C#, but I am farmiliar enough that I think I can make it work. If you have time Id still like to see it properly in C++, since I am only just starting to use delegates / events.

I do have a question though -

private SynchronizationContext synchronizationContext = WindowsFormsSynchronizationContext.Current;

I get the point of this, as it syncs to the current form thread... What will happen here if someone used my class in a console app Should I be somehow checking the current context to make sure there is a form, and if not, raising the event differently

WindowsFormsSynchronizationContext.Current will be null by default in a console application. Thread affinity is required only by Windows controls, there's really no need to ensure code runs on a specific thread if you're not dealing with Forms. So you can just test if the Current property is null before attempting to use the Send or Post methods--if it is null, just raise the event normally. Here's an uncompiled sample in C++/CLI:

public ref class MyClass
{
public: // events
event EventHandler^ MyEvent;

private: // data
SynchronizationContext^ synchronizationContext;

public: // c'tor
MyClass()
{
this->synchronizationContext = SynchronizationContext::Current;
}

protected: // instance methods
void OnMyEvent(EventArgs^ e)
{
if ((this->MyEvent != nullptr))
{
if ((this->synchronizationContext != nullptr))
{
this->synchronizationContext->Send((IDelegateCreateExpression NYI), e);
}
else
{
this->MyEvent->Invoke(this, e);
}
}
}
private: // implementation
void OnMyEventInternal(Object^ state)
{
this->MyEvent->Invoke(this, dynamic_cast<EventArgs^>(state));
}

public: // instance methods
void SomethingToCauseEvent()
{
this->OnMyEvent(EventArgs::Empty);
}
};






Re: .NET Base Class Library Cross Thread Problem with SerialPort DataReceived Event

Mikey0727

Hi

I am sorry, I am having trouble with this:

this->synchronizationContext->Send((IDelegateCreateExpression NYI), e);

as the argument for send, I had:

this->synchronizationContext->Send(gcnew System::Threading::SendOrPostCallback(&MyClass::OnMyEventInternal), e);

That didnt work, can you elaborate on: Send((IDelegateCreateExpression NYI)

thanks -





Re: .NET Base Class Library Cross Thread Problem with SerialPort DataReceived Event

Peter Ritchie

Sorry, I went the lazy route and ended up mixing in some C# principles (I've confirmed why I use C# for managed applications...). Here's some compiled and tested C++ code:

public ref class MyClass

{

public: // events

event EventHandler^ MyEvent;

private: // data

SynchronizationContext^ synchronizationContext;

public: // c'tor

MyClass()

{

this->synchronizationContext = SynchronizationContext::Current;

}

protected: // instance methods

void OnMyEvent(EventArgs^ e)

{

if ((this->synchronizationContext != nullptr))

{

SendOrPostCallback^ c = gcnew SendOrPostCallback(this, &MyClass::OnMyEventInternal);

synchronizationContext->Send(c, e);

}

else

{

this->MyEvent(this, e);

}

}

private: // implementation

void OnMyEventInternal(System::Object^ state)

{

this->MyEvent(this, dynamic_cast<EventArgs^>(state));

}

public: // instance methods

void SomethingToCauseEvent()

{

this->OnMyEvent(EventArgs::Empty);

}

};


And an example of its use:

private:

void myClass_MyEvent(Object^ sender, EventArgs^ e)

{

System::Diagnostics::Debug::WriteLine("test");

}

public:

SomeMethod(void)

{

MyClass^ myClass = gcnew MyClass() ;

myClass->MyEvent += gcnew EventHandler(this, &Form1::myClass_MyEvent);

myClass->SomethingToCauseEvent();

}






Re: .NET Base Class Library Cross Thread Problem with SerialPort DataReceived Event

Mikey0727

Thanks -

Last Question (I promise LOL) - in the C# example, to set the context, you use

Windows::Forms::WindowsFormsSynchronizationContext::Current

In the C++ example, you use:

System::Threading::SynchronizationContext::Current

Any impact from this difference