JavaScript Editor JavaScript Editor     JavaScript Debugger 



Team LiB
Previous Section Next Section

Creating a Windows Messenger Client

To learn how to use the MSNP component, it helps to create a simple Messenger client that emulates some of the standard features found in the Windows Messenger application. As a prerequisite, you should understand how a basic Messenger interaction works, as described here:

  1. You sign in to Messenger with a valid user name and get authenticated.

  2. If desired, you retrieve your list of contacts and their statuses.

  3. You start a session with one of your Messenger contacts. A session can be thought of as a separate chat window in the Messenger application. Before you can send messages to any user, either you (or the recipient) must start a session by opening a chat window. You can also create multiple sessions at once (although our simple example won't use this feature). Whenever a session is established, the contact list is updated.

  4. You send and receive messages through the server switchboard.

  5. At some later point, you end the session and sign out.

Figure 12-1 shows the client we'll create to demonstrate the MSNP component. It allows a user to log in, see other contacts, start a session, and send messages.

Click To expand
Figure 12-1: The custom Messenger client

To create this client, start by creating a new Windows project. Add the reference to the msnp.dll and import the MSNP namespace if desired.

In order to send and receive messages with the MSNP component, you must create a class that implements the ISessionHandler interface. As part of this interface, you'll need to implement methods such as MessageReceived() and ErrorReceived(). These methods will be triggered by the MSNP component in response to messages received from the Messenger network. (A more typical way to implement this type of design is to use events. However, this approach is equivalent.)

The ISessionHandler interface allows you to receive messages. To send messages, you must create an instance of the MSNPHelper class. The MSNPHelper class allows you to retrieve contacts, sign in and sign out, and create sessions. Every session is handled by a separate instance of the Session class. You use the Session class to send messages. Figure 12-2 diagrams this interaction.

Click To expand
Figure 12-2: Interacting with Messenger through the MSNP component

In our simple example, the ISessionHandler interface is implemented directly by the form:

Public Class MessengerForm
    Inherits System.Windows.Forms.Form
    Implements MSNP.ISessionHandler

The form also uses some form-level variables to track the current MSNPHelper and Session objects:

    ' The helper used to sign in and out and retrieve contacts.
    Private Helper As MSNP.MSNPHelper

    ' These variables track the current session as well as the related user.
    Private CurrentSessionUser As String
    Private CurrentSession As MSNP.Session

When the form loads, it signs in to a new Messenger session. The user e-mail address and password are hard-coded to facilitate testing, but you could easily add a login window. The IP address is retrieved for the dispatch server using the System.Net.Dns class.

Private Sub MessengerForm_Load(ByVal sender As System.Object, _
  ByVal e As System.EventArgs) Handles MyBase.Load

    ' Retrieve the IP address for the messenger server.
    Dim IP As String
    IP = System.Net.Dns.GetHostByName( _
      "messenger.hotmail.com").AddressList(0).ToString()

    ' For simplicity's sake, a test user is hard-coded.
    ' Note that that communication is always performed on port 1863.
    Helper = New MSNP.MSNPHelper(IP, 1863, "mymsgtest@hotmail.com", _
      "letmein", Me)

    ' SignIn with the supplied information.
    ' This method blocks until the sign operation is complete.
    ' An invalid user or password may simply stall the application without
    ' generating an error, so you may want to execute this method asynchronously.
    Helper.Signin()

    Me.RefreshContactList()
End Sub
Note 

Although the MSNPHelper requires that you supply the password in clear text, this password is never transmitted over the network. Instead, the password is hashed using the MD5 hashing algorithm and a value supplied by the server. For more information, refer to the detailed description of the underlying protocol at http://www.hypothetic.org/docs/msn/connecting.php.

When you create the MSNPHelper you supply the login information, the IP address and port to use, and an ISessionHandler object. In this example, the current form implements the ISessionHandler, so we pass that as a reference.

The next step is to call the form-level RefreshContactList() subroutine, which retrieves contact information and uses it to fill a ListView control:

Private Sub RefreshContactList()

    ' Fill the contact list.
    Dim Item As ListViewItem
    Dim Peer As MSNP.Contact
    For Each Peer In Me.Helper.FLContacts
        Item = lstContacts.Items.Add(Peer.FriendlyName)
        Item.SubItems.Add(Peer.State.ToString())
        Item.SubItems.Add(Peer.Substate.ToString())
        Item.SubItems.Add(Peer.UserName)
    Next

End Sub

This method is also called by the ISessionHandler UserJoined() and UserDeparted() methods. However, in this case the method won't execute on the main application thread, so the call must be marshaled using the Control.Invoke() method.

Public Sub UserDeparted(ByVal session As MSNP.Session, _
  ByVal userHandle As String) Implements MSNP.ISessionHandler.UserDeparted

    ' Refresh the contact list.
    Dim Invoker As New MethodInvoker(AddressOf Me.RefreshContactList)
    Me.Invoke(Invoker)

End Sub

Public Sub UserJoined(ByVal session As MSNP.Session, _
  ByVal userHandle As String, ByVal userFriendlyName As String) _
  Implements MSNP.ISessionHandler.UserJoined

    ' Refresh the contact list.
    Dim Invoker As New MethodInvoker(AddressOf Me.RefreshContactList)
    Me.Invoke(Invoker)

End Sub

Note that if the user's friendly name is different from his or her e-mail address, multiple entries may appear for the user in the contact list (you may have also noticed this phenomenon if you use the Microsoft Outlook Express Hotmail integration). You can use additional code to ignore entries with duplicate UserName values.

Nothing else happens until a user starts a session, or a session is started when another user sends a message. The user can start a session by selecting a user in the contact list and clicking the Create Session button. The button event handler uses the MSNPHelper.RequestSession() method, which returns immediately. The MSNP component will continue trying to establish the session for a maximum of about 30 seconds.

Private Sub cmdStartSession_Click(ByVal sender As System.Object, _
  ByVal e As System.EventArgs) Handles cmdStartSession.Click

    If Not Me.CurrentSession Is Nothing Then
        MessageBox.Show("There is already a current session.")
        Return

    Else
        If lstContacts.SelectedIndices.Count = 0 Then
            MessageBox.Show("No user is selected.")
              Return
        Else
            Dim Contact As String
            Contact = lstContacts.Items( _
              lstContacts.SelectedIndices(0)).SubItems(3).Text
            Helper.RequestSession(Contact, Guid.NewGuid())

        End If
    End If

End Sub

Note that every session requires an identifier that's generated by the client and is unique within the application. Our custom client simply creates a new GUID.

If the session is successfully established, the ISessionHandler.Session Started() method will be triggered. In our example, the method handler simply updates the form with the retrieved session ID and stores the session object in a member variable for use when sending messages later on. In addition, the ISessionHandler.SessionEnded() method removes these details.


Public Sub SessionStarted(ByVal session As MSNP.Session) _
  Implements MSNP.ISessionHandler.SessionStarted

    Dim Updater As New UpdateControlText(lblSession)
    Updater.ReplaceText(session.SessionIdentifier.ToString())
    Me.CurrentSession = session

End Sub

Public Sub SessionEnded(ByVal session As MSNP.Session) _
  Implements MSNP.ISessionHandler.SessionEnded

    ' Don't try to update the form if it's in the process of closing.
    If Not IsClosing Then
        Dim Updater As New UpdateControlText(lblSession)
        Updater.ReplaceText("")
    End If
    Me.CurrentSession = Nothing

End Sub

This code uses the UpdateControlText class, which can update the Text property of any control on the correct thread. This useful class is shown here:

Public Class UpdateControlText

    Private NewText As String
    Private ControlToUpdate As Control

    Public Sub New(ByVal controlToUpdate As Control)
        Me.ControlToUpdate = controlToUpdate
    End Sub

    Public Sub AddText(ByVal newText As String)
        SyncLock Me
            Me.NewText = newText
            Dim Invoker As New MethodInvoker(AddressOf AddText)
            Me.ControlToUpdate.Invoke(Invoker)
        End SyncLock
    End Sub

    ' This method executes on the user-interface thread.
    Private Sub AddText()
        Me.ControlToUpdate.Text &= NewText
    End Sub

    Public Sub ReplaceText(ByVal newText As String)
        SyncLock Me
            Me.NewText = newText
            Dim Invoker As New MethodInvoker(AddressOf ReplaceText)
            Me.ControlToUpdate.Invoke(Invoker)
        End SyncLock
    End Sub

    ' This method executes on the user-interface thread.
    Private Sub ReplaceText()
        Me.ControlToUpdate.Text = NewText
    End Sub

End Class

Now that a session is established, the client can send messages by clicking the Send button. The button event handler checks that there's a current session and uses the Session.SendMessage() method.

Private Sub cmdSend_Click(ByVal sender As System.Object, _
  ByVal e As System.EventArgs) Handles cmdSend.Click

    If Me.CurrentSession Is Nothing Then
        MessageBox.Show("There is no current session.")
        Return
    Else
        Me.CurrentSession.SendMessage(txtSend.Text)
        Dim NewText As String
        NewText = "SENT: " & txtSend.Text
        NewText &= Environment.NewLine & Environment.NewLine
        txtMessages.Text &= NewText
    End If

End Sub

Messages are received through the ISessionHandler.MessageReceived() method. Blank messages are ignored, because they're used to indicate that the user has started typing, thereby allowing you to display the "User is typing a message" status message in your application.

Public Sub MessageReceived(ByVal session As MSNP.Session, _
  ByVal message As MSNP.MimeMessage) _
  Implements MSNP.ISessionHandler.MessageReceived

    ' Add text.
    If message.Body <> "" Then
        Dim Updater As New UpdateControlText(txtMessages)
        Dim NewText As String
        NewText = "FROM: " & message.SenderFriendlyName
        NewText &= Environment.NewLine
        NewText &= "RECEIVED: " & message.Body
        NewText &= Environment.NewLine & Environment.NewLine
        Updater.AddText(NewText)
    End If
End Sub

Finally, when the form closes, it signs the user out of Windows Messenger.

Private Sub MessengerForm_Closed(ByVal sender As Object, _
  ByVal e As System.EventArgs) Handles MyBase.Closed

    If Not Me.CurrentSession Is Nothing Then
        Me.CurrentSession.EndSession()
    End If
    Helper.Signout()

End Sub

Figure 12-3 shows the interaction of two Windows Messenger peers, one of which uses the custom client.

Click To expand
Figure 12-3: Interaction with the custom Messenger

Team LiB
Previous Section Next Section


JavaScript Editor Free JavaScript Editor     JavaScript Editor