//+------------------------------------------------------------ // // 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); } }