Click here to Skip to main content
15,883,901 members
Please Sign up or sign in to vote.
1.00/5 (2 votes)
See more:
I seem to be having an issue where my program forces an error.
http://puu.sh/mUq0l/600ed167c1.png

I am not certain to how to fix it as I only have experience with the standard multi threading in C++.

[code]
C#
void setText(int data)
	{
		pingOutbox->Text = Convert::ToString(data);
		pingOutbox->Refresh();
		cout << data << endl;
	}

	void mainPingThing()
	{
		int speed;
		setText(runPing());
		Sleep(2500);
	}

	private: System::Void backgroundWorker1_DoWork_1(System::Object^  sender, System::ComponentModel::DoWorkEventArgs^  e) 
	{
		while (true) { mainPingThing(); }
	}

[/code]
This is the basic layout of what I have done. I do not know what corrections have to be made. Any help would be much appreciated as Microsoft's craziness knows no limits.
Posted
Comments
Philippe Mori 3-Feb-16 13:49pm    
You should write the error message directly in the question. We don't like to open external links if we don't have to...
Sergey Alexandrovich Kryukov 6-Feb-16 11:44am    
Excuse me, why posting a png somewhere and make use loading that page, if this is a pure textual information we could read as a part of your question? I understand that it would require you to type those 5 lines, but this is still better than the whole post with a png...

—SA

It's way to easy to blame Microsoft in all sins, including your own. At the same time, the exception message is crystal clear and explains what you did wrong: tried to works with some object in some non-UI thread, but this object is part of UI and was created and added to UI in a UI thread. This is invalid operation. All such operations should be done in a UI thread. Can you work with them in a non-UI thread? Of course you can.

You cannot call anything related to UI from non-UI thread. Instead, you need to use the method Invoke or BeginInvoke of System.Windows.Threading.Dispatcher (for both Forms or WPF) or System.Windows.Forms.Control (Forms only).

You will find detailed explanation of how it works and code samples in my past answers:
Control.Invoke() vs. Control.BeginInvoke(),
Problem with Treeview Scanner And MD5.

See also more references on threading:
.NET event on main thread,
How to get a keydown event to operate on a different thread in vb.net,
Control events not firing after enable disable + multithreading.

[EDIT]

The mechanism I described above is universal in both System.Windows.Forms and WPF UI and can be used in any thread. But I forgot to mention the mechanism specific to System.ComponentModel.BackgroundWorker. Philippe Mori reminded me about it in his comment to this solution:
BackgroundWorker.ReportProgress Method (System.ComponentModel)[^],
BackgroundWorker.ProgressChanged Event (System.ComponentModel)[^].

Basically, you can directly call System.ComponentModel.BackgroundWorker.ReportProgress in any thread. This call is abstracted from any kind of UI. The delegation to the UI thread happens if you handle the event System.ComponentModel.BackgroundWorker.ProgressChanged; and this is done in a UI-specific way; naturally, in the event handler, you can address UI elements directly.

—SA
 
Share this answer
 
v3
Comments
Philippe Mori 3-Feb-16 13:17pm    
Or since he is already using a background worker, use ReportProgress member with an extra argument. And Refresh would also be useless.
Sergey Alexandrovich Kryukov 3-Feb-16 15:15pm    
Ah, good point, thank you very much. I just updated the answer and credited your comment.
—SA
As you are already using a background worker, the easiest way to fix the problem would be to use it properly!

C++
private: System::Void backgroundWorker1_DoWork_1(
    System::Object^  sender, System::ComponentModel::DoWorkEventArgs^  e) 
{
    while (true)
    {
        int data = runPing();
        backgroundWorker1->ReportProgress(0, Convert::ToString(data));
        Sleep(2500);
    }
}


And connect an handler for ProgressChanged event and then move some of your code to it:
C++
void backgroundWorker1_ProgressChanged(object sender,
    ProgressChangedEventArgs e)
{
    // I forgotten to change a . to ->. In C++/cli, you use the arrows for members 
    // while in C#, it is the dot.
    auto textData = e->UserData.ToString()UserState->ToString();
    pingOutbox->Text = textData;

    // Not necessary. The UI will update itself if the main thread is responding.
    // pingOutbox->Refresh();    

    cout << textData << endl;
}


It would probably also be possible to leave cout in the background loop but it might mess up the outputed text if multiple threads are writing to it.

Update
By the way, reading official documentation would help: How to: Implement a Form That Uses a Background Operation[^]

Also, it is very easy to search help using Google once you have the error message and you won't have to wait for someone to answer!
 
Share this answer
 
v8
Comments
Sergey Alexandrovich Kryukov 3-Feb-16 15:25pm    
5ed.
—SA
Member 10657083 4-Feb-16 6:18am    
What does UserData refer to?
Philippe Mori 4-Feb-16 8:38am    
It is the data specified in the second argument of ReportProgress. Thus you can pass any object you want in addition to a percentage and the event will be generated in the UI thread (assuming that the worker was started from that thread).
Member 10657083 5-Feb-16 9:46am    
error C2039: 'UserData': is not a member of 'System::ComponentModel::ProgressChangedEventArgs'
Philippe Mori 5-Feb-16 10:38am    
In fact, it is UserState. Usually, IntelliSense would told me such thing but here on Code Project, there are no such thing.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900