Read MS Exchange email in C#

一世执手 提交于 2019-11-26 12:01:59

It's a mess. MAPI or CDO via a .NET interop DLL is officially unsupported by Microsoft--it will appear to work fine, but there are problems with memory leaks due to their differing memory models. You could use CDOEX, but that only works on the Exchange server itself, not remotely; useless. You could interop with Outlook, but now you've just made a dependency on Outlook; overkill. Finally, you could use Exchange 2003's WebDAV support, but WebDAV is complicated, .NET has poor built-in support for it, and (to add insult to injury) Exchange 2007 nearly completely drops WebDAV support.

What's a guy to do? I ended up using AfterLogic's IMAP component to communicate with my Exchange 2003 server via IMAP, and this ended up working very well. (I normally seek out free or open-source libraries, but I found all of the .NET ones wanting--especially when it comes to some of the quirks of 2003's IMAP implementation--and this one was cheap enough and worked on the first try. I know there are others out there.)

If your organization is on Exchange 2007, however, you're in luck. Exchange 2007 comes with a SOAP-based Web service interface that finally provides a unified, language-independent way of interacting with the Exchange server. If you can make 2007+ a requirement, this is definitely the way to go. (Sadly for me, my company has a "but 2003 isn't broken" policy.)

If you need to bridge both Exchange 2003 and 2007, IMAP or POP3 is definitely the way to go.

Um,

I might be a bit too late here but isn't this kinda the point to EWS ?

https://msdn.microsoft.com/en-us/library/dd633710(EXCHG.80).aspx

Takes about 6 lines of code to get the mail from a mailbox:

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 );
}
  1. The currently preferred (Exchange 2013 and 2016) API is EWS. It is purely HTTP based and can be accessed from any language, but there are .Net and Java specific libraries.

    You can use EWSEditor to play with the API.

  2. Extended MAPI. This is the native API used by Outlook. It ends up using the MSEMS Exchange MAPI provider, which can talk to Exchange using RPC (Exchange 2013 no longer supports it) or RPC-over-HTTP (Exchange 2007 or newer) or MAPI-over-HTTP (Exchange 2013 and newer).

    The API itself can only be accessed from unmanaged C++ or Delphi. You can also use Redemption (any language) - its RDO family of objects is an Extended MAPI wrapper. To use Extended MAPI, you need to install either Outlook or the standalone (Exchange) version of MAPI (on extended support, and it does not support Unicode PST and MSG files and cannot access Exchange 2016). Extended MAPI can be used in a service.

    You can play with the API using OutlookSpy or MFCMAPI.

  3. Outlook Object Model - not Exchange specific, but it allows access to all data available in Outlook on the machine where the code runs. Cannot be used in a service.

  4. Exchange Active Sync. Microsoft no longer invests any significant resources into this protocol.

  5. Outlook used to install CDO 1.21 library (it wraps Extended MAPI), but it had been deprecated by Microsoft and no longer receives any updates.

  6. There used to be a third-party .Net MAPI wrapper called MAPI33, but it is no longer being developed or supported.

  7. WebDAV - deprecated.

  8. Collaborative Data Objects for Exchange (CDOEX) - deprecated.

  9. Exchange OLE DB Provider (EXOLEDB) - deprecated.

Here is some old code I had laying around to do WebDAV. I think it was written against Exchange 2003, but I don't remember any more. Feel free to borrow it if its helpful...

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;
    }
}

And 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; }
    }
}

I used code that was published on CodeProject.com. If you want to use POP3, it is one of the better solutions that I have found.

If your Exchange server is configured to support POP or IMAP, that's an easy way out.

Another option is WebDAV access. there is a library available for that. This might be your best option.

I think there are options using COM objects to access Exchange, but I'm not sure how easy it is.

It all depends on what exactly your administrator is willing to give you access to I guess.

Chris Hynes

You should be able to use MAPI to access the mailbox and get the information you need. Unfortunately the only .NET MAPI library (MAPI33) I know of seems to be unmaintained. This used to be a great way to access MAPI through .NET, but I can't speak to its effectiveness now. There's more information about where you can get it here: Download location for MAPI33.dll?

Paul Rowland

I got a solution working in the end using Redemption, have a look at these questions...

One option is to use Outlook. We have a mail manager application that access an exchange server and uses outlook as the interface. Its dirty but it works.

Example code:

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);
        }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!