Click here to Skip to main content
15,905,679 members
Please Sign up or sign in to vote.
3.00/5 (1 vote)
See more:
Hi ,I didnt use any thread in this code but I have this
"Cross-thread operation not valid: Control 'txtStatus' accessed from a thread other than the thread it was created on."
when I click the "Connect" button on the form,please help me,thank you
C#
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;

namespace ExampleNetworkAsyncTCPClient
{
    public partial class Form1 : Form
    {
        private Socket client;
        private byte[] data = new byte[1024];
        private int size = 1024;
        void Connected(IAsyncResult iar)
        {
            client = (Socket)iar.AsyncState;
            try
            {
                client.EndConnect(iar);
                txtStatus.Text = "Connected to " + client.RemoteEndPoint.ToString();
            }
            catch (SocketException)
            {
                txtStatus.Text = "Error connecting";
            }
        }
        void ReceiveData(IAsyncResult iar)
        {
            Socket remote = (Socket)iar.AsyncState;
            int rcv = remote.EndReceive(iar);
            string stringData = Encoding.ASCII.GetString(data, 0, rcv);
            lstList.Items.Add(stringData);
        }
        void SendData(IAsyncResult iar)
        {
            Socket remote = (Socket)iar.AsyncState;
            int sent = remote.EndSend(iar);
            remote.BeginReceive(data, 0, size, SocketFlags.None, new AsyncCallback(ReceiveData), remote);
        }
        public Form1()
        {
            InitializeComponent();
        }

        private void button3_Click(object sender, EventArgs e)
        {
            client.Close();
            txtStatus.Text = "Disconnected";
        }

        private void cmdConnect_Click(object sender, EventArgs e)
        {
            txtStatus.Text = "Connecting...";
            Socket newSock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9050);
            newSock.BeginConnect(ipep, new AsyncCallback(Connected), newSock);

        }

        private void cmdSend_Click(object sender, EventArgs e)
        {
            byte[] message = Encoding.ASCII.GetBytes(txtMessage.Text);
            txtMessage.Clear();
            client.BeginSend(message, 0, message.Length, SocketFlags.None, new AsyncCallback(SendData), client);
            

        }
    }
}
Posted
Updated 22-Jan-12 5:42am

There are two bunches of problems here. First, why using any asynchronous API? Let me advise you: don't even play with the idea of dealing with network and not using threading. You need to use any blocking operations including network in a separate thread. Now, I think that asynchronous API was offered when threading was not a common place. It uses thread behind the hood anyway, but without your control. If you are using your own thread, it's way more straightforward, easy to implement and maintain.

The second problem is that you tried to do something with the UI from some non-UI thread. How to solve this problem if you really need to work with UI somehow? Here is how:

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:
How to get a keydown event to operate on a different thread in vb.net[^],
Control events not firing after enable disable + multithreading[^].

[EDIT]

I provided a full code sample showing the threading and invocation techniques I tried to mention above. Please see my other answer.

Also, a bonus piece of advice of networking: I would recommend to use not the lower-level Socket class, but more convenient classes System.Net.Sockets.TcpListener and System.Net.Sockets.TcpClient, please see:
http://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.aspx[^],
http://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient.aspx[^].

Please also see my past answer on this topic for some useful ideas on using threads with networking: Multple clients from same port Number[^].

—SA
 
Share this answer
 
v2
Comments
Amir Mahfoozi 22-Jan-12 14:02pm    
+5
Sergey Alexandrovich Kryukov 22-Jan-12 14:12pm    
Thank you, Amir.
--SA
maysam_p82 22-Jan-12 14:17pm    
Hi thanks for your time. First,Im in education,and im reading an ebook,this is a book example and is not my idea.But i cant understand your control.begininvokk()method.what do you mean of it. What control I should to use?and what's place of code that i should to use that?can you show me where I must use that and show me an example?
Sergey Alexandrovich Kryukov 22-Jan-12 15:13pm    
Well, if you in education, this is excellent. I'm just helping with your education. Not all book examples are good. If you can make some asynchronous API work, this can be useful, too, to feel the techniques, but main purpose would be to understand how awkward asynchronous are when compared to explicit threads.

Now, which control? It should be a call like anyConrol.BeginInvoke(/*..*/), where anyControl is really any control which is currently included in the UI which is run by UI thread (the one you started as Application.Run, see your entry point method like "Main"). If this is a code in your form class, it can be this.BeginInvoke, as "this" is a Form in this case, and the Form is Control.

I though it was clear from my other posts I referenced.
--SA

