Pull to refresh

Как получить все адреса из 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. }

Это моя первая в жизни статья, так что сильно не бейте :)
Спасибо за внимание.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.