Click here to Skip to main content
15,885,365 members
Articles / Programming Languages / C#

Exchange Domain Mail SMTP Sink

Rate me:
Please Sign up or sign in to vote.
4.33/5 (4 votes)
1 Nov 20062 min read 54.3K   634   21  
How to create a domain mail sink for Exchange Server.
//+------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: message.cs
//
// Contents: Mailmsg wrapper
//
// Classes: Message
//
// Functions:
//
//-------------------------------------------------------------
using System;
using System.Collections;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Text;
using System.Threading;
using Microsoft.Exchange.Transport.EventInterop;

namespace Microsoft.Exchange.Transport.EventWrappers
{
    //
    // Managed wrapper for mailmsg.
    //
    public class Message : 
        PropertyAccessor
    {
        //
        // public methods:
        //

        //
        // CoCreate an in-memory mailmsg from scratch.
        //
        public Message()
        {
            pMsg = (IMailMsgProperties) new MailMsgClass();
            this.PrivateRecips = new RecipCollection(this);
        }
        //
        // Construct a new wrapper message over an existing mailmsg.
        //
        public Message(object pMsg) :
            base((IMailMsgProperties) pMsg)
        {
            this.PrivateRecips = new RecipCollection(this);
        }

        public void Commit()
        {
            pMsg.Commit(null);
        }
        public void ForkForRecipients(
            out Message msgNew,
            out RecipsAdd recipsAddNew)
        {
            MailMsg pMsgNew;
            IMailMsgRecipientsAdd pRecipsAddNew;

            pMsg.ForkForRecipients(
                out pMsgNew,
                out pRecipsAddNew);
            
            msgNew = new Message(pMsgNew);
            recipsAddNew = new RecipsAdd(this, pRecipsAddNew);
        }
        public void RebindAfterFork(
            Message msgOrig,
            object storeDriver)
        {
            pMsg.RebindAfterFork(
                (MailMsg) msgOrig.pMsg,
                storeDriver);
        }
        public byte[] ReadContent(
            uint dwOffset,
            uint dwLength)
        {
            IntPtr pBuffer = IntPtr.Zero;

            try
            {
                uint dwLengthRead;

                pBuffer = Marshal.AllocCoTaskMem(
                    (int) dwLength);

                pMsg.ReadContent(
                    dwOffset,
                    dwLength,
                    out dwLengthRead,
                    pBuffer,
                    null);

                byte[] rgb;
                rgb = new byte[dwLengthRead];
                Marshal.Copy(pBuffer, rgb, 0, (int) dwLengthRead);
                return rgb;
            }
            finally
            {
                if(pBuffer != IntPtr.Zero)
                {
                    Marshal.FreeCoTaskMem(pBuffer);
                }
            }
                    
        }
        public void WriteContent(
            uint dwOffset,
            uint dwLength,
            out uint dwLengthWritten,
            byte[] rgb)
        {
            pMsg.WriteContent(
                dwOffset,
                dwLength,
                out dwLengthWritten,
                rgb,
                null);
        }
        public uint GetContentSize()
        {
            uint dwRet;
            pMsg.GetContentSize(
                out dwRet,
                null);
            return dwRet;
        }
        public void SetContentSize(
            uint dwSize)
        {
            pMsg.SetContentSize(
                dwSize,
                null);
        }
        public RecipsAdd AllocNewList()
        {
            IMailMsgRecipientsAdd pRecipsAdd;

            ((IMailMsgRecipients) pMsg).AllocNewList(
                out pRecipsAdd);

            return new RecipsAdd(this, pRecipsAdd);
        }
        public void WriteList(
            RecipsAdd ra)
        {
            ((IMailMsgRecipients) pMsg).WriteList(
                ra.pRecipsAdd);
        }

        //
        // Write the RFC 822 content to a stream
        // This only works on mailmsgs with a store driver backing.
        //
        public void CopyContentToStream(
            Stream UserStream)
        {
            uint dwOffset = 0;
            byte[] rgb;

            do
            {
                rgb = ReadContent(
                    dwOffset,
                    1024);

                if(rgb.Length > 0)
                {
                    UserStream.Write(
                        rgb,
                        0,
                        rgb.Length);
                }
                dwOffset += (uint) rgb.Length;

            } while(rgb.Length == 1024);
            //
            // Set the position back
            //
            UserStream.Position = 0;
        }
        //
        // Convert to a string.
        //
        public override string ToString()
        {
            return "Microsoft.Exchange.Transport.Interop.Message: msgId = " + 
                Rfc822MsgId + 
                "; subject = " + Rfc822MsgSubject + 
                "; 821 sender = " + SenderAddressSMTP +
                "; MessageStatus = " + MessageStatus;
        }
        //
        // Public properties:
        //
        
        // Recipient collection.
        //
        public RecipCollection Recips
        {
            get
            {
                return PrivateRecips;
            }
        }
        //
        // Gets a direct pointer to the COM mailmsg interface.
        //
        public IMailMsgProperties MailMsg
        {
            get
            {
                return pMsg;
            }
        }
        //
        // Mailmsg Property accessors:
        //
        public string SenderAddressSMTP
        { 
            get 
            {
                return GetStringA(
                    (uint) IMMPID_MP_ENUM.IMMPID_MP_SENDER_ADDRESS_SMTP );
            }
            set
            {
                PutStringA(
                    (uint) IMMPID_MP_ENUM.IMMPID_MP_SENDER_ADDRESS_SMTP, 
                    value);
            }
        }
        public string SenderAddressX500
        {
            get
            {
                return GetStringA( 
                    (uint) IMMPID_MP_ENUM.IMMPID_MP_SENDER_ADDRESS_X500 );
            }
            set
            {
                PutStringA(
                    (uint) IMMPID_MP_ENUM.IMMPID_MP_SENDER_ADDRESS_X500,
                    value);
            }
        }
        public int HrCatStatus
        {
            get
            {
                return (int) GetDWORD(
                    (uint) IMMPID_MP_ENUM.IMMPID_MP_HR_CAT_STATUS );
            }
            set
            {
                PutDWORD(
                    (uint) IMMPID_MP_ENUM.IMMPID_MP_HR_CAT_STATUS,
                    (uint) value);
            }
        }
        public enum MessageStatusEnum : uint
        {
            Success = 0,
            Retry = 1,
            AbortDelivery = 2,
            BadMail = 3,
            Submitted = 4,
            Categorized = 5,
            AbandonDelivery = 6
        }
        public MessageStatusEnum MessageStatus
        {
            get
            {
                return (MessageStatusEnum) GetDWORD(
                    (uint) IMMPID_MP_ENUM.IMMPID_MP_MESSAGE_STATUS );
            }
            set
            {
                PutDWORD(
                    (uint) IMMPID_MP_ENUM.IMMPID_MP_MESSAGE_STATUS,
                    (uint) value);
            }
        }
        public enum MsgClassEnum : uint
        {
            System = 0,
            Replication = 1,
            DeliveryRepot = 2,
            NonDelieryReport = 3
        }
                     
        public MsgClassEnum MsgClass
        {
            get
            {
                return (MsgClassEnum) GetDWORD(
                    (uint) IMMPID_MP_ENUM.IMMPID_MP_MSGCLASS );
            }
            set
            {
                PutDWORD(
                    (uint) IMMPID_MP_ENUM.IMMPID_MP_MSGCLASS,
                    (uint) value);
            }
        }
        public uint SizeHint
        {
            get
            {
                return GetDWORD(
                    (uint) IMMPID_MP_ENUM.IMMPID_MP_MSG_SIZE_HINT );
            }
            set
            {
                PutDWORD(
                    (uint) IMMPID_MP_ENUM.IMMPID_MP_MSG_SIZE_HINT,
                    value);
            }
        }
        public string Rfc822BccAddress
        {
            get
            {
                return GetStringA(
                    (uint) IMMPID_MP_ENUM.IMMPID_MP_RFC822_BCC_ADDRESS );
            }
        }
        public string Rfc822CcAddress
        {
            get
            {
                return GetStringA(
                    (uint) IMMPID_MP_ENUM.IMMPID_MP_RFC822_CC_ADDRESS );
            }
        }
        public string Rfc822FromAddress
        {
            get
            {
                return GetStringA(
                    (uint) IMMPID_MP_ENUM.IMMPID_MP_RFC822_FROM_ADDRESS );
            }
        }
        public string Rfc822MsgId
        {
            get
            {
                return GetStringA(
                    (uint) IMMPID_MP_ENUM.IMMPID_MP_RFC822_MSG_ID );
            }
            set
            {
                PutStringA(
                    (uint) IMMPID_MP_ENUM.IMMPID_MP_RFC822_MSG_ID, 
                    value);
            }
        }
        public string Rfc822MsgSubject
        {
            get
            {
                return GetStringA(
                    (uint) IMMPID_MP_ENUM.IMMPID_MP_RFC822_MSG_SUBJECT );
            }
            set
            {
                PutStringA(
                    (uint) IMMPID_MP_ENUM.IMMPID_MP_RFC822_MSG_SUBJECT, 
                    value);
            }
        }
        public string Rfc822ToAddress
        {
            get
            {
                return GetStringA(
                    (uint) IMMPID_MP_ENUM.IMMPID_MP_RFC822_TO_ADDRESS );
            }
        }

        //
        // Add more per-msg property accessors above this line.
        //

        //
        // Protected methods.
        //
        protected virtual Recip CreateRecip(
            IMailMsgRecipientsBase pRecipBase,
            uint idx)
        {
            return new Recip(pRecipBase, idx);
        }
        //
        // Internal method.
        //
        internal Recip CreateRecipInternal(
            IMailMsgRecipientsBase pRecipBase,
            uint idx)
        {
            return CreateRecip(pRecipBase, idx);
        }

        //
        // Private methods.
        //
        private Recip GetRecip(uint idx)
        {
            if(idx >= RecipCount)
            {
                return null;
            }
            else
            {
                return CreateRecip((IMailMsgRecipientsBase)pMsg, idx);
            }
        }
        private uint RecipCount
        {
            get
            {
                uint cRecips;
                ((IMailMsgRecipients)pMsg).Count(out cRecips);
                return cRecips;
            }
        }
        protected uint AllocPropIDRange(
            Guid guidProps,
            uint dwNumProps)
        {
            uint dwPropOffset;
            
            ((IMailMsgPropertyManagement)pMsg).AllocPropIDRange(
                ref guidProps,
                dwNumProps,
                out dwPropOffset);

            return dwPropOffset;
        }
        //
        // Private data.
        //
        private RecipCollection PrivateRecips;

        //
        // Subclasses
        //


        //
        // RecipCollection
        //
        public class RecipCollection :
            System.MarshalByRefObject,
            IEnumerable
        {
            internal RecipCollection(Message msg)
            {
                this.msg = msg;
            }
            public IEnumerator GetEnumerator()
            {
                return new RecipEnumerator(msg);
            }        
            public Recip this[uint index]
            {
                get
                {
                    return msg.GetRecip(index);
                }
            }
            public uint Count
            {
                get
                {
                    return msg.RecipCount;
                }
            }
            private Message msg;
        }
        //
        // RecipEnumerator
        //
        public class RecipEnumerator : IEnumerator
        {
            public RecipEnumerator(Message msg)
            {
                m_msg = msg;
            }
            public bool MoveNext()
            {
                Recip nextRecip = m_msg.GetRecip(m_nPos);
                if(nextRecip != null)
                {
                    m_currentRecip = nextRecip;
                    m_nPos++;
                    return true;
                }
                else
                {
                    return false;
                }
            }
            public void Reset()
            {
                m_nPos = 0;
            }
            public object Current
            {
                get
                {
                    return m_currentRecip;
                }
            }
            private uint m_nPos = 0;
            private Message m_msg;
            private Recip m_currentRecip = null;
        }
    }   

