93

我需要能够监视和阅读来自 MS Exchange Server(我公司内部)上特定邮箱的电子邮件。我还需要能够阅读发件人的电子邮件地址、主题、邮件正文并下载附件(如果有)。

使用 C#(或 VB.NET)执行此操作的最佳方法是什么?

4

9 回答 9

91

一团糟。Microsoft 正式不支持通过.NET 互操作 DLL 的 MAPI 或 CDO ——它看起来工作正常,但由于它们不同的内存模型而存在内存泄漏问题。您可以使用 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 绝对是最佳选择。

于 2009-03-17T00:30:01.787 回答
70

嗯,

我在这里可能有点太晚了,但这不是 EWS 的重点吗?

https://msdn.microsoft.com/en-us/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 );
}
于 2011-07-08T09:05:50.113 回答
21
  1. 当前首选(Exchange 2013 和 2016)API 是EWS。它完全基于 HTTP,可以从任何语言访问,但有.NetJava特定的库。

    您可以使用EWSEditor来使用 API。

  2. 扩展 MAPI。这是 Outlook 使用的本机 API。它最终使用MSEMSExchange MAPI 提供程序,它可以使用 RPC(Exchange 2013 不再支持它)或 RPC-over-HTTP(Exchange 2007 或更高版本)或 MAPI-over-HTTP(Exchange 2013 和更高版本)与 Exchange 通信。

    API 本身只能从非托管 C++ 或Delphi访问。您还可以使用Redemption(任何语言) - 它的RDO对象系列是扩展的 MAPI 包装器。要使用扩展 MAPI,您需要安装 Outlook 或MAPI 的独立 (Exchange) 版本(在扩展支持下,它不支持 Unicode PST 和 MSG 文件,并且无法访问 Exchange 2016)。可在服务中使用扩展 MAPI。

    您可以使用OutlookSpyMFCMAPI使用 API 。

  3. Outlook 对象模型- 不是特定于 Exchange,但它允许访问运行代码的机器上 Outlook 中的所有可用数据。不能在服务中使用。

  4. 交换活动同步。Microsoft 不再为此协议投入任何重要资源。

  5. Outlook 用于安装 CDO 1.21 库(它包装了扩展 MAPI),但它已被 Microsoft 弃用并且不再接收任何更新。

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

  7. WebDAV - 已弃用。

  8. 用于 Exchange 的协作数据对象 (CDOEX) - 已弃用。

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

于 2016-03-17T18:26:12.033 回答
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; }
    }
}
于 2009-03-17T01:20:25.457 回答
1

我使用了在 CodeProject.com 上发布的代码。如果您想使用 POP3,它是我找到的更好的解决方案之一。

于 2009-03-17T00:03:59.463 回答
0

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

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

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

我猜这完全取决于您的管理员到底愿意为您提供什么访问权限。

于 2009-03-16T23:50:03.053 回答
0

您应该能够使用 MAPI 访问邮箱并获取您需要的信息。不幸的是,我所知道的唯一 .NET MAPI 库 (MAPI33) 似乎没有维护。这曾经是通过 .NET 访问 MAPI 的好方法,但我现在无法评价它的有效性。有关您可以从何处获取它的更多信息:MAPI33.dll 的下载位置?

于 2009-03-17T00:01:17.720 回答
0

我最终得到了一个使用 Redemption 的解决方案,看看这些问题......

于 2009-03-17T01:17:27.397 回答
0

一种选择是使用 Outlook。我们有一个邮件管理器应用程序可以访问交换服务器并使用 Outlook 作为界面。它很脏,但它有效。

示例代码:

public Outlook.MAPIFolder getInbox()
        {
            mailSession = new Outlook.Application();
            mailNamespace = mailSession.GetNamespace("MAPI");
            mailNamespace.Logon(mail_username, mail_password, false, true);
            return MailNamespace.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox);
        }
于 2009-03-17T03:58:32.533 回答