Mapi.cs
branchOUT-283
changeset 2353 81975e284b07
child 2354 e5b26ee3c384
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/Mapi.cs	Mon Sep 03 14:04:38 2018 +0200
     1.3 @@ -0,0 +1,667 @@
     1.4 +´╗┐using System;
     1.5 +using System.Runtime.InteropServices;
     1.6 +using Outlook = Microsoft.Office.Interop.Outlook;
     1.7 +
     1.8 +namespace pEp
     1.9 +{
    1.10 +    internal class Mapi
    1.11 +    {
    1.12 +        #region Definitions
    1.13 +        /// <summary>
    1.14 +        /// A collection of MAPI error or warning codes.
    1.15 +        /// </summary>
    1.16 +        internal static class HResult
    1.17 +        {
    1.18 +            public const uint S_FALSE                           = 0x00000001;
    1.19 +            public const uint S_OK                              = 0x00000000;
    1.20 +
    1.21 +            public const uint E_NOTIMPL                         = 0x80004001;
    1.22 +
    1.23 +            public const uint MAPI_E_CALL_FAILED                = 0x80004005;
    1.24 +            public const uint MAPI_E_NOT_ENOUGH_MEMORY          = 0x8007000E;
    1.25 +            public const uint MAPI_E_INVALID_PARAMETER          = 0x80000003;
    1.26 +            public const uint MAPI_E_INTERFACE_NOT_SUPPORTED    = 0x80000004;
    1.27 +            public const uint MAPI_E_NO_ACCESS                  = 0x80000009;
    1.28 +
    1.29 +            public const uint E_INVALIDARG                      = 0x80070057;
    1.30 +            public const uint E_OUTOFMEMORY                     = 0x80000002;
    1.31 +            public const uint E_UNEXPECTED                      = 0x8000FFFF;
    1.32 +            public const uint E_FAIL                            = 0x80000008;
    1.33 +
    1.34 +            public const uint MAPI_E_NO_SUPPORT                 = 0x80040000 | 0x102;
    1.35 +            public const uint MAPI_E_BAD_CHARWIDTH              = 0x80040000 | 0x103;
    1.36 +            public const uint MAPI_E_STRING_TOO_LONG            = 0x80040000 | 0x105;
    1.37 +            public const uint MAPI_E_UNKNOWN_FLAGS              = 0x80040000 | 0x106;
    1.38 +            public const uint MAPI_E_INVALID_ENTRYID            = 0x80040000 | 0x107;
    1.39 +            public const uint MAPI_E_INVALID_OBJECT             = 0x80040000 | 0x108;
    1.40 +            public const uint MAPI_E_OBJECT_CHANGED             = 0x80040000 | 0x109;
    1.41 +            public const uint MAPI_E_OBJECT_DELETED             = 0x80040000 | 0x10A;
    1.42 +            public const uint MAPI_E_BUSY                       = 0x80040000 | 0x10B;
    1.43 +            public const uint MAPI_E_NOT_ENOUGH_DISK            = 0x80040000 | 0x10D;
    1.44 +            public const uint MAPI_E_NOT_ENOUGH_RESOURCES       = 0x80040000 | 0x10E;
    1.45 +            public const uint MAPI_E_NOT_FOUND                  = 0x80040000 | 0x10F;
    1.46 +            public const uint MAPI_E_VERSION                    = 0x80040000 | 0x110;
    1.47 +            public const uint MAPI_E_LOGON_FAILED               = 0x80040000 | 0x111;
    1.48 +            public const uint MAPI_E_SESSION_LIMIT              = 0x80040000 | 0x112;
    1.49 +            public const uint MAPI_E_USER_CANCEL                = 0x80040000 | 0x113;
    1.50 +            public const uint MAPI_E_UNABLE_TO_ABORT            = 0x80040000 | 0x114;
    1.51 +            public const uint MAPI_E_NETWORK_ERROR              = 0x80040000 | 0x115;
    1.52 +            public const uint MAPI_E_DISK_ERROR                 = 0x80040000 | 0x116;
    1.53 +            public const uint MAPI_E_TOO_COMPLEX                = 0x80040000 | 0x117;
    1.54 +            public const uint MAPI_E_BAD_COLUMN                 = 0x80040000 | 0x118;
    1.55 +            public const uint MAPI_E_EXTENDED_ERROR             = 0x80040000 | 0x119;
    1.56 +            public const uint MAPI_E_COMPUTED                   = 0x80040000 | 0x11A;
    1.57 +            public const uint MAPI_E_CORRUPT_DATA               = 0x80040000 | 0x11B;
    1.58 +            public const uint MAPI_E_UNCONFIGURED               = 0x80040000 | 0x11C;
    1.59 +            public const uint MAPI_E_FAILONEPROVIDER            = 0x80040000 | 0x11D;
    1.60 +            public const uint MAPI_E_UNKNOWN_CPID               = 0x80040000 | 0x11E;
    1.61 +            public const uint MAPI_E_UNKNOWN_LCID               = 0x80040000 | 0x11F;
    1.62 +
    1.63 +            public const uint MAPI_E_CORRUPT_STORE              = 0x80040000 | 0x600;
    1.64 +            public const uint MAPI_E_NOT_IN_QUEUE               = 0x80040000 | 0x601;
    1.65 +            public const uint MAPI_E_NO_SUPPRESS                = 0x80040000 | 0x602;
    1.66 +            public const uint MAPI_E_COLLISION                  = 0x80040000 | 0x604;
    1.67 +            public const uint MAPI_E_NOT_INITIALIZED            = 0x80040000 | 0x605;
    1.68 +            public const uint MAPI_E_NON_STANDARD               = 0x80040000 | 0x606;
    1.69 +            public const uint MAPI_E_NO_RECIPIENTS              = 0x80040000 | 0x607;
    1.70 +            public const uint MAPI_E_SUBMITTED                  = 0x80040000 | 0x608;
    1.71 +            public const uint MAPI_E_HAS_FOLDERS                = 0x80040000 | 0x609;
    1.72 +            public const uint MAPI_E_HAS_MESSAGES               = 0x80040000 | 0x60A;
    1.73 +            public const uint MAPI_E_FOLDER_CYCLE               = 0x80040000 | 0x60B;
    1.74 +        };
    1.75 +
    1.76 +        /// <summary>
    1.77 +        /// Interface IDs used to retrieve the specific MAPI Interfaces from the IUnknown object.
    1.78 +        /// </summary>
    1.79 +        internal static class MAPIInterfaceIds
    1.80 +        {
    1.81 +            public const string IMAPISession        = "00020300-0000-0000-C000-000000000046";
    1.82 +            public const string IMAPIProp           = "00020303-0000-0000-C000-000000000046";
    1.83 +            public const string IMAPITable          = "00020301-0000-0000-C000-000000000046";
    1.84 +            public const string IMAPIMsgStore       = "00020306-0000-0000-C000-000000000046";
    1.85 +            public const string IMAPIFolder         = "0002030C-0000-0000-C000-000000000046";
    1.86 +            public const string IMAPISpoolerService = "0002031E-0000-0000-C000-000000000046";
    1.87 +            public const string IMAPIStatus         = "0002031E-0000-0000-C000-000000000046";
    1.88 +            public const string IMessage            = "00020307-0000-0000-C000-000000000046";
    1.89 +            public const string IAddrBook           = "00020309-0000-0000-C000-000000000046";
    1.90 +            public const string IProfSect           = "00020304-0000-0000-C000-000000000046";
    1.91 +            public const string IMAPIContainer      = "0002030B-0000-0000-C000-000000000046";
    1.92 +            public const string IABContainer        = "0002030D-0000-0000-C000-000000000046";
    1.93 +            public const string IMsgServiceAdmin    = "0002031D-0000-0000-C000-000000000046";
    1.94 +            public const string IProfAdmin          = "0002031C-0000-0000-C000-000000000046";
    1.95 +            public const string IMailUser           = "0002030A-0000-0000-C000-000000000046";
    1.96 +            public const string IDistList           = "0002030E-0000-0000-C000-000000000046";
    1.97 +            public const string IAttachment         = "00020308-0000-0000-C000-000000000046";
    1.98 +            public const string IMAPIControl        = "0002031B-0000-0000-C000-000000000046";
    1.99 +            public const string IMAPILogonRemote    = "00020346-0000-0000-C000-000000000046";
   1.100 +            public const string IMAPIForm           = "00020327-0000-0000-C000-000000000046";
   1.101 +        }
   1.102 +
   1.103 +        /// <summary>
   1.104 +        /// Save options for the IMAPIProp.SaveChanges method.
   1.105 +        /// </summary>
   1.106 +        internal static class SaveOptions
   1.107 +        {
   1.108 +            public const int KEEP_OPEN_READONLY     = 0x00000001;
   1.109 +            public const int KEEP_OPEN_READWRITE    = 0x00000002;
   1.110 +            public const int FORCE_SAVE             = 0x00000004;
   1.111 +        }
   1.112 +        #endregion
   1.113 +
   1.114 +        #region Interfaces
   1.115 +        /// <summary>
   1.116 +        /// Enables clients, service providers, and MAPI to work with properties. All objects that 
   1.117 +        /// support properties implement this interface.
   1.118 +        /// </summary>
   1.119 +        [
   1.120 +            ComImport,
   1.121 +            ComVisible(false),
   1.122 +            InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
   1.123 +            Guid(Mapi.MAPIInterfaceIds.IMAPIProp)
   1.124 +        ]
   1.125 +        internal interface IMAPIProp
   1.126 +        {
   1.127 +            [return: MarshalAs(UnmanagedType.I4)]
   1.128 +            [PreserveSig]
   1.129 +            int GetLastError();
   1.130 +            [return: MarshalAs(UnmanagedType.I4)]
   1.131 +            [PreserveSig]
   1.132 +            int SaveChanges(uint uFlags);
   1.133 +            [return: MarshalAs(UnmanagedType.I4)]
   1.134 +            [PreserveSig]
   1.135 +            int GetProps();
   1.136 +            [return: MarshalAs(UnmanagedType.I4)]
   1.137 +            [PreserveSig]
   1.138 +            int GetPropList();
   1.139 +            [return: MarshalAs(UnmanagedType.I4)]
   1.140 +            [PreserveSig]
   1.141 +            int OpenProperty();
   1.142 +            [return: MarshalAs(UnmanagedType.I4)]
   1.143 +            [PreserveSig]
   1.144 +            int SetProps(uint values, IntPtr propArray, IntPtr problems);
   1.145 +            [return: MarshalAs(UnmanagedType.I4)]
   1.146 +            [PreserveSig]
   1.147 +            int DeleteProps();
   1.148 +            [return: MarshalAs(UnmanagedType.I4)]
   1.149 +            [PreserveSig]
   1.150 +            int CopyTo();
   1.151 +            [return: MarshalAs(UnmanagedType.I4)]
   1.152 +            [PreserveSig]
   1.153 +            int CopyProps();
   1.154 +            [return: MarshalAs(UnmanagedType.I4)]
   1.155 +            [PreserveSig]
   1.156 +            int GetNamesFromIDs();
   1.157 +            [return: MarshalAs(UnmanagedType.I4)]
   1.158 +            [PreserveSig]
   1.159 +            int GetIDsFromNames();
   1.160 +        }
   1.161 +        #endregion
   1.162 +
   1.163 +        #region Structures
   1.164 +
   1.165 +        /// <summary>
   1.166 +        /// The SPropValue structure describes a MAPI property.
   1.167 +        /// </summary>
   1.168 +        public struct SPropValue
   1.169 +        {
   1.170 +            /// <summary>
   1.171 +            /// 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.
   1.172 +            /// </summary>
   1.173 +            public uint PropTag;
   1.174 +
   1.175 +            /// <summary>
   1.176 +            /// Reserved for MAPI; do not use.
   1.177 +            /// </summary>
   1.178 +            public uint DwAlignPad;
   1.179 +
   1.180 +            /// <summary>
   1.181 +            /// Union of data values, the specific value dictated by the property type.
   1.182 +            /// </summary>
   1.183 +            public PV Value;
   1.184 +        }
   1.185 +
   1.186 +        /// <summary>
   1.187 +        /// Union of data values for the SPropValue.value property.
   1.188 +        /// </summary>
   1.189 +        [StructLayout(LayoutKind.Explicit, Size = 8)]
   1.190 +        public struct PV
   1.191 +        {
   1.192 +            [FieldOffset(0)]
   1.193 +            public short i;
   1.194 +            [FieldOffset(0)]
   1.195 +            public int l;
   1.196 +            [FieldOffset(0)]
   1.197 +            public uint ul;
   1.198 +            [FieldOffset(0)]
   1.199 +            public float flt;
   1.200 +            [FieldOffset(0)]
   1.201 +            public double dbl;
   1.202 +            [FieldOffset(0)]
   1.203 +            public ushort b;
   1.204 +            [FieldOffset(0)]
   1.205 +            public double at;
   1.206 +            [FieldOffset(0)]
   1.207 +            public IntPtr lpszA;
   1.208 +            [FieldOffset(0)]
   1.209 +            public IntPtr lpszW;
   1.210 +            [FieldOffset(0)]
   1.211 +            public IntPtr lpguid;
   1.212 +            /*[FieldOffset(0)]
   1.213 +            public IntPtr bin;*/
   1.214 +            [FieldOffset(0)]
   1.215 +            public ulong li;
   1.216 +            [FieldOffset(0)]
   1.217 +            public SRowSet bin;
   1.218 +        }
   1.219 +
   1.220 +        /// <summary>
   1.221 +        /// Contains an array of SRow structures. Each SRow structure describes a row from a table.
   1.222 +        /// </summary>
   1.223 +        [StructLayout(LayoutKind.Sequential)]
   1.224 +        public struct SRowSet
   1.225 +        {
   1.226 +            public uint cRows;
   1.227 +            public IntPtr aRow; // pSRow
   1.228 +
   1.229 +            public byte[] AsBytes
   1.230 +            {
   1.231 +                get
   1.232 +                {
   1.233 +                    byte[] b = new byte[this.cRows];
   1.234 +                    for (int i = 0; i < this.cRows; i++)
   1.235 +                        b[i] = Marshal.ReadByte(aRow, i);
   1.236 +                    return b;
   1.237 +                }
   1.238 +            }
   1.239 +        }
   1.240 +        #endregion
   1.241 +
   1.242 +        #region Methods
   1.243 +        /// <summary>
   1.244 +        /// Sets the given MAPI properties on the Outlook mail item.
   1.245 +        /// Note: the caller has to make sure that the count of MAPI properties
   1.246 +        /// and respective values matches and that the given values match the
   1.247 +        /// required data type for each MAPI property.
   1.248 +        /// </summary>
   1.249 +        /// <param name="omi">The Outlook mail item to set its properties.</param>
   1.250 +        /// <param name="mapiProperties">The MAPI properties to set.</param>
   1.251 +        /// <param name="values">The values to set.</param>
   1.252 +        /// <returns>True if the method succeeds, otherwise false.</returns>
   1.253 +        public static bool SetMAPIProperties(Outlook.MailItem omi, MapiProperty.MapiProp[] mapiProperties, object[] values)
   1.254 +        {
   1.255 +            // The return status of this method
   1.256 +            bool success = false;
   1.257 +
   1.258 +            // Pointer to IUnknown interface
   1.259 +            IntPtr IUnknown = IntPtr.Zero;
   1.260 +
   1.261 +            // Pointer to the MAPI properties array
   1.262 +            IntPtr propArray = IntPtr.Zero;
   1.263 +
   1.264 +            // Pointer to the value
   1.265 +            IntPtr valuePtr = IntPtr.Zero;
   1.266 +
   1.267 +            // Get MAPI object from mail item
   1.268 +            object mapiObject = omi?.MAPIOBJECT;
   1.269 +            if (mapiObject == null)
   1.270 +            {
   1.271 +                Log.Error("SetMAPIProperties: MAPI object is null. Property could not be set.");
   1.272 +                return success;
   1.273 +            }
   1.274 +
   1.275 +            // Make sure the properties count matches the values count
   1.276 +            int propCount = mapiProperties.Length;
   1.277 +            if (propCount != values.Length)
   1.278 +            {
   1.279 +                Log.Error("SetMAPIProperties: Mismatch between tag count and value count.");
   1.280 +                return success;
   1.281 +            }
   1.282 +
   1.283 +            try
   1.284 +            {
   1.285 +                // Initialize MAPI
   1.286 +                Mapi.MAPIInitialize(IntPtr.Zero);
   1.287 +
   1.288 +                // Get the IUnknown interface from the MAPI object
   1.289 +                IUnknown = Marshal.GetIUnknownForObject(mapiObject);
   1.290 +
   1.291 +                // Get the IMAPIProp interface
   1.292 +                Mapi.IMAPIProp IMAPIProp = (Mapi.IMAPIProp)Marshal.GetTypedObjectForIUnknown(IUnknown, typeof(Mapi.IMAPIProp));
   1.293 +
   1.294 +                // Create the SPropValue structure
   1.295 +                Mapi.SPropValue sPropValue = new Mapi.SPropValue();
   1.296 +                var propValueSize = Marshal.SizeOf(sPropValue);
   1.297 +
   1.298 +                // Allocate memory for the array of SPropValues to set
   1.299 +                propArray = Marshal.AllocHGlobal(propValueSize * propCount);
   1.300 +
   1.301 +                // Create the property values array
   1.302 +                for (int i = 0; i < propCount; i++)
   1.303 +                {
   1.304 +                    // Get the current property/value pair
   1.305 +                    MapiProperty.MapiProp mapiProperty = mapiProperties[i];
   1.306 +                    uint tag = mapiProperty.Tag;
   1.307 +                    object value = values[i];
   1.308 +
   1.309 +                    // Set the property value's property tag
   1.310 +                    sPropValue.PropTag = (uint)tag;
   1.311 +
   1.312 +                    // Set the property value's value
   1.313 +                    switch (mapiProperty.DataType)
   1.314 +                    {
   1.315 +                        case MapiProperty.MapiDataType.PtypBoolean:
   1.316 +                            {
   1.317 +                                try
   1.318 +                                {
   1.319 +                                    sPropValue.Value.b = (ushort)Convert.ToInt16((bool)value);
   1.320 +                                }
   1.321 +                                catch (Exception ex)
   1.322 +                                {
   1.323 +                                    throw new Exception(string.Format("Error converting to Boolean. Property tag: {0}. Value: {1}. Exception: {2}.", mapiProperty.DaslName, value?.ToString(), ex.ToString()));
   1.324 +                                }
   1.325 +                            }
   1.326 +                            break;
   1.327 +                        case MapiProperty.MapiDataType.PtypString:
   1.328 +                            {
   1.329 +                                try
   1.330 +                                {
   1.331 +                                    valuePtr = Marshal.StringToHGlobalUni(value as string);
   1.332 +                                    sPropValue.Value.lpszW = valuePtr;
   1.333 +                                }
   1.334 +                                catch (Exception ex)
   1.335 +                                {
   1.336 +                                    throw new Exception(string.Format("Error converting to String. Property tag: {0}. Value: {1}. Exception: {2}.", mapiProperty.DaslName, value?.ToString(), ex.ToString()));
   1.337 +                                }
   1.338 +                            }
   1.339 +                            break;
   1.340 +                        case MapiProperty.MapiDataType.PtypString8:
   1.341 +                            {
   1.342 +                                try
   1.343 +                                {
   1.344 +                                    valuePtr = Marshal.StringToHGlobalAnsi(value as string);
   1.345 +                                    sPropValue.Value.lpszA = valuePtr;
   1.346 +                                }
   1.347 +                                catch (Exception ex)
   1.348 +                                {
   1.349 +                                    throw new Exception(string.Format("Error converting to String8. Property tag: {0}. Value: {1}. Exception: {2}.", mapiProperty.DaslName, value?.ToString(), ex.ToString()));
   1.350 +                                }
   1.351 +                            }
   1.352 +                            break;
   1.353 +                        case MapiProperty.MapiDataType.PtypInteger16:
   1.354 +                            {
   1.355 +                                try
   1.356 +                                {
   1.357 +                                    sPropValue.Value.i = (short)value;
   1.358 +                                }
   1.359 +                                catch (Exception ex)
   1.360 +                                {
   1.361 +                                    throw new Exception(string.Format("Error converting to Short. Property tag: {0}. Value: {1}. Exception: {2}.", mapiProperty.DaslName, value?.ToString(), ex.ToString()));
   1.362 +                                }
   1.363 +                            }
   1.364 +                            break;
   1.365 +                        case MapiProperty.MapiDataType.PtypInteger32:
   1.366 +                            {
   1.367 +                                try
   1.368 +                                {
   1.369 +                                    sPropValue.Value.l = (int)value;
   1.370 +                                }
   1.371 +                                catch (Exception ex)
   1.372 +                                {
   1.373 +                                    throw new Exception(string.Format("Error converting to Integer. Property tag: {0}. Value: {1}. Exception: {2}.", mapiProperty.DaslName, value?.ToString(), ex.ToString()));
   1.374 +                                }
   1.375 +                            }
   1.376 +                            break;
   1.377 +                        case MapiProperty.MapiDataType.PtypFloating32:
   1.378 +                            {
   1.379 +                                try
   1.380 +                                {
   1.381 +                                    sPropValue.Value.flt = (float)value;
   1.382 +                                }
   1.383 +                                catch (Exception ex)
   1.384 +                                {
   1.385 +                                    throw new Exception(string.Format("Error converting to Float. Property tag: {0}. Value: {1}. Exception: {2}.", mapiProperty.DaslName, value?.ToString(), ex.ToString()));
   1.386 +                                }
   1.387 +                            }
   1.388 +                            break;
   1.389 +                        case MapiProperty.MapiDataType.PtypFloating64:
   1.390 +                        case MapiProperty.MapiDataType.PtypFloatingTime:
   1.391 +                            {
   1.392 +                                try
   1.393 +                                {
   1.394 +                                    sPropValue.Value.at = (double)value;
   1.395 +                                }
   1.396 +                                catch (Exception ex)
   1.397 +                                {
   1.398 +                                    throw new Exception(string.Format("Error converting to Double. Property tag: {0}. Value: {1}. Exception: {2}.", mapiProperty.DaslName, value?.ToString(), ex.ToString()));
   1.399 +                                }
   1.400 +                            }
   1.401 +                            break;
   1.402 +                        case MapiProperty.MapiDataType.PtypInteger64:
   1.403 +                            {
   1.404 +                                try
   1.405 +                                {
   1.406 +                                    sPropValue.Value.li = (ulong)value;
   1.407 +                                }
   1.408 +                                catch (Exception ex)
   1.409 +                                {
   1.410 +                                    throw new Exception(string.Format("Error converting to Ulong. Property tag: {0}. Value: {1}. Exception: {2}.", mapiProperty.DaslName, value?.ToString(), ex.ToString()));
   1.411 +                                }
   1.412 +                            }
   1.413 +                            break;
   1.414 +                        case MapiProperty.MapiDataType.PtypGuid:
   1.415 +                            {
   1.416 +                                try
   1.417 +                                {
   1.418 +                                    IntPtr guidPtr = Marshal.AllocHGlobal(Marshal.SizeOf(value));
   1.419 +                                    Marshal.StructureToPtr((Guid)value, guidPtr, false);
   1.420 +                                    sPropValue.Value.lpguid = guidPtr;
   1.421 +                                }
   1.422 +                                catch (Exception ex)
   1.423 +                                {
   1.424 +                                    throw new Exception(string.Format("Error converting to Guid. Property tag: {0}. Value: {1}. Exception: {2}.", mapiProperty.DaslName, value?.ToString(), ex.ToString()));
   1.425 +                                }
   1.426 +                            }
   1.427 +                            break;
   1.428 +                        case MapiProperty.MapiDataType.PtypBinary:
   1.429 +                            {
   1.430 +                                try
   1.431 +                                {
   1.432 +                                    sPropValue.Value.bin = (Mapi.SRowSet)value;
   1.433 +                                }
   1.434 +                                catch (Exception ex)
   1.435 +                                {
   1.436 +                                    throw new Exception(string.Format("Error converting to Binary. Property tag: {0}. Value: {1}. Exception: {2}.", mapiProperty.DaslName, value?.ToString(), ex.ToString()));
   1.437 +                                }
   1.438 +                            }
   1.439 +                            break;
   1.440 +                        default:
   1.441 +                            {
   1.442 +                                throw new Exception(string.Format("Error creating SPropValue. Data type {0} not supported.", Enum.GetName(typeof(MapiProperty.MapiDataType), mapiProperty.DataType)));
   1.443 +                            }
   1.444 +                    }
   1.445 +                    Marshal.StructureToPtr(sPropValue, (IntPtr)((uint)propArray + (propValueSize * i)), false);
   1.446 +
   1.447 +                    if (valuePtr != IntPtr.Zero)
   1.448 +                    {
   1.449 +                        Marshal.FreeHGlobal(valuePtr);
   1.450 +                    }
   1.451 +                }
   1.452 +
   1.453 +                // Set properties and save
   1.454 +                IntPtr problems = IntPtr.Zero;
   1.455 +                if (IMAPIProp.SetProps((uint)propCount, propArray, problems) == Mapi.HResult.S_OK)
   1.456 +                {
   1.457 +                    success = (IMAPIProp.SaveChanges(Mapi.SaveOptions.KEEP_OPEN_READWRITE) == Mapi.HResult.S_OK);
   1.458 +                }
   1.459 +
   1.460 +                // Log problems
   1.461 +                if (problems != IntPtr.Zero)
   1.462 +                {
   1.463 +
   1.464 +                }
   1.465 +            }
   1.466 +            catch (Exception ex)
   1.467 +            {
   1.468 +                Log.Error("SetMAPIProperties: Error occured. " + ex.ToString());
   1.469 +                success = false;
   1.470 +            }
   1.471 +            finally
   1.472 +            {
   1.473 +                if (IUnknown != IntPtr.Zero)
   1.474 +                {
   1.475 +                    Marshal.Release(IUnknown);
   1.476 +                }
   1.477 +
   1.478 +                if (propArray != IntPtr.Zero)
   1.479 +                {
   1.480 +                    Marshal.FreeHGlobal(propArray);
   1.481 +                }
   1.482 +
   1.483 +                if (valuePtr != IntPtr.Zero)
   1.484 +                {
   1.485 +                    Marshal.FreeHGlobal(valuePtr);
   1.486 +                }
   1.487 +
   1.488 +                Mapi.MAPIUninitialize();
   1.489 +            }
   1.490 +
   1.491 +            return success;
   1.492 +        }
   1.493 +
   1.494 +        /// <summary>
   1.495 +        /// Sets the given MAPI property on the Outlook mail item.
   1.496 +        /// Note: the caller has to make sure that the given value matches the
   1.497 +        /// required data type for the MAPI property.
   1.498 +        /// </summary>
   1.499 +        /// <param name="omi">The Outlook mail item to set the property for.</param>
   1.500 +        /// <param name="mapiProperties">The MAPI property to set.</param>
   1.501 +        /// <param name="values">The value to set.</param>
   1.502 +        /// <returns>True if the method succeeds, otherwise false.</returns>
   1.503 +        public static bool SetMAPIProperty(Outlook.MailItem omi, MapiProperty.MapiProp mapiProperty, object value)
   1.504 +        {
   1.505 +            // The return status of this method
   1.506 +            bool success = false;
   1.507 +
   1.508 +            // Pointer to IUnknown interface
   1.509 +            IntPtr IUnknown = IntPtr.Zero;
   1.510 +
   1.511 +            // Pointer to IMessage interface
   1.512 +            IntPtr IMessage = IntPtr.Zero;
   1.513 +
   1.514 +            // Pointer to IMAPIProp interface
   1.515 +            IntPtr IMAPIProp = IntPtr.Zero;
   1.516 +
   1.517 +            // Structure that will hold the property value
   1.518 +            Mapi.SPropValue propValue;
   1.519 +
   1.520 +            // A pointer that points to the SPropValue structure 
   1.521 +            IntPtr ptrPropValue = IntPtr.Zero;
   1.522 +
   1.523 +            // Get MAPI object from mail item
   1.524 +            object mapiObject = omi?.MAPIOBJECT;
   1.525 +            if (mapiObject == null)
   1.526 +            {
   1.527 +                Log.Error("SetMAPIProperty: MAPI object is null. Property could not be set.");
   1.528 +                return success;
   1.529 +            }
   1.530 +
   1.531 +            try
   1.532 +            {
   1.533 +                // Initialize MAPI
   1.534 +                Mapi.MAPIInitialize(IntPtr.Zero);
   1.535 +
   1.536 +                // Get the IUnknown interface from the MAPI object
   1.537 +                IUnknown = Marshal.GetIUnknownForObject(mapiObject);
   1.538 +
   1.539 +                // Set the IMessage interface GUID that we pass to retrieve the IMessage interface.
   1.540 +                Guid guidIMessage = new Guid(Mapi.MAPIInterfaceIds.IMessage);
   1.541 +
   1.542 +                // Try to retrieve the IMessage interface
   1.543 +                if (Marshal.QueryInterface(IUnknown, ref guidIMessage, out IMessage) != Mapi.HResult.S_OK)
   1.544 +                {
   1.545 +                    Log.Error("SetMAPIProperty: Could not retrieve IMessage interface. Property could not be set.");
   1.546 +                    return success;
   1.547 +                }
   1.548 +
   1.549 +                // Set the IMAPIProp interface GUID that we pass to retrieve the IMAPIProp Interface.
   1.550 +                Guid guidIMAPIProp = new Guid(Mapi.MAPIInterfaceIds.IMAPIProp);
   1.551 +
   1.552 +                // Try to retrieve the IMAPIProp interface
   1.553 +                if ((Marshal.QueryInterface(IMessage, ref guidIMAPIProp, out IMAPIProp) != Mapi.HResult.S_OK) ||
   1.554 +                    (IMAPIProp == IntPtr.Zero))
   1.555 +                {
   1.556 +                    Log.Error("SetMAPIProperty: Could not retrieve IMAPIProp interface. Property could not be set.");
   1.557 +                    return success;
   1.558 +                }
   1.559 +
   1.560 +                // Create the SPropValue structure
   1.561 +                propValue = new Mapi.SPropValue
   1.562 +                {
   1.563 +                    PropTag = mapiProperty.Tag
   1.564 +                };
   1.565 +                propValue.Value.li = (bool)value ? 1UL : 0UL;
   1.566 +
   1.567 +                // Get pointer to property value structure
   1.568 +                ptrPropValue = Marshal.AllocHGlobal(Marshal.SizeOf(propValue));
   1.569 +                Marshal.StructureToPtr(propValue, ptrPropValue, false);
   1.570 +
   1.571 +                // Try to set the property
   1.572 +                Mapi.HrSetOneProp(IMAPIProp, ptrPropValue);
   1.573 +
   1.574 +                // Save changes
   1.575 +                Mapi.IMAPIProp mapiProp = (Mapi.IMAPIProp)Marshal.GetTypedObjectForIUnknown(IUnknown, typeof(Mapi.IMAPIProp));
   1.576 +                success = (mapiProp.SaveChanges(Mapi.SaveOptions.KEEP_OPEN_READWRITE) == Mapi.HResult.S_OK);
   1.577 +            }
   1.578 +            catch (Exception ex)
   1.579 +            {
   1.580 +                Log.Error("SetMAPIProperty: Error occured. " + ex.ToString());
   1.581 +                success = false;
   1.582 +            }
   1.583 +            finally
   1.584 +            {
   1.585 +                // Free used memory structures
   1.586 +                if (ptrPropValue != IntPtr.Zero)
   1.587 +                {
   1.588 +                    Mapi.MAPIFreeBuffer(ptrPropValue);
   1.589 +                }
   1.590 +
   1.591 +                // Clean up all references to COM Objects
   1.592 +                if (IMAPIProp != IntPtr.Zero)
   1.593 +                {
   1.594 +                    Marshal.Release(IMAPIProp);
   1.595 +                }
   1.596 +
   1.597 +                if (IMessage != IntPtr.Zero)
   1.598 +                {
   1.599 +                    Marshal.Release(IMessage);
   1.600 +                }
   1.601 +
   1.602 +                if (IUnknown != IntPtr.Zero)
   1.603 +                {
   1.604 +                    Marshal.Release(IUnknown);
   1.605 +                }
   1.606 +
   1.607 +                Mapi.MAPIUninitialize();
   1.608 +            }
   1.609 +
   1.610 +            return success;
   1.611 +        }
   1.612 +        #endregion
   1.613 +
   1.614 +        #region MAPI DLL Imports
   1.615 +        /// <summary>
   1.616 +        /// The MAPIInitialize function increments the MAPI subsystem reference count and initializes global data for the MAPI DLL.
   1.617 +        /// </summary>
   1.618 +        /// <param name="lpMapiInit">[in] Pointer to a MAPIINIT_0 structure. The lpMapiInit parameter can be set to NULL.</param>
   1.619 +        /// <returns>
   1.620 +        /// S_OK
   1.621 +        /// The MAPI subsystem was initialized successfully.
   1.622 +        /// </returns>
   1.623 +        [DllImport("mapi32.DLL", CharSet = CharSet.Ansi)]
   1.624 +        internal static extern int MAPIInitialize(IntPtr lpMapiInit);
   1.625 +
   1.626 +        /// <summary>
   1.627 +        /// The MAPIUninitialize function decrements the reference count, cleans up, and deletes per-instance global data for the MAPI DLL.
   1.628 +        /// </summary>
   1.629 +        [DllImport("mapi32.DLL", CharSet = CharSet.Ansi)]
   1.630 +        internal static extern void MAPIUninitialize();
   1.631 +
   1.632 +        /// <summary>
   1.633 +        /// The HrGetOneProp function retrieves the value of a single property from a property interface, that is, an interface derived from IMAPIProp.
   1.634 +        /// </summary>
   1.635 +        /// <param name="pmp">[in] Pointer to the IMAPIProp interface from which the property value is to be retrieved.</param>
   1.636 +        /// <param name="ulPropTag">[in] Property tag of the property to be retrieved.</param>
   1.637 +        /// <param name="ppprop">[out] Pointer to a pointer to the returned SPropValue structure defining the retrieved property value.</param>
   1.638 +        /// <remarks>
   1.639 +        /// Unlike the IMAPIProp::GetProps method, the HrGetOneProp function never returns any warning.
   1.640 +        /// Because it retrieves only one property, it simply either succeeds or fails. For retrieving multiple properties,
   1.641 +        /// GetProps is faster. 
   1.642 +        ///
   1.643 +        /// You can set or change a single property with the HrSetOneProp function.
   1.644 +        /// </remarks>
   1.645 +        [DllImport("mapi32.DLL", CharSet = CharSet.Ansi, EntryPoint = "HrGetOneProp@12")]
   1.646 +        internal static extern void HrGetOneProp(IntPtr pmp, uint ulPropTag, out IntPtr ppprop);
   1.647 +
   1.648 +        /// <summary>
   1.649 +        /// The HrSetOneProp function sets or changes the value of a single property on a property interface, that is, an interface derived from IMAPIProp.
   1.650 +        /// </summary>
   1.651 +        /// <param name="pmp">[in] Pointer to an IMAPIProp interface on which the property value is to be set or changed.</param>
   1.652 +        /// <param name="pprop">[in] Pointer to the SPropValue structure defining the property to be set or changed.</param>
   1.653 +        /// <remarks>
   1.654 +        /// Unlike the IMAPIProp::SetProps method, the HrSetOneProp function never returns any warning.
   1.655 +        /// Because it sets only one property, it simply either succeeds or fails.
   1.656 +        /// For setting or changing multiple properties, SetProps is faster. 
   1.657 +        /// </remarks>
   1.658 +        [DllImport("mapi32.DLL", CharSet = CharSet.Ansi, EntryPoint = "HrSetOneProp@8")]
   1.659 +        internal static extern void HrSetOneProp(IntPtr pmp, IntPtr pprop);
   1.660 +
   1.661 +        /// <summary>
   1.662 +        /// The MAPIFreeBuffer function frees a memory buffer allocated with a call to the MAPIAllocateBuffer function or the MAPIAllocateMore function.
   1.663 +        /// </summary>
   1.664 +        /// <param name="lpBuffer">[in] Pointer to a previously allocated memory buffer. If NULL is passed in the lpBuffer parameter, MAPIFreeBuffer does nothing.</param>
   1.665 +        [DllImport("mapi32.DLL", CharSet = CharSet.Ansi, EntryPoint = "MAPIFreeBuffer@4")]
   1.666 +        internal static extern void MAPIFreeBuffer(IntPtr lpBuffer);
   1.667 +        #endregion
   1.668 +
   1.669 +    }
   1.670 +}