home *** CD-ROM | disk | FTP | other *** search
/ Chip 2002 September / Chip_2002-09_cd1.bin / zkuste / vbasic / Data / Utils / XZipComp.exe / XceedWinsock.Cab / F112667_unChat.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  2000-05-16  |  29.5 KB  |  731 lines

  1. unit unChat;
  2.  
  3. {
  4.  Xceed Winsock Library Sample: Chat
  5.  Copyright (c) 2000 Xceed Software Inc.
  6.  
  7.  This is a traditional chat application that uses a connection-oriented
  8.  protocol (TCP/IP) to connect another user running the same application.
  9.  Multiple users can connect to the same destination chat application.
  10.  
  11.  It specifically demonstrates how to:
  12.    - Use a listening socket to listen for incoming connections
  13.    - Manage multiple connection-oriented sockets
  14.    - Receive event notifications through dispatch calls using the
  15.      unXceedWinsockEvents.pas distributed with the library.
  16.    - Tranfer strings
  17.    - Use the TCP/IP protocol
  18.  
  19.  This file is part of the Xceed Winsock Library Samples.
  20.  The source code in this file is only intended as a supplement
  21.  to Xceed Winsock Library's documentation, and is provided "as is",
  22.  without warranty of any kind, either expressed or implied.
  23. }
  24.  
  25. interface
  26.  
  27. uses
  28.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  29.   XCEEDWINSOCKlib_TLB, StdCtrls, ActiveX, ComObj,
  30.   { These files are distributed with the Xceed Winsock Library to help you
  31.     receive events from the the different event objects. }
  32.   xwlIncomingConnectionEvents,
  33.   xwlConnectionEvents,
  34.   xwlStringTransferEvents;
  35.  
  36. type
  37.   TfrmChat = class( TForm,
  38.                     { These interfaces contain the events that the different
  39.                       helper classes can trigger when handling Invoke requests }
  40.                     IIncomingConnectionEvents,
  41.                     IConnectionEvents,
  42.                     IStringTransferEvents )
  43.     { Controls on the form }
  44.     lblChatThread: TLabel;
  45.     lblEnterMessage: TLabel;
  46.     lblNickname: TLabel;
  47.     edtNickname: TEdit;
  48.     btnClearScreen: TButton;
  49.     btnConnect: TButton;
  50.     btnDisconnect: TButton;
  51.     btnSend: TButton;
  52.     btnClose: TButton;
  53.     memoChatThread: TMemo;
  54.     memoMessage: TMemo;
  55.  
  56.     { Standard event procedures }
  57.     procedure FormCreate(Sender: TObject);
  58.     procedure FormDestroy(Sender: TObject);
  59.     procedure btnConnectClick(Sender: TObject);
  60.     procedure btnDisconnectClick(Sender: TObject);
  61.     procedure btnClearScreenClick(Sender: TObject);
  62.     procedure btnCloseClick(Sender: TObject);
  63.     procedure btnSendClick(Sender: TObject);
  64.     procedure memoMessageKeyPress(Sender: TObject; var Key: Char);
  65.  
  66.     { IIncomingConnectionEvents methods }
  67.     procedure OnConnection( const xListeningSocket : IDispatch;
  68.                             const xRemoteAddress : IDispatch;
  69.                             vaCallerData : OleVariant;
  70.                             lExpectedCalleeDataSize : Integer;
  71.                             var vaCalleeData : OleVariant;
  72.                             var xQualityOfService : IdXWQualityOfServiceInfo;
  73.                             var lUserParam : Integer;
  74.                             var bReject : WordBool );
  75.     procedure OnConnectionProcessed( const xListeningSocket : IDispatch;
  76.                                      const xIncomingSocket : IDispatch;
  77.                                      lUserParam : Integer );
  78.     procedure OnListeningError( const xListeningSocket : IDispatch;
  79.                                 lUserParam: Integer;
  80.                                 lResultCode: Integer );
  81.  
  82.     { IConnectionEvents }
  83.     procedure OnDisconnected( const xSocket: IDispatch;
  84.                               vaCallerData: OleVariant;
  85.                               var vaCalleeData: OleVariant );
  86.  
  87.     { IStringTransferEvents }
  88.     procedure OnStringSent( const xSocket: IDispatch;
  89.                             lUserParam: Integer;
  90.                             lResultCode: Integer );
  91.     procedure OnStringReceived( const xSocket: IDispatch;
  92.                                 const sString: WideString;
  93.                                 lUserParam: Integer;
  94.                                 lResultCode: Integer );
  95.     procedure OnStringAvailable( const xSocket: IDispatch;
  96.                                  lCharsReceived: Integer;
  97.                                  lCharsAvailable: Integer );
  98.     procedure OnOutOfBandStringReceived( const xSocket: IDispatch;
  99.                                          const sString: WideString;
  100.                                          lResultCode: Integer );
  101.  
  102.   private
  103.     { These objects wil receive dispatch events from the library for us. }
  104.     { They will then call one of the methods above }
  105.  
  106.     m_xIncomingConnectionEvents : TIncomingConnectionEvents;
  107.     m_xConnectionEvents : TConnectionEvents;
  108.     m_xStringTransferEvents : TStringTransferEvents;
  109.  
  110.     { These members are created once and reused throughout the application
  111.       to avoid recreating them each time they are required. }
  112.  
  113.     m_xTCPprotocol : Protocol;  { The TCP protocol to use (see Create) }
  114.  
  115.     m_xListeningSocket : ListeningSocket; { The socket that monitors incoming
  116.                                             connections (see
  117.                                             StartListeningForChatConnections) }
  118.  
  119.     { This list will hold all active connections throughout the chat
  120.       application's execution. Each item in the list is kept as a
  121.       ConnectionOrientedSocket. Each time an item is added or removed
  122.       from this list, we must call _AddRef or _Release on the item. }
  123.     m_xConnections : TList;
  124.  
  125.     { Private procedures }
  126.  
  127.     procedure StartListeningForChatConnections;
  128.     procedure SendToConnections( sDataToSend : String; bWait : Boolean );
  129.     procedure StopListeningForChatConnections;
  130.     procedure DisconnectFromChatSessions( sNickname : String );
  131.     procedure ConnectToChatSession( sNickname : String );
  132.     procedure RemoveConnection( xSocket : ConnectionOrientedSocket );
  133.     procedure DisplayMessage( sMsg : String );
  134.     procedure SetInterfaceState( bEnabled : Boolean );
  135.   public
  136.   end;
  137.  
  138. var
  139.   frmChat: TfrmChat; { Our main form }
  140.  
  141. implementation
  142.  
  143. {$R *.DFM}
  144.  
  145. {------------------------------------------------------------------------------}
  146. {                                SYSTEM EVENTS                                 }
  147. {------------------------------------------------------------------------------}
  148.  
  149. { FormCreate: Triggered by Delphi when a form instance is being created.
  150.               Here, we initialize all global variables that we need
  151.               throughout the application. }
  152.  
  153. procedure TfrmChat.FormCreate(Sender: TObject);
  154. var
  155.   xProtocols : Protocols; { This object will provide all available protocols }
  156. begin
  157.   { Create our event objects that will receive events from the Library }
  158.  
  159.   m_xIncomingConnectionEvents := TIncomingConnectionEvents.Create( Self );
  160.   m_xConnectionEvents := TConnectionEvents.Create( Self );
  161.   m_xStringTransferEvents := TStringTransferEvents.Create( Self );
  162.  
  163.   { To avoid creating a TCP protocol object again and again, we get one
  164.     once and for all! }
  165.  
  166.   xProtocols := CoProtocols.Create;
  167.  
  168.   { The GetProtocol method helps isolate a specific protocol of interest.
  169.     Our sample uses the TCP/IP protocol, which is connection-oriented. }
  170.     
  171.   m_xTCPprotocol := xProtocols.GetProtocol( wafUnspecified, wstUnspecified, wptIP_TCP );
  172.  
  173.   { Instantiate our global list of active connections that will actually
  174.     contain ConnectionOrientedSocket instances }
  175.  
  176.   m_xConnections := TList.Create;
  177.  
  178.   { Start listening for incoming connections now that the form is loaded }
  179.  
  180.   StartListeningForChatConnections;
  181. end;
  182.  
  183. {------------------------------------------------------------------------------}
  184. { FormDestroy: Called by Delphi when the form instance is being released.
  185.                We make sure to release all our instances too! }
  186.  
  187. procedure TfrmChat.FormDestroy(Sender: TObject);
  188. begin
  189.   { Stop listening for incoming connections. }
  190.  
  191.   StopListeningForChatConnections;
  192.  
  193.   { Disconnect everyone that we are connected to }
  194.  
  195.   DisconnectFromChatSessions( edtNickname.Text );
  196.  
  197.   { Free each instance }
  198.  
  199.   m_xTCPprotocol := Nil;
  200.   m_xConnections.Free;
  201.  
  202.   m_xIncomingConnectionEvents.Free;
  203.   m_xConnectionEvents.Free;
  204.   m_xStringTransferEvents.Free;
  205. end;
  206.  
  207. {------------------------------------------------------------------------------}
  208. { btnConnectClick: Called when a user clicks the Connect button. }
  209.  
  210. procedure TfrmChat.btnConnectClick(Sender: TObject);
  211. begin
  212.   ConnectToChatSession( edtNickname.Text );
  213. end;
  214.  
  215. {------------------------------------------------------------------------------}
  216. { btnDisconnectClick: Called when a user clicks the Disconnect button. }
  217.  
  218. procedure TfrmChat.btnDisconnectClick(Sender: TObject);
  219. begin
  220.   DisconnectFromChatSessions( edtNickname.Text );
  221. end;
  222.  
  223. {------------------------------------------------------------------------------}
  224. { btnClearScreenClick: Called when a user clicks the Clear Screen button. }
  225.  
  226. procedure TfrmChat.btnClearScreenClick(Sender: TObject);
  227. begin
  228.   memoChatThread.Text := '';
  229. end;
  230.  
  231. {------------------------------------------------------------------------------}
  232. { btnCloseClick: Called when a user clicks the Close button. }
  233.  
  234. procedure TfrmChat.btnCloseClick(Sender: TObject);
  235. begin
  236.   { Close the form. The FormDestroy event will be triggered, which will
  237.     cause all connections to be closed and all objects to be freed. }
  238.  
  239.   Close;
  240. end;
  241.  
  242. {------------------------------------------------------------------------------}
  243. { btnSendClick: Called when the user clicks on the Send button. We send the
  244.                 text, reset the text field contents and set the focus back
  245.                 to the text field. }
  246.  
  247. procedure TfrmChat.btnSendClick(Sender: TObject);
  248. var
  249.   sMessage : String;
  250. begin
  251.   { Format the output text to include our nickname }
  252.  
  253.   sMessage := edtNickname.Text + ' > ' + memoMessage.Text + #13#10;
  254.  
  255.   { Send the text to all active connections so they can display it }
  256.  
  257.   SendToConnections( sMessage, False );
  258.  
  259.   { Display the message in our own output window }
  260.  
  261.   DisplayMessage( sMessage );
  262.  
  263.   { Clear the edit field so its ready for user to enter a new message }
  264.  
  265.   memoMessage.Text := '';
  266.   memoMessage.SetFocus;
  267. end;
  268.  
  269. {-------------------------------------------------}
  270. { memoMessageKeyPress: Called everytime a user presses a key while the memo
  271.                        field has the focus. If the pressed key is "Enter", we
  272.                        perform a virtual click on the "Send" button. }
  273.  
  274. procedure TfrmChat.memoMessageKeyPress(Sender: TObject; var Key: Char);
  275. begin
  276.   if ( Key = Chr(13) ) or ( Key = Chr(10) ) then
  277.   begin
  278.     btnSend.Click;  { Fake this system event }
  279.     Key := Chr(0);  { Cancel this pressed key }
  280.   end;
  281. end;
  282.  
  283. {------------------------------------------------------------------------------}
  284. {                             XCEED WINSOCK EVENTS                             }
  285. {------------------------------------------------------------------------------}
  286.  
  287. { OnConnection: Triggered when a connection is about to be made with a client
  288.                 application. The "bReject" is false by default, so since we
  289.                 accept any connections in this sample, we can just ignore this
  290.                 event. }
  291.  
  292. procedure TfrmChat.OnConnection( const xListeningSocket : IDispatch;
  293.                                  const xRemoteAddress : IDispatch;
  294.                                  vaCallerData : OleVariant;
  295.                                  lExpectedCalleeDataSize : Integer;
  296.                                  var vaCalleeData : OleVariant;
  297.                                  var xQualityOfService : IdXWQualityOfServiceInfo;
  298.                                  var lUserParam : Integer;
  299.                                  var bReject : WordBool );
  300. begin
  301.   { Nothing special to do }
  302. end;
  303.  
  304. {------------------------------------------------------------------------------}
  305. { OnConnectionProcessed: Triggered when a connection was established with a
  306.                          client application. This event provides a brand new
  307.                          ConnectionOrientedSocket instance representing the
  308.                          new connection. We'll add this new socket to our list
  309.                          of active connections. }
  310.  
  311. procedure TfrmChat.OnConnectionProcessed( const xListeningSocket: IDispatch;
  312.                                           const xIncomingSocket: IDispatch;
  313.                                           lUserParam: Integer );
  314. var
  315.   xConnectionOrientedSocket : ConnectionOrientedSocket; { The new socket object }
  316.   xRemoteAddress : Address;
  317. begin
  318.   { This event is ready to handle many types of connections. We'll need to
  319.     "typecast" the xIncomingSocket to the proper type, using the "As" operator. }
  320.  
  321.   xConnectionOrientedSocket := xIncomingSocket As ConnectionOrientedSocket;
  322.  
  323.   { Add this new socket to our global list of active connections, making sure
  324.     to add a reference on the object, since the TList object won't do this
  325.     automatically, and all COM objects must have their ref count incremented
  326.     everytime another object holds a pointer to them. }
  327.  
  328.   xConnectionOrientedSocket._AddRef;
  329.   m_xConnections.Add( Pointer( xConnectionOrientedSocket ) );
  330.   btnDisconnect.Enabled := True;
  331.  
  332.   { Display a message that a new connection has been established. }
  333.  
  334.   xRemoteAddress := xConnectionOrientedSocket.RemoteAddress As Address;
  335.   DisplayMessage( 'New user has connected from ' + xRemoteAddress.GetAddressString + #13#10 );
  336.  
  337.   { Inform the other chat application of who we are. }
  338.  
  339.   xConnectionOrientedSocket.AsyncSendString( 'You are now in session with ' +
  340.                                              frmChat.edtNickname.Text + #13#10,
  341.                                              wnfAnsiStrings, 0, wsoNone );
  342. end;
  343.  
  344. {------------------------------------------------------------------------------}
  345. { OnListeningError: Triggered when an error occurs while listening for
  346.                     incoming connections. We ignore this event in this sample. }
  347.  
  348. procedure TfrmChat.OnListeningError( const xListeningSocket : IDispatch;
  349.                                      lUserParam: Integer;
  350.                                      lResultCode: Integer );
  351. begin
  352.   { Ignored }
  353. end;
  354.  
  355. {------------------------------------------------------------------------------}
  356. { OnDisconnected: Triggered when a connection-oriented socket becomes
  357.                   disconnected for any reason. Some protocols support
  358.                   "disconnection data", which is data exchanged at the
  359.                   disconnection phase. In this sample, it is irrelevent
  360.                   and we ignore this data. }
  361.  
  362. procedure TfrmChat.OnDisconnected( const xSocket: IDispatch;
  363.                                    vaCallerData: OleVariant;
  364.                                    var vaCalleeData: OleVariant );
  365. begin
  366.   { Display a message informing that someone has disconnected }
  367.  
  368.   DisplayMessage( 'A user has disconnected!' + #13#10 );
  369.  
  370.   { Remove the socket that just disconnected from our list of active
  371.     connections. }
  372.  
  373.   RemoveConnection( xSocket As ConnectionOrientedSocket );
  374. end;
  375.  
  376. {------------------------------------------------------------------------------}
  377. { OnStringSent: Triggered when a call to "AsyncSendString" completed. }
  378.  
  379. procedure TfrmChat.OnStringSent( const xSocket: IDispatch;
  380.                                  lUserParam: Integer;
  381.                                  lResultCode: Integer );
  382. begin
  383.   { We ignore this event }
  384. end;
  385.  
  386. {------------------------------------------------------------------------------}
  387. { OnStringReceived: When advising for one of the "always" flags, this event is
  388.                     triggered everytime a new string is received from a
  389.                     connection-oriented socket. This is useful when you don't
  390.                     want to call "ReceiveString" or "AsyncReceiveString"
  391.                     at particular moments, but rather react to incoming
  392.                     strings.
  393.                     When advising for one of the "OnDemand" flags, this event is
  394.                     triggered for every "AsyncReceiveString" or
  395.                     "AsyncReceiveLine" that completes. }
  396.  
  397. procedure TfrmChat.OnStringReceived( const xSocket: IDispatch;
  398.                                      const sString: WideString;
  399.                                      lUserParam: Integer;
  400.                                      lResultCode: Integer );
  401. begin
  402.   { We simply display the incoming data in our text box }
  403.  
  404.   DisplayMessage( sString );
  405. end;
  406.  
  407. {------------------------------------------------------------------------------}
  408. { OnStringAvailable: Triggered when new data has arrived and was buffered by
  409.                      the library. You then should call "ReceiveString" or
  410.                      "AsyncReceiveString" to retrieve just the amount of bytes
  411.                      you're interested in. In this sample, we prefer handling
  412.                      the OnStringReceived event everytime data arrives. }
  413.  
  414. procedure TfrmChat.OnStringAvailable( const xSocket: IDispatch;
  415.                                       lCharsReceived: Integer;
  416.                                       lCharsAvailable: Integer );
  417. begin
  418.   { We ignore this event }
  419. end;
  420.  
  421. {------------------------------------------------------------------------------}
  422. { OnOutOfBandStringReceived: Triggered when out-of-band data is received while
  423.                              you advised to receive OOB data with the
  424.                              wsaAdviseOutOfBandReceivedAlways flag. }
  425.  
  426. procedure TfrmChat.OnOutOfBandStringReceived( const xSocket: IDispatch;
  427.                                               const sString: WideString;
  428.                                               lResultCode: Integer );
  429. begin
  430.   { We ignore this event }
  431. end;
  432.  
  433. {------------------------------------------------------------------------------}
  434. {                              PRIVATE PROCEDURES                              }
  435. {------------------------------------------------------------------------------}
  436.  
  437. { StartListeningForChatConnections: This procedure creates a ListeningSocket
  438.                                     and makes it start listening for incoming
  439.                                     connections. }
  440.  
  441. procedure TfrmChat.StartListeningForChatConnections;
  442. var
  443.   xLocal   : InetAddress;   { Will represent our local IP address }
  444.   xFactory : SocketFactory; { Allows creation of various types of sockets }
  445. begin
  446.   { The SocketFactory class is used to create all kinds of sockets. }
  447.  
  448.   xFactory := CoSocketFactory.Create;
  449.  
  450.   { Create our listening socket. As a parameter, we provide the global
  451.     m_xTCPprotocol variable which defines the protocol we want the listening
  452.     socket to use }
  453.  
  454.   { The listening socket that we are about to create will be handling all
  455.     incoming connection requests from other chat samples that are trying to
  456.     establish a connection with us. }
  457.  
  458.   m_xListeningSocket := xFactory.CreateListeningSocket( m_xTCPprotocol, 0 );
  459.  
  460.   { Later, we will call our listening socket's StartListening method.
  461.     This will cause the library to trigger the OnConnectionProcessed
  462.     event whenever a connection is established. Whenever the
  463.     OnConnectionProcess event is triggered, the library provides (as
  464.     a parameter of the event) a reference to a newly created
  465.     ConnectionOrientedSocket object that you can use in order to
  466.     communicate with the new connection.
  467.  
  468.     The ConnectionOrientedSocket object offers various Advise methods.
  469.     An Advise method allows you to tell the library that you wish to
  470.     receive certain events. So you can, for example, make the
  471.     socket trigger string transfer events whenever a string is received
  472.     from its connection. Each time a new socket is provided by the
  473.     OnConnectionProcessed event metionned above, it must be Advised.
  474.  
  475.     When using a listening socket, there's a better way to proceed.
  476.     An interesting feature of a listening socket is that it lets you
  477.     call the same Advise methods offered by the ConnectionOrientedSocket
  478.     object. For the listening socket itself, these Advise calls have
  479.     no meaning. But all newly created ConnectionOrientedSocket instances
  480.     provided by the OnConnectionProcessed event will inherit the listening
  481.     socket's Advises. You won't need to individually Advise each newly
  482.     created socket as described in the previous paragraph. That's why,
  483.     in the next two lines of code below, we Advise the listening socket
  484.     about some events we want future ConnectionOrientedSockets to trigger. }
  485.  
  486.   m_xListeningSocket.ConnectionAdvise( m_xConnectionEvents.m_xEventObject,
  487.                                        wcaAdviseDisconnected );
  488.   m_xListeningSocket.StringTransferAdvise( m_xStringTransferEvents.m_xEventObject,
  489.                                            wsaAdviseReceivedAlways,
  490.                                            wnfAnsiStrings );
  491.  
  492.   { "Listening" means to check for connections on a specific local address. Our
  493.     sample uses IP addresses, so we create an InetAddress object. Any newly
  494.     instantiated InetAddress, Inet6Address or IpxAddress is always initialized
  495.     by the library to a value representing the default local address for the
  496.     current machine. }
  497.  
  498.   xLocal := CoInetAddress.Create;
  499.  
  500.   { Now we specify the IP port we will be listening on. Our sample uses
  501.     port 1555, which is not used by any other known services. }
  502.  
  503.   xLocal.Port := 1555;
  504.  
  505.   try
  506.     { Now we are ready to start listening on this default local address for
  507.       any incoming connections. We tell the library that we want to receive
  508.       all incoming connection events throught the m_xIncomingConnectionEvents
  509.       object. }
  510.  
  511.     m_xListeningSocket.StartListening( xLocal,
  512.                                        m_xIncomingConnectionEvents.m_xEventObject,
  513.                                        wifAdviseConnectionProcessed );
  514.  
  515.     DisplayMessage( 'Waiting for incoming connections...' + #13#10 );
  516.   except
  517.     on E:EOleException do
  518.     begin
  519.       DisplayMessage( 'Error: ' + E.Message + #13#10 );
  520.     end;
  521.   end;
  522. end;
  523.  
  524. {------------------------------------------------------------------------------}
  525. { SendToConnections: Send a string message to all current connections. If the
  526.                      bWait parameter is True, it uses a blocking call in order
  527.                      to ensure that the string has really been sent. This is
  528.                      used only when sending a "disconnection" message to the
  529.                      other users before closing all sockets. If bWait is False,
  530.                      it sends the string asynchronously (non-blocking). }
  531.  
  532. procedure TfrmChat.SendToConnections( sDataToSend : String; bWait : Boolean );
  533. var
  534.   xSocket : ConnectionOrientedSocket;
  535.   nIterator : Integer;
  536. begin
  537.   nIterator := 0;
  538.  
  539.   { For each ConnectionOrientedSocket in our list... }
  540.   While nIterator < m_xConnections.Count do
  541.   begin
  542.     { A TList keeps objects as a pointer. We must typecast each item to what
  543.       these items really are: ConnectionOrientedSocket instances. }
  544.  
  545.     xSocket := ConnectionOrientedSocket( m_xConnections[ nIterator ] );
  546.  
  547.     if bWait then
  548.       xSocket.SendString( sDataToSend, wnfAnsiStrings, wsoNone )
  549.     else
  550.       xSocket.AsyncSendString( sDataToSend, wnfAnsiStrings, 0, wsoNone );
  551.  
  552.     nIterator := nIterator + 1;
  553.   end;
  554. end;
  555.  
  556. {------------------------------------------------------------------------------}
  557. { StopListeningForChatConnections: This procedure makes our listening socket
  558.                                    stop listening for incoming connections, and
  559.                                    also releases this listening socket. }
  560.  
  561. procedure TfrmChat.StopListeningForChatConnections;
  562. begin
  563.   { We always create our listening socket right before we tell it to
  564.     start listening. So now, right after we stop listening, we release our
  565.     instance by assigning Nil to our member variable. }
  566.  
  567.   m_xListeningSocket.StopListening;
  568.   m_xListeningSocket := Nil;
  569. end;
  570.  
  571. {------------------------------------------------------------------------------}
  572. { DisconnectFromChatSessions: Sends a "disconnecting" message to all active
  573.                               connections and then disconnects them all. }
  574.  
  575. procedure TfrmChat.DisconnectFromChatSessions(sNickname : String);
  576. var
  577.   xSocket : ConnectionOrientedSocket;
  578. begin
  579.   { Inform each remote user that we are leaving }
  580.  
  581.   SendToConnections( sNickname + ' is disconnecting.', True );
  582.  
  583.   { Close each connection. We could just release each reference we have in our
  584.     list, but this is a cleaner way to end a conversation. }
  585.  
  586.   while m_xConnections.Count > 0 do
  587.   begin
  588.     xSocket := ConnectionOrientedSocket( m_xConnections[0] );
  589.     xSocket.Disconnect;
  590.  
  591.     { Remove it from our list of active connections. }
  592.     RemoveConnection( xSocket );
  593.   end;
  594. end;
  595.  
  596. {------------------------------------------------------------------------------}
  597. { ConnectToChatSession: Creates a new connection-oriented socket and uses it
  598.                         to connect to a remote host running the chat
  599.                         application. }
  600.  
  601. procedure TfrmChat.ConnectToChatSession;
  602. var
  603.   xFactory     : SocketFactory;            { Allows creation of various types of sockets }
  604.   xSocket      : ConnectionOrientedSocket; { This will be the socket we will use }
  605.   sHostAddress : String;                   { The remote address entered by the user }
  606.   xRemote      : InetAddress;              { This object will contain the remote address }
  607. begin
  608.   { Disable the interface }
  609.  
  610.   SetInterfaceState( False );
  611.  
  612.   try
  613.     { Instanciate the socket factory }
  614.  
  615.     xFactory := CoSocketFactory.Create;
  616.  
  617.     { Create the connection-oriented socket. Use the global m_xTCPprotocol
  618.       object which represents the protocol that the socket should use. }
  619.  
  620.     xSocket := xFactory.CreateConnectionOrientedSocket( m_xTCPprotocol, 0 );
  621.  
  622.     { Now we advise the library that we wish to receive certain events. Keep
  623.       in mind that this ConnectionOrientedSocket is not generated by our
  624.       listening socket, and therefore does not inherit the Advises we performed
  625.       on our listening socket. That's why we need to Advise it. }
  626.  
  627.     xSocket.ConnectionAdvise( m_xConnectionEvents.m_xEventObject,
  628.                               wcaAdviseDisconnected);
  629.     xSocket.StringTransferAdvise( m_xStringTransferEvents.m_xEventObject,
  630.                                   wsaAdviseReceivedAlways, wnfAnsiStrings);
  631.  
  632.     { Now, it's time to connect somewhere! Lets display a dialog box to ask
  633.       for the remote address where to connect to. }
  634.  
  635.     sHostAddress := InputBox( 'Xceed Winsock Library Sample - Chat',
  636.                               'Enter the IP address of the machine you wish to connect to:',
  637.                               '');
  638.  
  639.     if Length(sHostAddress) > 6 Then
  640.     begin
  641.       { Create an InetAddress object that will represent the address. }
  642.  
  643.       xRemote := CoInetAddress.Create;
  644.  
  645.       { Call the InetAddress's SetAddressString to set the InetAddress' object's
  646.         address directly from the string version entered by the user. }
  647.  
  648.       xRemote.SetAddressString( sHostAddress );
  649.  
  650.       { We now set the port number we'll be connecting to. }
  651.  
  652.       xRemote.Port := 1555;
  653.  
  654.       { Display that we are trying to connect... }
  655.  
  656.       DisplayMessage( 'Connecting to <' + sHostAddress + '>...' + #13#10 );
  657.  
  658.       { Now attempt to establish a connection to the remote host. }
  659.  
  660.       xSocket.Connect( xRemote );
  661.  
  662.       { If we get here, no error has occured and we can assume we
  663.         are connected. So now we add this socket to our list of active
  664.         connections. When we do this, we need to notify COM that we are
  665.         keeping a reference on this object, so that the object is not
  666.         deleted because COM thinks nobody needs it anymore. }
  667.  
  668.       xSocket._Addref;
  669.       m_xConnections.Add( Pointer( xSocket ) );
  670.  
  671.       { Let's inform the remote host of who we are. }
  672.  
  673.       xSocket.AsyncSendString( sNickname + ' has joined the session!' + #13#10,
  674.                                wnfAnsiStrings, 0, wsoNone );
  675.  
  676.     end;
  677.   except
  678.      on E:EOleException do
  679.      begin
  680.        DisplayMessage( 'Error: ' + E.Message + #13#10 );
  681.      end;
  682.   end;
  683.  
  684.   { re-enable the interface }
  685.  
  686.   SetInterfaceState( True );
  687. end;
  688.  
  689. {------------------------------------------------------------------------------}
  690. { RemoveConnection: Removes a ConnectionOrientedSocket from the list of active
  691.                     connections. The reference count on the socket must be
  692.                     decreased using the "_Release" function because it was
  693.                     incremented (with "_Addref") when the socket was added to
  694.                     the list. }
  695.  
  696. procedure TfrmChat.RemoveConnection(xSocket : ConnectionOrientedSocket);
  697. begin
  698.   m_xConnections.Remove( Pointer( xSocket ) );
  699.   xSocket._Release;
  700. end;
  701.  
  702. {------------------------------------------------------------------------------}
  703. { DisplayMessage: This procedure displays a message in the output window }
  704.  
  705. procedure TfrmChat.DisplayMessage(sMsg : String);
  706. begin
  707.   memoChatThread.Text := memoChatThread.Text + sMsg;
  708.   Application.ProcessMessages; { Give Windows a chance to display it }
  709. end;
  710.  
  711. {------------------------------------------------------------------------------}
  712. { SetInterfaceState: Change the state of the interface, enabling and disabling
  713.                      buttons on the screen, changing the mouse pointer. }
  714.  
  715. procedure TfrmChat.SetInterfaceState(bEnabled : Boolean);
  716. begin
  717.   btnConnect.Enabled := bEnabled;
  718.   btnSend.Enabled := bEnabled;
  719.   btnClearScreen.Enabled := bEnabled;
  720.   btnDisconnect.Enabled := bEnabled And ( m_xConnections.Count > 0 );
  721.   btnClose.Enabled := bEnabled;
  722.  
  723.   if bEnabled then
  724.     Screen.Cursor := crDefault
  725.   else
  726.     Screen.Cursor := crHourglass;
  727. end;
  728.  
  729. end.
  730.  
  731.