    //
    // Wrapper for mailmsg recip.
    //
    public class Recip : PropertyAccessor
    {
        public Recip(IMailMsgRecipientsBase pRecips, uint idx) :
            base(pRecips, idx)
        {
        }

        //
        // Convert to a string.
        //
        public override string ToString()
        {
            return "Microsoft.Exchange.Transport.Interop.Recip: SMTPAddress = " + 
                SMTPAddress + 
                "; domain = " + Domain +
                "; flags = " + RecipientFlags +
                "; error code = " + ErrorCode;
        }
        //
        // Mailmsg Property accessors:
        //
        public string SMTPAddress
        {
            get
            {
                return GetStringA( 
                    (uint) IMMPID_RP_ENUM.IMMPID_RP_ADDRESS_SMTP );
            }
        }
        public string SMTPAddressDomain
        {
            //
            // Return the domain part of the smtp address.  
            // Example: return sample.com for the address "someone@sample.com".
            //
            get
            {
                string strSMTPAddress = SMTPAddress;
                int atIndex = strSMTPAddress.IndexOf('@');
                if(atIndex != -1)
                {
                    return strSMTPAddress.Substring(atIndex+1);
                }
                else
                {
                    return null;
                }
            }
        }
        public string X500Address
        {
            get
            {
                return GetStringA( 
                    (uint) IMMPID_RP_ENUM.IMMPID_RP_ADDRESS_X500 );
            }
        }
        public string Domain
        {
            get
            {
                return GetStringA(
                    (uint) IMMPID_RP_ENUM.IMMPID_RP_DOMAIN );
            }
            set
            {
                PutStringA(
                    (uint) IMMPID_RP_ENUM.IMMPID_RP_DOMAIN, 
                    value);
            }
        }
        public int ErrorCode
        {
            get
            {
                return (int) GetDWORD(
                    (uint) IMMPID_RP_ENUM.IMMPID_RP_ERROR_CODE );
            }
            set
            {
                PutDWORD(
                    (uint) IMMPID_RP_ENUM.IMMPID_RP_ERROR_CODE,
                    (uint) value);
            }
        }

