发布日期: 7/5/2004 | 更新日期: 7/5/2004
Andrew W. Troelsen
Intertech Training
单击此处可下载本文的代码示例。
摘要: 本文介绍了 Microsoft Outlook 2003 对象模型介,并探讨了如何使用 C# 编程语言生成 Outlook 识别的应用程序和 Outlook 外接程序。(35 页打印页)
注 本文假设您已经熟悉 C# 编程语言和 .NET 平台。Outlook 2003 的开发知识不是必需或期望的。
适用于:
Microsoft Visual Studio .NET 2003
Microsoft Visual C# .NET 2003
Microsoft Outlook 2003
本页内容
作为对象模型的 Outlook 2003
在将应用程序的功能公开给外部程序方面,Microsoft 有很长的历史。例如,如果项目需要拼写检查功能,您可以利用从 Microsoft_ Word 公开的对象模型。以同样的方式,如果正在生成的应用程序需要 Microsoft_ Outlook_ 2003 提供的功能,则可以利用关联的对象模型。简单地说,Outlook 2003 对象模型允许您与下列各项交互:
| • |
电子邮件项。 |
| • |
Outlook 联系人数据库。 |
| • |
Outlook 日历。 |
| • |
Outlook 注释和任务。 |
| • |
Outlook 本身的UI(资源管理器、检查器、命令栏等)。 |
这显然是所包含功能的子集,但我肯定您有了大概的了解:可以通过关联的对象模型来访问 Outlook 2003 的功能。
Outlook 主互操作程序集 (PIA)
到目前为止,Outlook 的功能是通过一个基于 COM 的进程内服务器 (msoutl.olb) 来公开的。.NET 开发人员希望与这些 COM 类型交互,因此,您需要通过互操作层来这样做。Microsoft Corporation 已经提供了一个 Outlook 2003 附带的"正式的"互操作程序集(即主互操作程序集)。
该程序集已经强命名,并驻留在名称 Microsoft.Office.Interop.Outlook.dll 下面的全局程序集缓存中。要从 Microsoft_ Visual Studio_ .NET 2003 引用该程序集,请从"Add References"对话框访问"COM"选项卡,并选择"Microsoft Outlook 11.0 Object Library"(图 1)。

图 1. 引用 Outlook PIA
注 如果利用 Outlook PIA 的以前版本(或多个版本)生成自定义应用程序,一定要阅读 http://go.microsoft.com/fwlink/?LinkId=30833,该文讨论了某些可能的版本冲突。
有关互操作程序集的简介
任何互操作程序集的最终目标都是要提供外观与体验与原始 COM 类型相似的 .NET 类型。互操作层(结合运行库生成的代理,该代理的术语名称是"运行库可调用包装",即 RCW)处理各种关于封送处理数据类型的详细信息。例如,如果接口方法定义为接受一个基于 COM 的 BSTR 参数,则 .NET 开发人员可以自由传递基于 CLR 的 System.String。
对于每个 COM 类,互操作程序集包含总是带有"–Class"后缀的具体类型,以及名称相同的托管等价项。例如,以下 COM IDL 定义:
coclass MyComObject


