Mapi.cs
branchOUT-283
changeset 2353 81975e284b07
child 2354 e5b26ee3c384
equal deleted inserted replaced
2351:1a6bc4294582 2353:81975e284b07
       
     1 ´╗┐using System;
       
     2 using System.Runtime.InteropServices;
       
     3 using Outlook = Microsoft.Office.Interop.Outlook;
       
     4 
       
     5 namespace pEp
       
     6 {
       
     7     internal class Mapi
       
     8     {
       
     9         #region Definitions
       
    10         /// <summary>
       
    11         /// A collection of MAPI error or warning codes.
       
    12         /// </summary>
       
    13         internal static class HResult
       
    14         {
       
    15             public const uint S_FALSE                           = 0x00000001;
       
    16             public const uint S_OK                              = 0x00000000;
       
    17 
       
    18             public const uint E_NOTIMPL                         = 0x80004001;
       
    19 
       
    20             public const uint MAPI_E_CALL_FAILED                = 0x80004005;
       
    21             public const uint MAPI_E_NOT_ENOUGH_MEMORY          = 0x8007000E;
       
    22             public const uint MAPI_E_INVALID_PARAMETER          = 0x80000003;
       
    23             public const uint MAPI_E_INTERFACE_NOT_SUPPORTED    = 0x80000004;
       
    24             public const uint MAPI_E_NO_ACCESS                  = 0x80000009;
       
    25 
       
    26             public const uint E_INVALIDARG                      = 0x80070057;
       
    27             public const uint E_OUTOFMEMORY                     = 0x80000002;
       
    28             public const uint E_UNEXPECTED                      = 0x8000FFFF;
       
    29             public const uint E_FAIL                            = 0x80000008;
       
    30 
       
    31             public const uint MAPI_E_NO_SUPPORT                 = 0x80040000 | 0x102;
       
    32             public const uint MAPI_E_BAD_CHARWIDTH              = 0x80040000 | 0x103;
       
    33             public const uint MAPI_E_STRING_TOO_LONG            = 0x80040000 | 0x105;
       
    34             public const uint MAPI_E_UNKNOWN_FLAGS              = 0x80040000 | 0x106;
       
    35             public const uint MAPI_E_INVALID_ENTRYID            = 0x80040000 | 0x107;
       
    36             public const uint MAPI_E_INVALID_OBJECT             = 0x80040000 | 0x108;
       
    37             public const uint MAPI_E_OBJECT_CHANGED             = 0x80040000 | 0x109;
       
    38             public const uint MAPI_E_OBJECT_DELETED             = 0x80040000 | 0x10A;
       
    39             public const uint MAPI_E_BUSY                       = 0x80040000 | 0x10B;
       
    40             public const uint MAPI_E_NOT_ENOUGH_DISK            = 0x80040000 | 0x10D;
       
    41             public const uint MAPI_E_NOT_ENOUGH_RESOURCES       = 0x80040000 | 0x10E;
       
    42             public const uint MAPI_E_NOT_FOUND                  = 0x80040000 | 0x10F;
       
    43             public const uint MAPI_E_VERSION                    = 0x80040000 | 0x110;
       
    44             public const uint MAPI_E_LOGON_FAILED               = 0x80040000 | 0x111;
       
    45             public const uint MAPI_E_SESSION_LIMIT              = 0x80040000 | 0x112;
       
    46             public const uint MAPI_E_USER_CANCEL                = 0x80040000 | 0x113;
       
    47             public const uint MAPI_E_UNABLE_TO_ABORT            = 0x80040000 | 0x114;
       
    48             public const uint MAPI_E_NETWORK_ERROR              = 0x80040000 | 0x115;
       
    49             public const uint MAPI_E_DISK_ERROR                 = 0x80040000 | 0x116;
       
    50             public const uint MAPI_E_TOO_COMPLEX                = 0x80040000 | 0x117;
       
    51             public const uint MAPI_E_BAD_COLUMN                 = 0x80040000 | 0x118;
       
    52             public const uint MAPI_E_EXTENDED_ERROR             = 0x80040000 | 0x119;
       
    53             public const uint MAPI_E_COMPUTED                   = 0x80040000 | 0x11A;
       
    54             public const uint MAPI_E_CORRUPT_DATA               = 0x80040000 | 0x11B;
       
    55             public const uint MAPI_E_UNCONFIGURED               = 0x80040000 | 0x11C;
       
    56             public const uint MAPI_E_FAILONEPROVIDER            = 0x80040000 | 0x11D;
       
    57             public const uint MAPI_E_UNKNOWN_CPID               = 0x80040000 | 0x11E;
       
    58             public const uint MAPI_E_UNKNOWN_LCID               = 0x80040000 | 0x11F;
       
    59 
       
    60             public const uint MAPI_E_CORRUPT_STORE              = 0x80040000 | 0x600;
       
    61             public const uint MAPI_E_NOT_IN_QUEUE               = 0x80040000 | 0x601;
       
    62             public const uint MAPI_E_NO_SUPPRESS                = 0x80040000 | 0x602;
       
    63             public const uint MAPI_E_COLLISION                  = 0x80040000 | 0x604;
       
    64             public const uint MAPI_E_NOT_INITIALIZED            = 0x80040000 | 0x605;
       
    65             public const uint MAPI_E_NON_STANDARD               = 0x80040000 | 0x606;
       
    66             public const uint MAPI_E_NO_RECIPIENTS              = 0x80040000 | 0x607;
       
    67             public const uint MAPI_E_SUBMITTED                  = 0x80040000 | 0x608;
       
    68             public const uint MAPI_E_HAS_FOLDERS                = 0x80040000 | 0x609;
       
    69             public const uint MAPI_E_HAS_MESSAGES               = 0x80040000 | 0x60A;
       
    70             public const uint MAPI_E_FOLDER_CYCLE               = 0x80040000 | 0x60B;
       
    71         };
       
    72 
       
    73         /// <summary>
       
    74         /// Interface IDs used to retrieve the specific MAPI Interfaces from the IUnknown object.
       
    75         /// </summary>
       
    76         internal static class MAPIInterfaceIds
       
    77         {
       
    78             public const string IMAPISession        = "00020300-0000-0000-C000-000000000046";
       
    79             public const string IMAPIProp           = "00020303-0000-0000-C000-000000000046";
       
    80             public const string IMAPITable          = "00020301-0000-0000-C000-000000000046";
       
    81             public const string IMAPIMsgStore       = "00020306-0000-0000-C000-000000000046";
       
    82             public const string IMAPIFolder         = "0002030C-0000-0000-C000-000000000046";
       
    83             public const string IMAPISpoolerService = "0002031E-0000-0000-C000-000000000046";
       
    84             public const string IMAPIStatus         = "0002031E-0000-0000-C000-000000000046";
       
    85             public const string IMessage            = "00020307-0000-0000-C000-000000000046";
       
    86             public const string IAddrBook           = "00020309-0000-0000-C000-000000000046";
       
    87             public const string IProfSect           = "00020304-0000-0000-C000-000000000046";
       
    88             public const string IMAPIContainer      = "0002030B-0000-0000-C000-000000000046";
       
    89             public const string IABContainer        = "0002030D-0000-0000-C000-000000000046";
       
    90             public const string IMsgServiceAdmin    = "0002031D-0000-0000-C000-000000000046";
       
    91             public const string IProfAdmin          = "0002031C-0000-0000-C000-000000000046";
       
    92             public const string IMailUser           = "0002030A-0000-0000-C000-000000000046";
       
    93             public const string IDistList           = "0002030E-0000-0000-C000-000000000046";
       
    94             public const string IAttachment         = "00020308-0000-0000-C000-000000000046";
       
    95             public const string IMAPIControl        = "0002031B-0000-0000-C000-000000000046";
       
    96             public const string IMAPILogonRemote    = "00020346-0000-0000-C000-000000000046";
       
    97             public const string IMAPIForm           = "00020327-0000-0000-C000-000000000046";
       
    98         }
       
    99 
       
   100         /// <summary>
       
   101         /// Save options for the IMAPIProp.SaveChanges method.
       
   102         /// </summary>
       
   103         internal static class SaveOptions
       
   104         {
       
   105             public const int KEEP_OPEN_READONLY     = 0x00000001;
       
   106             public const int KEEP_OPEN_READWRITE    = 0x00000002;
       
   107             public const int FORCE_SAVE             = 0x00000004;
       
   108         }
       
   109         #endregion
       
   110 
       
   111         #region Interfaces
       
   112         /// <summary>
       
   113         /// Enables clients, service providers, and MAPI to work with properties. All objects that 
       
   114         /// support properties implement this interface.
       
   115         /// </summary>
       
   116         [
       
   117             ComImport,
       
   118             ComVisible(false),
       
   119             InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
       
   120             Guid(Mapi.MAPIInterfaceIds.IMAPIProp)
       
   121         ]
       
   122         internal interface IMAPIProp
       
   123         {
       
   124             [return: MarshalAs(UnmanagedType.I4)]
       
   125             [PreserveSig]
       
   126             int GetLastError();
       
   127             [return: MarshalAs(UnmanagedType.I4)]
       
   128             [PreserveSig]
       
   129             int SaveChanges(uint uFlags);
       
   130             [return: MarshalAs(UnmanagedType.I4)]
       
   131             [PreserveSig]
       
   132             int GetProps();
       
   133             [return: MarshalAs(UnmanagedType.I4)]
       
   134             [PreserveSig]
       
   135             int GetPropList();
       
   136             [return: MarshalAs(UnmanagedType.I4)]
       
   137             [PreserveSig]
       
   138             int OpenProperty();
       
   139             [return: MarshalAs(UnmanagedType.I4)]
       
   140             [PreserveSig]
       
   141             int SetProps(uint values, IntPtr propArray, IntPtr problems);
       
   142             [return: MarshalAs(UnmanagedType.I4)]
       
   143             [PreserveSig]
       
   144             int DeleteProps();
       
   145             [return: MarshalAs(UnmanagedType.I4)]
       
   146             [PreserveSig]
       
   147             int CopyTo();
       
   148             [return: MarshalAs(UnmanagedType.I4)]
       
   149             [PreserveSig]
       
   150             int CopyProps();
       
   151             [return: MarshalAs(UnmanagedType.I4)]
       
   152             [PreserveSig]
       
   153             int GetNamesFromIDs();
       
   154             [return: MarshalAs(UnmanagedType.I4)]
       
   155             [PreserveSig]
       
   156             int GetIDsFromNames();
       
   157         }
       
   158         #endregion
       
   159 
       
   160         #region Structures
       
   161 
       
   162         /// <summary>
       
   163         /// The SPropValue structure describes a MAPI property.
       
   164         /// </summary>
       
   165         public struct SPropValue
       
   166         {
       
   167             /// <summary>
       
   168             /// Property tag for the property. Property tags are 32-bit unsigned integers consisting of the property's unique identifier in the high-order 16 bits and the property's type in the low-order 16 bits.
       
   169             /// </summary>
       
   170             public uint PropTag;
       
   171 
       
   172             /// <summary>
       
   173             /// Reserved for MAPI; do not use.
       
   174             /// </summary>
       
   175             public uint DwAlignPad;
       
   176 
       
   177             /// <summary>
       
   178             /// Union of data values, the specific value dictated by the property type.
       
   179             /// </summary>
       
   180             public PV Value;
       
   181         }
       
   182 
       
   183         /// <summary>
       
   184         /// Union of data values for the SPropValue.value property.
       
   185         /// </summary>
       
   186         [StructLayout(LayoutKind.Explicit, Size = 8)]
       
   187         public struct PV
       
   188         {
       
   189             [FieldOffset(0)]
       
   190             public short i;
       
   191             [FieldOffset(0)]
       
   192             public int l;
       
   193             [FieldOffset(0)]
       
   194             public uint ul;
       
   195             [FieldOffset(0)]
       
   196             public float flt;
       
   197             [FieldOffset(0)]
       
   198             public double dbl;
       
   199             [FieldOffset(0)]
       
   200             public ushort b;
       
   201             [FieldOffset(0)]
       
   202             public double at;
       
   203             [FieldOffset(0)]
       
   204             public IntPtr lpszA;
       
   205             [FieldOffset(0)]
       
   206             public IntPtr lpszW;
       
   207             [FieldOffset(0)]
       
   208             public IntPtr lpguid;
       
   209             /*[FieldOffset(0)]
       
   210             public IntPtr bin;*/
       
   211             [FieldOffset(0)]
       
   212             public ulong li;
       
   213             [FieldOffset(0)]
       
   214             public SRowSet bin;
       
   215         }
       
   216 
       
   217         /// <summary>
       
   218         /// Contains an array of SRow structures. Each SRow structure describes a row from a table.
       
   219         /// </summary>
       
   220         [StructLayout(LayoutKind.Sequential)]
       
   221         public struct SRowSet
       
   222         {
       
   223             public uint cRows;
       
   224             public IntPtr aRow; // pSRow
       
   225 
       
   226             public byte[] AsBytes
       
   227             {
       
   228                 get
       
   229                 {
       
   230                     byte[] b = new byte[this.cRows];
       
   231                     for (int i = 0; i < this.cRows; i++)
       
   232                         b[i] = Marshal.ReadByte(aRow, i);
       
   233                     return b;
       
   234                 }
       
   235             }
       
   236         }
       
   237         #endregion
       
   238 
       
   239         #region Methods
       
   240         /// <summary>
       
   241         /// Sets the given MAPI properties on the Outlook mail item.
       
   242         /// Note: the caller has to make sure that the count of MAPI properties
       
   243         /// and respective values matches and that the given values match the
       
   244         /// required data type for each MAPI property.
       
   245         /// </summary>
       
   246         /// <param name="omi">The Outlook mail item to set its properties.</param>
       
   247         /// <param name="mapiProperties">The MAPI properties to set.</param>
       
   248         /// <param name="values">The values to set.</param>
       
   249         /// <returns>True if the method succeeds, otherwise false.</returns>
       
   250         public static bool SetMAPIProperties(Outlook.MailItem omi, MapiProperty.MapiProp[] mapiProperties, object[] values)
       
   251         {
       
   252             // The return status of this method
       
   253             bool success = false;
       
   254 
       
   255             // Pointer to IUnknown interface
       
   256             IntPtr IUnknown = IntPtr.Zero;
       
   257 
       
   258             // Pointer to the MAPI properties array
       
   259             IntPtr propArray = IntPtr.Zero;
       
   260 
       
   261             // Pointer to the value
       
   262             IntPtr valuePtr = IntPtr.Zero;
       
   263 
       
   264             // Get MAPI object from mail item
       
   265             object mapiObject = omi?.MAPIOBJECT;
       
   266             if (mapiObject == null)
       
   267             {
       
   268                 Log.Error("SetMAPIProperties: MAPI object is null. Property could not be set.");
       
   269                 return success;
       
   270             }
       
   271 
       
   272             // Make sure the properties count matches the values count
       
   273             int propCount = mapiProperties.Length;
       
   274             if (propCount != values.Length)
       
   275             {
       
   276                 Log.Error("SetMAPIProperties: Mismatch between tag count and value count.");
       
   277                 return success;
       
   278             }
       
   279 
       
   280             try
       
   281             {
       
   282                 // Initialize MAPI
       
   283                 Mapi.MAPIInitialize(IntPtr.Zero);
       
   284 
       
   285                 // Get the IUnknown interface from the MAPI object
       
   286                 IUnknown = Marshal.GetIUnknownForObject(mapiObject);
       
   287 
       
   288                 // Get the IMAPIProp interface
       
   289                 Mapi.IMAPIProp IMAPIProp = (Mapi.IMAPIProp)Marshal.GetTypedObjectForIUnknown(IUnknown, typeof(Mapi.IMAPIProp));
       
   290 
       
   291                 // Create the SPropValue structure
       
   292                 Mapi.SPropValue sPropValue = new Mapi.SPropValue();
       
   293                 var propValueSize = Marshal.SizeOf(sPropValue);
       
   294 
       
   295                 // Allocate memory for the array of SPropValues to set
       
   296                 propArray = Marshal.AllocHGlobal(propValueSize * propCount);
       
   297 
       
   298                 // Create the property values array
       
   299                 for (int i = 0; i < propCount; i++)
       
   300                 {
       
   301                     // Get the current property/value pair
       
   302                     MapiProperty.MapiProp mapiProperty = mapiProperties[i];
       
   303                     uint tag = mapiProperty.Tag;
       
   304                     object value = values[i];
       
   305 
       
   306                     // Set the property value's property tag
       
   307                     sPropValue.PropTag = (uint)tag;
       
   308 
       
   309                     // Set the property value's value
       
   310                     switch (mapiProperty.DataType)
       
   311                     {
       
   312                         case MapiProperty.MapiDataType.PtypBoolean:
       
   313                             {
       
   314                                 try
       
   315                                 {
       
   316                                     sPropValue.Value.b = (ushort)Convert.ToInt16((bool)value);
       
   317                                 }
       
   318                                 catch (Exception ex)
       
   319                                 {
       
   320                                     throw new Exception(string.Format("Error converting to Boolean. Property tag: {0}. Value: {1}. Exception: {2}.", mapiProperty.DaslName, value?.ToString(), ex.ToString()));
       
   321                                 }
       
   322                             }
       
   323                             break;
       
   324                         case MapiProperty.MapiDataType.PtypString:
       
   325                             {
       
   326                                 try
       
   327                                 {
       
   328                                     valuePtr = Marshal.StringToHGlobalUni(value as string);
       
   329                                     sPropValue.Value.lpszW = valuePtr;
       
   330                                 }
       
   331                                 catch (Exception ex)
       
   332                                 {
       
   333                                     throw new Exception(string.Format("Error converting to String. Property tag: {0}. Value: {1}. Exception: {2}.", mapiProperty.DaslName, value?.ToString(), ex.ToString()));
       
   334                                 }
       
   335                             }
       
   336                             break;
       
   337                         case MapiProperty.MapiDataType.PtypString8:
       
   338                             {
       
   339                                 try
       
   340                                 {
       
   341                                     valuePtr = Marshal.StringToHGlobalAnsi(value as string);
       
   342                                     sPropValue.Value.lpszA = valuePtr;
       
   343                                 }
       
   344                                 catch (Exception ex)
       
   345                                 {
       
   346                                     throw new Exception(string.Format("Error converting to String8. Property tag: {0}. Value: {1}. Exception: {2}.", mapiProperty.DaslName, value?.ToString(), ex.ToString()));
       
   347                                 }
       
   348                             }
       
   349                             break;
       
   350                         case MapiProperty.MapiDataType.PtypInteger16:
       
   351                             {
       
   352                                 try
       
   353                                 {
       
   354                                     sPropValue.Value.i = (short)value;
       
   355                                 }
       
   356                                 catch (Exception ex)
       
   357                                 {
       
   358                                     throw new Exception(string.Format("Error converting to Short. Property tag: {0}. Value: {1}. Exception: {2}.", mapiProperty.DaslName, value?.ToString(), ex.ToString()));
       
   359                                 }
       
   360                             }
       
   361                             break;
       
   362                         case MapiProperty.MapiDataType.PtypInteger32:
       
   363                             {
       
   364                                 try
       
   365                                 {
       
   366                                     sPropValue.Value.l = (int)value;
       
   367                                 }
       
   368                                 catch (Exception ex)
       
   369                                 {
       
   370                                     throw new Exception(string.Format("Error converting to Integer. Property tag: {0}. Value: {1}. Exception: {2}.", mapiProperty.DaslName, value?.ToString(), ex.ToString()));
       
   371                                 }
       
   372                             }
       
   373                             break;
       
   374                         case MapiProperty.MapiDataType.PtypFloating32:
       
   375                             {
       
   376                                 try
       
   377                                 {
       
   378                                     sPropValue.Value.flt = (float)value;
       
   379                                 }
       
   380                                 catch (Exception ex)
       
   381                                 {
       
   382                                     throw new Exception(string.Format("Error converting to Float. Property tag: {0}. Value: {1}. Exception: {2}.", mapiProperty.DaslName, value?.ToString(), ex.ToString()));
       
   383                                 }
       
   384                             }
       
   385                             break;
       
   386                         case MapiProperty.MapiDataType.PtypFloating64:
       
   387                         case MapiProperty.MapiDataType.PtypFloatingTime:
       
   388                             {
       
   389                                 try
       
   390                                 {
       
   391                                     sPropValue.Value.at = (double)value;
       
   392                                 }
       
   393                                 catch (Exception ex)
       
   394                                 {
       
   395                                     throw new Exception(string.Format("Error converting to Double. Property tag: {0}. Value: {1}. Exception: {2}.", mapiProperty.DaslName, value?.ToString(), ex.ToString()));
       
   396                                 }
       
   397                             }
       
   398                             break;
       
   399                         case MapiProperty.MapiDataType.PtypInteger64:
       
   400                             {
       
   401                                 try
       
   402                                 {
       
   403                                     sPropValue.Value.li = (ulong)value;
       
   404                                 }
       
   405                                 catch (Exception ex)
       
   406                                 {
       
   407                                     throw new Exception(string.Format("Error converting to Ulong. Property tag: {0}. Value: {1}. Exception: {2}.", mapiProperty.DaslName, value?.ToString(), ex.ToString()));
       
   408                                 }
       
   409                             }
       
   410                             break;
       
   411                         case MapiProperty.MapiDataType.PtypGuid:
       
   412                             {
       
   413                                 try
       
   414                                 {
       
   415                                     IntPtr guidPtr = Marshal.AllocHGlobal(Marshal.SizeOf(value));
       
   416                                     Marshal.StructureToPtr((Guid)value, guidPtr, false);
       
   417                                     sPropValue.Value.lpguid = guidPtr;
       
   418                                 }
       
   419                                 catch (Exception ex)
       
   420                                 {
       
   421                                     throw new Exception(string.Format("Error converting to Guid. Property tag: {0}. Value: {1}. Exception: {2}.", mapiProperty.DaslName, value?.ToString(), ex.ToString()));
       
   422                                 }
       
   423                             }
       
   424                             break;
       
   425                         case MapiProperty.MapiDataType.PtypBinary:
       
   426                             {
       
   427                                 try
       
   428                                 {
       
   429                                     sPropValue.Value.bin = (Mapi.SRowSet)value;
       
   430                                 }
       
   431                                 catch (Exception ex)
       
   432                                 {
       
   433                                     throw new Exception(string.Format("Error converting to Binary. Property tag: {0}. Value: {1}. Exception: {2}.", mapiProperty.DaslName, value?.ToString(), ex.ToString()));
       
   434                                 }
       
   435                             }
       
   436                             break;
       
   437                         default:
       
   438                             {
       
   439                                 throw new Exception(string.Format("Error creating SPropValue. Data type {0} not supported.", Enum.GetName(typeof(MapiProperty.MapiDataType), mapiProperty.DataType)));
       
   440                             }
       
   441                     }
       
   442                     Marshal.StructureToPtr(sPropValue, (IntPtr)((uint)propArray + (propValueSize * i)), false);
       
   443 
       
   444                     if (valuePtr != IntPtr.Zero)
       
   445                     {
       
   446                         Marshal.FreeHGlobal(valuePtr);
       
   447                     }
       
   448                 }
       
   449 
       
   450                 // Set properties and save
       
   451                 IntPtr problems = IntPtr.Zero;
       
   452                 if (IMAPIProp.SetProps((uint)propCount, propArray, problems) == Mapi.HResult.S_OK)
       
   453                 {
       
   454                     success = (IMAPIProp.SaveChanges(Mapi.SaveOptions.KEEP_OPEN_READWRITE) == Mapi.HResult.S_OK);
       
   455                 }
       
   456 
       
   457                 // Log problems
       
   458                 if (problems != IntPtr.Zero)
       
   459                 {
       
   460 
       
   461                 }
       
   462             }
       
   463             catch (Exception ex)
       
   464             {
       
   465                 Log.Error("SetMAPIProperties: Error occured. " + ex.ToString());
       
   466                 success = false;
       
   467             }
       
   468             finally
       
   469             {
       
   470                 if (IUnknown != IntPtr.Zero)
       
   471                 {
       
   472                     Marshal.Release(IUnknown);
       
   473                 }
       
   474 
       
   475                 if (propArray != IntPtr.Zero)
       
   476                 {
       
   477                     Marshal.FreeHGlobal(propArray);
       
   478                 }
       
   479 
       
   480                 if (valuePtr != IntPtr.Zero)
       
   481                 {
       
   482                     Marshal.FreeHGlobal(valuePtr);
       
   483                 }
       
   484 
       
   485                 Mapi.MAPIUninitialize();
       
   486             }
       
   487 
       
   488             return success;
       
   489         }
       
   490 
       
   491         /// <summary>
       
   492         /// Sets the given MAPI property on the Outlook mail item.
       
   493         /// Note: the caller has to make sure that the given value matches the
       
   494         /// required data type for the MAPI property.
       
   495         /// </summary>
       
   496         /// <param name="omi">The Outlook mail item to set the property for.</param>
       
   497         /// <param name="mapiProperties">The MAPI property to set.</param>
       
   498         /// <param name="values">The value to set.</param>
       
   499         /// <returns>True if the method succeeds, otherwise false.</returns>
       
   500         public static bool SetMAPIProperty(Outlook.MailItem omi, MapiProperty.MapiProp mapiProperty, object value)
       
   501         {
       
   502             // The return status of this method
       
   503             bool success = false;
       
   504 
       
   505             // Pointer to IUnknown interface
       
   506             IntPtr IUnknown = IntPtr.Zero;
       
   507 
       
   508             // Pointer to IMessage interface
       
   509             IntPtr IMessage = IntPtr.Zero;
       
   510 
       
   511             // Pointer to IMAPIProp interface
       
   512             IntPtr IMAPIProp = IntPtr.Zero;
       
   513 
       
   514             // Structure that will hold the property value
       
   515             Mapi.SPropValue propValue;
       
   516 
       
   517             // A pointer that points to the SPropValue structure 
       
   518             IntPtr ptrPropValue = IntPtr.Zero;
       
   519 
       
   520             // Get MAPI object from mail item
       
   521             object mapiObject = omi?.MAPIOBJECT;
       
   522             if (mapiObject == null)
       
   523             {
       
   524                 Log.Error("SetMAPIProperty: MAPI object is null. Property could not be set.");
       
   525                 return success;
       
   526             }
       
   527 
       
   528             try
       
   529             {
       
   530                 // Initialize MAPI
       
   531                 Mapi.MAPIInitialize(IntPtr.Zero);
       
   532 
       
   533                 // Get the IUnknown interface from the MAPI object
       
   534                 IUnknown = Marshal.GetIUnknownForObject(mapiObject);
       
   535 
       
   536                 // Set the IMessage interface GUID that we pass to retrieve the IMessage interface.
       
   537                 Guid guidIMessage = new Guid(Mapi.MAPIInterfaceIds.IMessage);
       
   538 
       
   539                 // Try to retrieve the IMessage interface
       
   540                 if (Marshal.QueryInterface(IUnknown, ref guidIMessage, out IMessage) != Mapi.HResult.S_OK)
       
   541                 {
       
   542                     Log.Error("SetMAPIProperty: Could not retrieve IMessage interface. Property could not be set.");
       
   543                     return success;
       
   544                 }
       
   545 
       
   546                 // Set the IMAPIProp interface GUID that we pass to retrieve the IMAPIProp Interface.
       
   547                 Guid guidIMAPIProp = new Guid(Mapi.MAPIInterfaceIds.IMAPIProp);
       
   548 
       
   549                 // Try to retrieve the IMAPIProp interface
       
   550                 if ((Marshal.QueryInterface(IMessage, ref guidIMAPIProp, out IMAPIProp) != Mapi.HResult.S_OK) ||
       
   551                     (IMAPIProp == IntPtr.Zero))
       
   552                 {
       
   553                     Log.Error("SetMAPIProperty: Could not retrieve IMAPIProp interface. Property could not be set.");
       
   554                     return success;
       
   555                 }
       
   556 
       
   557                 // Create the SPropValue structure
       
   558                 propValue = new Mapi.SPropValue
       
   559                 {
       
   560                     PropTag = mapiProperty.Tag
       
   561                 };
       
   562                 propValue.Value.li = (bool)value ? 1UL : 0UL;
       
   563 
       
   564                 // Get pointer to property value structure
       
   565                 ptrPropValue = Marshal.AllocHGlobal(Marshal.SizeOf(propValue));
       
   566                 Marshal.StructureToPtr(propValue, ptrPropValue, false);
       
   567 
       
   568                 // Try to set the property
       
   569                 Mapi.HrSetOneProp(IMAPIProp, ptrPropValue);
       
   570 
       
   571                 // Save changes
       
   572                 Mapi.IMAPIProp mapiProp = (Mapi.IMAPIProp)Marshal.GetTypedObjectForIUnknown(IUnknown, typeof(Mapi.IMAPIProp));
       
   573                 success = (mapiProp.SaveChanges(Mapi.SaveOptions.KEEP_OPEN_READWRITE) == Mapi.HResult.S_OK);
       
   574             }
       
   575             catch (Exception ex)
       
   576             {
       
   577                 Log.Error("SetMAPIProperty: Error occured. " + ex.ToString());
       
   578                 success = false;
       
   579             }
       
   580             finally
       
   581             {
       
   582                 // Free used memory structures
       
   583                 if (ptrPropValue != IntPtr.Zero)
       
   584                 {
       
   585                     Mapi.MAPIFreeBuffer(ptrPropValue);
       
   586                 }
       
   587 
       
   588                 // Clean up all references to COM Objects
       
   589                 if (IMAPIProp != IntPtr.Zero)
       
   590                 {
       
   591                     Marshal.Release(IMAPIProp);
       
   592                 }
       
   593 
       
   594                 if (IMessage != IntPtr.Zero)
       
   595                 {
       
   596                     Marshal.Release(IMessage);
       
   597                 }
       
   598 
       
   599                 if (IUnknown != IntPtr.Zero)
       
   600                 {
       
   601                     Marshal.Release(IUnknown);
       
   602                 }
       
   603 
       
   604                 Mapi.MAPIUninitialize();
       
   605             }
       
   606 
       
   607             return success;
       
   608         }
       
   609         #endregion
       
   610 
       
   611         #region MAPI DLL Imports
       
   612         /// <summary>
       
   613         /// The MAPIInitialize function increments the MAPI subsystem reference count and initializes global data for the MAPI DLL.
       
   614         /// </summary>
       
   615         /// <param name="lpMapiInit">[in] Pointer to a MAPIINIT_0 structure. The lpMapiInit parameter can be set to NULL.</param>
       
   616         /// <returns>
       
   617         /// S_OK
       
   618         /// The MAPI subsystem was initialized successfully.
       
   619         /// </returns>
       
   620         [DllImport("mapi32.DLL", CharSet = CharSet.Ansi)]
       
   621         internal static extern int MAPIInitialize(IntPtr lpMapiInit);
       
   622 
       
   623         /// <summary>
       
   624         /// The MAPIUninitialize function decrements the reference count, cleans up, and deletes per-instance global data for the MAPI DLL.
       
   625         /// </summary>
       
   626         [DllImport("mapi32.DLL", CharSet = CharSet.Ansi)]
       
   627         internal static extern void MAPIUninitialize();
       
   628 
       
   629         /// <summary>
       
   630         /// The HrGetOneProp function retrieves the value of a single property from a property interface, that is, an interface derived from IMAPIProp.
       
   631         /// </summary>
       
   632         /// <param name="pmp">[in] Pointer to the IMAPIProp interface from which the property value is to be retrieved.</param>
       
   633         /// <param name="ulPropTag">[in] Property tag of the property to be retrieved.</param>
       
   634         /// <param name="ppprop">[out] Pointer to a pointer to the returned SPropValue structure defining the retrieved property value.</param>
       
   635         /// <remarks>
       
   636         /// Unlike the IMAPIProp::GetProps method, the HrGetOneProp function never returns any warning.
       
   637         /// Because it retrieves only one property, it simply either succeeds or fails. For retrieving multiple properties,
       
   638         /// GetProps is faster. 
       
   639         ///
       
   640         /// You can set or change a single property with the HrSetOneProp function.
       
   641         /// </remarks>
       
   642         [DllImport("mapi32.DLL", CharSet = CharSet.Ansi, EntryPoint = "HrGetOneProp@12")]
       
   643         internal static extern void HrGetOneProp(IntPtr pmp, uint ulPropTag, out IntPtr ppprop);
       
   644 
       
   645         /// <summary>
       
   646         /// The HrSetOneProp function sets or changes the value of a single property on a property interface, that is, an interface derived from IMAPIProp.
       
   647         /// </summary>
       
   648         /// <param name="pmp">[in] Pointer to an IMAPIProp interface on which the property value is to be set or changed.</param>
       
   649         /// <param name="pprop">[in] Pointer to the SPropValue structure defining the property to be set or changed.</param>
       
   650         /// <remarks>
       
   651         /// Unlike the IMAPIProp::SetProps method, the HrSetOneProp function never returns any warning.
       
   652         /// Because it sets only one property, it simply either succeeds or fails.
       
   653         /// For setting or changing multiple properties, SetProps is faster. 
       
   654         /// </remarks>
       
   655         [DllImport("mapi32.DLL", CharSet = CharSet.Ansi, EntryPoint = "HrSetOneProp@8")]
       
   656         internal static extern void HrSetOneProp(IntPtr pmp, IntPtr pprop);
       
   657 
       
   658         /// <summary>
       
   659         /// The MAPIFreeBuffer function frees a memory buffer allocated with a call to the MAPIAllocateBuffer function or the MAPIAllocateMore function.
       
   660         /// </summary>
       
   661         /// <param name="lpBuffer">[in] Pointer to a previously allocated memory buffer. If NULL is passed in the lpBuffer parameter, MAPIFreeBuffer does nothing.</param>
       
   662         [DllImport("mapi32.DLL", CharSet = CharSet.Ansi, EntryPoint = "MAPIFreeBuffer@4")]
       
   663         internal static extern void MAPIFreeBuffer(IntPtr lpBuffer);
       
   664         #endregion
       
   665 
       
   666     }
       
   667 }