Integrar Outlook y C#


Outlook es una aplicación para organizar y manejar correos, tareas, contactos, recordatorios, etc.

Podemos usar C# para enviar, listar, leer correos, y otras acciones que generalmente hacemos con Outlook, por ejemplo, si queremos tener una aplicación, y en esa aplicación tener una opción para estar leyendo los correos, o enviar correos, además de obtener la lista de los contactos, adicionar contactos.

Manos a la obra!

–          Creamos un nuevo proyecto en Visual Studio

–          Se debe importar la referencia Microsoft.Office.Interop.Outlook:

Ahora creamos la interfaz grafica, la mía es esta, no es muy buena pero al menos sirve para mostrar algunas cosas 😛 :

Lo que tengo pensado hacer es:

–          Listar correos (Asunto, Remitente, Texto, Hora)

–          Listar Contactos

–          Adicionar y Modificar un contacto

–          Eliminar un contacto

–          Enviar un Correo

–          Y una que otra información que podamos obtener del OutLook

En el código, lo que hacemos es:

–          Declarar una variable que será el objeto de la aplicación Outlook

–          Obtener el Namespace de la aplicación sobre la cual trabajamos

En el código declaramos como variables globales los siguientes objetos:

Microsoft.Office.Interop.Outlook.Application outlookApp = new Microsoft.Office.Interop.Outlook.Application();
NameSpace outlookNS = null;
MAPIFolder mails = null;

En el constructor inicializamos los objetos:

outlookNS = outlookApp.GetNamespace("MAPI");
mails = outlookNS.GetDefaultFolder(OlDefaultFolders.olFolderInbox);

Al enviar la cadena “MAPI” estamos obteniendo, los datos del namespace, actualmente MAPI es el único namespace valido.

El objeto MAPIFolder, representa una carpeta de Outlook, una carpeta puede contener otras carpetas y así sucesivamente, el método GetDefaultFolder(índice), donde índice es el número de la carpeta del espacio de nombre actual.

Mostrar los correos:

Para mostrar los correos que tenemos en nuestra carpeta inbox, debemos recorrer la carpeta, obtener los ítems,  verificar que sean de tipo MailItem y luego mostrar los datos.

Los objetos ítem de la clase MAPIFolder, pueden ser Notas, Citas, Post, Correos, Recordatorios, Tareas, etc., por eso debemos intentar convertirlos en MailItem para luego obtener sus características, si no hacemos esto se lanzara una excepción.

foreach (object obj in mails.Items)
{
  MailItem item = obj as MailItem;
  if (item != null)
  {
   listView1.Items.Add("Hora :" + item.ReceivedTime + " , Remitente :" + item.SenderName + " , Asunto :" + item.Subject);
  }
}

Mostrar los correos de una carpeta determinada:

En muchos casos tenemos muchas carpetas dentro de la carpeta Inbox, para poder categorizar los correos, en el ejemplo anterior, se obtenían todos los correos, pero si queremos mostrar solo los de una en especifica debemos hacer lo siguiente:

string folderName = "PruebaCorreos";
try
 {
  outlookApp.ActiveExplorer().CurrentFolder = mails.Folders[folderName];
  label3.Text = "Total de Correos: " + outlookApp.ActiveExplorer().CurrentFolder.Items.Count.ToString();
  foreach (object obj in outlookApp.ActiveExplorer().CurrentFolder.Items)
   {
     MailItem item = obj as MailItem;
     if (item != null)
      {
       listView1.Items.Add(item.Subject);
      }
    }
  }
catch
  {
   MessageBox.Show("No existe una carpeta llamada:  " + folderName + ".");
   }

Hemos cambiado el control ListBox por un ListView debido a que tenemos más opciones.

Debemos tener en cuenta que la carpeta: “PruebaCorreos” debe existir dentro de la carpeta Inbox:

Si copiamos esto en el botón de Ver Correos y luego ejecutamos:

Al ejecutar, se van a mostrar los correos que se encuentren en la carpeta “PruebaCorreos”, como dato si ven en el campo de Información General, aparecen que hay en total 4 correos, lo cual es mentira, existen 4 Items, de los cuales 3 son correos, por lo tanto debemos especificar en el count que los ítems que va a buscar son de la clase MailItem:

Items Correos = outlookApp.ActiveExplorer().CurrentFolder.Items.Restrict("[MessageClass]='IPM.Note'");
label3.Text = "Total de Correos: " + Correos.Count.ToString();

El metodo Restrict, permite tener un filtro para hacer una busqueda, el filtro que aplicamos es el tipo de la clase del Item, para los correos el tipo es IPM.Note, la lista completa de los tipos de Item en esta página: http://msdn.microsoft.com/en-us/library/bb176446.aspx

