Leer el correo electrónico de MS Exchange en C #

91

Necesito la capacidad de monitorear y leer el correo electrónico de un buzón en particular en un servidor MS Exchange (interno de mi empresa). También necesito poder leer la dirección de correo electrónico del remitente, el asunto, el cuerpo del mensaje y descargar un archivo adjunto, si lo hubiera.

¿Cuál es la mejor manera de hacer esto usando C # (o VB.NET)?

vajarov
fuente
4
Desde entonces, Microsoft ha lanzado la API administrada de servicios web de Exchange para Exchange 2007 SP1 y v2010, que le permite ingresar de manera programática a su buzón de correo sin la necesidad de Outlook. Tengo dos artículos en mi blog que discuten este enfoque: - C #: Obtener todos los correos electrónicos de Exchange usando los servicios web de Exchange
ΩmegaMan
Exchange Web Services Managed API 1.0 SDK es el método recomendado por Microsoft para actualizar Exchange mediante programación para Exchange Server 2007 SP1 y versiones posteriores. msdn.microsoft.com/en-us/library/dd633710(EXCHG.80).aspx
jlo

Respuestas:

90

Es un desastre. MAPI o CDO a través de una DLL de interoperabilidad .NET no es oficialmente compatible con Microsoft; parecerá que funciona bien, pero hay problemas con pérdidas de memoria debido a sus diferentes modelos de memoria. Puede utilizar CDOEX, pero eso solo funciona en el servidor Exchange, no de forma remota; inútil. Podría interoperar con Outlook, pero ahora acaba de hacer una dependencia en Outlook; exageración. Por último, puede utilizar el soporte WebDAV de Exchange 2003 , pero WebDAV es complicado, .NET tiene un soporte integrado deficiente y (para colmo de males) Exchange 2007 prácticamente elimina por completo el soporte WebDAV.

¿Qué puede hacer un chico? Terminé usando el componente IMAP de AfterLogic para comunicarme con mi servidor Exchange 2003 a través de IMAP, y esto terminó funcionando muy bien. (Normalmente busco bibliotecas gratuitas o de código abierto, pero encontré que todas las de .NET faltan, especialmente cuando se trata de algunas de las peculiaridades de la implementación IMAP de 2003, y esta era lo suficientemente barata y funcionó en la primera probar. Sé que hay otros por ahí).

Sin embargo, si su organización está en Exchange 2007, está de suerte. Exchange 2007 viene con una interfaz de servicio web basada en SOAP que finalmente proporciona una forma unificada e independiente del idioma de interactuar con el servidor Exchange. Si puede hacer que 2007+ sea un requisito, este es definitivamente el camino a seguir. (Lamentablemente para mí, mi empresa tiene una política de "pero 2003 no está roto").

Si necesita unir Exchange 2003 y 2007, IMAP o POP3 es definitivamente el camino a seguir.

Nicolás Piasecki
fuente
21
Microsoft empaquetó el servicio web basado en SOAP para simplificar el acceso; ahora se recomienda utilizar el SDK de la API 1.0 administrada de Exchange Web Services: msdn.microsoft.com/en-us/library/dd633710(EXCHG.80).aspx
jlo
4
Es casi como si Microsoft lo hubiera diseñado para ser inoperable con cualquier cosa que no sea Outlook
Chris S
67

Um,

Puede que sea un poco tarde aquí, pero ¿no es este el punto de EWS?

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

