Control.Invoke not as synchronous as I thought

Tech-Archive recommends: Repair Windows Errors & Optimize Windows Performance

msimpson_at_computer.org
Date: 02/07/05

  • Next message: Haz: "Re: Custome StatusBar control"
    Date: 7 Feb 2005 13:05:42 -0800
    
    

    I've been fighting with multithreading/gui issues for the last couple
    of weeks and thought I found the solution in Control.Invoke, but things
    still aren't working out as I expected. In order to try to isolate the
    problem, I stripped my code down to the minimum possible to see the
    problem. Basically what I want is to have a main function that does
    the following:
    1. Create a form that contains a RichTextBox. When the form opens, it
    will create a socket, listen (using BeginAccept) for incoming
    connections, and display "Server started on address xxx" on the
    RichTextBox.
    2. Create a client to connect to the server. When the server sees the
    connection, it should display "Client connected" in the textbox.
    3. Verify the contents of the textbox (which should be "Server
    started... Client connected".
    4. Shut everything down.

    Because a separate thread is spawned when the server socket sees a
    connection, I use Invoke to update the textbox. I thought that by
    using Invoke (synchronous) rather than BeginInvoke (asynchronous), the
    update to the message box would occur before that thread would continue
    processing, but that's not the case. When I verify the contents of the
    textbox in step 3, it doesn't contain the "client connected" message
    sent by the thread spawned by the connection request.

    The results I'm seeing are:

     1 [2296] frmServer:frmServer.<<create>> {
     2 [2296] IPEndPoint:localHostAddress.<<create>>(hostAddress,
    nPortNumber) -> localHostAddress
     3 [2296] Socket:m_listenSocket.<<create>>
     4 [2296] Socket:m_listenSocket.Bind(localHostAddress)
     5 [2296] Socket:m_listenSocket.Listen(10)
     6 [2296] beginning invoke of '*** Server started on address
    127.0.0.1:13535'
     7 [2296] frmServer:frmServer.UpdateHistory('*** Server started on
    address 127.0.0.1:13535') {
     8 [2296] }
     9 [2296] Socket:m_listenSocket.BeginAccept(OnConnectionRequest)
    10 [2296] }
    11 [2296] Client:client.<<create>> ('test1') {
    12 [2296] }
    13 [2296] Client:client.ConnectToServer(hostAddress) {
    14 [2296] Socket:m_socket.<<create>>
    15 [2296] Socket:m_socket.Connect(hostAddress)
    16 [2296] [m_socket.Connected] {
    17 [2452] Server:m_server.OnConnectionRequest {
    18 [2452] Socket:m_listenSocket.EndAccept(ar) -> connectedSocket
    19 [2452] beginning invoke of 'client has joined'
    20 [2296] }
    21 [2296] }
    22 [2296] historyBox.Text = *** Server started on address
    127.0.0.1:13535
    23 [2296] Client:client.Shutdown() {
    24 [2296] Socket:m_socket.Shutdown
    25 [2296] Socket:m_socket.Close
    26 [2296] }

    Thread 2296 is the main thread, and 2452 is the thread spawned to
    receive the connection. Lines 6 and 7 show the initial Invoke and
    immediate updating for the "Server started..." message. Line 19 shows
    the Invoke call for the "client connected" message, but I never see the
    actual updating function entered. Can anyone clarify my understanding
    of exactly what synchronous means with regards to Invoke? And even
    more important, is there any way to successfully accomplish what I'm
    attempting (safely update a gui control from socket threads and have
    the gui updated before I test the contents of the text box in the main
    thread)?

    Thanks!

    Here is the code I used:
    [frmServer.cs]
    using System;
    using System.Drawing;
    using System.Collections;
    using System.ComponentModel;
    using System.Net;
    using System.Net.Sockets;
    using System.Windows.Forms;

    namespace SimplyAsync
    {
            /// <summary>
            /// Summary description for frmServer.
            /// </summary>
            public class frmServer : System.Windows.Forms.Form
            {
                    private System.Windows.Forms.RichTextBox txtHistory;
                    /// <summary>
                    /// Required designer variable.
                    /// </summary>
                    private System.ComponentModel.Container components = null;
                    protected Socket m_listenSocket;

                    // For the asynchronous updating of the window
                    delegate void HistoryCallback(String newMessage, Color messageColor);
                    HistoryCallback updateHistory;

                    public frmServer(int nPortNumber)
                    {
                            DebugMessage.Print("frmServer:frmServer.<<create>> {");

                            //
                            // Required for Windows Form Designer support
                            //
                            InitializeComponent();

                            updateHistory = new HistoryCallback(UpdateHistory);

                            IPHostEntry hostEntry = Dns.Resolve("localhost");
                            IPAddress hostAddress = hostEntry.AddressList[0];
                            DebugMessage.Print("IPEndPoint:localHostAddress.<<create>>(hostAddress,
    nPortNumber) -> localHostAddress");
                            IPEndPoint localHostAddress = new IPEndPoint(hostAddress,
    nPortNumber);
                            DebugMessage.Print("Socket:m_listenSocket.<<create>>");
                            m_listenSocket = new Socket(AddressFamily.InterNetwork,
    SocketType.Stream, ProtocolType.Tcp);
                            try {
                                    DebugMessage.Print("Socket:m_listenSocket.Bind(localHostAddress)");
                                    m_listenSocket.Bind(localHostAddress);
                                    DebugMessage.Print("Socket:m_listenSocket.Listen(10)");
                                    m_listenSocket.Listen(10);

                                    // start accepting incoming connections
                                    String sServerListening = String.Format("Server started on address
    {0}:{1}", hostAddress.ToString(), nPortNumber);
                                    String sMessage = "*** " + sServerListening + "\n";
                                    DebugMessage.Print("beginning invoke of '" + sMessage + "'");
                                    if (txtHistory.InvokeRequired) {
                                            Invoke(updateHistory, new Object[] {sMessage, Color.Green});
                                    } else {
                                            updateHistory(sMessage, Color.Green);
                                    }

                                    DebugMessage.Print("Socket:m_listenSocket.BeginAccept(OnConnectionRequest)");
                                    m_listenSocket.BeginAccept(new AsyncCallback(OnConnectionRequest),
    m_listenSocket);
                            } catch (SocketException se) {
                                    if (se.ErrorCode == 10048) {
                                            DebugMessage.Print("Server error: This address and port are
    already in use.");
                                    } else {
                                            DebugMessage.Print("Server error: Socket Error " + se.ErrorCode +
    " trying to bind server.");
                                    }
                            } finally {
                                    DebugMessage.Print("}");
                            }
                    }

                    private void OnConnectionRequest(IAsyncResult ar)
                    {
                            DebugMessage.Print("Server:m_server.OnConnectionRequest {");
                            try {
                                    // EndAccept method returns a socket that we get a reference to
                                    DebugMessage.Print("Socket:m_listenSocket.EndAccept(ar) ->
    connectedSocket");
                                    Socket connectedSocket = m_listenSocket.EndAccept(ar);

                                    String sMessage = "client has joined";
                                    if (txtHistory.InvokeRequired) {
                                            DebugMessage.Print("beginning invoke of '" + sMessage + "'");
                                            Invoke(updateHistory, new Object[] {sMessage, Color.Green});
                                    } else {
                                            updateHistory(sMessage, Color.Green);
                                    }

                                    // Listen for another connection
                                    DebugMessage.Print("Socket:m_listenSocket.BeginAccept(OnConnectionRequest)");
                                    m_listenSocket.BeginAccept(new
    System.AsyncCallback(OnConnectionRequest), m_listenSocket);
                            } catch (ObjectDisposedException ode) {
                                    // Just to catch those cases where we're shutting down
                                    DebugMessage.Print("Server:server.OnConnectionRequest - handled
    ObjectDisposedException");
                            } catch (Exception e) {
                                    DebugMessage.Print("Server error: " + e.Message);
                            } finally {
                                    DebugMessage.Print("}");
                            }
                    }

                    void UpdateHistory(String newMessage, Color messageColor)
                    {
                            DebugMessage.Print("frmServer:frmServer.UpdateHistory('" +
    newMessage + "') {");
                            txtHistory.SelectionColor = messageColor;
                            txtHistory.AppendText(newMessage);
                            DebugMessage.Print("}");
                    }

                    /// <summary>
                    /// Clean up any resources being used.
                    /// </summary>
                    protected override void Dispose( bool disposing )
                    {
                            if( disposing )
                            {
                                    if(components != null)
                                    {
                                            components.Dispose();
                                    }
                            }
                            base.Dispose( disposing );
                    }

                    #region Windows Form Designer generated code
                    /// <summary>
                    /// Required method for Designer support - do not modify
                    /// the contents of this method with the code editor.
                    /// </summary>
                    private void InitializeComponent()
                    {
                            this.txtHistory = new System.Windows.Forms.RichTextBox();
                            this.SuspendLayout();
                            //
                            // txtHistory
                            //
                            this.txtHistory.Location = new System.Drawing.Point(0, 0);
                            this.txtHistory.Name = "txtHistory";
                            this.txtHistory.ReadOnly = true;
                            this.txtHistory.Size = new System.Drawing.Size(288, 272);
                            this.txtHistory.TabIndex = 0;
                            this.txtHistory.Text = "";
                            //
                            // frmServer
                            //
                            this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
                            this.ClientSize = new System.Drawing.Size(292, 273);
                            this.Controls.Add(this.txtHistory);
                            this.Name = "frmServer";
                            this.Text = "frmServer";
                            this.ResumeLayout(false);

                    }
                    #endregion
            }
    }

    [SimpleAsync.cs]
    using System;
    using System.Collections;
    using System.Net;
    using System.Windows.Forms;

    namespace SimplyAsync
    {
            /// <summary>
            /// Summary description for SimpleAsync.
            /// </summary>
            public class SimpleAsync
            {
                    public static void Main(string[] args)
                    {
                            // Create and display the server form
                            frmServer server = new frmServer(13535);
                            server.Show();

                            // Create the client and connect to the server
                            Client client = new Client("test1");
                            IPEndPoint hostAddress = new
    IPEndPoint(Dns.Resolve("localhost").AddressList[0], 13535);
                            client.ConnectToServer(hostAddress);

                            // Display the contents of the text box
                            RichTextBox historyBox = null;
                            IEnumerator enumControls = server.Controls.GetEnumerator();
                            while (enumControls.MoveNext() != false) {
                                    if (enumControls.Current is RichTextBox) {
                                            historyBox = (RichTextBox)enumControls.Current;
                                            break;
                                    }
                            }

                            DebugMessage.Print("historyBox.Text = " + historyBox.Text);

                            // Shut everything down
                            client.Shutdown();
                            server.Close();

                            // Wait for user input
                            Console.ReadLine();
                    }
            }
    }


  • Next message: Haz: "Re: Custome StatusBar control"

    Relevant Pages

    • Re: Outgoing POP3 email missing/lost/not received
      ... Funny thing is that I have had this ISP for 8 years and it has always been ... It looks like when you last ran CEICW, you set the ISP's mail server to: ... Internet Connection Wizard. ... After the wizard completes, the following network connection ...
      (microsoft.public.windows.server.sbs)
    • Re: Cannot connect client to server 2003
      ... you need to reconfigure the IP schema of your SBS ... On the SBS 2003 Server open the Server Management console. ... On the Connection Type page, click Broadband, and then click Next. ... Alternate DNS server, type the IP addresses that are provided by your ISP ...
      (microsoft.public.windows.server.sbs)
    • Re: Outgoing POP3 email missing/lost/not received
      ... ISP's mail server instead of the domain name on the ... SUMMARY OF SETTINGS FOR CONFIGURE E-MAIL AND INTERNET ... Internet Connection Wizard. ... After the wizard completes, the following network connection ...
      (microsoft.public.windows.server.sbs)
    • Re: Networking Question - VLANs on SBS 2003 Premium SP1
      ... port on the old router so I now have a segregated WLAN. ... be sure you do not enable any DHCP server in internal network. ... On the Connection Type page, click Broadband, and then click Next. ... On the Network Connection, You must enable and configure the network ...
      (microsoft.public.windows.server.sbs)
    • Re: Still cant connect to RWW or OWA remotely
      ... it certainly appears to be something about the SBS configuration. ... Meridian.local Ethernet adapter Local Area Connection: ... Windows SMALL BUSINESS SERVER 2003 Windows IP Configuration ... 192.168.254.254) directly to a port on the router and then ...
      (microsoft.public.windows.server.sbs)