Как стать автором
Обновить

Как получить все адреса из MS Exchange через MAPI (C#)

Доброго времени суток всем!
Мне пришлось столкнуться с этой задачей на работе во время написания плагина к Outlook 2003
В процессе выяснилось, что все E-Mail адреса по имени из Глобального списка адресов (GAL) MS Exchange вытащить не так то просто.
Поковыряв интернет, смог найти только получение одного адреса SMPT по умолчанию, написанное на C#. Вооружившись энтузиазмом и интересом я немного переделал этот код под свои нужды (чтобы он выдавал все адреса).
Итак, для начала нам понадобится импотрировать MAPIшные функции:
  1. [DllImport("MAPI32.DLL", CharSet = CharSet.Ansi, EntryPoint = "HrGetOneProp@12")]
  2. private static extern void HrGetOneProp(IntPtr pmp, uint ulPropTag, out IntPtr ppProp);
  3. /* Это нам не пригодится, но на всякий напишу */
  4. [DllImport("MAPI32.DLL", CharSet = CharSet.Ansi, EntryPoint = "HrSetOneProp@8")]
  5. private static extern void HrSetOneProp(IntPtr pmp, IntPtr pprop);
  6.  
  7. [DllImport("MAPI32.DLL", CharSet = CharSet.Ansi, EntryPoint = "MAPIFreeBuffer@4")]
  8. private static extern void MAPIFreeBuffer(IntPtr lpBuffer);
  9.  
  10. [DllImport("MAPI32.DLL", CharSet = CharSet.Ansi)]
  11. private static extern int MAPIInitialize(IntPtr lpMapiInit);
  12.  
  13. [DllImport("MAPI32.DLL", CharSet = CharSet.Ansi)]
  14. private static extern void MAPIUninitialize();
  15.  
  16. private const uint PR_EMS_AB_PROXY_ADDRESSES = 0x800F101F; //код свойства, которое нам надо вытащить
  17.  
  18. private const long mask = 0x00000000FFFFFFFF;
  19.                   //нужна для того, чтобы отделить потом количество строк в массиве результата от указателя на начало массива
  20.  
  21. //Структура, указатель на которую нам вернет функция HrGetOneProp()
  22. private struct SPropValue
  23. {
  24.     public uint ulPropTag;
  25.     public uint dwAlignPad;
  26.     public long Value;
  27. }

После всех приготовлений, напишем функцию, которая будет принимать на вход MAPI объект из AddressEntry
(этот интерфейс есть в пространстве имен Microsoft.Office.Interop.Outlook и как его вытащить GAL можно найти в инете, либо это будет отдельная статья)
Заметьте, что при компилировании данного кода надо поставить галочку Allow Unsafe Code в настройках компилятора.
Сама функция:
  1. private static unsafe string[] GetMapiProperty(AddressEntry olAddress)
  2. {
  3.     if (olAddress == null) return null;
  4.     //вытаскиваем из переданного интерфейса МАПИ объект
  5.     var oMapiObject = olAddress.MAPIOBJECT;
  6.     string[] retval = null;
  7.     IntPtr pPropValue = IntPtr.Zero; //указатель на структуру SPropValue
  8.     IntPtr pIUnknown = IntPtr.Zero; //Указатель на IUnknown интерфейс
  9.     IntPtr imapiProperty = IntPtr.Zero; //Указатель на свойство интерфейса
  10.  
  11.     try
  12.     {
  13.         //инициализируем МАПИ
  14.         MAPIInitialize(IntPtr.Zero);
  15.         //Запрашиваем интерфейс для нашего объекта
  16.         pIUnknown = Marshal.GetIUnknownForObject(oMapiObject);
  17.         //Просим указатель на свойство
  18.         var guidMapiProp = new Guid(0x00020303, 0, 0, 0xC0, 0, 0, 0, 0, 0, 0, 0x46);
  19.         if (Marshal.QueryInterface(pIUnknown, ref guidMapiProp, out imapiProperty) != 0) return null;
  20.  
  21.         try
  22.         {
  23.             // и если все ок, то вытаскиваем указатель на значения свойства
  24.             HrGetOneProp(imapiProperty, PR_EMS_AB_PROXY_ADDRESSES, out pPropValue);
  25.  
  26.             if (pPropValue == IntPtr.Zero) return null;
  27.             //берем из памяти структуру по указателю
  28.             var propValue =
  29.                  (SPropValue) Marshal.PtrToStructure(pPropValue, typeof (SPropValue));
  30.             //propValue.Value содержит 64-битное целое, первая половина которого
  31.             //указатель, а вторая число E-mail'ов в массиве
  32.             // поэтому надо отделить одно от другого
  33.             var adrcnt = propValue.Value & mask;
  34.             var adrptr = new IntPtr((propValue.Value | mask) >> 32);
  35.             retval = new string[adrcnt];
  36.             //пробегаем по всем строкам и суем их в результирующий массив
  37.             for (int i = 0; i < adrcnt; i++)
  38.             {
  39.                 //читаем указатель на строку. i * sizeof(IntPtr) - смещение
  40.                 var strptr = Marshal.ReadIntPtr(adrptr, i*sizeof (IntPtr));
  41.                 //Вытаскиваем строку по адресу (из МСДН узнал, что они юникодные)
  42.                 var email = Marshal.PtrToStringUni(strptr);
  43.  
  44.                 if (email != null) retval[i] = email;
  45.             }
  46.         }
  47.         catch (Exception ex)
  48.         {
  49.             throw ex; //тут можно поставить обработчик по вкусу...
  50.         }
  51.     }
  52.     finally
  53.     {
  54.         //после всех манипуляций чистим за собой
  55.         if (pPropValue != IntPtr.Zero)
  56.         {
  57.             MAPIFreeBuffer(pPropValue);
  58.         }
  59.  
  60.         if (imapiProperty != IntPtr.Zero)
  61.         {
  62.             Marshal.Release(imapiProperty);
  63.         }
  64.  
  65.         if (pIUnknown != IntPtr.Zero)
  66.         {
  67.             Marshal.Release(pIUnknown);
  68.         }
  69.         MAPIUninitialize();
  70.     }
  71.     
  72.         return retval;
  73. }

Это моя первая в жизни статья, так что сильно не бейте :)
Спасибо за внимание.
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.