{
// Assume this interface defines three methods named
// One(), Two(), Three().
[default] interface IMyComObject;
// Assume this interface defines two methods named
// Four(), Five().
interface ISomeOtherInterface;
}
结果是两个名为 MyComObject 和 MyComObjectClass 的 .NET 类类型。MyComObject 类型只公开 [default] 接口的成员。希望访问其他接口的功能时,需要执行显式转换(这将在后台触发对 IUnknown::QueryInterface() 的调用):
// Create a new MyComObject.
MyComObject c = new MyComObject();
c.One();
// To call Four() or Five(), we need to obtain the
// ISomeOtherInterface explicity.
ISomeOtherInterface itfOther = (ISomeOtherInterface)c;
itfOther.Five();
另一方面,如果利用 MyComObjectClass 类型,您就能够使用单个对象引用来访问每个接口中的每个成员:
// Create a new MyComObjectClass.
MyComObjectClass c = new MyComObjectClass();
c.One();
c.Five();
实际上,带 –Class 后缀的类型是原始 COM 类型的所有接口方法的联合。假定 Outlook 2003 coclass 最初旨在仅支持单个 [default] COM 接口,则通常可以忽略以 –Class 为后缀的类型,并利用名称相同的 .NET 代理。
注 如果希望更深入讨论 COM 到 .NET 的转换过程,访问 http://blogs.msdn.com/eric_carter/archive/2004/05/06/127698.aspx 将很有帮助。
关于从托管语言与 COM 类型的交互,最后一点与 CLR 垃圾收集器的角色有关。.NET 内存管理模型在本质上是不确定的,因为我们作为开发人员不会准确知道对象将在什么时候被销毁,而只知道它最终会被销毁。另一方面,COM 内存模型在本质上具有很麻烦的确定性,因为我们被迫使用各种 IUnknown::AddRef() 和 IUnknown::Release() 调用来手动调整对 COM 类型的对象内部引用计数(尽管 Visual Basic_ 6.0 试图隐藏这一点)。
针对互操作程序集中的类型进行编程时,将像任何 CLR 引用类型一样对代理类型进行垃圾收集。一旦代理已经被垃圾收集,RCW 将把所有需要的 IUnknown::Release() 调用转发给关联的 COM 对象,并在这时销毁代理。使用该技术,可以确信只要代理在内存中是活动的,则关联的 COM 对象在内存中也是活动的。
如果希望确保 COM 类型以更及时和更可预言的方式被销毁,则可以利用 System.Runtime.InteropServices.Marshal 类型。该类定义了一个静态方法,名为 ReleaseComObject()。只是传递对象引用,关联的 COM 类型将被当场销毁:
using System.Runtime.InteropServices;
class MyClass


{
public void SomeMethod()

{
MyComObjectClass c = new MyComObjectClass();

// Explicitly destroy COM type after use.
Marshal.ReleaseComObject(c);
}
}
虽然销毁 COM 类型的想法听起来可能很吸引人,但必须知道在 AppDomain 中的其他 CLR 对象现在无法使用基本 COM 类型。有了这种(危险的)可能性,本文中的示例代码将避免使用 Marshal.ReleaseComObject()。
注 在即将发布的 .NET 平台(Microsoft_ Visual Studio.NET_ 2005,即 Whidbey)的版本中,这个问题已经得到解决。有关进一步的详细信息,请参阅 http://blogs.msdn.com/yvesdolc/archive/2004/04/17/115379.aspx。
Outlook 2003 对象模型
一旦引用了 Outlook PIA,下一个任务就是调查 Microsoft.Office.Interop.Outlook 命名空间中的很多类型(图 2)。

图 2. Microsoft.Office.Interop.Outlook 命名空间
不管类型的大小是多少,好消息是对象模型本身组织得非常好,并利用了常见设计模式。因此,一旦了解如何遍历联系人列表,则遍历收件箱项就会很简单。
其他的好消息是,整个对象模型在帮助文件 (vbaol11.chm) 中有完整的介绍,默认情况下该帮助文件位于 :\Program Files\Microsoft Office\OFFICE11\1033 下(图 3)。

图 3. Outlook 2003 文档
现在,坏消息(取决于您的视点)是帮助系统使用 VBScript 代码示例和成员原型。假定本文没有试图详细介绍 Outlook 2003 对象模型中的每个类型,请您参考该帮助系统来获得完整的信息。下面,让我们来研究某些核心类类型。
Application 类型
第一个要知道的类型被适当地称为"Application",它是层次结构中其他所有对象的根。一旦获得该类型的对象,就能以编程方式控制 Outlook 本身的所有方面。表 1 列出了某些需要注意的(但决不是所有)成员。
表 1。选择 Application 类型的成员
|
ActiveExplorer()
ActiveInspector() |
这些方法分别从当前 Outlook 实例检索 Explorer / Inspector 类型。本文随后描述资源管理器/检查器模型。 |
|
CreateItem() |
允许通过编程创建新的 Outlook 项。 |
|
GetNamespace() |
提供对数据存储项的访问。到 Outlook 2003 为止,MAPI 是唯一可以使用的命名空间,它用于访问 Outlook 文件夹组(收件箱、注释等)。 |
|
Quit() |
终止当前 Outlook 会话。 |
|
COMAddIns |
该属性允许您在运行时发现插入到当前 Outlook 实例中的外接程序集合。 |
|
Explorers
Inspectors |
这些属性允许获得强类型的 Explorers/Inspectors 集合。 |
获得 Application 类型的确切方式将根据所开发软件的种类而有略微的不同。如果要生成一个合并 Outlook 2003 的自定义应用程序,则要做的所有事情是使用 C# new 关键字创建该类型:
// Create an Outlook Application object.
Application outLookApp = new Application();
另一方面,生成 Outlook 2003 外接程序时(本文随后介绍),将通过名为 OnConnection() 的方法传递 Application 实例:
public void OnConnection(object application,
Extensibility.ext_ConnectMode connectMode,
object addInInst, ref System.Array custom)