        public uint RecipientFlags
        {
            get
            {
                return GetDWORD(
                    (uint) IMMPID_RP_ENUM.IMMPID_RP_RECIPIENT_FLAGS );
            }
            set
            {
                PutDWORD(
                    (uint) IMMPID_RP_ENUM.IMMPID_RP_RECIPIENT_FLAGS,
                    value);
            }
        }
        // You should not modify or use these bits.
        static public uint RP_RECIP_FLAGS_RESERVED      = 0x0000000F;
        //  Notify on success - set if RFC1891 NOTIFY=SUCCESS is used.
        static public uint RP_DSN_NOTIFY_SUCCESS        = 0x01000000;
        //  Notify on failure - set if RFC1891 NOTIFY=FAILURE is used.
        static public uint RP_DSN_NOTIFY_FAILURE        = 0x02000000;
        //  Notify on delay - set if RFC1891 NOTIFY=DELAY is used.            
        static public uint RP_DSN_NOTIFY_DELAY          = 0x04000000;
        //  Never notify - set if RFC1891 NOTIFY=NEVER is used.
        static public uint RP_DSN_NOTIFY_NEVER          = 0x08000000;
        //  Mask of all notify parameters.
        static public uint RP_DSN_NOTIFY_MASK           = 0x0F000000;

