在C#中阅读MS Exchange电子邮件

93

我需要有能力监控并读取公司内部MS Exchange服务器上特定邮箱的电子邮件。我还需要能够阅读发件人的电子邮件地址、主题、消息正文以及下载附件(如果有的话)。

使用C#(或VB.NET),最好的方法是什么?


5
微软已经发布了Exchange Web Services Managed API,用于Exchange 2007 SP1和v2010。这使得可以通过程序访问你的邮箱,而无需使用Outlook。我的博客上有两篇文章讨论了这种方法:- C#:使用Exchange Web Services从Exchange获取所有电子邮件 - ΩmegaMan
Exchange Web Services Managed API 1.0 SDK是微软推荐的方法,用于在Exchange Server 2007 SP1及以上版本中以编程方式更新Exchange。 http://msdn.microsoft.com/en-us/library/dd633710(EXCHG.80).aspx - jlo
8个回答

91

现在的情况很混乱。使用MAPI或CDO通过.NET互操作DLL是Microsoft官方不支持的,虽然它看起来可以正常工作,但由于它们的内存模型不同而存在内存泄漏问题。您可以使用CDOEX,但它仅适用于Exchange服务器本身,无法远程访问;毫无用处。您可以与Outlook交互,但现在您只是要依赖Outlook;这有些过度。最后,您可以使用Exchange 2003的WebDAV支持,但WebDAV很复杂,.NET对其的内置支持很差,并且(雪上加霜)Exchange 2007几乎完全放弃了WebDAV支持。

那么该怎么办呢?我最终使用AfterLogic的IMAP组件通过IMAP与我的Exchange 2003服务器通信,这种方法非常有效。(我通常会寻找免费或开源库,但我发现所有.NET库都有缺陷--特别是在处理2003 IMAP实现中的某些怪癖时--而这个组件足够便宜并且一次就奏效。我知道还有其他类似的库存在。)

然而,如果您的组织正在使用Exchange 2007,则非常幸运。Exchange 2007带有基于SOAP的Web服务接口,最终提供了一种统一的、与语言无关的与Exchange服务器交互的方式。如果您可以将2007+设置为要求,那么这绝对是最好的选择。(遗憾的是,我的公司有一个“但2003没有问题”的政策。)

如果您需要桥接Exchange 2003和2007,那么IMAP或POP3绝对是最好的选择。


21
微软对基于SOAP的Web服务进行了封装,以简化访问 - 现在建议使用Exchange Web Services Managed API 1.0 SDK来实现此目的。链接:http://msdn.microsoft.com/en-us/library/dd633710(EXCHG.80).aspx - jlo
4
几乎可以说微软是设计这个产品只能与Outlook兼容的。 - Chris S

72

抱歉,可能有些晚了,但这难道不是使用 EWS 的重点吗?

https://msdn.microsoft.com/zh-cn/library/dd633710(EXCHG.80).aspx

只需要大约6行代码就可以从邮箱中获取邮件:

ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);

//service.Credentials = new NetworkCredential( "{Active Directory ID}", "{Password}", "{Domain Name}" );

service.AutodiscoverUrl( "First.Last@MyCompany.com" );

FindItemsResults<Item> findResults = service.FindItems(
   WellKnownFolderName.Inbox,
   new ItemView( 10 ) 
);

foreach ( Item item in findResults.Items )
{
   Console.WriteLine( item.Subject );
}

5
EWS Managed API简化了与Microsoft Exchange Server 2007 Service Pack 1(SP1)及更高版本的Microsoft Exchange通信的应用程序的实现。 - Chris S
2
请注意,这实际上是对一条年代久远的消息进行了“死灰复燃”的操作,但是这段代码让我在大约五分钟内就能为类似的项目运行起来。第一次通过就完美地工作了。在我看来,这是一个比所选答案更现代化/全面化的解决方案...供其他人参考。 - David W
2
关于运行此程序的注意事项。您需要安装NuGet包“Microsoft Exchange WebServices”。 - John M
4
第一次尝试时这个方法对我有效。这应该成为新的被接受的答案。 - kroe761
我想问一下,如果我在 service.autodiscoverurl 中使用的是除了我的邮箱地址之外的电子邮件地址,那么我需要输入 service.credentials,对吗? - gymcode
通常是的,除非您在域交换服务器上进行了一些奇怪的操作,否则服务器会要求您提供访问该邮箱的凭据。 - War

