Leia o e-mail do MS Exchange em C #

90

Preciso ser capaz de monitorar e ler e-mails de uma determinada caixa de correio em um servidor MS Exchange (interno da minha empresa). Também preciso ler o endereço de e-mail do remetente, o assunto, o corpo da mensagem e fazer o download de um anexo, se houver.

Qual é a melhor maneira de fazer isso usando C # (ou VB.NET)?

Vajarov
fonte
4
Desde então, a Microsoft lançou a API Gerenciada de Serviços da Web do Exchange para Exchange 2007 SP1 e v2010, que permite entrar programaticamente em sua caixa de correio sem a necessidade do Outlook. Tenho dois artigos em meu blog que discutem essa abordagem: - C #: Obtendo todos os emails do Exchange usando os serviços da Web do Exchange
ΩmegaMan
O Exchange Web Services Managed API 1.0 SDK é o método recomendado pela Microsoft para atualizar o Exchange programaticamente para o Exchange Server 2007 SP1 e superior. msdn.microsoft.com/en-us/library/dd633710(EXCHG.80).aspx
jlo

Respostas:

90

É uma bagunça. O MAPI ou o CDO por meio de uma DLL de interoperabilidade .NET não é oficialmente suportado pela Microsoft - parecerá funcionar bem, mas há problemas com vazamentos de memória devido aos modelos de memória diferentes. Você pode usar o CDOEX, mas isso só funciona no próprio servidor Exchange, não remotamente; sem utilidade. Você poderia interoperar com o Outlook, mas agora acabou de criar uma dependência do Outlook; exagero. Finalmente, você poderia usar o suporte WebDAV do Exchange 2003 , mas o WebDAV é complicado, o .NET tem suporte embutido pobre para ele e (para piorar a situação) o Exchange 2007 quase deixa o suporte WebDAV completamente .

O que um cara deve fazer? Acabei usando o componente IMAP do AfterLogic para me comunicar com meu servidor Exchange 2003 via IMAP, e isso acabou funcionando muito bem. (Eu normalmente procuro bibliotecas gratuitas ou de código aberto, mas achei todas as do .NET que faltavam - especialmente quando se trata de algumas das peculiaridades da implementação IMAP de 2003 - e esta era barata o suficiente e funcionou na primeira tente. Sei que existem outros por aí.)

Se sua organização usa o Exchange 2007, entretanto, você está com sorte. O Exchange 2007 vem com uma interface de serviço da Web baseada em SOAP que finalmente fornece uma maneira unificada e independente de linguagem de interagir com o servidor Exchange. Se você pode tornar o 2007+ um requisito, este é definitivamente o caminho a seguir. (Infelizmente para mim, minha empresa tem uma política "mas 2003 não está quebrado".)

Se você precisa conectar o Exchange 2003 e 2007, IMAP ou POP3 é definitivamente o caminho a percorrer.

Nicholas Piasecki
fonte
21
O serviço da web baseado em SOAP foi desenvolvido pela Microsoft para simplificar o acesso - agora é uma prática recomendada usar o Exchange Web Services Managed API 1.0 SDK: msdn.microsoft.com/en-us/library/dd633710(EXCHG.80).aspx
jlo
4
É quase como se a Microsoft o tivesse projetado para ser inoperante com qualquer coisa, exceto Outlook
Chris S
67

Hum,

Eu posso chegar um pouco tarde aqui, mas não é esse o ponto do EWS?

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

Requer cerca de 6 linhas de código para obter o e-mail de uma caixa de correio:

ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);

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

service.AutodiscoverUrl( "[email protected]" );

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

