在 C# 中枚举 Outlook 项目时出现异常
Posted
技术标签:
【中文标题】在 C# 中枚举 Outlook 项目时出现异常【英文标题】:Exception while enumerating Outlook Items in C# 【发布时间】:2010-01-27 00:26:37 【问题描述】:我正在尝试编写一个应用程序来监控几个邮箱,当找到邮件时,从每个项目中获取一些信息,然后一旦我有了项目列表,我就可以采取适当的措施。
但无论我如何处理,我都会达到 Exchange 强制执行的 255 RPC 连接限制。
我完全不知道是什么导致了错误——据我所知,我已经把所有东西都捆绑在一个方法中,并且正在调用 Marshal.ReleaseComObject.... 我什至接受了性能打击打开和关闭 Outlook 应用程序句柄本身。
任何建议都将不胜感激......(我似乎无法弄清楚为什么我的代码在预览中看起来有问题,所以为了安全起见,我也将它放在 pastebin 上......http://pastebin.com/m637eb95)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Office.Interop.Outlook;
using Microsoft.Office.Interop;
using System.Runtime.InteropServices;
namespace HandleMailingResponses
class OutlookFolderTableScraper
public List<OutlookItem> GetItemsFromFolder(string folderName)
List<OutlookItem> returnList = new List<OutlookItem>();
Application outlookHandle = new Application();
NameSpace outlookNamespace = outlookHandle.GetNamespace("MAPI");
Folders rootOutlookFolders = outlookNamespace.Folders;
outlookNamespace.Logon(null, null, null, true);
Folder requestedRoot = enumerateFolders(rootOutlookFolders, folderName);
Folders theseFolders = requestedRoot.Folders;
Folder thisInbox = enumerateFolders(theseFolders, "Inbox");
Marshal.ReleaseComObject(requestedRoot);
requestedRoot = null;
Marshal.ReleaseComObject(rootOutlookFolders);
rootOutlookFolders = null;
string storeID = thisInbox.StoreID;
Table thisTable = thisInbox.GetTable("",OlTableContents.olUserItems);
//By default each item has the columns EntryID, Subject, CreationTime, LastModificationTime and MessageClass
//we can add any of the other properties the MailItem or ReportItem object would have....
Columns theseColumns = thisTable.Columns;
theseColumns.Add("SenderEmailAddress");
Marshal.ReleaseComObject(thisInbox);
thisInbox = null;
outlookNamespace.Logoff();
Marshal.ReleaseComObject(outlookNamespace);
outlookNamespace = null;
outlookHandle.Quit();
Marshal.ReleaseComObject(outlookHandle);
outlookHandle = null;
int count = 0;
while (!thisTable.EndOfTable)
Row thisRow = thisTable.GetNextRow();
object[] theseValues = (object[]) thisRow.GetValues();
Console.WriteLine("processed 0",count++);
//get the body from this item
string messageClass = (string)theseValues[4];
string entryID = (string)theseValues[0];
string body = getItemBody(entryID,storeID, messageClass);
returnList.Add(new OutlookItem((string)theseValues[5], (string)theseValues[1], body, messageClass, entryID));
return returnList;
private string getItemBody(string entryID, string storeID, string messageClass)
Application outlookHandle = new Application();
NameSpace outlookNamespace = outlookHandle.GetNamespace("MAPI");
outlookNamespace.Logon(null, null, null, true);
string body;
if (messageClass.ToLower().StartsWith("report"))
ReportItem thisItem = (ReportItem)outlookNamespace.GetItemFromID(entryID, storeID);
body = thisItem.Body;
thisItem.Close(OlInspectorClose.olDiscard);
//release this com reference
int releaseResult;
do
releaseResult = Marshal.ReleaseComObject(thisItem);
while (releaseResult != 0);
else
MailItem thisItem = (MailItem)outlookNamespace.GetItemFromID(entryID, storeID);
body = thisItem.Body;
thisItem.Close(OlInspectorClose.olDiscard);
//release this com reference
int releaseResult;
do
releaseResult = Marshal.ReleaseComObject(thisItem);
while (releaseResult != 0);
outlookNamespace.Logoff();
outlookNamespace = null;
outlookHandle.Quit();
outlookHandle = null;
GC.Collect();
GC.WaitForPendingFinalizers();
return body;
/// <summary>
/// Iterates through an Outlook.Folders object searching for a folder with the given name
/// </summary>
/// <param name="rootFolder">An Outlook.Folder object</param>
/// <param name="targetFolder"></param>
/// <returns></returns>
private Folder enumerateFolders(Folders rootFolders, string targetFolder)
Folder returnFolder = null;
System.Collections.IEnumerator thisEnumerator = rootFolders.GetEnumerator();
while (thisEnumerator.MoveNext())
Folder f = (Folder)thisEnumerator.Current;
string name = f.Name;
if (targetFolder.ToLower().Equals(name.ToLower()))
returnFolder = f;
break;
ICustomAdapter adapter = (ICustomAdapter)thisEnumerator;
Marshal.ReleaseComObject(adapter.GetUnderlyingObject());
adapter = null;
return returnFolder;
【问题讨论】:
您是否从多个线程调用此代码? 在我看来,如果您使用 Exchange 2007 API(作为 Web 服务启用)或简单的邮箱访问协议(如 POP3 或 IMAP),如果目标邮件服务器支持它。我以前轻松使用过这两种方法,但发现通过 COM 编写 MS Office 程序在最好的情况下是一种错误的资源消耗。但遗憾的是,我不能对这段代码的具体问题发表太多评论...... @JP Alioto - 代码是从单个线程调用的,如果我删除对打开项目以获取正文的方法的调用,则没有问题。 @ewall - 我将研究 Exchange 07 API,但我之前没有使用过它(或 POP/IMAP),我觉得这个解决方案非常接近,我很想修复它:) 【参考方案1】:不要在每次调用函数时都创建新的 Application 对象,将其保留为类的私有成员变量,直到不再需要它为止。这样,您只需在构造函数中调用 Namespace.Logon。
【讨论】:
@McAden - 我将应用程序和命名空间调用移到方法中,希望关闭它们可以让系统正确释放 COM 对象。如果我删除对 Item.Body 的调用,应用程序将运行所有 5000 多个项目,所以我确信问题在于我如何释放对象 您可以在这些对象上调用 Marshal.ReleaseComObject 如果您将它们设为静态,在 AppDomain.CurrentDomain.DomainUnload 等的事件处理程序中。 AppDomain 肯定有这样的事件。以上是关于在 C# 中枚举 Outlook 项目时出现异常的主要内容,如果未能解决你的问题,请参考以下文章
在 C# 中使用具有正确私钥和公钥对的 RSA 解密时出现错误数据异常
尝试不重写文件并将其保存在 C# 的新位置时出现意外的系统异常
尝试从 WinJS 读取 C# WinRT 组件中的空字符串时出现异常