{
// Assume 'applicationObject' is a member variable of
// type Microsoft.Office.Interop.Outlook.Application.
applicationObject = (Microsoft.Office.Interop.Outlook.Application)
application;
}
除了各种属性和方法以外,Application 类型还定义了在各种环境中触发的很多事件(StartUp、Quit、ItemSend、NewMailEx)。请考虑以下代码片段:
public class MyApp


{
public static void Main()

{
// Create an Outlook Application object.
Application outLookApp = new Application();
// Rig up the NewMailEx event.
outLookApp.NewMailEx += new
ApplicationEvents_11_NewMailExEventHandler(outLookApp_NewMailEx);

}
private static void outLookApp_NewMailEx(string EntryIDCollection)

{
// Do something interesting when a new e-mail arrives.
}
}
再次,在给定互操作层的角色后,处理基于 COM 的事件的过程看起来与处理 CLR 事件的过程相同。暂时不要理会细节,只需注意 NewMailEx 事件与一个具体的委托(ApplicationEvents_11_NewMailExEventHandler) 一起工作,这个委托可以调用任何接受 System.String 作为其唯一参数,并且不返回任何内容的方法。
Outlook"Item"类类型
一旦有了 Application 类型,就能创建新的 Outlook"项"。通过 Microsoft.Office.Interop.Outlook.OlItemType 枚举可以列出可能的项:
public enum OlItemType


{
olAppointmentItem = 1;
olContactItem = 2;
olDistributionListItem = 7;
olJournalItem = 4;
olMailItem = 0;
olNoteItem = 5;
olPostItem = 6;
olTaskItem = 3;
} 假设您希望通过编程来创建新的 Outlook Task 项。要这样做,请指定
OlItemType.olTaskItem 作为
Application.CreateItem() 的参数:
public static void Main()


{
// Create an Outlook Application object.
Application outLookApp = new Application();
// Create a new TaskItem.
TaskItem newTask =
(TaskItem)outLookApp.CreateItem(OlItemType.olTaskItem);
// Configure the task at hand and save it.
newTask.Body = "Don't forget to send DOM the links
";
newTask.DueDate = DateTime.Now;
newTask.Importance = OlImportance.olImportanceHigh;
newTask.Subject = "Get DOM to stop bugging me.";
newTask.Save();
}
注意,CreateItem() 的返回值是一般的 OlItemType;因此需要显式地将结果转换为正确的类型(在这里是 TaskItem)。这时,只需要使用类型的公共接口来配置项。一旦执行,将能够在 Outlook 任务检查器中查找任务(图 4)。

图 4. 通过编程生成的任务
尽管 OlItemType 枚举的名称很简单,但表 2 详细列出了 OlItemType 枚举的成员与 Application.CreateItem() 产生的返回类型之间的关系。
表 2。OlItemType enum/Outlook 类类型关系
|
olAppointmentItem |
AppointmentItem |
表示单个约会。 |
|
olContactItem |
ContactItem |
表示单个联系人。 |
|
olDistributionListItem |
DistributionListItem |
表示一个通讯组列表。 |
|
olJournalItem |
JournalItem |
表示单个日记项。 |
|
olMailItem |
MailItem |
表示单个电子邮件项。 |
|
olNoteItem |
NoteItem |
表示单个注释。 |
|
olPostItem |
PostItem |
表示其他人可能浏览的公用文件夹中的公告。 |
|
olTaskItem |
TaskItem |
表示单个任务。 |
获得现有 Outlook 项
除了创建新项以外,Outlook 2003 模型还允许获得(并且可能修改)现有项。不管对枚举哪个 Outlook 项感兴趣,基本过程都是:
| • |
从 Application.GetNamespace() 获得 NameSpace 类型。 |
| • |
从 NameSpace.GetDefaultFolder() 获得 MAPIFolder 类型。 |
| • |
使用 MAPIFolder.Items 索引器枚举子项。 |
指定字符串"MAPI"作为 GetNamespace() 的参数时,将收到一个 NameSpace 类型,该类型表示具体的 Outlook 数据存储的抽象级别(目前,"MAPI"是唯一有效的命名空间)。MAPIFolder 类型可以表示给定用户的邮件存储中的任何文件夹(已删除项、收件箱、日记项等)。文件夹选项的完整范围由 OlDefaultFolders 枚举来表示:
public enum OlDefaultFolders


