用于构建通信类应用程序的 MSN Messenger 库






4.70/5 (13投票s)
本文介绍了一个用于利用 MSN Messenger 服务和协议生成通信应用程序的库。
- 下载 msnlibrary_demo - 210.04 KB
- 下载 msnlibrary_demo_src - 195.58 KB
- 下载 msnlibrary_library_src - 225.49 KB
引言
本文介绍了一个通用的 MSN Messenger 框架,用于生成 MSN Messenger 风格的应用程序(既可以作为独立应用程序,也可以集成到大型应用程序中(例如 Microsoft Groove)。通信是现代软件基础设施的重要组成部分,而使用此库可以避免复杂的服务器/客户端设计编程,而是利用现有技术,该技术得到了广泛支持。
本文无意提供 MSN 客户端或服务器的替代品,而是说明如何使用包含的库来快速轻松地创建它们。演示应用程序显示了生成通信应用程序所需的最低限度,但是 CIRIP 应用程序(也已链接到上方)展示了更复杂和精密的实现。
该库支持 MSNP8 和 MSNP9 版本协议,并且可以用于从 .NET 1.0 及更高版本(包括 .NET 3.5 和 Windows Presentation Foundation,如演示中所用)的任何 .NET 语言中生成 MSN 客户端。
该库目前支持以下功能
- 登录/退出 MSN 网络
- 用户状态
- 用户友好名称
- 联系人列表同步(只读,尚不支持添加新联系人或在组之间移动联系人)
- 联系人友好名称、电话号码和状态
- 对话、发送和接收消息(包括字体信息和正在输入的文字)
- 邀请其他用户加入对话
- 对话插件
该库目前不支持以下功能*
- 文件传输
- 联系人图片(以及本地图片)
- 戳(Nudges)
- 表情符号(但可以通过插件轻松添加)
- 活动
- 代理支持(除非通过 Internet Explorer 设置)
* 这些功能可能会在后续版本中添加,请请求其他功能,我将尽力优先处理。
背景
"MSN Messenger 是由微软在 1999 年至 2005 年开发和分发的免费即时通讯客户端,并在 2007 年为运行 Microsoft Windows 操作系统的计算机(Windows Vista 除外)发布,主要面向家庭用户。2006 年 2 月,作为微软 Windows Live 系列在线服务和软件的一部分,它被重命名为 Windows Live Messenger。
MSN Messenger 通常指的是 .NET Messenger Service(允许系统运行的协议和服务器),而不是任何特定的客户端。
MSN Messenger 使用 Microsoft Notification Protocol (MSNP) 通过 TCP(以及可选的 HTTP 来处理代理)连接到 .NET Messenger Service — 这是在 messenger.hotmail.com 的 1863 端口上提供的服务。其当前版本为 13 (MSNP13),由 MSN Messenger 7.5 及其他第三方客户端使用。该协议并非完全保密;微软于 1999 年在 Internet Draft 中向开发者公开了版本 2 (MSNP2),但从未向公众发布版本 8、9、10、11 或 12。.NET Messenger Service 服务器目前仅接受 8 及更高版本的协议,因此 8、9、10、11 和 12 版本的新命令的语法仅通过 Wireshark 等嗅探器得知。MSNP13 将是在 Windows Live Messenger 中使用的协议。截至目前,该程序仍不兼容 Mac OS X 的浏览器。" 维基百科 --- MSN Messenger
有关 MSN 协议的更深入介绍,请参阅 http://www.hypothetic.org/docs/msn/index.php;本文不涵盖 MSN 协议,因为它旨在消除对该主题的任何先验知识的需求。
代码结构
所有代码都围绕 MSNController(主要)、MSNSwitchboardController 和 MSNSwitchboard 类。
MSNController
创建无需输入参数。
MSNController controller = new MSNController();
MSNController 类具有以下属性;
- String Username {get; set;} - 获取或设置本地客户端的用户名;必须在登录前设置,否则身份验证将失败。
- String Password {get; set;} - 获取或设置本地客户端的密码;必须在登录前设置,否则身份验证将失败。此外,get 属性只返回星号,即如果 .Password 设置为 thePassword,则 get 调用将返回 ***********。
- String FriendlyName {get; set;} - 获取或设置本地客户端的友好名称。必须在控制器连接后才能设置。
- MSNEnumerations.UserStatus Status {get; set;} - 获取或设置本地客户端的状态(例如,在线、忙碌、显示为离线等)。set 属性只能在登录后调用,否则可能会抛出异常(不要依赖此行为)。
- MSNEnumerations.LoginStatus LoginStatus {get; set;} - 获取或设置连接状态,set 属性只接受有效组合(例如,请求登录状态在控制器已登录时会抛出异常)。
MSNController 类还通过以下属性提供对其他关键对象的访问(主要在连接时可用);
- MSNContactsList ContactsList {get;} - 获取 MSNContactsList 对象,该对象包含联系人列表的同步版本。
- MSNSwitchboardController SwitchboardController {get;} - 获取处理对话创建(和重新创建)的 MSNSwitchboardController。
MSNController 类具有以下方法;
- void loginMSNClient() - 已弃用,用于登录控制器。
- void logoutMSNClient() - 已弃用,用于退出控制器。
- void startConversation(List<String> initialUsers) - 开始与以下用户名的对话。指定的用户名必须已在联系人列表中,否则对话将不包含该联系人。
MSNController 的主要连接点是通过以下事件(尽管轮询也能正常工作,但强烈建议使用事件,并且通过属性设置变量然后在相关事件上更新用户界面 (UI) 是更好的做法);
- event MSNEventDelegates.StatusChangedEventDelegate LocalClientStatusChanged - 本地客户端状态更改时触发(在线、忙碌、离开等)。**
- event MSNEventDelegates.FriendlyNameChangedEventDelegate LocalFriendlyNameChanged - 本地客户端友好名称更改时触发;登录后也会触发一次,以指示初始友好名称。**
- event MSNEventDelegates.LoginStatusChangedEventDelegate LoginStatusChanged - 控制器连接、断开或正在连接时触发。从 logged_out 到 logging_in 再到 logged_out 的状态更改表示身份验证失败。
- event MSNEventDelegates.LoginSettingsChangedEventDelegate LoginSettingsChanged - 用户名或密码属性更改时触发。
- event MSNEventDelegates.FriendlyNameChangedEventDelegate FriendlyNameChanged - 在线联系人的友好名称更改时触发。连接初始时也会触发,以指定初始友好名称。**
- event MSNEventDelegates.PhoneNumberChangedEventDelegate PhoneNumberChanged - 在线联系人的电话号码更改时触发。在初始联系人列表同步期间也会触发。**
- event MSNEventDelegates.GroupModifiedEventDelegate GroupModified - 在添加或删除组时触发。在初始联系人列表同步期间也会触发。**
- event MSNEventDelegates.GroupMemberChangedEventDelegate GroupMemberChanged - 在联系人被添加到组或从组中移除时触发。在初始联系人列表同步期间也会触发。**
- event MSNEventDelegates.ContactStatusChangedEventDelegate ContactStatusChanged - 联系人状态更改时触发。**
- event MSNEventDelegates.ContactAddedEventDelegate ContactAdded - 联系人被添加到联系人列表或从联系人列表中移除时触发。在初始联系人列表同步期间也会触发。**
- event MSNEventDelegates.MasterListContactAdded MasterListContactAdded - 联系人被添加到正向或反向列表(即被阻止的用户、已请求将您添加为联系人的用户)或从中移除时触发。**
** 仅在控制器连接时触发。
MSNAuthentication
库外无访问权限;用于与 Microsoft 密码进行身份验证,以及处理身份验证挑战。
MSNContactsList
在连接时维护联系人列表的同步版本。组数据通过 MSNGroup 类包装,联系人数据(不包括本地用户)通过 MSNContact 类包装。
- Dictionary<String, MSNContact> Contacts {get;} - 通过用户名返回联系人字典的引用。
- Dictionary<String, MSNGroup> Groups {get;} - 通过组名返回组字典的引用。
MSNContact
具有以下属性和方法;
- String Username {get;} - 获取联系人的用户名。
- String FriendlyName {get; set;} - 获取或设置联系人的友好名称。***
- MSNEnumerations.UserStatus Status {get; set;} - 获取或设置联系人的状态。***
- MSNListenableList<MSNGroup> Groups {get;} - 获取联系人所属的组列表。***
- void setListMember(MSNEnumbers.ContactLists list, bool member) - 将用户添加到联系人列表(例如,阻止列表)或从中移除。***
- bool getListMember(MSNEnumerations.ContactLists list) - 如果联系人在指定列表中,则返回 true,否则返回 false。
- void setPhone(MSNEnumerations.PhoneTypes phoneType, String value) - 设置指定的电话号码。***
- String getPhone(MSNEnumerations.PhoneTypes phoneType) - 获取联系人的指定电话号码,如果未设置则返回 ""。
*** 仅修改内部数据结构,不修改在线版本。
MSNGroup
具有以下属性和方法;
- String GroupName {get;} - 获取组的友好名称。
- int GroupNumber {get;} - 获取组的标识号。
- MSNListenableList<MsnContact> Contacts {get;} - 获取组内联系人列表。
*** 仅修改内部数据结构,不修改在线版本。
MSNSwitchboardController
此类用于发起新对话(以及由此产生的对话窗口)。该类只有两个事件,但如果需要允许对话,则必须处理这两个事件;
- event MSNEventDelegates.SwitchboardCreated SwitchboardCreated - 新对话开始时生成(由本地用户或联系人发起)。请注意,当联系人打开对话窗口时会生成此事件,而不是在发送第一条消息时生成,因此如果在此事件中创建对话窗口,则可能收不到任何消息,如果联系人打开对话窗口然后立即关闭而未发送消息。
- event MSNEventDelegates.SwitchboardReCreated SwitchboardReCreated - 当对话关闭后重新打开时生成;例如,联系人开始对话,关闭其对话窗口,然后重新打开对话窗口。与 SwitchboardCreated 事件类似,当移除窗口创建时会调用事件,而不是发送消息。
MSNSwitchboard
此类处理单个对话,并通过 IMSNSwitchboardPlugin 接口支持对话插件。
- event MSNEventDelegates.SwitchboardUserConnected UserConnected - 联系人加入对话时触发,包括初始对话联系人和任何其他用户。
- event MSNEventDelegates.MessageRecieved MessageRecieved - 消息已由插件处理(且消息设置为显示,默认为 true)后触发。
- event MSNEventDelegates.MessageSent MessageSent - 消息已由插件处理然后发送(且消息设置为显示,默认为 true)后触发。
- void sendMessage(MSNUserMessage message) - 发送指定的消息(经过插件处理后)。
- void invite(String username) - 将指定用户名的用户邀请到对话中(用户名必须在联系人列表中)。
- void closeConversation() - 关闭对话(尽管对话可以通过 MSNSwitchboardController 中的 SwitchboardReCreated 事件重新启动)。
- List<String> getConnectedUsers() - 返回当前在对话中的用户名列表(不包括已受邀但尚未加入的用户)。
- MSNListenableList<IMSNSwitchboardPlugin> Plugins {get;} - 返回当前插件列表,应将插件添加到此列表中。
请注意,当 MSNController 断开连接时,对话不会自动终止;因此,可以退出登录但仍与现有对话成员聊天。
演练
连接到 MSN 网络
- 创建 MSNController 对象(controller)
- 通过 controller.Username 设置用户名
- LoginSettingsChanged 事件触发
- 通过 controller.Password 设置密码
- LoginSettingsChanged 事件触发
- 调用 controller.LoginStatus = MSNEnumerations.LoggedIn
- 库尝试连接
- LoginStatusChanged 触发,显示 MSNEnumerations.LoggingIn
- LoginStatusChanged 触发,显示 MSNEnumerations.LoggedIn
- LoggedIn 已处理,客户端通过 controller.UserStatus 发送初始用户状态
- LocalClientStatusChanged 事件触发
- 联系人列表开始同步,通过 ContactAdded、GroupModified、GroupMemberChanged、ContactStatusChanged 和 FriendlyNameChanged 事件
开始对话
- 调用 controller.startConversation,并提供要初始加入对话的用户列表
- SwitchboardCreated 事件被调用,并带有一个新的 switchboard
- 客户端代码从事件 switchboard 创建一个新的对话窗口
- UserConnected 事件对每个对话成员调用一次
发送消息(在已有对话的情况下)
- 用户输入新消息,客户端将其转换为 MSNUserOutgoingMessage
- 客户端通过 switchboard.sendMessage(MSNUserMessage message) 发送消息
- 库依次将消息传递给每个插件
- 如果 message.getSend() 为 true,则消息发送到 MSN 网络
- 如果 message.getDisplay() 为 true,则触发 MessageSent 事件
接收消息(在已有对话的情况下)
- 联系人通过对话 switchboard 发送消息
- 库将其转换为 MSNUserIncommingMessage
- 库依次将消息传递给每个插件
- 如果 message.getDisplay() 为 true,则触发 MessageRecieved 事件
Using the Code
主应用程序窗口
界面组件
- TextBox usernameTextBox - 本地用户的用户名
- PasswordBox passwordBox - 本地用户的密码
- Button connectionButton - 用于连接/断开 MSN 网络
- DropDownBox statusBox - 用于设置本地用户状态(例如,在线、忙碌、离开等)
- TreeView contactsTreeView - 用于显示联系人/组
- TreeViewItem contactsTreeViewItem - contactsTreeView 的基节点,在 Header 中显示“联系人”
连接
在 connectionButton 的点击处理程序中,将用户名和密码设置到控制器并设置连接状态。
private void connectionButton_Click(object sender, RoutedEventArgs e)
{
if (controller.LoginStatus == MSNEnumerations.LoginStatus.LOGGED_OUT) {
controller.LoginStatus = MSNEnumerations.LoginStatus.LOGGED_IN;
}
else if (controller.LoginStatus == MSNEnumerations.LoginStatus.LOGGED_IN) {
controller.Username = usernameTextBox.Text;
controller.Password= passwordBox.Password;
controller.LoginStatus = MSNEnumerations.LoginStatus.LOGGED_OUT;
}
}
处理 LoginStatusChanged;更新连接按钮文本,断开连接时清除数据结构,连接时设置新状态。此外,建议在连接时禁用连接按钮,以防止用户在正在连接时尝试连接(尤其是在互联网连接缓慢的情况下)。请注意,事件处理程序不在事件分发线程上,因此需要更新 UI 组件。
private void controller_LoginStatusChanged(MSNEnumerations.LoginStatus newStatus)
{
this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
new RefreshDelegate(delegate()
{
if (controller.LoginStatus == MSNEnumerations.LoginStatus.LOGGED_OUT) {
connectionButton.Content = "Connect";
connectionButton.IsEnabled = true;
statusComboBox_SelectionChanged(this, null);
baseNode.Items.Clear();
contactsTreeNodes.Clear();
groupsTreeNodes.Clear();
conversationWindows.Clear();
}
else if (controller.LoginStatus == MSNEnumerations.LoginStatus.LOGGED_IN) {
connectionButton.Content = "Disconnect";
connectionButton.IsEnabled = true;
//Fire a status changed to start contact list synchronisation
statusComboBox_SelectionChanged(this, null);
}
else {
connectionButton.Content = "Connecting...";
connectionButton.IsEnabled = false;
}
}));
}
用户状态
调用 controller.Status 并传入一个有效的状态枚举对象。请注意,此事件处理不是理想的,因为如果用户状态由插件更改,则更改不会反映在 UI 中。UI 应通过相应的事件处理程序更新。
private void statusComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (statusComboBox.SelectedItem != null &&
controller != null &&
controller.LoginStatus == MSNEnumerations.LoginStatus.LOGGED_IN) {
String status = ((ComboBoxItem)(statusComboBox.SelectedItem)).Content.ToString().Trim();
MSNEnumerations.UserStatus userStatus = MSNEnumerations.UserStatus.online;
if (status.Equals("Away")) {
userStatus = MSNEnumerations.UserStatus.away;
}
else if (status.Equals("Busy")) {
userStatus = MSNEnumerations.UserStatus.busy;
}
else if (status.Equals("Appear Offline")) {
userStatus = MSNEnumerations.UserStatus.offline;
}
if (controller != null) {
controller.Status = userStatus;
}
}
}
联系人列表同步
联系人信息同时存储在 UI 和两个字典中(显著提高性能),如下所示。
private Dictionary<String, TreeViewItem> contactsTreeNodes =
new Dictionary<string, TreeViewItem>(); //username, treenode
private Dictionary<String, TreeViewItem> groupsTreeNodes =
new Dictionary<string, TreeViewItem>(); //group name, treenode
处理联系人添加或移除;如果添加,则创建一个新的 TreeViewItem 并附加到 contactsTreeViewItem。Header 可以包含用户名,直到友好名称已知,但用户名也应存储在 tag 中,以便根据节点进行简单的用户名提取(方便以后启动对话)。移除涉及从其父节点和数据结构中移除节点。
private void controller_ContactAdded(string username, bool added)
{
this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
new RefreshDelegate(delegate()
{
if (added) {
TreeViewItem tvi = new TreeViewItem();
tvi.Header = username;
tvi.Tag = username;
tvi.ToolTip = new ToolTip() { Content = username };
tvi.MouseDoubleClick += new MouseButtonEventHandler(tvi_MouseDoubleClick);
contactsTreeNodes.Add(username, tvi);
baseNode.Items.Add(tvi);
}
else {
TreeViewItem tvi = contactsTreeNodes[username];
((TreeViewItem)tvi.Parent).Items.Remove(tvi);
contactsTreeNodes.Remove(username);
}
}));
}
请注意,已为启动对话的事件处理程序添加了双击处理程序(见下文)。
通过查找联系人字典中的相关节点并更新其 Header 来处理联系人友好名称的更改。
private void controller_FriendlyNameChanged(string username, string friendlyName)
{
this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
new RefreshDelegate(delegate()
{
TreeViewItem tvi = contactsTreeNodes[username];
tvi.Header = friendlyName;
}));
}
组的创建和修改方式类似。
private void controller_GroupModified(string groupName, bool added)
{
this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
new RefreshDelegate(delegate()
{
if (added) {
TreeViewItem tvi = new TreeViewItem();
tvi.Header = groupName;
tvi.IsExpanded = true;
groupsTreeNodes.Add(groupName, tvi);
baseNode.Items.Add(tvi);
}
else {
TreeViewItem tvi = contactsTreeNodes[groupName];
((TreeViewItem)tvi.Parent).Items.Remove(tvi);
groupsTreeNodes.Remove(groupName);
}
}));
}
private void controller_GroupMemberChanged(string username, string groupName, bool added)
{
this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
new RefreshDelegate(delegate()
{
if (added) {
TreeViewItem contactTvi = contactsTreeNodes[username];
TreeViewItem groupTvi = groupsTreeNodes[groupName];
//remove from old parent
((TreeViewItem)contactTvi.Parent).Items.Remove(contactTvi);
//add to new parent
groupTvi.Items.Add(contactTvi);
}
}));
}
开始对话
对话的启动由联系人节点的双击处理程序处理;用户名在节点的 Tag 属性中。
private void tvi_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (controller != null &&
controller.LoginStatus == MSNEnumerations.LoginStatus.LOGGED_IN) {
TreeViewItem tvi = (TreeViewItem)sender;
String name = null;
if (tvi.Tag != null) {
name = tvi.Tag.ToString();
}
List<String> users = new List<string>();
users.Add(name);
controller.startConversation(users);
}
}
对话窗口(ConversationWindow)在 SwitchboardCreated 事件中创建,并在 SwitchboardReCreated 事件处理程序中重新打开。为了方便 SwitchboardReCreated 事件处理程序的查找,添加了一个 MSNSwitchboard、ConversationWindow 的字典。
private Dictionary<MSNSwitchboard, ConversationWindow> conversationWindows =
new Dictionary<MSNSwitchboard, ConversationWindow>(); //switchboard, associated ui element
private void SwitchboardController_SwitchboardReCreated(MSNSwitchboard switchboard)
{
this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
new RefreshDelegate(delegate()
{
ConversationWindow window = conversationWindows[switchboard];
window.Show();
}));
}
private void SwitchboardController_SwitchboardCreated(MSNSwitchboard switchboard)
{
this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
new RefreshDelegate(delegate()
{
ConversationWindow window = new ConversationWindow(controller, switchboard);
conversationWindows.Add(switchboard, window);
window.Show();
}));
}
对话窗口
用户界面组件
- System.Windows.WebBrowser conversationFrame - 用于显示消息。未使用 Frame,因为它目前不支持内存中的 HTML,而只支持 Uri(Web 或本地)。
- TextBox sendTextBox - 用于用户输入要发送的消息。
- Button sendButton - 用于发送 sendTextBox 中的内容;应限制为最大消息长度(100 个字符)。
在线用户
加入对话的用户应在 conversationFrame 中指示,除非是初始用户加入。窗口标题也会更新。ConnectedUsers 是在线用户列表。conversationHTML 是一个包含完整消息历史记录的字符串,然后通过 conversationFrame.DocumentText 更新到显示屏。
private void switchboard_UserConnected(string username, bool joined)
{
Console.WriteLine("CONNECTED " + username);
this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
new RefreshDelegate(delegate()
{
//recievedBox.AppendText(message.getUserPayload() + "\r\n");
if (joined) {
if (connectedUsers.Count > 0) {
conversationHTML += username + " connected...
";
conversationFrame.DocumentText = conversationHTML;
}
connectedUsers.Add(username);
this.Title += " - " + username;
}
else {
if (connectedUsers.Count > 1) {
conversationHTML += username + " disconnected...
";
conversationFrame.DocumentText = conversationHTML;
}
connectedUsers.Remove(username);
this.Title.Replace(" - " + username, "");
}
}));
}
发送消息
消息的发送通过 sendButton 的点击处理程序执行。此外,当用户在 sendTextBox 中输入时,应发送 MSNUserTypingMessages,以便联系人的 UI 可以显示“username”正在输入。同样,MessageRecieved 处理程序也应处理这些消息。
private void sendButton_Click(object sender, RoutedEventArgs e)
{
//do NOT update gui from this
switchboard.sendMessage(new MSNUserOutgoingMessage("Times New Roman", sendTextBox.Text));
sendTextBox.Text = "";
}
消息显示
消息显示仅由 MessageSent 和 MessageRecieved 事件处理程序更新(而不是 sendButton 的点击事件处理程序),以便任何内容都可以先由任何插件处理。
private void switchboard_MessageSent(MSNUserMessage message)
{
if (message.getMessageType() == MSNEnumerations.UserMessageType.outgoing_text_message) {
Console.WriteLine("MESSAGE>>> " + message.getUserPayload());
this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
new RefreshDelegate(delegate()
{
//recievedBox.AppendText(message.getUserPayload() + "\r\n");
conversationHTML += controller.FriendlyName + " says: "
+ "<font color=\"Gray\">" + message.getUserPayload() + "</font><br />";
conversationFrame.DocumentText = conversationHTML;
}));
}
}
private void switchboard_MessageRecieved(MSNUserMessage message)
{
if (message.getMessageType() == MSNEnumerations.UserMessageType.incomming_text_message) {
Console.WriteLine("MESSAGE<<< " + message.getUserPayload());
this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
new RefreshDelegate(delegate()
{
conversationHTML += controller.ContactsList.Contacts[message.getUsername()].FriendlyName + " says: "
+ "<font color=\"Gray\">" + message.getUserPayload() + "</font><br />";
conversationFrame.DocumentText = conversationHTML;
}));
}
}
编写插件
插件应实现 IMSNSwitchboardPlugin 接口,该接口具有以下方法;
- void processOutgoingMessage(MSNUserMessage message) - 在 UI 调用 sendMessage 后,但在消息实际发送到 MSN 网络之前调用。
- void processIngoingMessage(MSNUserMessage message) - 在库从 MSN 网络接收消息后,但在通过 MessageRecieved 事件发送到 UI 之前调用。
- MSNSwitchboard Switchboard {get; set;} - 用于设置与对话对应的 MSNSwitchboard。
示例插件
此插件会将任何等于 @website 的传出消息替换为 http://www.derek-bartram.co.uk,并在本地客户端上显示已发送的网站地址。
public class WebsitePlugin : IMSNSwitchboardPlugin
{
private MSNSwitchboard switchboard = null;
#region IMSNSwitchboardPlugin Members
public void processOutgoingMessage(MSNUserMessage message)
{
if (switchboard != null &&
message.getMessageType() == MSNEnumerations.UserMessageType.outgoing_text_message &&
message.getUserPayload().Equals("@website"))
{
message.setDisplay(false); //don't display the message locally
message.setUserPayload("http://www.derek-bartram.co.uk"); //update send text
#region create local content
MSNUserOutgoingMessage localMessage = new MSNUserOutgoingMessage("Times New Roman", "Sent Website");
localMessage.setSend(false); //don't send it to remote user
localMessage.ProcessByPlugins = false; //don't process it through plugins as it has already been processed
switchboard.sendMessage(localMessage); //send it
#endregion
}
}
public void processIngoingMessage(MSNUserMessage message)
{
//do nothing, this plugin only affects outgoing messages
}
public MSNSwitchboard Switchboard
{
get
{
return switchboard;
}
set
{
switchboard = value;
}
}
#endregion
}
历史
版本 1.0.0.0 - 初始构建
其他许可说明
欢迎在您的工作中随意使用此代码,但请注意,我们采用修改后的 The Code Project Open License (CPOL);基本上它与标准许可证相同,但未经事先授权,此代码 **不得** 用于商业或非营利性商业用途。请参阅随附的源代码和演示文件中的 license.txt 或 license.pdf。