maysam_p82 22-Jan-12 14:27pm    
Also,what did you mean "don't even play with the idea of dealing with network"?:(

This is a code sample created on OP's request:



First, let's make a thread wrapper. Later on, you can add some random algorithm, input numeric ranges in it. For now, it's important to make a skeleton: generate some number and fire an event with new value; also, it can be started/paused/resumed/aborted. The thread itself is hidden for a user. Note the use of event handle — this is the way to use long-living thread and keep it in a wait state without wasting any CPU time. On a wait of the handle, a thread is switched out and never scheduled back to execution until waken up by the call to ManualResetEvent.Set, timeout or Abort called in any other thread.

The thread is abstracted from the UI. It's responsibility of the UI thread to add an event handle to the invocation list of event NumberGenerated.

The thread wrapper is very important to avoid parametrized thread start and for better encapsulation in general.

Please see my past answers for more detail:
How to pass ref parameter to the thread[^],
change paramters of thread (producer) after it started[^].

C#
namespace RandomTest {
    using System.Threading;
    
    internal class ThreadWrapper {

        internal class NumberGeneratedEventArgs : System.EventArgs {
            internal NumberGeneratedEventArgs(int number) { this.fNumber = number; }
            internal int Number { get { return fNumber; } }
            int fNumber;
        } //class NumberGeneratedEventArgs
        
        internal ThreadWrapper(int sleepTime) {
            this.thread = new Thread(this.Body);
            this.sleepTime = sleepTime;
        } //ThreadWrapper

        internal void Start() { this.thread.Start(); }
        internal void Abort() { this.thread.Abort(); }
        internal void Pause() { this.waitHandle.Reset(); }
        internal void Resume() { this.waitHandle.Set(); }

        internal event System.EventHandler<NumberGeneratedEventArgs>
            NumberGenerated;

        void Body() {
            int value = 0;
            while (true) {
                waitHandle.WaitOne();
                if (NumberGenerated != null)
                    NumberGenerated.Invoke(this, new NumberGeneratedEventArgs(value));
                value++;
                Thread.Sleep(sleepTime);
            } //loop
        } //Body
        
        Thread thread;
        int sleepTime;
        ManualResetEvent waitHandle = new ManualResetEvent(false); //non-signalled
    
    } //ThreadWrapper

} //namespace RandomTest


Now, let's use it in the form. I intentionally made it all in one file and avoided use of Designer, so all code would be in one place:

C#
namespace RandomTest {
    using System.Windows.Forms;

    public partial class FormMain : Form {

        const string ButtonStop = "&Stop";
        const string ButtonStart = "&Start";
        const int SleepTime = 500;

        delegate void NumberAction(Label label, int value);
        //in .NET version above 2.0 this line is not needed
        //use System.Action<Label, int> instead

        public FormMain() {
            Padding = new Padding(10);
            Button button = new Button();
            button.Text = ButtonStart;
            button.Dock = DockStyle.Bottom;
            Controls.Add(button);
            Label output = new Label();
            output.Dock = DockStyle.Fill;
            output.AutoSize = false;
            Controls.Add(output);
            button.Click += delegate(object sender, System.EventArgs eventArgs) {
                if (running) {
                    button.Text = ButtonStart;
                    wrapper.Pause();
                } else {
                    button.Text = ButtonStop;
                    wrapper.Resume();
                } //if
                running = !running;
            }; //button.click 
            wrapper.NumberGenerated += delegate(object sender, ThreadWrapper.NumberGeneratedEventArgs eventArgs) {
                output.Invoke(new NumberAction(delegate(Label label, int value) {
                    label.Text = value.ToString();
                }), output, eventArgs.Number);
            }; //wrapper.NumberGenerated
            wrapper.Start();
            this.Closing += delegate(object sender, System.ComponentModel.CancelEventArgs e) {
                wrapper.Abort();
            };
        } //FormMain

        bool running;
        ThreadWrapper wrapper = new ThreadWrapper(SleepTime);

    } //class FormMain

} //namespace RandomTest


I also tried to use the syntax compatible with C# v.2, which is still used. In later versions, lambda syntax of anonymous delegates is highly beneficial. Here is how:

C#
button.Click += (sender, eventArgs) => { /* ... */ };

There are many benefits of this syntax. First of all, look at the handler: you don't need to use the parameters in this case. With lambda syntax, you don't even need to look for parameters types — they are inferred by a compiler from the type of the event Click. Even when you need to use one or both parameters, Intellisense will show you the members of those types you can use, but you still won't need to remember or find out parameter names.

—SA
 
Share this answer
 
v2
Comments
maysam_p82 24-Jan-12 14:27pm    
thanks for your attention,but ,unfortunately i couldn't understand your approach and your code.I'm not experienced enough to understand these partial codes! I was read MSDN and I found a temporary way,a property,Control.CheckForIllegalCrossThreadCalls = false; . This is for .NET Framework 1.1 that also my book is for.But I found that is not an appropriate way for Multithreading programming.But in my this special discussion Threads was not target. Network programming was my target. Although, I'm interested in learning thread,and I request you show me some simpler and more realized samples ,thank you so much.Thanks for your attention.:-)

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