22
  1. Graph - 目前,访问存储在Exchange Server上的电子邮件、联系人、约会、任务等数据以及Microsoft(Teams、Sharepoint等)托管的其他数据的首选统一API(纯HTTP)。
    使用Graph ExplorerOutlookSpy(我是它的作者)来操作API。
    Microsoft提供了Graph SDK以供多种语言使用。

  2. EWS。完全支持。它是纯HTTP的,可以从任何语言访问,但有.NetJava特定的库。
    微软表示不会添加新功能,而Graph更可取,即使Graph中并非所有EWS功能都可用(例如通过ExportItems/ImportItems公开的高保真度快速传输流导出/导入)。
    您可以使用EWSEditorOutlookSpy(我是它的作者)来操作API。

  3. Extended MAPI。这是Outlook使用的本机API。它最终使用MSEMS Exchange MAPI提供程序,该提供程序可以使用RPC(Exchange 2013不再支持)或RPC-over-HTTP(Exchange 2007或更新版本)或MAPI-over-HTTP(Exchange 2013及更高版本)与Exchange通信。
    API本身只能从未托管的C++或Delphi中访问。您还可以使用Redemption(任何语言,我是其作者)-其RDO对象系列是Extended MAPI包装器。要使用Extended MAPI,您需要安装Outlook或独立(Exchange)版本的MAPI(在扩展支持上,并且不支持Unicode PST和MSG文件,也无法访问Exchange 2016)。 Extended MAPI可用于服务。
    您可以使用OutlookSpy(我是它的作者)或MFCMAPI来操作API。

  4. Outlook Object Model - 不是Exchange特定的,但它允许访问代码运行所在计算机上Outlook中的所有数据。不能用于服务。

  5. Exchange Active Sync。微软不再投入任何重要资源到这个协议中。

  6. Outlook曾经安装CDO 1.21库(它包装了Extended MAPI),但已被Microsoft弃用,不再接收任何更新。

  7. 曾经有一个名为MAPI33的第三方.Net MAPI包装器,但它不再得到开发或支持。

  8. WebDAV - 已弃用。

  9. Exchange OLE DB提供程序(EXOLEDB)-已弃用。


1
EwsEditor已经迁移到GitHub: https://github.com/dseph/EwsEditor - Opmet

11

这里有一些我之前为WebDAV编写的老代码。我想它是针对Exchange 2003编写的,但我不记得了。如果有帮助,请随意借鉴...

class MailUtil
{
    private CredentialCache creds = new CredentialCache();

    public MailUtil()
    {
        // set up webdav connection to exchange
        this.creds = new CredentialCache();
        this.creds.Add(new Uri("http://mail.domain.com/Exchange/me@domain.com/Inbox/"), "Basic", new NetworkCredential("myUserName", "myPassword", "WINDOWSDOMAIN"));
    }

