Now that you have written a simple TIBCO SmartSockets program, this section presents two programs that use some of the more advanced SmartSockets features. These programs are not presented in a tutorial format; instead portions of the Visual Studio .NET Visual Basic 7 code for each program are reviewed and key elements are discussed.
The SSChat program is an RTclient that implements a simple real-time, multi-user chat room. The example files are located in the installation directory under Examples\Microsoft.NET\vb7\sschat. The SSChat program is written using the SmartSockets publish-subscribe technology to implement this function with a minimum amount of code. The entire SSChat program is under 200 lines of Visual Basic code.
The SSChat application starts with the Login window, as shown in Figure 9. The Login window is a standard Visual Studio .NET form named frmLogin. It allows the user to input their full name and handle, and to specify an RTserver to connect to.
The essential details of SSChat's implementation are included within the main form, frmMain.
There are several form class variables defined as follows:
Dim mtHi As TipcMt Dim mtBye As TipcMt Dim msgHi As TipcMsg Dim msgOut As TipcMsg Dim RTserver_Conn As TipcSrv Dim HiCb_parms As New Collection Dim tsThread As ThreadStart Dim trtsThread As Thread Dim rtthrdclass As rtthrdclass
The first two variables, mtHi
and mtBye
, hold TipcMt objects specifying two of the custom message types SSChat defines. The second two, msgHi and msgOut, hold pre-built messages that are sent multiple times during an execution of SSChat. Finally, RTserver_Conn is a TipcSrv object that manages the connection to RTserver.
Looking at the mainInit()
method shows the actions taken upon entering the chat room. The custom message types are registered and the server name is configured. A connection to RTserver is created, process callbacks are instantiated using user callbacks implemented from TipcProcessCb, and a “Hi” (mtHi) message is constructed containing the user's name and chat handle, then sent. Please note that in this example, callbacks are used for completeness. The use of delegates is recommended over callbacks, and is demonstrated in the next example, WhoWhere.
Public Sub mainInit() Dim btn As Short Dim server_names As String Dim hi_cb As New HiCb Dim bye_cb As New ByeCb Dim data_cb As New DataCb ctlUser.Text = fmLogin.DefInstance.ctlHandle.Text ctlName.Text = fmLogin.DefInstance.ctlName.Text ctlScript.ForeColor = System.Drawing.Color.Blue ctlScript.Text = "[ " & Format(Now(), "General Date") & " ]" If mtHi Is Nothing Then mtHi = TipcSvc.createMt("hi", 1, "str str int2") mtBye = TipcSvc.createMt("bye", 2, "str") msgOut = TipcSvc.createMsg(TipcSvc.lookupMt("string_data")) End If msgOut.Dest = "_all" msgOut.appendStr(fmLogin.DefInstance.ctlHandle.Text) msgHi = msgOut.Clone msgHi.Type = mtHi msgHi.appendStr(fmLogin.DefInstance.ctlName.Text) msgHi.appendInt2(Int(CDbl(True))) server_names = "" If Len(fmLogin.DefInstance.ctlServer.Text) > 0 Then server_names = fmLogin.DefInstance.ctlServer.Text End If RTserver_Conn = TipcSvc.Srv RTserver_Conn.setOption("ss.unique_subject", fmLogin.DefInstance.ctlHandle.Text) RTserver_Conn.setOption("ss.project", "rtworks") RTserver_Conn.setOption("ss.server_names", server_names) RTserver_Conn.create() HiCb_parms.Add(RTserver_Conn) HiCb_parms.Add(msgHi) HiCb_parms.Add(msgOut) HiCb_parms.Add(ctlScript) RTserver_Conn.addProcessCb(bye_cb, mtBye, ctlScript) RTserver_Conn.addProcessCb(data_cb, TipcSvc.lookupMt("string_data"), ctlScript) RTserver_Conn.addProcessCb(hi_cb, mtHi, HiCb_parms) RTserver_Conn.setSubjectSubscribe("_all", True) RTserver_Conn.send(msgHi) RTserver_Conn.Flush() ' ' Here, we need a thread to allow us to do the MainLoop to ' receive messages from the server. So, create the appropriate ' objects, and set the server conn to ' use within the thread. Then, start the thread. rtthrdclass = New rtThrdClass tsThread = New ThreadStart(AddressOf rtthrdclass.mainThreadProc) trtsThread = New Thread(tsThread) rtthrdclass.thrdServerConn = RTserver_Conn rtthrdclass.thrdStop = False trtsThread.Start() End Sub
By cloning the msgOut
message, the Dest
property is the same for msgHi
. Appending True
requests other SSChat clients to reply upon receipt of the message.
To receive messages, the SSChat application must maintain a message loop. This is done in a separate thread. It is a simple loop that processes events and checks for messages without a timeout to provide the best possible GUI response.
Public Class rtThrdClass Public thrdServerConn As TipcSrv Public thrdStop As Boolean Public Sub mainThreadProc() While thrdStop = False thrdServerConn.MainLoop(0) Thread.Sleep(0) End While End Sub End Class
The loop is terminated when the thrdStop
flag is set to true
in mainCleanup()
.
Once the form has been loaded, SSChat is in its primary mode, waiting for keystrokes from the user or messages from RTserver. Examine what happens when a key is pressed. Note that when you press the Enter key, the current text is sent out to the other chat participants.
Private Sub ctlMessage_KeyDown(…) Handles ctlMessage.KeyDown Dim KeyCode As Short = eventArgs.KeyCode Dim Shift As Short = eventArgs.KeyData \ &H10000 Dim str_Renamed As String If (Shift And VB6.ShiftConstants.ShiftMask) = 0 Then If KeyCode = System.Windows.Forms.Keys.Return Then msgOut.NumFields = 1 str_Renamed = ctlMessage.Text If VB.Left(str_Renamed, 1) = Chr(13) Then str_Renamed = VB.Right(str_Renamed, Len(str_Renamed) - 2) End If msgOut.appendStr(str_Renamed) RTserver_Conn.send(msgOut) RTserver_Conn.Flush() ctlMessage.Text = "" End If End If End Sub
Now that you have reviewed the sending process of chat text from SSChat, take a look at the three message processing events that handle the receiving process. Note that these events are implementing the TipcProcessCb interface. The TipcMsg event from the first ctlHiCb
receives the “Hi” messages:
Friend Class HiCb Implements TipcProcessCb Public Sub process(ByVal msg As TIBCO.SMARTSOCKETS.TipcMsg, ByVal arg As Object) Implements TIBCO.SMARTSOCKETS.TipcProcessCb.process Dim msgIn As TipcMsg Dim msgHi As TipcMsg Dim msgOut As TipcMsg Dim srv_conn As TipcSrv Dim cb_parms As Collection Dim ctlScript As TextBox Dim inHandle As String Dim inUserName As String Dim inInt As Int32 msgIn = msg cb_parms = arg msgIn.Current = 0 inHandle = msgIn.nextStr() inUserName = msgIn.nextStr() inInt = msgIn.nextInt2() srv_conn = cb_parms.Item(1) msgHi = cb_parms.Item(2) msgHi.Current = 0 msgOut = cb_parms.Item(3) msgOut.Current = 0 ctlScript = cb_parms.Item(4) If inUserName.CompareTo(msgOut.nextStr()) = 0 Then If inInt Then msgHi.Dest = msgIn.Sender msgHi.NumFields = msgHi.NumFields - 1 msgHi.appendInt2((Int(CDbl(False)))) srv_conn.send(msgHi) srv_conn.flush() End If End If ctlScript.AppendText(ControlChars.NewLine & ControlChars.NewLine) ctlScript.ForeColor = System.Drawing.Color.Blue ctlScript.AppendText("[ " + inHandle + " connected as " + inUserName + " ]") End Sub End Class
Now look at the code that handles the “Bye” messages, sent when a user leaves the chat room, terminating the SSChat process. It is very similar to the “Hi” message event handler, except it does not send any replies.
Friend Class ByeCb Implements TipcProcessCb Public Sub process(ByVal msg As TIBCO.SMARTSOCKETS.TipcMsg, ByVal arg As Object) Implements TIBCO.SMARTSOCKETS.TipcProcessCb.process Dim msgIn As TipcMsg Dim ctlScript As TextBox msgIn = msg ctlScript = arg ctlScript.AppendText(ControlChars.NewLine & ControlChars.NewLine) ctlScript.ForeColor = System.Drawing.Color.Blue ctlScript.AppendText("[ " + msgIn.nextStr + " disconnected ]") End Sub End Class
The event handler for chat data messages is shown below (similar to the other two message events, it updates the output window with the originating chat user's handle and chat text):
Friend Class DataCb Implements TipcProcessCb Public Sub process(ByVal msg As TIBCO.SMARTSOCKETS.TipcMsg, ByVal arg As Object) Implements TIBCO.SMARTSOCKETS.TipcProcessCb.process Dim msgIn As TipcMsg Dim ctlScript As TextBox msgIn = msg ctlScript = arg ctlScript.AppendText(ControlChars.NewLine & ControlChars.NewLine) ctlScript.AppendText(msgIn.nextStr() + ": ") ctlScript.AppendText(msgIn.nextStr()) End Sub End Class
In the code above, the first msgIn.nextStr()
is the chat handle and the second msgIn.nextStr()
retrieves the actual user message from TipcMsg.
Finally, SSChat's mainCleanup()subroutine, which stops the listener thread, sends the “Bye” message, and disconnects from RTserver (if a connection has been established) is shown below:
Public Sub mainCleanup() Dim msgBye As TipcMsg If Not trtsThread Is Nothing Then ' ' Tell the thread to stop rtthrdclass.thrdStop = True ' ' Wait until the thread has ended trtsThread.Join(10000) trtsThread = Nothing End If If Not RTserver_Conn Is Nothing Then If RTserver_Conn.ConnStatusEx <> TipcDefs.CONN_NONE Then msgBye = TipcSvc.createMsg(mtBye) msgBye.Dest = msgOut.Dest msgOut.Current = 0 msgBye.appendStr(msgOut.nextStr()) RTserver_Conn.send(msgBye) RTserver_Conn.flush() RTserver_Conn.destroy(TipcDefs.CONN_NONE) End If RTserver_Conn = Nothing End If If Not mtBye Is Nothing Then mtBye.destroy() mtBye = Nothing End If If Not mtHi Is Nothing Then mtHi.destroy() mtHi = Nothing End If End Sub
The data flow in the SSChat program is:
The WhoWhere program is a graphical RTclient, implementing an in/out message board useful for tracking employee whereabouts. The WhoWhere program is located in the installation directory under Examples\Microsoft.NET\vb7\whowhere.
WhoWhere is an electronic counterpart to the sign in and out boards commonly used in corporate offices. WhoWhere uses TIBCO SmartSockets publish-subscribe technology to update other users message boards. Other SmartSockets features demonstrated by this program are:
WhoWhere tracks employee whereabouts. Each employee belongs to a department. The department an employee belongs to specifies the SmartSockets subject used to set apart message board groups. This allows potentially thousands of users in hundreds of departments to be present on the same LAN or WAN, without any conflict.
The WhoWhere application is composed of several Visual Studio .NET forms and one code module. Most of the forms (those for logging in, managing the configuration and specifying leave information) are handled with the usual Visual Studio .NET techniques; examination of the source code should be fairly self-explanatory. The important parts of the application are handled by the Display Board form and the modGlobals module.
First, look at some of the data structures used in the modGlobals module:
Public Structure configType Dim Name As String Dim Password As String Dim Lunch As Short Dim Email As String Dim Homepage As String Dim MailApp As String Dim WebApp As String Dim server As String Dim Alerts As Short Dim Department As String Dim EmailBrowser As Short End Structure Public Config As configType
The configType structure holds the local configuration information. This data is stored in the Windows registry by the SaveSettings subroutine and reloaded with LoadSettings. This allows the configuration to be persistent between executions of the application. The data includes the user's name, password and other configuration information. A subset of this information is maintained for all the other known employees on the message board as well, as shown in the userType data structure:
Public Structure userType Dim Who As String Dim Email As String Dim Homepage As String Dim Message As String Dim Where As String Dim ReturnInfo As String End Structure Public Myself As userType Public Users(maxUsers) As userType Public nUsers As Integer Public wwSubject As String Public Srv As TipcSrv Public mtAnnounce As TipcMt Public mtUpdate As TipcMt Public mtResponse As TipcMt Public Enum wwMessageTypes wwAnnounce = 100 wwUpdate wwResponse End Enum Public AnnounceCbParms As New Collection
Myself
holds a copy of this user's message board information; Users()
is the array that holds the actual message board information. nUsers
is the number of users in the array and therefore the number of users displayed on the board. wwSubject
holds the publish-subscribe subject name used for the WhoWhere client communication.
A global handle to the RTserver connection in the application is held in the Srv variable. Additionally, WhoWhere defines three new message types identified by the numbers 100, 101, and 102 as the enumeration wwMessageTypes specifies; mtAnnounce, mtUpdate and mtResponse act as global references to these message type objects that are created. Next, the initializeClient subroutine connects to RTserver and assigns user-defined delegates to SmartSockets events.
Public Sub initializeClient() displayErrors = True If Srv Is Nothing Then ' get a handle to our server connection Srv = TipcSvc.Srv ' set our options before we connect Srv.setOption("ss.server_names", Config.server) Srv.setOption("ss.project", programName) Srv.create(TipcDefs.CONN_FULL) destroyMts() ' create custom message types mtAnnounce = TipcSvc.createMt("wwAnnounce", wwMessageTypes.wwAnnounce, "int2 msg") mtResponse = TipcSvc.createMt("wwResponse", wwMessageTypes.wwResponse, "int2 msg") ' update is the msg that gets sent inside announce ' and response, and also by itself for updating status mtUpdate = TipcSvc.createMt("wwUpdate", wwMessageTypes.wwUpdate, "int2 str str str str str str") ' install event handlers for message types ' in this example, we use delegates instead of ' callbacks. For .NET applications, delegates ' are much more efficient. AddHandler Srv.TipcMsgEvent, AddressOf MsgHandler AddHandler Srv.TipcErrorEvent, AddressOf ErrorHandler End If wwSubject = "/" & programName & "/" & Config.Department Dim status As Boolean status = Srv.getSubjectSubscribe(wwSubject) If status = False Then Srv.setSubjectSubscribe(wwSubject, True) End If Srv.flush() ' send a message announcing our arrival doUpdate() End Sub
Only message board traffic published to the wwSubject subject is seen by this client. It is this hierarchical naming feature of SmartSockets that accounts for its scalability. Without changing the client, and with only one level of partitioning (the department), a large number of separate message boards can coexist. This is demonstrated in the RTserver.SubjectSetSubscribe statement in the initalizeClient() subroutine above. As illustrated in the code, a call to the doUpdate subroutine is made. This sends the equivalent of a “Hello” message from this RTclient to the other users displaying this department's message board. The following code for doUpdate builds and sends an announcement message using the mtAnnounce object as a reference (notice how a second SmartSockets message of type mtUpdate is included inside the announcement message):
Public Sub doUpdate() ' build and send an anouncement message ' for initially joining a message board subject ' or doing an 'update all' Dim am, myData As TipcMsg myData = TipcSvc.createMsg(mtUpdate) With myData .appendInt2(messageFormat) .appendStr(Myself.Who) .appendStr(Myself.Email) .appendStr(Myself.Homepage) .appendStr(Myself.Where) .appendStr(Myself.Message) .appendStr(Myself.ReturnInfo) End With am = TipcSvc.createMsg(mtAnnounce) am.appendInt2(messageFormat) am.appendMsg(myData) am.Dest = wwSubject Srv.send(am) Srv.flush() End Sub
The following subroutine, publishMyStatus, is used to send an update message when the current user's status changes, for example, when they leave or return to the office. A message of type mtUpdate is sent alone this time, not included inside another message.
Private Sub publishMyStatus() ' build and send just an update message when ' our status changes (go to lunch, return, etc.) Dim myData As TipcMsg myData = TipcSvc.createMsg(mtUpdate) With myData .appendInt2(messageFormat) .appendStr(Myself.Who) .appendStr(Myself.Email) .appendStr(Myself.Homepage) .appendStr(Myself.Where) .appendStr(Myself.Message) .appendStr(Myself.ReturnInfo) .Dest = wwSubject End With Srv.send(myData) Srv.flush() End Sub
The setAway
subroutine updates the buttons available on the WhoWhere graphical user interface (GUI). There are two subroutines, goAway and comeBack, that make calls to publishMyStatus
, as shown here:
Public Sub goAway() publishMyStatus() frmBoard.DefInstance.setAway(True) End Sub Public Sub comeBack() Myself.Where = inStatus publishMyStatus() frmBoard.DefInstance.setAway(False) End Sub End Module
The Display Board Form is the main user-interface object of the WhoWhere application. This form displays the message board with dynamic updating and allows the user to interact with the program through the command buttons. In the next example, the form's Load subroutine calls the other initialization routines such as initializeGUI, which positions and sizes various screen elements. initializeClient configures the environment for SmartSockets, assigns delegates to SmartSockets events and manages subscriptions to the relevant subjects.
A thread is also created to run a loop processing messages in the background. This is how the application receives and processes messages using the TipcSrv.Mainloop method. Note that the loop must be terminated by setting a flag in the frmBoard_Close subroutine.
Private Sub frmBoard_Load(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles MyBase.Load Me.Left = Val(GetSetting(regAppName, "WindowPos", "Left", Str(System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width – Me.Width))) Me.Top = Val(GetSetting(regAppName, "WindowPos", "Top", "0")) Me.Text = programName & " v" & programversion initializeGUI() noErrBox = True initializeClient() ' ' Here, we need a thread to allow us to do the MainLoop to ' receive messages from the server. So, create the appropriate ' objects, and set the server connection to ' use within the thread. Then, start the thread. rtthrdclass = New rtThrdClass() tsThread = New ThreadStart( AddressOf (rtthrdclass.mainThreadProc) trtsThread = New Thread(tsThread) rtthrdclass.thrdServerConn = Srv rtthrdclass.thrdStop = False trtsThread.Start() Me.Show() Me.Visible = True End Sub
Here is the rtThreadClass, demonstrating the use of TipcSrv.Mainloop to listen for and process messages.
Public Class rtThrdClass Public thrdServerConn As TipcSrv Public thrdStop As Boolean Public Sub mainThreadProc() While thrdStop = False thrdServerConn.MainLoop(0) Thread.Sleep(0) End While End Sub End Class
The form's Unload code saves the program settings and disconnects from the RTserver.
Private Sub frmBoard_Closed(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles MyBase.Closed DoLoop = False SaveSetting(regAppName, "WindowPos", "Left", Me.Left.ToString) SaveSetting(regAppName, "WindowPos", "Top", Me.Top.ToString) saveSettings() If Not rtthrdclass Is Nothing Then ' ' Tell the thread to stop rtthrdclass.thrdStop = True ' ' Wait until the thread has ended trtsThread.Join(10000) trtsThread = Nothing Srv.destroy() End If End Sub
As shown in the next example, the comeBack and goAway subroutines are called when the absence or return (btnBack) buttons are clicked:
Private Sub btnHome_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles btnHome.Click Myself.Where = "HOME" Myself.Message = "(left for the day)" Myself.ReturnInfo = "the next work day" goAway() btnBack.Focus() End Sub Private Sub btnLunch_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles btnLunch.Click Myself.Where = "LUNCH" Myself.Message = "(at lunch)" Myself.ReturnInfo = "at " & Format(DateAdd(Microsoft.VisualBasic.DateInterval.Minute, Config.Lunch, TimeOfDay)) goAway() btnBack.Focus() End Sub Private Sub btnExtended_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles btnExtended.Click frmExtended.DefInstance.ShowDialog() If extendedCancel Then Exit Sub goAway() btnBack.Focus() End Sub Private Sub btnBack_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles btnBack.Click comeBack() End Sub
The displayBoard subroutine is the most important in terms of user interface; it re-populates the message board with data from the Users() array. The board has nUsers+1 rows; the extra is used to display the column headings.
Public Sub displayBoard() Dim i As Short Dim wh As String If board.Items.Count > 0 Then board.Items.Clear() End If For i = 0 To nUsers - 1 Dim itm As New ListViewItem(Users(i).Who, i) wh = Users(i).Where If wh <> inStatus Then wh = wh & " - returns " & Users(i).ReturnInfo End If itm.SubItems.Add(wh) board.Items.Insert(i, itm) Next i btnMail.Visible = False btnWeb.Visible = False If Config.Alerts Then Beep() End If End Sub
The msgHandler
delegate handles announce, response, and update messages. It then passes the messages to various subroutines. The newUser
subroutine adds a user to the message board. Note that is was registered in initializeClient()
. As shown this example, if msgHandler
processes an announcement message, it responds by publishing a response message directly back to the originator:
' This is the message handler delegate to handle messages when they arrive. Public Sub MsgHandler(ByVal target As Object, ByVal args As TipcMsgEventArgs) Dim msg As TipcMsg Dim mt As TipcMt msg = args.Msg mt = msg.Type If mt.Num = wwMessageTypes.wwAnnounce Then HandleAnnounceMessage(msg) Exit Sub End If If mt.Num = wwMessageTypes.wwResponse Then HandleResponseMsg(msg) Exit Sub End If If mt.Num = wwMessageTypes.wwUpdate Then HandleUpdateMsg(msg) Exit Sub End If MsgBox("Received unexpected message of type " & mt.Name) End Sub
The following are called from the msgHandler delegate to handle different message types.
Public Sub HandleAnnounceMessage(ByVal msg As TipcMsg) Dim mver As Short Dim m2 As TipcMsg Dim resp As TipcMsg Dim myData As TipcMsg mver = msg.nextInt2 If mver > messageFormat Then frmBoard.DefInstance.wrongMessageFormat("ANNOUNCE", mver) Else m2 = msg.nextMsg mver = m2.nextInt2 frmBoard.DefInstance.newUser(m2) resp = TipcSvc.createMsg(mtResponse) resp.Dest = msg.Sender resp.appendInt2(mver) myData = TipcSvc.createMsg(mtUpdate) With myData .appendInt2(messageFormat) .appendStr(Myself.Who) .appendStr(Myself.Email) .appendStr(Myself.Homepage) .appendStr(Myself.Where) .appendStr(Myself.Message) .appendStr(Myself.ReturnInfo) End With resp.appendMsg(myData) Srv.send(resp) Srv.flush() End If End Sub Public Sub HandleResponseMsg(ByVal msg As TipcMsg) Dim mver As Short Dim m2 As TipcMsg Dim resp As TipcMsg Dim myData As TipcMsg mver = msg.nextInt2 If mver > messageFormat Then frmBoard.DefInstance.wrongMessageFormat("RESPONSE", mver) Else m2 = msg.nextMsg mver = m2.nextInt2 frmBoard.DefInstance.newUser(m2) End If End Sub Public Sub HandleUpdateMsg(ByVal msg As TipcMsg) Dim mver As Short mver = msg.nextInt2 If mver > messageFormat Then frmBoard.DefInstance.wrongMessageFormat("UPDATE", mver) Else frmBoard.DefInstance.updateUser(msg) End If End Sub
The TipcErrorEvent event is fired when a SmartSockets error occurs. The delegate that is registered in initializeClient()
will be called when the event is fired. In this case, the only action is to display relevant error information for the user to acknowledge:
Public Sub ErrorHandler(ByVal target As Object, ByVal args As TipcErrorEventArgs) If displayErrors Then MsgBox("SmartSockets error: " + args.errNum + ", " + args.errString, MsgBoxStyle.Exclamation + MsgBoxStyle.ApplicationModal + MsgBoxStyle.OKOnly, "SmartSockets Error") End If End Sub
The newUser subroutine takes an update message as a parameter, and adds the user information contained within to the Users() array, first removing any old instance of the user. In this example, the number of users is incremented and displayBoard is called to refresh the form display:
Public Sub newUser(ByRef um As TipcMsg) Dim j As Object Dim thisname As String thisname = um.NextStr ' if user already on board, remove them Dim i As Short Dim wasRemoved As Boolean i = 0 While (i < nUsers And Not wasRemoved) If Users(i).Who = thisname Then For j = i To nUsers - 2 Users(j) = Users(j + 1) Next j nUsers = nUsers - 1 wasRemoved = True End If i = i + 1 End While With Users(nUsers) .Who = thisname .Email = um.NextStr .Homepage = um.NextStr .Where = um.NextStr .Message = um.NextStr .ReturnInfo = um.NextStr End With nUsers = nUsers + 1 displayBoard() showCount() End Sub
As shown in the next example, the updateUser
subroutine takes an update message as a parameter, and updates the user information contained within to the Users()
array. If the user is not currently in the array, they are added. Like the newUser
subroutine, it calls displayBoard
to refresh the form display.
Public Sub updateUser(ByRef um As TipcMsg) Dim i As Object Dim thisname As String Dim uIndex As Short thisname = um.NextStr ' user already on board? uIndex = -1 For i = 0 To nUsers - 1 If Users(i).Who = thisname Then uIndex = i Exit For End If Next i ' add this user if necessary If uIndex = -1 Then uIndex = nUsers nUsers = nUsers + 1 End If With Users(uIndex) .Who = thisname .Email = um.NextStr .Homepage = um.NextStr .Where = um.NextStr .Message = um.NextStr .ReturnInfo = um.NextStr End With displayBoard() showCount() End Sub
The btnUpdate subroutine, as shown below, is invoked when the Update All button is clicked. It resets the user count and calls doUpdate, re-publishing an announce message. The other users' message board applications will see this and send response messages directly to the running WhoWhere RTclient, populating the Users() array as the message callback events are invoked.
Private Sub btnUpdate_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles btnUpdate.Click nUsers = 0 doUpdate() board.Focus() End Sub
The flow of messages used in the WhoWhere program is:
There are some points to note when you examine all the WhoWhere source code:
This value is decoded with passages similar to the next example in the message processing events:
If mver > messageFormat Then frmBoard.DefInstance.wrongMessageFormat("ANNOUNCE", mver) Else … End If
The code extracts the first field from the messages, a two-byte integer, and checks it against the global constant messageFormat (see modGlobals for the definition of messageFormat). This ensures that if newer versions of WhoWhere, with different message grammars are present in the same department, the older RTclients do not corrupt their data with incompatible messages. Your applications may need more sophisticated message version control. This can be implemented with the UserProp property of TipcMsg objects.
TIBCO SmartSockets™ .NET User’s Guide and Tutorial Software Release 6.8, July 2006 Copyright © TIBCO Software Inc. All rights reserved www.tibco.com |