        //The following flags can be used in searches, but should not be set directly.

        //  Recipient has either been delivered or should not be delivered.
        //  (This flag is provided to check status of recipient; it should never be used
        //  directly.)
        static public uint  RP_HANDLED                  = 0x00000010;

        //  Some form of hard failure happend.
        //  (This flag is provided to check status of recipient; it should never be used
        //  directly).
        static public uint  RP_GENERAL_FAILURE          = 0x00000020;

        //  Final DSN has been sent (or no DSN needs to be sent).
        //  (This flag is provided to check status of recipient... it should never be used
        //  directly).
        static public uint  RP_DSN_HANDLED              = 0x00000040;

        //  The recipient has been delivered successfully.
        static public uint  RP_DELIVERED                = 0x00000110;

        //  NDR (FAILED DSN) for this recipient has been sent.
        static public uint  RP_DSN_SENT_NDR             = 0x00000450;

        //  Recipient has a hard failure.
        static public uint  RP_FAILED                   = 0x00000830;

        //  This recipient was not resolved by categorization.
        static public uint  RP_UNRESOLVED               = 0x00001030;

        //  This recipient is an expanded distribution list.
        static public uint  RP_EXPANDED                 = 0x00002010;

        //  At least one Delay DSN sent.
        static public uint  RP_DSN_SENT_DELAYED         = 0x00004000;

        //  Expanded DSN has been sent.
        static public uint  RP_DSN_SENT_EXPANDED        = 0x00008040;

        //  Relayed DSN has been sent.
        static public uint  RP_DSN_SENT_RELAYED         = 0x00010040;

        //  Delivered DSN has been sent.
        static public uint  RP_DSN_SENT_DELIVERED       = 0x00020040;