    /// <summary>
    /// Gets all unread emails in a user's Inbox
    /// </summary>
    /// <returns>A list of unread mail messages</returns>
    public List<model.Mail> GetUnreadMail()
    {
        List<model.Mail> unreadMail = new List<model.Mail>();

        string reqStr =
            @"<?xml version=""1.0""?>
                <g:searchrequest xmlns:g=""DAV:"">
                    <g:sql>
                        SELECT
                            ""urn:schemas:mailheader:from"", ""urn:schemas:httpmail:textdescription""
                        FROM
                            ""http://mail.domain.com/Exchange/me@domain.com/Inbox/"" 
                        WHERE 
                            ""urn:schemas:httpmail:read"" = FALSE 
                            AND ""urn:schemas:httpmail:subject"" = 'tbintg' 
                            AND ""DAV:contentclass"" = 'urn:content-classes:message' 
                        </g:sql>
                </g:searchrequest>";

        byte[] reqBytes = Encoding.UTF8.GetBytes(reqStr);

        // set up web request
        HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("http://mail.domain.com/Exchange/me@domain.com/Inbox/");
        request.Credentials = this.creds;
        request.Method = "SEARCH";
        request.ContentLength = reqBytes.Length;
        request.ContentType = "text/xml";
        request.Timeout = 300000;

        using (Stream requestStream = request.GetRequestStream())
        {
            try
            {
                requestStream.Write(reqBytes, 0, reqBytes.Length);
            }
            catch
            {
            }
            finally
            {
                requestStream.Close();
            }
        }

        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        using (Stream responseStream = response.GetResponseStream())
        {
            try
            {
                XmlDocument document = new XmlDocument();
                document.Load(responseStream);

                // set up namespaces
                XmlNamespaceManager nsmgr = new XmlNamespaceManager(document.NameTable);
                nsmgr.AddNamespace("a", "DAV:");
                nsmgr.AddNamespace("b", "urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/");
                nsmgr.AddNamespace("c", "xml:");
                nsmgr.AddNamespace("d", "urn:schemas:mailheader:");
                nsmgr.AddNamespace("e", "urn:schemas:httpmail:");

                // Load each response (each mail item) into an object
                XmlNodeList responseNodes = document.GetElementsByTagName("a:response");
                foreach (XmlNode responseNode in responseNodes)
                {
                    // get the <propstat> node that contains valid HTTP responses
                    XmlNode uriNode = responseNode.SelectSingleNode("child::a:href", nsmgr);
                    XmlNode propstatNode = responseNode.SelectSingleNode("descendant::a:propstat[a:status='HTTP/1.1 200 OK']", nsmgr);
                    if (propstatNode != null)
                    {
                        // read properties of this response, and load into a data object
                        XmlNode fromNode = propstatNode.SelectSingleNode("descendant::d:from", nsmgr);
                        XmlNode descNode = propstatNode.SelectSingleNode("descendant::e:textdescription", nsmgr);

                        // make new data object
                        model.Mail mail = new model.Mail();
                        if (uriNode != null)
                            mail.Uri = uriNode.InnerText;
                        if (fromNode != null)
                            mail.From = fromNode.InnerText;
                        if (descNode != null)
                            mail.Body = descNode.InnerText;
                        unreadMail.Add(mail);
                    }
                }

            }
            catch (Exception e)
            {
                string msg = e.Message;
            }
            finally
            {
                responseStream.Close();
            }
        }

        return unreadMail;
    }
}

并且是 model.Mail:

class Mail
{
    private string uri;
    private string from;
    private string body;

    public string Uri
    {
        get { return this.uri; }
        set { this.uri = value; }
    }

    public string From
    {
        get { return this.from; }
        set { this.from = value; }
    }

    public string Body
    {
        get { return this.body; }
        set { this.body = value; }
    }
}

1
注意:Exchange Server 2010 已停止支持 WebDAV,改用 EWS。 - Our Man in Bananas

1

0

如果您的Exchange服务器配置支持POP或IMAP,则这是一种简单的方法。

另一个选择是WebDAV访问。有一个可用于此。这可能是您最好的选择。

我认为使用COM对象访问Exchange也有选项,但我不确定它有多容易。

我想这完全取决于您的管理员愿意给您访问权限的具体内容。


0

你应该能够使用MAPI来访问邮箱并获取所需信息。不幸的是,我所知道的唯一的.NET MAPI库(MAPI33)似乎已经不再维护了。这曾经是通过.NET访问MAPI的好方法,但我无法确定它现在的有效性。可以在这里获取有关其下载位置的更多信息:MAPI33.dll的下载位置?


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接