home *** CD-ROM | disk | FTP | other *** search
- /*+==========================================================================
- File: wintalk.cpp
-
- Summary: This contains the front-end logic to wintalk, and entry point.
- This application demonstrates the use of WFC and System.Net.DLL.
-
- Wintalk starts with a dialog that with to multi-line edit boxes
- and two buttons (one grayed out). In the background, it spins
- off a thread (ServerConnectionThread) which begins listening on
- TCP/IP port 5000. When an incoming connection arrives, the
- ServerConnectionThread posts a message to the UI thread to
- un-gray the "disconnect" button, and gray out the "connect"
- button, and a client thread is started which receive all data
- and sends the characters to the top edit window. The user types
- in the bottom edit window to transmit to the remote end.
-
- WFC's InvokeAsync is used to provide a thread switch from one
- of the "worker" threads (such as ServerConnectionThread) to
- the UI thread (which is started from Main, and messages pumped
- in Application.Run).
-
- Classes: Wintalk
-
- Functions:
- Overrides: Dispose
-
- Initialization: InitForm
-
- Thread procedures: ClientConnectionThreadProc,
- ServerConnectionThreadProc
-
- Async Invoked Methods: OnClientActive, OnClientClosed,
- OnServerAccept, OnServerReject
-
- Other: EnableClientConnection,
- DisableClientConnection
-
- UI Event Handlers: panel1_resize, button1_click,
- button2_click, WinTalk_activate
-
- Entry point: Main
-
- ----------------------------------------------------------------------------
- This file is part of the Microsoft NGWS Samples.
-
- Copyright (C) 1998-1999 Microsoft Corporation. All rights reserved.
- ==========================================================================+*/
-
- #using <mscorlib.dll>
- #using <System.WinForms.dll>
- #using <System.DLL>
- #using <Microsoft.Win32.Interop.dll>
- #using <System.Net.DLL>
- #using <System.Drawing.DLL>
-
- using namespace System;
- using namespace System::Threading;
- using namespace System::ComponentModel;
- using namespace System::WinForms;
- using namespace System::Runtime::InteropServices;
- using namespace System::Drawing;
- using namespace System::Net;
- using namespace System::Net::Sockets;
-
- // define delegates to be used with AsyncInvoke
- __delegate void AsyncInvokeDelegate();
- __delegate void AsyncInvokeStringDelegate(String *str);
-
-
- #define NULL 0
-
- #include "SendEdit.h"
- #include "ConnectDialog.h"
-
- __gc class WinTalk : public Form
- {
- protected:
- Socket* ClientConnection;
- Socket* ServerConnection;
-
- Thread* ClientConnectionThread;
- Thread* ServerConnectionThread;
-
- String* m_ListenStr;
-
- bool bIsInDispose;
-
- Container *components;
- SendEdit *edit2;
- DataEdit *edit1;
- Button *button1;
- Button *button2;
- Label *label1;
- Splitter *splitter1;
- Panel *panel1;
-
- public:
- int ServerPort;
-
- /*****************************************************************************
- Constructor : WinTalk
-
- Abstract: Constructs an instance of Wintalk on the given port.
-
- Input Parameters: port(int)
-
- ******************************************************************************/
- WinTalk(int port) : ClientConnection(NULL), ServerConnection(NULL),
- ClientConnectionThread(NULL),
- ServerConnectionThread(NULL),
- bIsInDispose(false)
- {
- this->ServerPort = port;
-
- components = new Container();
- edit2 = new SendEdit();
- edit1 = new DataEdit();
- button1 = new Button();
- button2 = new Button();
- label1 = new Label();
- splitter1 = new Splitter();
- panel1 = new Panel();
-
- InitForm();
-
- // start "Listening" thread
- ServerConnectionThread = new Thread(new ThreadStart(this, &WinTalk::ServerConnectionThreadProc));
- ServerConnectionThread->Start();
- }
-
- /*****************************************************************************
- Function: Dispose
-
- Abstract: Overrides Form.Dispose, cleans up the component list.
-
- Input Parameters: (None)
-
- Returns: void
- ******************************************************************************/
- void Dispose()
- {
- bIsInDispose = true;
- Form::Dispose();
- components->Dispose();
-
- DisableClientConnection();
-
- // Kill server thread
- try {
- if (ServerConnection != NULL)
- ServerConnection->Close();
- }
- catch (Exception *e) {}
- ServerConnection = NULL;
-
- if (ServerConnectionThread != NULL)
- ServerConnectionThread->Join();
- ServerConnectionThread = NULL;
-
- if (ClientConnectionThread != NULL)
- ClientConnectionThread->Join();
- ClientConnectionThread = NULL;
- }
-
- /*****************************************************************************
- Function: OnClientActive
-
- Abstract: Invoked asynchronously when a connection becomes active.
-
- Input Parameters: (None)
-
- Returns: void
- ******************************************************************************/
- void OnClientActive()
- {
- button1->Enabled = false;
- button2->Enabled = true;
- edit2->Focus(); // helps to eliminate confusion, sets focus to SendEdit
-
- IPEndPoint *ep = __try_cast<IPEndPoint*>(ClientConnection->RemoteEndpoint);
-
- String *addr = DNS::InetNtoa(ep->Address);
- int port = ep->Port;
- String *host = DNS::GetHostByAddr(addr)->Hostname;
- String *s = String::Concat(S"Talking to ", addr, S":");
- s->Concat(Convert::ToString(port), S"(", host, S")");
- Console::WriteLine(s);
- label1->Text = s;
- }
-
- /*****************************************************************************
- Function: OnClientClosed
-
- Abstract: Invoked asynchronously when the active connection is closed
-
- Input Parameters: (None)
-
- Returns: void
- ******************************************************************************/
- void OnClientClosed()
- {
- edit1->Append(S"\r\n****Notice: Connection Closed****\r\n");
- button1->Enabled = true;
- button2->Enabled = false;
- label1->Text = m_ListenStr;
- }
-
- /*****************************************************************************
- Function: OnServerAccept
-
- Abstract: Invoked asynchronously when an incoming connection is accepted
- by the server thread.
-
- Input Parameters: (None)
-
- Returns: void
- ******************************************************************************/
- void OnServerAccept()
- {
- edit1->Append(S"\r\n****CONNECTION ACTIVE****\r\n");
- WindowState = FormWindowState::Normal;
- }
-
- /*****************************************************************************
- Function: OnServerReject
-
- Abstract: Invoked asynchronously when an incoming connection is rejected
- by the server thread.
-
- Input Parameters: (None)
-
- Returns: void
- ******************************************************************************/
- void OnServerReject()
- {
- edit1->Append(S"\r\n****Warning: Incoming being rejected****\r\n");
- }
-
- /*****************************************************************************
- Function: EnableClientConnection
-
- Abstract: Called when "ClientConnection" becomes valid. Resets UI to
- proper state and spins off a ClientConnectionThread thread.
-
- Input Parameters: None
-
- Returns: Void
- ******************************************************************************/
- void EnableClientConnection()
- {
- if (ClientConnectionThread != NULL) {
- throw new ApplicationException(String::Concat(S"Client reader thread ",
- S"(ClientConnectionThread) already ",
- S"active!!"));
- }
- ClientConnectionThread = new Thread(new ThreadStart(this, &WinTalk::ClientConnectionThreadProc));
- ClientConnectionThread->Start();
- BeginInvoke(new AsyncInvokeDelegate(this, &WinTalk::OnClientActive));
- edit2->SetClientConnection(ClientConnection);
- }
-
- /*****************************************************************************
- Function: DisableClientConnection
-
- Abstract: Called when "ClientConnection" becomes invalid, or actually
- closes ClientConnection. Resets UI to proper state and if not
- called from ClientConnectionThread, waits for this thread to
- terminate.
-
- Input Parameters: None
-
- Returns: Void
- ******************************************************************************/
- void DisableClientConnection()
- {
- try {
- edit2->SetClientConnection(NULL);
- try {
- // CriticalSection::Enter(this);
- if (ClientConnection != NULL) {
- Console::WriteLine(String::Concat(S"DisableClientConnection: ",
- S"closing connection"));
- ClientConnection->Close();
- ClientConnection = NULL;
- }
- }
- __finally {
- // CriticalSection::Exit(this);
- }
- }
- catch (Exception *e) {
- MessageBox::Show(this,e->Message,S"Close Error");
- }
- if (Thread::CurrentThread == ClientConnectionThread) {
- ClientConnectionThread = NULL;
- if (this->HandleCreated) {
- BeginInvoke(new AsyncInvokeDelegate(this, &WinTalk::OnClientClosed));
- }
- } else {
- if (ClientConnectionThread != NULL) {
- ClientConnectionThread->Join();
- ClientConnectionThread = NULL;
- }
- }
- }
-
- /*****************************************************************************
- Function: ClientConnectionThreadProc
-
- Abstract: Called when a client connection is made (click the "connect"
- button, or incoming connection is made). This thread is
- created and started from the "EnableClientConnection" method.
-
- Input Parameters: None
-
- Returns: Void
- ******************************************************************************/
- void ClientConnectionThreadProc()
- {
- Console::WriteLine(S"ClientConnectionThreadProc thread is starting");
- try {
- wchar_t c = L'\0';
-
- bool ignore_next_return = false;
- Char b[] = new Char[1]; //not sure if __managed has a new keyword
-
- // read one character at a time from the Socket
- while (ClientConnection != NULL && ClientConnection->Receive(b,b->Length,0) > 0) {
- c = b[0];
- if (ignore_next_return && (c == L'\r' || c == L'\n')) {
- ignore_next_return = false;
- continue;
- }
- ignore_next_return = false;
-
- if (c == L'\b') {
- // backspace character causes use to truncate one char
- // off of the edit control
- edit1->BeginInvoke(new AsyncInvokeDelegate(edit1, &DataEdit::TruncateOneCharFromEnd));
- continue;
- }
-
- String *str = NULL;
-
- if (c == L'\r' && (ignore_next_return = true) || c == L'\n') {
- // if we receive \r\n, then we only pay attention to \r
- str = S"\r\n";
- } else {
- str = Convert::ToString(c);
- }
-
- Object *o __gc[] = new Object* __gc[1]; //not sure if __managed has a new keyword
- o[0] = str;
- edit1->BeginInvoke(new AsyncInvokeStringDelegate(edit1, &DataEdit::Append), o);
- }
- }
- catch (Exception *e) {
- Console::WriteLine(String::Concat(S"Client Connection thread: ", e->ToString()));
- }
-
- Console::WriteLine(S"Connection Closed");
-
- DisableClientConnection();
- Console::WriteLine(S"ClientConnectionThreadProc thread is ending");
- }
-
- /*****************************************************************************
- Function: ServerConnectionThreadProc
-
- Abstract: Listens on "ServerPort" and creates Socket's for each incoming
- request. If ClientConnection is already active, the new
- Socket is destroyed.
-
- Input Parameters: None
-
- Returns: Void
- ******************************************************************************/
- void ServerConnectionThreadProc()
- {
- Console::WriteLine(S"ServerConnectionThread starting");
- try {
- // create server socket and listen on port
- ServerConnection = new Socket(AddressFamily::AfINet,SocketType::SockStream,ProtocolType::ProtTCP);
- ServerConnection->Blocking = true;
- if (ServerConnection->Bind(new IPEndPoint(IPAddress::InaddrAny,ServerPort)) != 0) {
- throw new ApplicationException(String::Concat("Unable to bind to port ", Convert::ToString(ServerPort)));
- }
-
- ServerConnection->Listen(-1);
- m_ListenStr = String::Concat(S"WinTalk listening on port ", Convert::ToString(ServerPort));
- Console::WriteLine(m_ListenStr);
- label1->Text = m_ListenStr;
- }
- catch (Exception *e) {
- Console::WriteLine(String::Concat(S"Excpetion: ", e->Message));
- m_ListenStr = S"Not listening on any port";
- Console::WriteLine(m_ListenStr);
- label1->Text = m_ListenStr;
- return;
- }
-
- try {
- while (true) {
- // accept socket requests to our listening port
- Socket *s = ServerConnection->Accept();
-
- try {
- // CriticalSection::Enter(this);
- if (s == NULL) {
- throw new ApplicationException(S"Server connection closed");
- }
-
- // if connection already active, reject
- if (ClientConnection != NULL) {
- BeginInvoke(new AsyncInvokeDelegate(this, &WinTalk::OnServerReject));
- String *str = S"Sorry connection is being rejected....\n";
- Console::WriteLine(str);
-
- Char c[] = str->ToCharArray();
- Char b[] = new Char[c->Length];
-
- for (int i = 0; i < c->Length; i++) {
- b[i] = (char)c[i];
- }
- s->Send(b, b->Length, 0);
- s->Close();
- continue;
- }
-
- // no connection active, make the incoming one our current
- // connection
- ClientConnection = s;
-
- // notify user that connection has been made
- BeginInvoke(new AsyncInvokeDelegate(this, &WinTalk::OnServerAccept));
-
- // notify UI thread that connection is active and
- // spin off client thread to handle reads from socket
- EnableClientConnection();
- }
- __finally {
- // CriticalSection::Exit(this);
- }
- }
- }
- catch (Exception *e) {
- Console::WriteLine(String::Concat(S"Server Connection Thread: ", e->ToString()));
- if (this->HandleCreated) {
- MessageBox::Show(this,String::Concat(S"Server Connection: ", e->ToString()),
- S"WinTalk");
- }
- m_ListenStr = S"Not listening on any port";
- label1->Text = m_ListenStr;
- if (!bIsInDispose) {
- Console::WriteLine(String::Concat(S"Server Connection Thread: ", e->ToString()));
- MessageBox::Show(String::Concat(S"Server Connection: ", e->ToString()),S"WinTalk");
- }
- }
- try {
- if (ServerConnection != NULL) {
- ServerConnection->Close();
- ServerConnection = NULL;
- }
- }
- catch (Exception *e) {
- Console::WriteLine(String::Concat(S"Server Connection Thread(2): ", e->ToString()));
- }
- Console::WriteLine(S"Server Connection Thread exiting");
- }
-
-
- /*****************************************************************************
- Function: panel1_resize
-
- Abstract: Invoked when the WFC panel containing the two edit fields
- is resized. Adjusts the edit boxes to be equal size.
-
- Input Parameters: source(Object), e(EventArgs)
-
- Returns: Void
- ******************************************************************************/
- void panel1_resize(Object *source, EventArgs *e)
- {
- // Position the splitter in the center of the window
- edit1->Height = (panel1->Height - splitter1->Height) / 2;
- }
-
- /*****************************************************************************
- Function: button1_click
-
- Abstract: Invoked when the "Connect" button is clicked. Creates the
- "ConnectDialog" and enables the client connection.
-
- Input Parameters: source(Object), e(EventArgs)
-
- Returns: Void
- ******************************************************************************/
- void button1_click(Object *source, EventArgs *e)
- {
- Console::WriteLine(S"BUTTON1 clicked!!!");
- try {
- // CriticalSection::Enter(this);
- if (ClientConnection != NULL) {
- Console::WriteLine(S"ERROR Connection already is open!!");
- return;
- }
- ConnectDialog *dlg = new ConnectDialog();
- System::WinForms::DialogResult ret = dlg->ShowDialog(this);
- if (ret == DialogResult::OK) {
- try {
- ClientConnection = new Socket(AddressFamily::AfINet,SocketType::SockStream,ProtocolType::ProtTCP);
- IPAddress *host_addr = DNS::Resolve(dlg->host);
- if (host_addr == NULL) {
- throw new ApplicationException(String::Concat(S"Unable to resolve host ", dlg->host));
- }
- IPEndPoint *ep = new IPEndPoint(host_addr, dlg->port);
- if (ClientConnection->Connect(ep) != 0) {
- throw new ApplicationException(S"Client connection failed");
- }
- EnableClientConnection();
- }
- catch (Exception *ev) {
- DisableClientConnection();
- MessageBox::Show(this,ev->Message,S"Connect Error");
- }
- }
- }
- __finally {
- // CriticalSection::Exit(this);
- }
- }
-
- /*****************************************************************************
- Function: button2_click
-
- Abstract: Invoked when the "Disconnect" button is clicked. Disables the
- client connection.
-
- Input Parameters: source(Object), e(EventArgs)
-
- Returns: Void
- ******************************************************************************/
- void button2_click(Object *source, EventArgs *e)
- {
- Console::WriteLine(S"BUTTON2 clicked!!!");
- DisableClientConnection();
- }
-
- /*****************************************************************************
- Function: WinTalk_activate
-
- Abstract: Invoked when the Wintalk application window becomes active.
- Sets focus to the bottom edit control (less confusion to the
- user as to which edit box to type in).
-
- Input Parameters: source(Object), e(EventArgs)
-
- Returns: Void
- ******************************************************************************/
- void WinTalk_activate(Object *source, EventArgs *e)
- {
- edit2->Focus();
- }
-
-
- /*****************************************************************************
- Function: InitForm
-
- Abstract: Creates UI elements (buttons, edit controls, ...) on the
- Wintalk WFC form.
-
- Input Parameters: None
-
- Returns: Void
- ******************************************************************************/
- void InitForm()
- {
- this->Text = S"WinTalk";
- this->AutoScaleBaseSize = *new System::Drawing::Size(*new Point(5, 13)); //This line is really messed
- this->ClientSize = *new System::Drawing::Size(*new Point(406, 344));
- this->AddOnActivate(new EventHandler(this, &WinTalk::WinTalk_activate));
-
- edit2->Dock = DockStyle::Fill;
- edit2->Location = *new Point(0, 158);
- edit2->Size = *new System::Drawing::Size(*new Point(406, 154));
- edit2->TabIndex = 1;
- edit2->Text = S"";
- edit2->Multiline = true;
- edit2->ReadOnly = true;
- edit2->ScrollBars = ScrollBars::Vertical;
- edit2->BackColor = Color::White;
-
- edit1->Dock = DockStyle::Top;
- edit1->Size = *new System::Drawing::Size(*new Point(406, 153));
- edit1->TabIndex = 0;
- edit1->Text = S"";
- edit1->Multiline = true;
- edit1->ReadOnly = true;
- edit1->ScrollBars = ScrollBars::Vertical;
- edit1->BackColor = Color::White;
-
- label1->Anchor = AnchorStyles::BottomLeftRight;
- label1->Location = *new Point(0, 321);
- label1->Size = *new System::Drawing::Size(*new Point(230, 18));
- label1->TabIndex = 2;
- label1->TabStop = false;
- label1->Text = S"";
-
- button1->Anchor = AnchorStyles::BottomRight;
- button1->Location = *new Point(331, 317);
- button1->Size = *new System::Drawing::Size(*new Point(72, 24));
- button1->TabIndex = 0;
- button1->Text = S"Connect";
- button1->AddOnClick(new EventHandler(this, &WinTalk::button1_click));
-
- button2->Anchor = AnchorStyles::BottomRight;
- button2->Enabled = false;
- button2->Location = *new Point(235, 317);
- button2->Size = *new System::Drawing::Size(*new Point(88, 24));
- button2->TabIndex = 1;
- button2->Text = S"Disconnect";
- button2->AddOnClick(new EventHandler(this, &WinTalk::button2_click));
-
- splitter1->Cursor = Cursors::HSplit;
- splitter1->Dock = DockStyle::Top;
- splitter1->Location = *new Point(0, 153);
- splitter1->Size = *new System::Drawing::Size(*new Point(406, 5));
- splitter1->TabIndex = 2;
- splitter1->TabStop = false;
-
- panel1->Anchor = AnchorStyles::All;
- panel1->Size = *new System::Drawing::Size(*new Point(406, 312));
- panel1->TabIndex = 3;
- panel1->Text = S"panel1";
- panel1->AddOnResize(new EventHandler(this, &WinTalk::panel1_resize));
-
- Control *c1[] = new Control*[4];
- c1[0] = panel1;
- c1[1] = button2;
- c1[2] = button1;
- c1[3] = label1;
-
- this->Controls->All = c1;
-
- Control *c2[] = new Control*[3];
-
- c2[0] = splitter1;
- c2[1] = edit2;
- c2[2] = edit1;
-
- panel1->Controls->All = c2;
- }
-
-
- };
-
- /*****************************************************************************
- Function: Main
-
- Abstract: Entry point into application.
-
- Input Parameters: String[]
-
- Returns: void
-
- ******************************************************************************/
- void main()
- {
- int port = 5000;
- Application::Run(new WinTalk(port));
- }
-