Se necesitan aproximadamente 6 líneas de código para recibir el correo de un buzón:

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
fuente
5
"La API administrada de EWS simplifica la implementación de aplicaciones que se comunican con Microsoft Exchange Server 2007 Service Pack 1 (SP1) y versiones posteriores de Microsoft Exchange"
Chris S
2
Me doy cuenta de que esto es esencialmente un necrobump para un mensaje de hace años, pero este código me puso en funcionamiento para un proyecto similar en unos cinco minutos. Funcionó perfectamente la primera vez. Realmente una solución más contemporánea / completa que la respuesta seleccionada en mi opinión ... señalando la referencia de cualquier otra persona.
David W
2
Tenga en cuenta cómo hacerlo funcionar. Necesita instalar el paquete NuGet "Microsoft Exchange WebServices"
John M
4
Esto funcionó para mí en el primer intento. Esta debería ser la nueva respuesta aceptada.
kroe761
¿Puedo saber si tuviera que usar una dirección de correo electrónico además de mi propio buzón de correo en service.autodiscoverurl, tendré que ingresar service.credentials, estoy en lo cierto?
gymcode
19
  1. La API actualmente preferida (Exchange 2013 y 2016) es EWS . Está basado puramente en HTTP y se puede acceder a él desde cualquier idioma, pero existen bibliotecas específicas de .Net y Java .

    Puede utilizar EWSEditor para jugar con la API.

  2. MAPI extendido . Esta es la API nativa que usa Outlook. Termina utilizando el MSEMSproveedor MAPI de Exchange, que puede comunicarse con Exchange mediante RPC (Exchange 2013 ya no lo admite) o RPC sobre HTTP (Exchange 2007 o posterior) o MAPI sobre HTTP (Exchange 2013 y posterior).

    Solo se puede acceder a la API en sí desde C ++ o Delphi no administrados . También puede usar Redemption (cualquier idioma): su familia de objetos RDO es un contenedor MAPI extendido. Para usar MAPI extendido, debe instalar Outlook o la versión independiente (Exchange) de MAPI (con soporte extendido, no admite archivos Unicode PST y MSG y no puede acceder a Exchange 2016). MAPI extendido se puede utilizar en un servicio.

    Se puede jugar con la API usando OutlookSpy o MFCMAPI .

  3. Modelo de objetos de Outlook : no es específico de Exchange, pero permite el acceso a todos los datos disponibles en Outlook en la máquina donde se ejecuta el código. No se puede utilizar en un servicio.

  4. Exchange Active Sync . Microsoft ya no invierte recursos importantes en este protocolo.

  5. Outlook solía instalar la biblioteca CDO 1.21 (incluye MAPI extendido), pero Microsoft la ha desaprobado y ya no recibe actualizaciones.

  6. Solía ​​haber un contenedor .Net MAPI de terceros llamado MAPI33, pero ya no se desarrolla ni se admite.

  7. WebDAV: obsoleto.

  8. Objetos de datos colaborativos para Exchange (CDOEX): en desuso.

  9. Proveedor Exchange OLE DB (EXOLEDB): obsoleto.

Dmitry Streblechenko
fuente
el EwsEditor se ha trasladado a github: github.com/dseph/EwsEditor
Opmet
10

Aquí hay un código antiguo que tenía para hacer WebDAV. Creo que fue escrito contra Exchange 2003, pero no recuerdo nada más. Siéntase libre de pedirlo prestado si es ú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;
    }
}

Y modelo. Correo:

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; }
    }
}
Codificación con espiga
fuente
1
NOTA: La compatibilidad con WebDAV se eliminó de Exchange Server 2010, utilice EWS en su lugar.
Our Man in Bananas
0

Si su servidor Exchange está configurado para admitir POP o IMAP, esa es una salida fácil.

Otra opción es el acceso WebDAV. hay una biblioteca disponible para eso. Ésta podría ser tu mejor opción.

Creo que hay opciones que usan objetos COM para acceder a Exchange, pero no estoy seguro de lo fácil que es.

Todo depende de a qué exactamente su administrador esté dispuesto a darle acceso, supongo.

Denis Troller
fuente
0

Debería poder utilizar MAPI para acceder al buzón y obtener la información que necesita. Desafortunadamente, la única biblioteca .NET MAPI (MAPI33) que conozco parece no estar mantenida. Esta solía ser una excelente manera de acceder a MAPI a través de .NET, pero ahora no puedo hablar de su efectividad. Hay más información sobre dónde puede obtenerlo aquí: ¿ Ubicación de descarga para MAPI33.dll?

Chris Hynes
fuente
0

Una opción es utilizar Outlook. Tenemos una aplicación de administrador de correo que accede a un servidor de intercambio y utiliza Outlook como interfaz. Está sucio pero funciona.

Código de ejemplo:

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
fuente
1
¿Si quiero usar el servicio de Windows en Win2003 para acceder a Exchange 2003? ¿Necesito instalar Outlook 2003 o 2007 en el servidor win2003?
Kiquenet