        //  Remote MTA does not advertise DSN support (relay might be needed).
        static public uint  RP_REMOTE_MTA_NO_DSN        = 0x00080000;

        //  Error happened in store driver.
        static public uint  RP_ERROR_CONTEXT_STORE      = 0x00100000;

        //  Error happened during categorization.
        static public uint  RP_ERROR_CONTEXT_CAT        = 0x00200000;

        //  Error happened in a MTA (for example SMTP stack).
        static public uint  RP_ERROR_CONTEXT_MTA        = 0x00400000;

        // Flags that can be used for temp storage, while a component has access to recipients.
        //Once control of recipients is passed, value
        //is undefined.        
        static public uint  RP_VOLATILE_FLAGS_MASK      = 0xF0000000;

        public string SmtpStatusString
        {
            get
            {
                return GetStringA(
                    (uint) IMMPID_RP_ENUM.IMMPID_RP_SMTP_STATUS_STRING);
            }
            set
            {
                PutStringA(
                    (uint) IMMPID_RP_ENUM.IMMPID_RP_SMTP_STATUS_STRING,
                    value);
            }
        }

        //
        // Add more per-recip property accessors above this line.
        //
    }

    //
    // RecipsAdd
    //
    public class RecipsAdd :
        System.MarshalByRefObject,
        IDisposable
    {
        internal RecipsAdd(
            Message msg,
            IMailMsgRecipientsAdd pRecipsAdd)
        {
            this.msg = msg;
            this.pRecipsAdd = pRecipsAdd;
        }
        public void Dispose()
        {
            //
            // Release our reference to IMailMsgRecipientsAdd.
            //
            Marshal.ReleaseComObject(pRecipsAdd);
            GC.SuppressFinalize(this);
        }
        //
        // Add a new (primary) recipient via their SMTP address.
        //
        public Recip AddSMTPRecipient(
            string strSMTPAddress)
        {
            return AddSMTPRecipient(
                strSMTPAddress,
                null);
        }
        //
        // Add a new (primary) recipient via their SMTP address.
        // Copy over all non-address properties from an existing recipient
        // if sourceRecip is non-null.
        //
        public Recip AddSMTPRecipient(
            string strSMTPAddress,
            Recip sourceRecip)
        {
            return AddPrimary(
                strSMTPAddress,
                (uint) IMMPID_RP_ENUM.IMMPID_RP_ADDRESS_SMTP,
                sourceRecip);
        }

        //
        // Add a new (secondary) recipient via their SMTP address.
        // Throws a DuplicateRecipientException if the recipient address 
        // is already in mailmsg.
        // Copy over all non-address properties from an existing recipient
        // if sourceRecip is non-null.
        //
        public Recip AddSMTPRecipientSecondary(
            string strSMTPAddress,
            Recip sourceRecip)
        {
            return AddSecondary(
                strSMTPAddress,
                (uint) IMMPID_RP_ENUM.IMMPID_RP_ADDRESS_SMTP,
                sourceRecip);
        }                    

        //
        // Add a recipient to mailmsg.
        //
        private Recip AddPrimary(
            string strAddr,
            uint dwAddrProp,
            Recip sourceRecip)
        {
            uint dwNewIdx;

            pRecipsAdd.AddPrimary(
                1,
                ref strAddr,
                ref dwAddrProp,
                out dwNewIdx,
                (sourceRecip == null) ? null : sourceRecip.pRecips,
                (sourceRecip == null) ? 0 : sourceRecip.dwRecipIdx);

            return msg.CreateRecipInternal(pRecipsAdd, dwNewIdx);
        }
        private Recip AddSecondary(
            string strAddr,
            uint dwAddrProp,
            Recip sourceRecip)
        {
            uint dwNewIdx;

            try
            {
                pRecipsAdd.AddSecondary(
                    1,
                    ref strAddr,
                    ref dwAddrProp,
                    out dwNewIdx,
                    (sourceRecip == null) ? null : sourceRecip.pRecips,
                    (sourceRecip == null) ? 0 : sourceRecip.dwRecipIdx);
            }
            catch(COMException e)
            {
                if(e.ErrorCode != Constants.MAILMSG_E_DUPLICATE)
                    throw;
                throw new DuplicateRecipientException(
                    dwAddrProp,
                    strAddr);
            }

            return msg.CreateRecipInternal(pRecipsAdd, dwNewIdx);
        }
        //
        // Subclasses
        //
        public class DuplicateRecipientException :
            Exception
        {
            internal DuplicateRecipientException(
                uint dwAddrPropId,
                string strAddress)
            {
                this.HResult = Constants.MAILMSG_E_DUPLICATE;
                this.dwAddrPropId = dwAddrPropId;
                this.strAddress = strAddress;
            }
            public override string ToString()
            {
                return "Could not add a secondary recipient because of a duplicate collision.  Address: " +
                    + dwAddrPropId + ":" + strAddress;
            }
            private uint dwAddrPropId;
            private string strAddress;
        }

        //
        // Member data
        //
        protected Message msg;
        internal IMailMsgRecipientsAdd pRecipsAdd;
    }