Podemos aplicar más filtros, para mostrar los mensajes que no han sido leidos, además modificar la inserción de los correos, para que si el correo no está leído, pintar la fila de un color diferente:

label1.Text = "Correos No Leidos: " + Correos.Restrict("[Unread]=true").Count.ToString();
foreach (object obj in outlookApp.ActiveExplorer().CurrentFolder.Items)
{
 MailItem item = obj as MailItem;
 if (item != null)
 {
  if (item.UnRead)
  {
   ListViewItem unreadItem = new ListViewItem();
   unreadItem.BackColor = Color.Yellow;
   unreadItem.Text = item.Subject;
   listView1.Items.Add(unreadItem);
  }
  else
  {
   listView1.Items.Add("* " + item.Subject);
  }
 }
}

Si vemos en Outlook, podemos ver los correos que no estan leidos:

Y en la aplicación:

Ahora lo que queda es hacer que al presionar uno de los correos, se nos muestre la información.

Hacemos doble clic sobre el control ListView, y escribimos lo siguiente:

private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
 String filtro = "";
 for (int i = 0; i < listView1.Items.Count; i++)
 {
  if (listView1.Items[i].Selected)
  {
    filtro = listView1.Items[i].Text;
  }
 }
 string folderName = "PruebaCorreos";
 outlookApp.ActiveExplorer().CurrentFolder = mails.Folders[folderName];
 object seleccionado = outlookApp.ActiveExplorer().CurrentFolder.Items.Find("[Subject] = '" + filtro + "'");
 if (seleccionado != null)
 {
  MailItem Mail = seleccionado as MailItem;
  if (Mail != null)
  {
   label4.Text = "Remitente: " + Mail.SenderName;
   label5.Text = "Hora envio: " + Mail.ReceivedTime;
   label6.Text = "Correo Remitente: " + Mail.SenderEmailAddress;
   Mail.BodyFormat = OlBodyFormat.olFormatRichText;
   richTextBox1.Text = Mail.Body;
  }
 }
}

Al ejecutar el código puede que nos aparezca este mensaje de advertencia para poder ingresar a los datos de Outlook:

Al permitir el acceso tenemos:

Por el momento dejamos hasta aquí, en la siguiente entrada listaremos, agregamos y eliminamos contactos, y enviamos correos.

Codigo Fuente AQUI.

Referencias.

http://msdn.microsoft.com/en-us/library/aa289167(VS.71).aspx

http://msdn.microsoft.com/en-us/library/ms268869(VS.80).aspx

Anuncios