{
olFolderCalendar = 9;
olFolderConflicts = 19;
olFolderContacts = 10;
olFolderDeletedItems = 3;
olFolderDrafts = 16;
olFolderInbox = 6;
olFolderJournal = 11;
olFolderJunk = 23;
olFolderLocalFailures = 21;
olFolderNotes = 12;
olFolderOutbox = 4;
olFolderSentMail = 5;
olFolderServerFailures = 22;
olFolderSyncIssues = 20;
olFolderTasks = 13;
olPublicFoldersAllPublicFolders = 18;
}
要请求具体的文件夹,请将 OlDefaultFolders 枚举中的值指定为 NameSpace.GetDefaultFolder() 的参数。请考虑以下代码,这些代码枚举了当前用户的任务集合:
static void Main(string[] args)


{
// Create an Outlook Application object.
Application outLookApp = new Application();
// Print all tasks.
NameSpace outlookNS = outLookApp.GetNamespace("MAPI");
MAPIFolder theTasks =
outlookNS.GetDefaultFolder(OlDefaultFolders.olFolderTasks);
foreach(TaskItem task in theTasks.Items)

{
Console.WriteLine("-> Time Created: {0}", task.CreationTime);
Console.WriteLine("-> Body: {0}", task.Body);
}
}
Inspectors 和 Explorers
Outlook 对象模型不仅使您能够访问各种项,还定义了用来对用户界面进行操作的类型。Explorer 类型表示用于显示文件夹内容的窗口。另一方面,Inspectors 表示打开后可查看的单个项。Application 类维护一个由所有 Explorers 和 Inspectors 组成的集合,通过使用适当命名的 Explorers / Inspectors 属性可以获得这些类型:
Application app = new Application();
Explorers theExplorers = app.Explorers;
foreach(Explorer e in theExplorers)


{
// Do something with each Explorer
}
Application 类的 GetActiveExplorer() 和 GetActiveInspector() 方法可以用来获得当前活动的 UI 元素:
Application app = new Application();
Explorer activeExp = app.ActiveExplorer();
Console.WriteLine("Explorer caption: {0}", activeExp.Caption);
当您生成自定义的 Outlook 外接程序时,Explorers 和 Inspectors 是很有用的,因为它们让您能够将 UI 小部件附加到现有的 CommandBars 集合中。本文稍后将进一步介绍这方面的情况。
生成 Outlook 识别的应用程序
要重点操作 Outlook 的对象模型(而不是生成奇特的用户界面),第一个示例将利用简单的命令行用户界面。如果希望跟着做,请创建一个新的 C# 控制台应用程序,并命名为 OPine。Unix 用户可能知道,"Pine"是一个很流行的命令行电子邮件实用工具的名称。OPine 将模仿 Pine 的功能子集。具体来说,OPine 将响应以下命令:
| • |
dib:显示收件箱项 |
| • |
snm:发送新邮件项 |
| • |
cn:创建新注释 |
| • |
dn:显示现有注释 |
| • |
q:退出 OPine |
通过响应 NewMailEx 事件,OPine 还能在新邮件到达时通知用户。
注OPine 将利用 ApplicationClass 类型(而不是 Application)来解决一个在随后引用 System.Windows.Forms.dll 程序集时引入的名称冲突。也可以使用如下所示的 C# 别名解决名称冲突:
using OutLookApp = Microsoft.Office.Interop.Outlook.Application;
但在这种情况下,使用 –Class 类型将不会损害 OPine 示例。
处理"dib"命令
假定已经引用了 Outlook 2003 PIA,下一步是创建一个帮助器类 (OPineHelper),用该类定义一组执行批量处理的静态方法。首先,我们有一个名为 DisplayInbox() 的方法,该方法接受 ApplicationClass 类型作为其唯一参数。DisplayInbox() 的实现将获得当前的 MAPI 命名空间,以便检索收件箱文件夹中的每个 MailItem。在这里,我们将使用 MailItem 类型的各种属性,将接收时间、发件人名称和主题打印到控制台:
public static void DisplayInbox(ApplicationClass o)