    //
    // Wrapper around a mailmsg or a mailmsg recipient for accessing
    // properties.
    //
    public class PropertyAccessor :
        System.MarshalByRefObject,
        IDisposable
    {
        internal PropertyAccessor()
        {
            // pMsg or pRecips/dwRecipIdx must be set before using
            // other methods on this class.
        }
        //
        // Construct a PropertyAccessor for mailmsg per-msg properties.
        // As of this point, the PropertyAccessor "owns" the runtime callable wrapper (RCW).
        // It will release the mailmsg object when it is disposed.
        //
        internal PropertyAccessor(
            IMailMsgProperties pMsg)
        {
            this.pMsg = pMsg;
        }
        //
        // Construct a PropertyAccessor for mailmsg per-recip
        // properties.  This instance of PropertyAccessor does not
        // "own" the runtime callable wrapper (RCW) -- Dispose will do nothing of significance.
        // The RCW is owned by the PropertyAccessor instance used for
        // accessing mailmsg per-msg properties.
        //
        internal PropertyAccessor(
            IMailMsgRecipientsBase pRecips,
            uint dwRecipIdx)
        {
            this.pRecips = pRecips;
            this.dwRecipIdx = dwRecipIdx;
        }

        public void Dispose() 
        {
            if (this.pMsg != null) {
                Marshal.ReleaseComObject(pMsg);
                this.pMsg = null;
            }
            //
            // Both pMsg and pRecips point to the same runtime callable wrapper (RCW).
            // So, you only need to call ReleaseComObject on one of them.
            // After a client disposes a message object, none of the
            // Recip objects (on that message) will function.
            //
        }

        public override string ToString()
        {
            return "PropertyAccessor: pMsg = " + pMsg + "; pRecips = "
                + pRecips + "; dwRecipIdx = " + dwRecipIdx;
        }
        //
        // Throws a PropNotSetException if the property is not set.
        //
        protected Guid GetGuid(
            uint dwPropertyId)
        {
            byte[] rgb;
            rgb = GetProperty(dwPropertyId);
            if(rgb == null)
            {
                throw new PropNotSetException(this, dwPropertyId);
            }
            return new Guid(rgb);
        }
        //
        // Throws a PropNotSetException if the property is not set.
        //
        protected bool GetBool(
            uint dwPropertyId)
        {
            bool fSet;
            bool fValue;

            fSet = GetBool(
                dwPropertyId,
                out fValue);
            if(!fSet)
            {
                throw new PropNotSetException(this, dwPropertyId);
            }
            return fValue;
        }            
        //
        // Returns true if bool is set.
        // Returns false if bool is not set.
        //
        protected bool GetBool(
            uint dwPropertyId,
            out bool nBoolValue)
        {
            bool fSet;
            uint dw;
            nBoolValue = false;
            //
            // In mailmsg terms, DWORDs and BOOLs are the same thing,
            // so use GetDWORD here.
            //
            fSet = GetDWORD(
                dwPropertyId,
                out dw);
            if(fSet)
            {
                nBoolValue = (dw != 0);
            }
            return fSet;
        }
        //
        // Returns the set value.  If value is not set,
        // returns fDefaultValue.
        //
        protected bool GetBool(
            uint dwPropertyId,
            bool fDefaultValue)
        {
            bool fSet;
            bool fRet;

            fSet = GetBool(
                dwPropertyId,
                out fRet);
            if(!fSet)
            {
                fRet = fDefaultValue;
            }
            return fRet;
        }
        //
        // Throws a PropNotSetException if the property is not set.
        //
        protected uint GetDWORD(
            uint dwPropertyId)
        {
            bool fSet;
            uint dwValue;
            
            fSet = GetDWORD(
                dwPropertyId,
                out dwValue);
            if(!fSet)
            {
                throw new PropNotSetException(this, dwPropertyId);
            }
            return dwValue;
        }
        //
        // Returns true if DWORD property is set.
        // Returns false if DWORD property is not set.
        //
        protected bool GetDWORD(
            uint dwPropertyId,
            out uint dwValue)
        {
            byte[] rgb;
            rgb = GetProperty(dwPropertyId);
            if(rgb == null)
            {
                dwValue = 0;
                return false;
            }
            dwValue = BitConverter.ToUInt32(rgb, 0);
            return true;
        }
        //
        // Returns the DWORD property value.  Returns dwDefaultValue 
        // if property is not set.
        //
        protected uint GetDWORD(
            uint dwPropertyId,
            uint dwDefaultValue)
        {
            bool fSet;
            uint dwRet;

            fSet = GetDWORD(
                dwPropertyId,
                out dwRet);
            if(!fSet)
            {
                dwRet = dwDefaultValue;
            }
            return dwRet;
        }
        //
        // Returns null if property is not set.
        //
        protected string GetStringA(
            uint dwPropertyId)
        {
            byte[] rgb;
            rgb = GetProperty(dwPropertyId);
            if((rgb == null) || (rgb.Length < 1))
                return null;
            else 
            	  //-1 to Remove null termination.
                return new string(Encoding.Default.GetChars( rgb, 0, rgb.Length - 1 )); 
        }
        //
        // Returns null if property is not set.
        //
        protected string GetStringW(
            uint dwPropertyId)
        {
            UnicodeEncoding ue = new UnicodeEncoding();
            byte[] rgb;
            rgb = GetProperty(dwPropertyId);
            if((rgb == null) || (rgb.Length < 2))
                return null;
            // -2 to remove null term for wide character.
            else return new string(ue.GetChars( rgb, 0, rgb.Length - 2 )); 
        }
        //
        // Returns null if property is not set.
        //
        protected byte[] GetProperty(
            uint dwPropertyId)
        {
            IntPtr pbBuffer = IntPtr.Zero;

            try
            {
                byte[] rgbRet;
                IntPtr pbStupid = (IntPtr) 1;
                uint nSize = 0;
                int hr = 0;

                if(pMsg != null)
                {
                    hr = pMsg.GetProperty(
                        dwPropertyId,
                        0,
                        ref nSize,
                        pbStupid);
                }
                else
                {
                    hr = pRecips.GetProperty(
                        dwRecipIdx,
                        dwPropertyId,
                        0,
                        ref nSize,
                        pbStupid);
                }

                if(hr == Constants.MAILMSG_E_PROPNOTFOUND)
                {
                    //
                    // Property is not set.
                    //
                    return null;
                }
                else if(hr != Constants.HRFW32_ERROR_INSUFFICIENT_BUFFER)
                {
                    //
                    // Unexpected error from mailmsg.
                    //
                    throw(new COMException("unexpected error from mailmsg", hr));
                }

                pbBuffer = Marshal.AllocCoTaskMem( (int) nSize);

                if(pMsg != null)
                {
                    hr = pMsg.GetProperty(
                        dwPropertyId,
                        nSize,
                        ref nSize,
                        pbBuffer);
                }
                else
                {
                    hr = pRecips.GetProperty(
                        dwRecipIdx,
                        dwPropertyId,
                        nSize,
                        ref nSize,
                        pbBuffer);
                }

		  // hr is a failure code.
                if(hr < 0) 
                {
                    //
                    // Unexpected error from mailmsg.
                    //
                    throw(new COMException("unexpected error from mailmsg", hr));
                }

                rgbRet = new byte[nSize];
                Marshal.Copy(pbBuffer, rgbRet, 0, (int) nSize);
                return rgbRet;
            }
            finally
            {
                if(pbBuffer != IntPtr.Zero)
                {
                    Marshal.FreeCoTaskMem( pbBuffer );
                }
            }

        }

        protected void PutGuid(
            uint dwPropertyId,
            Guid gValue)
        {
            PutProperty(
                dwPropertyId,
                gValue.ToByteArray());
        }

        protected void PutBool(
            uint dwPropertyId,
            bool fValue)
        {
            PutDWORD(
                dwPropertyId,
                fValue ? (uint) 1 : (uint) 0);
        }
        protected void PutDWORD(
            uint dwPropertyId,
            uint dwValue)
        {
            PutProperty(
                dwPropertyId,
                BitConverter.GetBytes(dwValue));
        }

        protected void PutStringA(
            uint dwPropertyId,
            string str)
        {
            if(pMsg != null)
            {
                pMsg.PutStringA(
                    dwPropertyId,
                    str);
            }
            else
            {
                pRecips.PutStringA(
                    dwRecipIdx,
                    dwPropertyId,
                    str);
            }
        }
        protected void PutStringW(
            uint dwPropertyId,
            string str)
        {
            if(pMsg != null)
            {
                pMsg.PutStringW(
                    dwPropertyId,
                    str);
            }
            else
            {
                pRecips.PutStringW(
                    dwRecipIdx,
                    dwPropertyId,
                    str);
            }
        }
        protected void PutProperty(
            uint dwPropertyId,
            byte[] rgbValue)
        {
            if(pMsg != null)
            {
                pMsg.PutProperty(
                    dwPropertyId,
                    (uint) rgbValue.Length,
                    rgbValue);
            }
            else
            {
                pRecips.PutProperty(
                    dwRecipIdx,
                    dwPropertyId,
                    (uint) rgbValue.Length,
                    rgbValue);
            }
        }            

        //
        // Private member data.
        //
        internal IMailMsgProperties pMsg = null;
        internal IMailMsgRecipientsBase pRecips = null;
        internal uint dwRecipIdx = 0;

        //
        // Subclasses
        //
        [Serializable]
        public class PropNotSetException :
            Exception,
            ISerializable
        {
            internal PropNotSetException(
                PropertyAccessor pa,
                uint dwPropId)
            {
                this.HResult = Constants.MAILMSG_E_PROPNOTFOUND;
                this.pa = pa;
                this.dwPropId = dwPropId;
            }
            public PropNotSetException(
                SerializationInfo info, StreamingContext context) : 
                base(info, context)
            {
                pa = (PropertyAccessor) info.GetValue("pa", typeof(PropertyAccessor));
                dwPropId = info.GetUInt32("dwPropId");
            }
            void ISerializable.GetObjectData(
                SerializationInfo info,
                StreamingContext context)
            {
                base.GetObjectData(info,context);
                info.AddValue("pa", pa);
                info.AddValue("dwPropId", dwPropId);
            }
            public override string ToString()
            {
                return "Attempt to read an unset property.  PropId: "
                    + dwPropId + "; PropertyAccessor: " + pa;
            }
            private PropertyAccessor pa;
            private uint dwPropId;
            //
            // PropNotSetException is serializable.  So, if you add
            // member variables, you need to update the marshaling
            // code above.
            //
        }
    }

    
    public class Constants
    {
        //$$TODO: Get these from somewhere else?
        static public int S_OK = 0;
        static public int CAT_W_SOME_UNDELIVERABLE_MSGS = unchecked((int)0x80040546);
        static public int E_NOTIMPL = unchecked((int)0x80000001);
        static public int HRFW32_ERROR_FILE_NOT_FOUND = unchecked((int)0x80070002);
        static public int HRFW32_ERROR_HANDLE_EOF = unchecked((int)0x80070026);
        static public int HRFW32_ERROR_INSUFFICIENT_BUFFER = unchecked((int)0x8007007A);
        static public int MAILMSG_E_DUPLICATE = unchecked((int)0x80070050);
        static public int MAILMSG_E_PROPNOTFOUND = unchecked((int)0x800300FD);
        static public int STOREDRV_E_RETRY = unchecked((int)0x800404D5);
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
dB.
Team Leader Application Security Inc., www.appsecinc.com
United States United States
Daniel Doubrovkine has been in software engineering for twelve years and is currently development manager at Application Security Inc. in New York City. He has been involved in many software ventures, including Xo3 and Vestris Inc, was a development lead at Microsoft Corp. in Redmond, and director of Engineering at Visible Path Corp. in New York City. Daniel also builds and runs a foodie website, http://www.foodcandy.com.

Comments and Discussions