JavaScript Editor
JavaScript Debugger
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:
You sign in to Messenger with a valid user name and get authenticated.
If desired, you retrieve your list of contacts and their statuses.
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.
You send and receive messages through the server switchboard.
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.
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.
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.
Free JavaScript Editor
JavaScript Editor