{
// Get items in my inbox.
NameSpace outlookNS = o.GetNamespace("MAPI");
MAPIFolder inboxFolder
= outlookNS.GetDefaultFolder(OlDefaultFolders.olFolderInbox);
// Print out some basic info.
Console.WriteLine("You have {0} e-mails.",
inboxFolder.Items.Count);
Console.WriteLine();
foreach(object obj in inboxFolder.Items)

{
MailItem item = obj as MailItem;
if(item != null)

{
Console.WriteLine("-> Received: {0}",
item.ReceivedTime.ToString());
Console.WriteLine("-> Sender: {0}", item.SenderName);
Console.WriteLine("-> Subject: {0}", item.Subject);
Console.WriteLine();
}
}
}
注意,我们将通过 Items 属性所获得的项当作一般 System.Objects,而不是所期望的 MailItem 类型。此外还要注意,我们执行了一个动态检查,以确定当前项是否可以被视为 MailItem(通过 C# 的 as 关键字),以及如果这样我们将与类型的各种属性交互。我们执行该动态检查的理由是 Outlook 收件箱的确可以包含超过 MailItem 类型的项(例如,满足请求)。如果将 foreach 逻辑设置为:
foreach(MailItem item in inboxFolder.Items)


{

}
那么,如果遇到 MailItem 以外的任何内容,就可以收到运行库异常。
在任何情况下,除了 ReceivedTime、SenderName 和 Subject 属性,MailItem 类型还能够访问附件和重要性级别,以及内容的 HTML 表现形式(通过 HTMLBody 属性)。有关这方面的完整细节,请参阅 Outlook 2003 文档。
处理"snm"命令
OPineHelper 的下一个静态方法是 SendNewMail(),该方法负责代表用户创建和发送新的电子邮件。和前面看到的一样,我们将通过 ApplicationClass.CreateItem() 创建新的 MailItem 类型。阅读到这里,以下代码应当很容易理解:
public static void SendNewMail(ApplicationClass o)


{
// Create a new MailItem.
MailItem myMail =
(MailItem)o.CreateItem(OlItemType.olMailItem);
// Now gather input from user.
Console.Write("Receiver Name: ");
myMail.Recipients.Add(Console.ReadLine());
Console.Write("Subject: ");
myMail.Subject = Console.ReadLine();
Console.Write("Message Body: ");
myMail.Body = Console.ReadLine();
// Send it!
myMail.Send();
}
创建 (cn) 和显示 (dn) 注释
假如我们实际需要做的只是重复用来创建新电子邮件和遍历现有电子邮件项的过程,那么随后两个静态方法是很简单的。在以下代码中,请注意由 OlItemType 和 OlDefaultFolders 枚举所指定值:
public static void CreateNote(ApplicationClass o)


{
// Get access to notes.
NoteItem myNote =
(NoteItem)o.CreateItem(OlItemType.olNoteItem);
Console.Write("Enter your note: ");
myNote.Body = Console.ReadLine();
// Now save the note.
myNote.Save();
}
public static void DisplayNotes(ApplicationClass o)


{
// Get items in my inbox.
NameSpace outlookNS = o.GetNamespace("MAPI");
MAPIFolder notesFolder
= outlookNS.GetDefaultFolder(OlDefaultFolders.olFolderNotes);
// Print out some basic info.
Console.WriteLine("You have {0} notes.",
notesFolder.Items.Count);
Console.WriteLine();
foreach(NoteItem item in notesFolder.Items)

{
Console.WriteLine("-> Time Created: {0}", item.CreationTime);
Console.WriteLine("-> Body: {0}", item.Body);
Console.WriteLine();
}
}
最后接触
这里关心的最后的静态方法只是向最终用户显示一组选项:
public static void DisplayOPineOptions()


{
Console.WriteLine("***** Welcome To OPine *****");
Console.WriteLine("dib : Display Inbox");
Console.WriteLine("snm : Send New Mail");
Console.WriteLine("cn : Create Note");
Console.WriteLine("dn : Display Notes");
Console.WriteLine("q : Quit");
Console.WriteLine("****************************");
}
这将包装 OPine 帮助器类的创建过程;现在可以使用它。
实现 Main() 方法
到这里,我们准备实现 Main() 方法,该方法负责执行以下任务:
| • |
创建 ApplicationClass 类型的实例 |
| • |
通过 Console.ReadLine() 获得用户的命令选项 |
| • |
接受用户提供的字符串,并执行合适的方法 OPineHelper |
给出这些要点后,下面是一个可能的实现:
static void Main(string[] args)


{
// String to hold the user request.
string userOption;
// Create an Outlook application object.
ApplicationClass outLookApp = new ApplicationClass();
// Display info.
OPineHelper.DisplayOPineOptions();
do

{
Console.Write(" Please enter your command: ");
userOption = Console.ReadLine();
switch(userOption)

{
// Display Inbox (dib)
case "dib":
OPineHelper.DisplayInbox(outLookApp);
break;
// Create Note (cn)
case "cn":
OPineHelper.CreateNote(outLookApp);
break;
// Send New Mail (snm)
case "snm":
OPineHelper.SendNewMail(outLookApp);
break;
// Display Notes (dn)
case "dn":
OPineHelper.DisplayNotes(outLookApp);
break;
// Quit (q)
case "q":
userOption = "q";
break;
default: // Anything else? Just display options.
OPineHelper.DisplayOPineOptions();
break;
}
}while(userOption != "q");
}
处理 NewMailEx 事件
我们将添加到 OPine 中的最后一项功能是处理传入新电子邮件的能力。首先,在分配 ApplicationClass 类型之后处理 NewMailEx 事件:
// Create an Outlook application object.
ApplicationClass outLookApp = new ApplicationClass();
// Rig up the new message event.
outLookApp.NewMailEx += new
ApplicationEvents_11_NewMailExEventHandler(outLookApp_NewMailEx);
ApplicationEvents_11_NewMailExEventHandler 委托的目标需要一个类型为 System.String 的参数。该字符串的值表示新的 MailItem 本身的 ID(可以通过 NameSpace.GetItemFromID() 方法来获得 MailItem)。
在后面的事件处理程序中,注意我们使用 System.Windows.Forms.MessageBox 类型来通知用户有新的邮件,所以一定要添加对 System.Windows.Forms.dll 的引用(并使用指令集更新您的文件):
private static void outLookApp_NewMailEx(string EntryIDCollection)


{
if(DialogResult.Yes ==
MessageBox.Show("Do you want to see your message?",
"You've got mail!", MessageBoxButtons.YesNo))

{
// Get the incoming MailItem based on ID.
ApplicationClass o = new ApplicationClass();
NameSpace outlookNS = o.GetNamespace("MAPI");
MAPIFolder mFolder =
o.Session.GetDefaultFolder(OlDefaultFolders.olFolderInbox);
MailItem newMail = (MailItem)
outlookNS.GetItemFromID(EntryIDCollection, mFolder.StoreID);
// Now print out.
Console.WriteLine(" ***** New Message *****");
Console.WriteLine("-> Sender: {0}", newMail.SenderName);
Console.WriteLine("-> Subject: {0}", newMail.Subject);
Console.WriteLine("-> Body: {0}", newMail.Body);
Console.WriteLine("*********************** ");
}
} 这就是最后的步骤。现在我们可以执行编译,并对
OPine 进行测试(图 5)。
图 5. 运行中的 Opine
我敢肯定,您可以找到很多方式来扩展和改进 OPine,包括从基于控制台的 UI 移动到图形 UI(通过 Windows 窗体)。尽管我显然知道你们很少生成命令行电子邮件程序,但我希望该示例已经阐明了通过自定义应用程序与 Outlook 交互的过程。
与 Outlook 安全更新的冲突
运行 OPine 时,您肯定知道由 Outlook 启动的以下对话框(图 6)

图 6. Outlook 2003 安全警告
尽管这会干扰最终用户,但该行为是设计造成的。在 Outlook 2003 下面,(选择对象的)选择成员被认为是可能有安全风险的。因而,系统会提示用户输入权限才能继续操作,防止电子邮件蠕虫和病毒使用对象模型干坏事。
注 如果希望阅读关于哪些 Outlook 类型和成员导致该安全提示的文档,请参阅文章 What's New in Microsoft Office Outlook 2003 for Developers?
假如最终用户总是可以通过对安全提示作出"No"的响应来拒绝对 Outlook 的访问,您就能够在自定义应用程序中通过编程使用 Outlook 时自由地使用 try/catch 逻辑。例如,为了避免发生运行库故障,应当对 OPineHelper.DisplayNotes()(以及其余方法)进行如下更改:
public static void DisplayNotes(ApplicationClass o)


{
//