How to: Start a Lync IM conversation
Learn how to use Microsoft Lync 2013 SDK to programmatically start a Microsoft Lync 2013 conversation, invite a user, and send the first instant messaging (IM) text.
Applies to: Lync 2013 | Lync Server 2013
Prerequisites
The prerequisites for starting an IM conversation are as follows:
Microsoft Lync 2013 must be installed and running on the development computer.
You must have sign-in credentials for Microsoft Lync Server 2013.
Microsoft Lync 2013 SDK must be installed on the development computer.
Core concepts to know
The following topics describe the three components of a Lync 2013 conversation.
Topic |
Description |
---|---|
Describes the role of the conversation manager in creating conversations. |
|
Describes how conversation modes are encapsulated by modalities. |
|
Describes how conversation participants are encapsulated. |
IM conversation programming overview
Conversations can only be started when the user is signed in to Lync 2013. Read about How to: Sign a user in to Lync and be sure that your application logic provides this capability before adding an IM feature to your UI.
A Lync 2013 IM conversation is represented by three API objects:
Conversation object
Participant object collection
Modality object collection
Each of these objects exposes properties, methods, and events that play a role in programmatic interaction with the conversation. The following diagram shows you how the objects interact.
Start an IM conversation
The following procedure obtains a conversations manager and adds the conversation.
To start an IM conversation
Create a callback method that handles the ConversationAdded event.
Create a callback method that handles the ParticipantAdded event.
Get the LyncClient instance and verify that the client is signed in to the server.
For information about signing in to Microsoft Lync Server 2013, see How to: Sign a user in to Lync.
Get the ConversationManager class instance by reading the ConversationManager property.
Register for the ConversationAdded event.
Call AddConversation.
Important If you have a conference URL and intend to join the associated conference, call JoinConference instead of AddConversation. An instance of Conversation is returned.
Handle events
The next two procedures are examples of the steps to create event handlers for conversation-related events. Examples of the event handler methods are included later in this topic.
Handle the ConversationAdded event
Complete the following steps within a callback method that handles ConversationAdded.
To handle the ConversationAdded event
Check the modality state of the IM modality on the added conversation.
If the modality is ModalityState.Notified, the conversation was not started by the local user. For this procedure, handle the event for only the conversation added by the local user.
Tip The ConversationAdded event is raised when either a local user starts a conversation or a remote user invites the local user to a new conversation.
Register for conversation state change events on the Conversation instance.
Call the CanInvoke method and pass ConversationAction.AddParticipant. Continue the steps if a true value is returned.
If you can add a participant, get a Contact instance to add to the conversation by calling into GetContactByUri.
A contact that resolves to the passed URI is returned.
Get the IM Modality instance from the Modalities property of the Conversation instance in the data parameter of the ConversationAdded event handler: data.Conversation.Modalities[ModalityTypes.InstantMessagingModality].
Handle the ParticipantAdded event
Complete the following steps within a callback method that handles ParticipantAdded.
To handle the ParticipantAdded event
Read the IsSelf property of the added participant exposed by Participant property.
The following steps should not be executed for the self-participant.
Get the remote participant InstantMessageModality from Participant by reading the Modalities property collection and returning the InstantMessageModality.
Tip The IM modality is auto-accepted at both the local and remote endpoints. You do not need to call Connect on this modality. The IM modality is ready to use when you obtain it from the conversation's modalities collection.
Cast the Modality class instance to the InstantMessageModality class to obtain instant messaging receive features.
Register for InstantMessageReceived message received events to catch incoming instant messages from other participants.
Register for participant events on the remote participant.
Send and receive messages
As the previous figure illustrates, the procedure to send an IM message involve many more steps than those you execute to receive and render an instant message from a remote participant. In both cases, you use the InstantMessageContentType enumeration to either set the message format of a message you send or determine the message format of a message you receive. A sent or received message is encapsulated in a generic IDictionary that uses an enumerator of InstantMessageContentType as the key and a formatted message string as the value of the dictionary entry.
Send a message
Sending an instant message involves setting the local user’s composing state to typing, capturing and sending text, and setting the user’s composing state to idle. These actions are performed in this sequence and each depends on the completion of the previous step. You should use the System.AsyncCallback method you define for each operation to initiate the next operation in the sequence.
Important |
---|
If you send IM text in a format not supported by the target IM client, a LyncClientException is raised on your client. The exception message text is "Generic COM Exception. Code is 0x80EF019F." To avoid this exception, send text formatted as plain text. If you use any other formatting, you should enclose EndSendMessage in an try/catch block that catches LyncClientException. |
To send a message
Verify that the conversation is active and the conversation IM modality is connected.
When the local user begins to type in the form entry control designated to accept message text, call into CanInvoke, passing ModalityAction. SetIsTyping. If you cannot set this property, skip the next step.
Call BeginSetComposing on the conversation IM modality and pass true as the first argument.
In the callback invoked by the previous step or upon completion of typing in the form control, instantiate an instance of IDictionary<InstantMessageContentType, string> and add an entry containing a key as enumerator for the message text formatting and value as message text.
Call BeginSendMessage and pass your message dictionary as the first argument and then pass a System.AsyncCallback method in the second argument.
In the callback invoked after the previous step:
Call EndSendMessage. You must surround this call with a try/catch block, catching the LyncClientException. A remote contact can change his or her availability to DND or offline at any time. Such an availability change causes this exception to be raised when your message cannot be delivered to the remote contact.
Call BeginSetComposing on the conversation IM modality. Pass false as the first argument.
Receive a message
The following procedure handles the InstantMessageReceived event by getting the message text formatting enumerator for the incoming message and parsing the message text according to the specified formatting.
To receive a message
Get the message contents as IDictionary<InstantMessageContentType,string> by reading Contents.
A single message contains text in one of several formats enumerated by InstantMessageContentType. For each format sent, an entry exists in the dictionary you get from the previous step. Iterate on the dictionary items and assemble a display string based on the format and contents of the dictionary.
Important Lync 2013 formats IM text as InstantMessageContentType.Html or .Ink. Previous versions of the client format messages as .RichText. For the best interoperability experience, you should send messages in .PlainText.
Display the resulting string.
Code examples: Lync IM conversations
The procedure assumes that you have created a Windows.Forms object and populated it with a TextBox control for user entry.
Start a conversation
The following example creates a new conversation by calling into AddConversation on ConversationManager.
private string myRemoteParticipantUri;
private Conversation _Conversation;
//assume example has obtained and signed in to this LyncClient instance.
private LyncClient _LyncClient;
public void StartIMConversation(string participantUri)
{
_LyncClient.ConversationManager.ConversationAdded += ConversationsManager_ConversationAdded;
myRemoteParticipantUri = participantUri;
_Conversation = _LyncClient.ConversationManager.AddConversation();
}
Register for conversation events
The following example registers for Conversation events. The Conversation obtained from Conversation is a reference to the new conversation. You can register for events on the Conversation using the Conversation instance.
/// Handles ConversationAdded state change event raised on ConversationsManager
/// <param name="source">ConversationsManager: The source of the event</param>
/// <param name="data">ConversationsManagerEventArgs The event data. The incoming Conversation is obtained here</param>
void ConversationsManager_ConversationAdded(Object source, ConversationManagerEventArgs data)
{
// Register for Conversation state changed events.
data.Conversation.ParticipantAdded += Conversation_ParticipantAdded;
data.Conversation.StateChanged += Conversation_ConversationChangedEvent;
// Add a remote participant.
data.Conversation. AddParticipant(_LyncClient.ContactManager.GetContactByUri(this.myRemoteParticipantUri));
}
ParticipantAdded event
The following example handles the ParticipantAdded event. The example event handler gets the IM modality from the modality collection on the new participant and registers for messages received from the participant. InstantMessageReceived is raised on any participant when that participant sends a message.
/// <summary>
/// ParticipantAdded callback handles ParticpantAdded event raised by Conversation
/// </summary>
/// <param name="source">Conversation Source conversation.</param>
/// <param name="data">ParticpantCollectionEventArgs Event data</param>
void Conversation_ParticipantAdded(Object source, ParticipantCollectionChangedEventArgs data)
{
if (data.Participant.IsSelf == false)
{
if (((Conversation)source).Modalities.ContainsKey(ModalityTypes.InstantMessage))
{
((InstantMessageModality)data.Participant.Modalities[ModalityTypes.InstantMessage]).InstantMessageReceived += myInstantMessageModality_MessageReceived;
((InstantMessageModality)data.Participant.Modalities[ModalityTypes.InstantMessage]).IsTypingChanged += myInstantMessageModality_ComposingChanged;
}
}
}
User starts typing
A user’s cursor has entered a text entry control and the example sets the IsTyping property to true.
Important |
---|
The conversation IM modality must be used to set the typing property for the local user rather than the IM modality of the local participant. |
private void txtMessage_Enter(object sender, EventArgs e)
{
if ((InstantMessageModality)_Conversation.Modalities[ModalityTypes.InstantMessage]).CanInvoke(ModalityAction.SetIsTyping))
{
((InstantMessageModality)_Conversation.Modalities[ModalityTypes.InstantMessage]).BeginSetComposing(true, ComposingCallback, null);
}
}
User completes typing
A user’s cursor has left a text entry control and the example sets the IsTyping property to false.
Important |
---|
The conversation IM modality must be used to set the typing property for the local user rather than the IM modality of the local participant. |
private void txtMessage_Leave(object sender, EventArgs e)
{
if ((InstantMessageModality)_Conversation.Modalities[ModalityTypes.InstantMessage]).CanInvoke(ModalityAction.SetIsTyping))
{
((InstantMessageModality)_Conversation.Modalities[ModalityTypes.InstantMessage]).BeginSetComposing(false, ComposingCallback, null);
}
}
Send a message in plain text
The following example sends the text content of a UI text box to a remote conversation participant.
Important |
---|
The conversation IM modality must be used to send an instant message to other users instead of the local participant’s IM modality. |
private void SendMessage(string messageToSend)
{
try
{
IDictionary<InstantMessageContentType, string> textMessage = new Dictionary<InstantMessageContentType, string>();
textMessage.Add(InstantMessageContentType.PlainText, messageToSend);
if (((InstantMessageModality)_Conversation.Modalities[ModalityTypes.InstantMessage]).CanInvoke(ModalityAction.SendInstantMessage))
{
((InstantMessageModality)_Conversation.Modalities[ModalityTypes.InstantMessage]).BeginSendMessage(
textMessage
, SendMessageCallback
, textMessage);
}
}
catch (LyncPlatformException e)
{
MessageBox.Show("Client Platform Exception: " + e.Message, "Send Message");
}
}
SendMessage callback
The following example callback method is invoked by InstantMessageModality after BeginSendMessage is complete. Verify the asynchronous operation results if you want to see whether the message was sent successfully.
/// <summary>
/// Async callback method invoked by InstantMessageModality instance when SendMessage completes
/// </summary>
/// <param name="_asyncOperation">IAsyncResult The operation result</param>
///
private void SendMessageCallback( IAsyncResult ar)
{
((InstantMessageModality)_Conversation.Modalities[ModalityTypes.InstantMessage]).BeginSetComposing(false, ComposingCallback, null);
if (ar.IsCompleted == true)
{
try
{
((InstantMessageModality)_Conversation.Modalities[ModalityTypes.InstantMessage]).EndSendMessage(ar);
}
catch (LyncClientException lce)
{
MessageBox.Show("Lync Client Exception on EndSendMessage " + lce.Message);
}
}
}
ComposingChanged event
The following example handles the event raised when a participant starts or stops typing.
void myInstantMessageModality_ComposingChanged(object source, IsTypingChangedEventArgs data)
{
if (data.IsTyping == true
{
MessageBox.Show(((InstantMessageModality)source).Participant.Contact.GetContactInformation(ContactInformationType.DisplayName) + " is typing");
}
}
InstantMessageModality MessageReceived event
The following example handles the InstantMessageReceived and uses the PlainText enumerator. If this enumerator is used, message content is rendered as plain text regardless of how it was formatted by the sender. If the incoming message contains data that cannot be rendered as plain text, the data is not rendered.
/// <summary>
/// Callback is invoked when IM Modality state changes upon receipt of message
/// </summary>
/// <param name="source">InstantMessageModality Modality </param>
/// <param name="data">SendMessageEventArgs The new message.</param>
void myInstantMessageModality_MessageReceived(Object source, MessageSentEventArgs data)
{
IDictionary<InstantMessageContentType,string> messageFormatProperty = data.Contents;
if (messageFormatProperty.ContainsKey(InstantMessageContentType.PlainText))
{
string outVal = string.Empty;
string Sender = (string)((InstantMessageModality)source).Participant.Contact.GetContactInformation(ContactInformationType.DisplayName);
if (messageFormatProperty.TryGetValue(InstantMessageContentType.PlainText, out outVal))
{
//outVal will be an empty string if the received message contains only an image.
MessageBox.Show("New message: " + outVal);
//invoke delegate that sets text property of a form browser control with outVal
}
}
}