46 pensamientos en “Integrar Outlook y C#

  1. Hola
    Me resulto muy interesante todo el comentario que hace de como trabajar con la libreria Microsoft.Office.Interop.Outlook,
    yo estoy trabajando en esos temas y me surge una duda, esa clase trabaja con los correos que tienes previamente descargados en el outlook , es decir no se conecta directamente al servidor donde estan los correo.

  2. Sabes que no hay caso, trato de implementar esta solución pero desde un proyecto web y me sale el error de:

    Error al recuperar un generador de clases COM para el componente con CLSID {0006F03A-0000-0000-C000-000000000046} debido al siguiente error: 80070005.

    Al dejar el impersonate a true y seguridad de windows integrada se soluciona, pero como esta aplicacion la quiero poner dentro de otro sitio, no puedo dejar la seguridad integrada habilitada.

    ¿Alguna idea?

    • Hola, no me había ocurrido el error, pero pues estos podrían ser los posibles problemas:
      * Outlook (No la aplicación de C# o web) no se está ejecutando con el Usuario Administrador
      * El código de servidor se ejecuta sin un perfil de usuario o la identidad de usuario especificada en el contexto de inicio no tiene los permisos DCOM adecuados. (http://support.microsoft.com/kb/257757)
      * Sistemas Operativos, puede ser algún bug del actual Sistema operativo, verificar con otros para descartar esto.

      • Hola, pudiste solucionar el error [“generador de clases COM para el componente con CLSID {0006F03A-0000-0000-C000-000000000046} debido al siguiente error: 80070005″[. Lo he hecho desde un proyecto web y la aplicación aborta en la linea:

        Microsoft.Office.Interop.Outlook.Application outlookApp = new Microsoft.Office.Interop.Outlook.Application();

        Gracias por tu ayuda…

  3. Hola, muy bueno tu post fijate que lo he probado y si funciona, lo que tengo duda es que si tu sabes como le puedo hacer para validar diferentes cuentas en el servidor exchange por ejemplo: la primera vez quiero entrar con la cuenta yo@miempresa.com la segunda vez quiero entrar a la cuenta yo2@miempresa.com, ya que con este ejemplo entramos a la cuenta en uso, pero si yo quiero validar diferentes tu sabes si hay algun atributo o metodo dentro de la clase….. gracias <.p

  4. amigo muy interesante esta entrada, ojala y pronto podamos ver la siguente en la que podamos trabajar con contactos y envios de archivos

  5. Hola, yo de nuevo.
    Aún no he podido solucionar la consulta anterior lo probé es XP y en un servidor pero no hubo caso, en fin, escalé el tema.
    Ahora tengo otra duda, sabes que cuando envío un correo desde un usuario outlook, onda TestUser@miempresa.com y deseo leer el código del remitente con SenderEmailAddress me sale algo como “‘/O=Company, Inc./OU=Company/cn=Recipients/cn=TestUser'”.
    He buscado en la web y no encuentro como leer la dirección completa. Me dicen que use Redemtion en la mayoría de los casos, pero quiero evitar usar dll externas.

    • Intenta con esto después de obtener el correo:
      if (Mail.SenderEmailType == “EX”)
      {
      Recipient r = outlookNS.CreateRecipient(Mail.SenderEmailAddress);
      r.Resolve();
      ExchangeUser usuario = r.AddressEntry.GetExchangeUser();
      String mailEX = usuario.PrimarySmtpAddress;
      }
      Me cuentas

  6. Si, ya lo intenté, pero es el GetExchangeUser();
    que no aparece, estoy usando el Microsoft Office 11.0 Object Library para el outlook 2003.
    En fin, “creo” que son los correos internos que vienen en ese formato, asi que lo que hice fue capturar el nombre del remitente “TestUser” y agregar el @ y la extension de mi compañia “@MyCompany.com”.
    Aaaahhhh, y para instalar la aplicación, como no pude encontrar solución al error de la COM, la llamé desde una aplicación de consola activándola como tarea programada de windows.
    Como verás puros parches, pero funcionan.
    Saludos y gracias por responder mis consultas.

  7. Utilicé el codigo y funciona bien. Pero las credenciales las obtiene del perfil de outlook usado no? Hay posibilidad de asignar el perfil que uno quiera, para poder levantar el correo desde cualquier pc, sea fuera del dominio? Necesito conectarme a una cuenta exchange 2007 para leer un correo pero asignandole manualmente las credenciales, alguna idea???? Graciass

    • Hola, lo que encontré al parecer primero se debe configurar el outlook en la máquina donde se va a ejecutar el código de C#, es por esto que en mi código no pido ninguna credencial, el usuario con el que ingrese a Windows, tiene permisos para acceder al Exchange y a los correos, aunque hay un método para hacer Logon con diferentes profiles:

      outlookApp.Session.Logon(Missing.Value, Missing.Value, true, true);

      Donde el primer parámetro es el profiler, el segundo el password, el tercero un booleano para saber si muestra la caja de Dialogo, y el último es para iniciar una nueva sesión. Aunque este método se debe usar solo cuando outlook no se encuentra ejecutando.

      Voy a seguir buscando info para saber si lo que dices se puede hacer

  8. Hola, yo de nuevo.

    Necesito saber si puedes identificar si estas parado sobre un archivo .pst . Sucede que tengo esta línea de comando:

    outLookApp.Session.AddStore
    (@”C:\OutlookApp\MyAddin.pst”);

    y lo capturo con:
    MAPIFolder pstFile = nspOutLook.Session.Folders.GetLast();

    Pero de pronto se marea y me deja mi variable pstFile, por ejemplo, en el inbox.

    No puedo ponerle un nombre fijo porque no reconoce el nuevo nombre hasta que se cierra la aplicacion y se vuelve a ejecutar, y al dejarle el nombre por defecto “Carpeta personales” si esta en ingles no se llama asi.

    Entonces necesito saber, si la carpeta sobre la que estoy parado es o no un archivo .pst.

    ¿Me entiendes? ¿Alguna idea?

  9. Cordial saludo

    muy interesante esta informacion, actualmente estoy desarrollando un proyecto en el que me conecto a una cuenta de gmail por smtp para el envio de correos y actualizar una base de datos donde se registra la hora de envio del correo y a que email fue enviado, esta cuenta de correo de gmail la tengo configurada en outlook, ahora necesito recuperara los correos que entren a esta cuenta y actualizar en la base de datos el estado especificamente si retorna error de envio, utilizando este metodo podria recuperar el tipo de correo y la direccion de correo a la que fue enviado inicialmente?

    de antemano agradezco cualquier colaboracion

    • Claro, en cuánto sepas que objeto MailItem vas a usar, puedes modificar el código anterior y hacer lo siguiente:


      if (Mail != null)
      {
      //label4.Text = "Remitente: " + Mail.SenderName;
      //label5.Text = "Hora envio: " + Mail.ReceivedTime;
      //label6.Text = "Correo Remitente: " + Mail.SenderEmailAddress;
      //Mail.BodyFormat = OlBodyFormat.olFormatRichText;
      //richTextBox1.Text = Mail.Body;
      //Descargar los archivos adjuntos
      if (Mail.Attachments.Count > 0)
      {
      for (int i = 0; i < Mail.Attachments.Count; i++)
      {
      Mail.Attachments[i].SaveAsFile(@"C:\Temp\" + Mail.Attachments[i].FileName);
      }
      }
      }

      • en esta linea: Mail.Attachments[i].SaveAsFile(@”C:\temp\” + Mail.Attachments[i].FileName);
        Gener esta error:Índice de matriz fuera de límites. por que?

      • esto corrige lo del error del fuera de indice…

        foreach (Attachment attachment in item.Attachments)
        {
        attachment.SaveAsFile(@”C:\temp\” + attachment.FileName);
        }

    • mmm, quieres es obtener el contenido de los archivos adjuntos, si es así te toca por otras formas, ya que depende del tipo del archivo que enviaron, si el archivo es tipo texto puedes intentar leerlo con un StreamReader y si es de otro tipo puedes usar BinaryReader

    • Hola, una forma puede ser viendo el nombre del archivo y sabiendo la extensión:
      for (int i = 1; i < Mail.Attachments.Count; i++)
      {
      String name = Mail.Attachments[i].FileName;
      String ext = name.Substring(name.LastIndexOf(".") + 1);
      if(ext == "xml")…
      }

  10. Hola tengo un problema yo necesito enviar un correo cuando mi aplicacion este instalada, por lo que seria necesario que se mandara con la configuracion que tenga el outlook en la maquina que este corriendo mi aplicacion .Alguna sugerencia?

      • Tengo el Outlook configurado con una cuenta de Gmail.. y no me bota ningun error al ejecutar la aplicación.. Solo no me muestra los correos.

      • Si ya me salio… me gustaria saber si tienes alguna informacion de como poder crear un archivo de carpetas personales(.pst) desde c#. Si se pueden agregar filtros seria mejor..
        Gracias

  11. Hola a todos; me parece un código interesante ¿se llegó a escribir la “siguiente entrada” en la que se indica que se verá como listar contactos? en caso afirmativo, ¿me podrían indicar el enlace a ella, que no logro localizarla?

    Gracias,

  12. Hola, muy bueno el post, pero tengo un problema, a ver si puedes darme la solución.
    Si ejecuto el código usando el servidor de Visual Studio 2010 me funciona perfectamente, mientras que si lo ejecuto usando el IIS de mi servidor me da el siguiente error; “No se pudo recuperar el generador de clases COM para el componente con CLSID {0006F03A-0000-0000-C000-000000000046} debido al siguiente error: 80070005 Acceso denegado. (Excepción de HRESULT: 0x80070005 (E_ACCESSDENIED)).” en la siguiente línea de código:
    Outlook.Application outlookApp = (Outlook.Application)new Outlook.Application();
    Indicar que el Viisual Studio 2010 Framework 4.0 no me da ningún error de referencias, …

    Que estoy haciendo mal?
    Pueden echarme una mano?

  13. tengo una duda ?? funciona normal lo que posteaste, pero es necesario que este instalado el outlook(Paquete Office) en el servidor para que funcione..?? y en el caso de que el outlook no este detenido que codigo debo de poner para que funcione la aplicación??

  14. hola amigos, me ha resultado todo lo realizado, pero existe un problema los correos que vienen con archivos adjuntos, no me los lee, ¿existirá algún problema con el filtro aplicado?, a ¿alguien le pasa lo mismo?, espero sus respuestas, saludos

  15. Buenas!¿alguien sabe qué filtro había que utilizar para filtrar por fecha de recepción del correo?he probado con LastModificationTime y no hace nada..no sé si habrá alguno más referente a la fecha de recepción del correo.
    Muchas gracias, saludos.

  16. Hola
    Me funciona muy bien, me trae todos los mail, pero me esta pasando algo, en ciertos correos que me los coloca en la listView al seleccionarlo, no me muestra el mensaje, verifique el códo y funciona, pero cuando el subjetc es largo no lo encuentra para enviar el cuerpo del mensaje. Como se podria solicuionar esto

Deseas comentar o sugerir algo?

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s