I'm new to using delegates and invoke methods. Can someone please quickly identify what I'm missing here. I know this code is no where near completed right now, but as is, it's working for what I'm trying to test. Basically I'm creating a simple client application that is receiving a server message. Connections are fine, and the thread is receiving the server message. I just need to know how to properly marshal the message to my Form class control from my Client class.

It's the 'received.Invoke(response);' that isn't right somehow.

The form control is attempting to receive the message but it can't, because the marshalling isn't working.

Maybe I need a second delegate for something??

HELP!!!

I've only added the code directly dealing with the thread.

Form code snippets:

public partial class frmChat : Form
    {
        // create new client object
        private Client client = new Client();

        public frmChat()
        {
            InitializeComponent();

            client.received += new Client.receiveFunc(chatClientLibrary_Receieved);
            //calc.Added += new CalculatorFunctions.AnswerFunc(mathLibrary_Added);
            
        }



        private void menuItemConnect_Click(object sender, EventArgs e)
        {
            client.execute();
            
        }

        // method for outuptting message to textbox
        private void chatClientLibrary_Receieved(string msg) 
        {
            
            txtBoxConversation.Text = msg;
	    }
    }

Client Class Code snippets:

public delegate void receiveFunc(string msg);
public event receiveFunc received;


Thread receiveThread = new Thread(new ThreadStart(threadListen));
 receiveThread.Start();


private void threadListen()
 {
 
    NetworkStream stream = this.client.GetStream();   // object to receive series of bytes

    byte[] inbuffer = new byte[256];

     bool done = false;

    //while (stream.DataAvailable)
    while (!done)
     {

           while (stream.DataAvailable)
           {

                    int numRead = stream.Read(inbuffer, 0, inbuffer.Length);

                    string response = Encoding.ASCII.GetString(inbuffer, 0, numRead);

                    if (response == "Quit")
                    {
                        done = true;
                    }
                    else
                    {

                        received.Invoke(response);
                        
                    }

              }
                
         }   // end while instream.DataAvailable

    }

Recommended Answers

All 4 Replies

I think your delegate invocation is fine. I've produced a slimmed-down version of what you're doing there, and I am not having an issue with the receiveFunc event hander.

class Program
    {
        static void Main(string[] args)
        {
            frmChat frm = new frmChat();
            Console.Read();
        }
    }

    public partial class frmChat
    {
        private Client client = new Client();

        public frmChat()
        {
            client.received += new Client.receiveFunc(chatClientLibrary_Receieved);
        }

        private void chatClientLibrary_Receieved(string msg)
        {
            Console.WriteLine(msg);
        }
    }

    public class Client
    {
        public delegate void receiveFunc(string msg);
        public event receiveFunc received;

        public Client()
        {
            ThreadStart threadStart = new ThreadStart(ThreadListen);
            Thread thread = new Thread(threadStart);
            thread.Start();
        }

        public void ThreadListen()
        {
            received.Invoke("Test Invocation");
        }
    }

That runs on my machine with the expected result. I think the question should be whether the code leading up to your invocation is executing as you expect. I would try to step through the execution of threadListen() and see what is happening.

Hi, I have stepped through with the debugger. The form control just can't accept the message through the thread boundry. Perhaps outputting to a console works differently?
I'm fairly certain that it involves my "received.Invoke(response);" statement. This statement might have need a method as a parameter, however I'm stumped as to what.

You're right about one thing, there certainly was something lost in translation when I used a console app to test your code. As it turns out, using a form throws an exception because of a lack of a threadsafe call.

However, this winform code works.

public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private Client client;

        private void Form1_Load(object sender, EventArgs e)
        {
            client = new Client();
            client.received += new Client.receiveFunc(chatClientLibrary_Receieved);
            client.Execute();
        }

        delegate void SetTextCallback(string msg);

        private void chatClientLibrary_Receieved(string msg)
        {
            if (this.label1.InvokeRequired)
            {
                // It's on a different thread, so use Invoke.
                SetTextCallback d = new SetTextCallback(chatClientLibrary_Receieved);
                this.Invoke(d, new object[] { msg });
            }
            else
            {
                // It's on the same thread, no need for Invoke
                this.label1.Text = msg;
            }
        }
    }

    public class Client
    {
        public delegate void receiveFunc(string msg);
        public event receiveFunc received;

        public void Execute()
        {
            ThreadStart threadStart = new ThreadStart(ThreadListen);
            Thread thread = new Thread(threadStart);
            thread.Start();
        }

        public void ThreadListen()
        {
            received.Invoke("Test Invocation");
        }
    }

For more on making thread-safe calls to form controls: http://msdn.microsoft.com/en-us/library/ms171728.aspx

The GUI/controls can only be updated on the thread which they were created (your main thread), that is the cause of your error. Another unsafe thing here is losing the reference to your thread as it could be garbage collected. Do you really need to create a thread or would using asynchronous delegates get the job done? If you create a thread and do not set the Thread.IsBackground = true; before firing it up then it will not let your application close down until the thread has been aborted or stopped.

Here are a few more things to consider:

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.Threading;

namespace daniweb
{
  public partial class frmThreadSync : Form
  {
    private Client client;
    private int counter;

    public frmThreadSync()
    {
      InitializeComponent();
      counter = 0;
    }

    private void frmThreadSync_Load(object sender, EventArgs e)
    {
      client = new Client(this);
      client.received += new Client.receiveFunc(client_received);
    }

    void client_received(string msg)
    {
      System.Threading.Interlocked.Increment(ref counter);
      this.Text = string.Format("[{0:F0}] {1}", counter, msg);
    }

    private void button1_Click(object sender, EventArgs e)
    {
      client.Execute();
    }

    private void button2_Click(object sender, EventArgs e)
    {
      client.Execute2();
    }
  }
  public class Client : IDisposable
  {
    private WeakReference ctrl; //Dont want our reference to hold up garbage collection
    private Thread thread;
    public delegate void receiveFunc(string msg);
    public event receiveFunc received;


    public Client()
    {
      ThreadStart threadStart = new ThreadStart(ThreadListen);
      this.thread = new Thread(threadStart);
      this.ctrl = null;
    }
    public Client(Control ctrl)
      : this()
    {
      this.ctrl = new WeakReference(ctrl, false);
    }

    public void Execute()
    {
      thread.Start();
    }

    public void Execute2()
    {
      new Action(ThreadListen).BeginInvoke(new AsyncCallback(ThreadListenCallback), null);
    }

    public void ThreadListen()
    {
      var del = this.received;
      Control invoker = (((this.ctrl != null) && this.ctrl.IsAlive && !((Control)this.ctrl.Target).IsDisposed) ? (Control)this.ctrl.Target : default(Control));
      if (invoker != null)
        invoker.Invoke(del, "Test invocation");
      else
        del.Invoke("Test invocation");
    }

    private void ThreadListenCallback(IAsyncResult ar)
    {
      System.Runtime.Remoting.Messaging.AsyncResult result = (System.Runtime.Remoting.Messaging.AsyncResult)ar;
      Action del = (Action)result.AsyncDelegate;
      del.EndInvoke(ar); //Frees up unmanaged resources
    }

    #region IDisposable Members
    public void Dispose()
    {
      ctrl = null;
      if (thread != null) //Maybe do something if its still running?
      {
      }
      thread = null;
    }
    #endregion
  }
}
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.