foreach ( Item item in findResults.Items )
{
   Console.WriteLine( item.Subject );
}
Guerra
fonte
5
"A API gerenciada EWS simplifica a implementação de aplicativos que se comunicam com o Microsoft Exchange Server 2007 Service Pack 1 (SP1) e versões posteriores do Microsoft Exchange"
Chris S
2
Percebo que isso é essencialmente um necrobump para uma mensagem de anos atrás, mas esse código me colocou em funcionamento para um projeto semelhante em cerca de cinco minutos. Funcionou perfeitamente da primeira vez. Realmente uma solução mais contemporânea / abrangente do que a resposta selecionada da IMO ... notando para a referência de qualquer outra pessoa.
David W
2
Observação sobre como fazer isso funcionar. Você precisa instalar o pacote NuGet "Microsoft Exchange WebServices"
John M
4
Isso funcionou para mim na primeira tentativa. Esta deve ser a nova resposta aceita.
kroe761
Posso saber se for usar um endereço de e-mail diferente da minha própria caixa de correio no service.autodiscoverurl, precisarei inserir o service.credentials, estou certo?
código da academia de
19
  1. A API atualmente preferida (Exchange 2013 e 2016) é EWS . É puramente baseado em HTTP e pode ser acessado de qualquer linguagem, mas existem bibliotecas específicas .Net e Java .

    Você pode usar o EWSEditor para brincar com a API.

  2. MAPI estendido . Esta é a API nativa usada pelo Outlook. Ele acaba usando o MSEMSprovedor Exchange MAPI, que pode se comunicar com o Exchange usando RPC (o Exchange 2013 não oferece mais suporte) ou RPC-over-HTTP (Exchange 2007 ou mais recente) ou MAPI-over-HTTP (Exchange 2013 e mais recente).

    A própria API só pode ser acessada a partir de C ++ não gerenciado ou Delphi . Você também pode usar o Redemption (qualquer idioma) - sua família de objetos RDO é um wrapper MAPI estendido. Para usar MAPI estendido, você precisa instalar o Outlook ou a versão autônoma (Exchange) do MAPI (com suporte estendido e não oferece suporte a arquivos Unicode PST e MSG e não pode acessar o Exchange 2016). O MAPI estendido pode ser usado em um serviço.

    Você pode brincar com a API usando OutlookSpy ou MFCMAPI .

  3. Modelo de objeto do Outlook - não específico do Exchange, mas permite acesso a todos os dados disponíveis no Outlook na máquina onde o código é executado. Não pode ser usado em um serviço.

  4. Exchange Active Sync . A Microsoft não investe mais recursos significativos neste protocolo.

  5. O Outlook costumava instalar a biblioteca CDO 1.21 (envolve o Extended MAPI), mas foi preterido pela Microsoft e não recebe mais nenhuma atualização.

  6. Costumava haver um wrapper .Net MAPI de terceiros chamado MAPI33, mas ele não está mais sendo desenvolvido ou com suporte.

  7. WebDAV - obsoleto.

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

  9. Provedor OLE DB do Exchange (EXOLEDB) - preterido.

Dmitry Streblechenko
fonte
o EwsEditor mudou para github: github.com/dseph/EwsEditor
Opmet
10

Aqui está um código antigo que criei para fazer WebDAV. Acho que foi escrito contra o Exchange 2003, mas não me lembro mais. Sinta-se à vontade para pegá-lo emprestado se for útil ...

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/[email protected]/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/[email protected]/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/[email protected]/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;
    }
}

E modelo.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; }
    }
}
CodingWithSpike
fonte
1
NOTA: O suporte WebDAV foi retirado do Exchange Server 2010; em vez disso, use o EWS.
Our Man in Bananas
0

Se o seu servidor Exchange estiver configurado para oferecer suporte a POP ou IMAP, essa é uma saída fácil.

Outra opção é o acesso WebDAV. existe uma biblioteca disponível para isso. Esta pode ser sua melhor opção.

Acho que há opções usando objetos COM para acessar o Exchange, mas não tenho certeza de como é fácil.

Tudo depende do que exatamente o seu administrador está disposto a lhe dar acesso, eu acho.

Denis Troller
fonte
0

Você deve ser capaz de usar o MAPI para acessar a caixa de correio e obter as informações de que precisa. Infelizmente, a única biblioteca .NET MAPI (MAPI33) que conheço parece não ter manutenção. Essa costumava ser uma ótima maneira de acessar MAPI por meio do .NET, mas não posso falar sobre sua eficácia agora. Há mais informações sobre onde você pode obtê-lo aqui: Local do download para MAPI33.dll?

Chris Hynes
fonte
0

Uma opção é usar o Outlook. Temos um aplicativo gerenciador de e-mail que acessa um servidor Exchange e usa o Outlook como interface. É sujo, mas funciona.

Código de exemplo:

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);
        }
duncane
fonte
1
Se eu quiser usar o Windows Service no Win2003 para acessar o Exchange 2003 ?? Preciso instalar o Outlook 2003 ou 2007 no Server win2003?
Kiquenet 19/11/10