尝试读取或写入受保护的内存

时间:2010-11-16 14:11:29

标签: .net pointers

我遇到下面的MAPI类问题。(原始来源http://www.codeproject.com/KB/IP/SendFileToNET.aspx

当用户尝试使用SendMailPopup方法发送电子邮件时,电子邮件程序会正确打开,但在邮件窗口关闭后,程序有时会崩溃并显示以下消息: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

我怀疑该错误是由清理方法引起的,我设法通过使用Int64存储指针而不是原始版本中使用的int来减少崩溃的频率。然而,我的.net不安全编程知识相当有限,所以有人可以帮助我找出造成其他崩溃的原因

更新:显然清理方法不会导致异常,因为程序即使被注释掉也会崩溃,因此唯一可能的原因是MAPI32.ddl sendmail方法。作为参数传递给它的指针可能有问题。

用户正在使用具有64位处理器和32位winxp的系统。

更新

此线程中提供的解决方案确实降低了崩溃的频率,但并未完全解决问题。 100%工作的唯一解决方案是使用实际进行MAPI调用的c ++编写一个小型控制台应用程序。我们的.NET应用程序通过启动控制台应用程序并使用参数将数据传递给它来与mapi连接。

 public class MAPI
    {
        public bool AddRecipientTo(string email)
        {
           return AddRecipient(email, HowTo.MAPI_TO);
        }

        public bool AddRecipientCC(string email)
        {
           return AddRecipient(email, HowTo.MAPI_CC);
        }

        public bool AddRecipientBCC(string email)
        {
          return AddRecipient(email, HowTo.MAPI_BCC);
        }

        public void AddAttachment(string strAttachmentFileName)
        {
            m_attachments.Add(strAttachmentFileName);
        }

        public int SendMailPopup(string strSubject, string strBody)
        {
            return SendMail(strSubject, strBody, MAPI_LOGON_UI | MAPI_DIALOG);
        }

        public int SendMailDirect(string strSubject, string strBody)
        {
            return SendMail(strSubject, strBody, MAPI_LOGON_UI);
        }


        [DllImport("MAPI32.DLL")]
        static extern int MAPISendMail(IntPtr sess, IntPtr hwnd, MapiMessage message, int flg, int rsv);

        int SendMail(string strSubject, string strBody, int how)
        {
            MapiMessage msg = new MapiMessage();
            msg.subject = strSubject;
            msg.noteText = strBody;

            msg.recips = GetRecipients(out msg.recipCount);
            msg.files = GetAttachments(out msg.fileCount);

            m_lastError = MAPISendMail(new IntPtr(0L), new IntPtr(0L), msg, how, 0);
            if (m_lastError > 1)
                MessageBox.Show("MAPISendMail failed! " + GetLastError(), "MAPISendMail");

            Cleanup(ref msg);
            return m_lastError;
        }

        bool AddRecipient(string email, HowTo howTo)
        {
           if (!String.IsNullOrEmpty(email))
            {
            MapiRecipDesc recipient = new MapiRecipDesc();
            recipient.recipClass = (int)howTo;
            recipient.name = email;
            m_recipients.Add(recipient);
            return true;
            }
            else
            {
            return false;
            }
        }

        IntPtr GetRecipients(out int recipCount)
        {
            recipCount = 0;
            if (m_recipients.Count == 0)
                return IntPtr.Zero;

            int size = Marshal.SizeOf(typeof(MapiRecipDesc));
            IntPtr intPtr = Marshal.AllocHGlobal(m_recipients.Count * size);

            int ptr = (int)intPtr;
            foreach (MapiRecipDesc mapiDesc in m_recipients)
            {
                Marshal.StructureToPtr(mapiDesc, (IntPtr)ptr, false);
                ptr += size;
            }

            recipCount = m_recipients.Count;
            return intPtr;
        }

        IntPtr GetAttachments(out int fileCount)
        {
            fileCount = 0;
            if (m_attachments == null)
                return IntPtr.Zero;

            if ((m_attachments.Count <= 0) || (m_attachments.Count > maxAttachments))
                return IntPtr.Zero;

            int size = Marshal.SizeOf(typeof(MapiFileDesc));
            IntPtr intPtr = Marshal.AllocHGlobal(m_attachments.Count * size);

            MapiFileDesc mapiFileDesc = new MapiFileDesc();
            mapiFileDesc.position = -1;
            int ptr = (int)intPtr;

            foreach (string strAttachment in m_attachments)
            {
                mapiFileDesc.name = Path.GetFileName(strAttachment);
                mapiFileDesc.path = strAttachment;
                Marshal.StructureToPtr(mapiFileDesc, (IntPtr)ptr, false);
                ptr += size;
            }

            fileCount = m_attachments.Count;
            return intPtr;
        }

        void Cleanup(ref MapiMessage msg)
        {
            try
            {
                int size = Marshal.SizeOf(typeof(MapiRecipDesc));
                Int64 ptr = 0;

                if (msg.recips != IntPtr.Zero)
                {
                    ptr = msg.recips.ToInt64();
                    for (int i = 0; i < msg.recipCount; i++)
                    {
                        Marshal.DestroyStructure((IntPtr)ptr, typeof(MapiRecipDesc));
                        ptr += size;
                    }
                    Marshal.FreeHGlobal(msg.recips);
                }

                if (msg.files != IntPtr.Zero)
                {
                    size = Marshal.SizeOf(typeof(MapiFileDesc));

                    ptr = msg.files.ToInt64();
                    for (int i = 0; i < msg.fileCount; i++)
                    {
                        Marshal.DestroyStructure((IntPtr)ptr, typeof(MapiFileDesc));
                        ptr += size;
                    }
                    Marshal.FreeHGlobal(msg.files);
                }
                m_recipients.Clear();
                m_attachments.Clear();
            }
            catch (Exception e)
            {
                SmtpSender errorSender = new SmtpSender();
                errorSender.SendAutomaticError(e.StackTrace + e.Message, "Virhe mapi sähköpostin lähetyksessä" + MySession.ProjectName + " Käyttäjä:" + MySession.LoginName);
            }


        }

        public string GetLastError()
        {
            if (m_lastError <= 26)
                return errors[m_lastError];
            return "MAPI error [" + m_lastError.ToString() + "]";
        }

        readonly string[] errors = new string[] {
        "OK [0]", "User abort [1]", "Yleinen virhe sähköpostin lähettämisessä [2]", "MAPI login failure [3]",
        "Disk full [4]", "Insufficient memory [5]", "Access denied [6]", "-unknown- [7]",
        "Too many sessions [8]", "Too many files were specified [9]", "Too many recipients were specified [10]", "A specified attachment was not found [11]",
        "Attachment open failure [12]", "Attachment write failure [13]", "Unknown recipient [14]", "Bad recipient type [15]",
        "No messages [16]", "Invalid message [17]", "Text too large [18]", "Invalid session [19]",
        "Type not supported [20]", "A recipient was specified ambiguously [21]", "Message in use [22]", "Network failure [23]",
        "Invalid edit fields [24]", "Asiakkaalle ei ole määritetty sähköpostiosoitetta.", "Not supported [26]" 
        };


        List<MapiRecipDesc> m_recipients = new List<MapiRecipDesc>();
        List<string> m_attachments = new List<string>();
        int m_lastError = 0;

        const int MAPI_LOGON_UI = 0x00000001;
        const int MAPI_DIALOG = 0x00000008;
        const int maxAttachments = 20;

        enum HowTo { MAPI_ORIG = 0, MAPI_TO, MAPI_CC, MAPI_BCC };
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public class MapiMessage
    {
        public int reserved;
        public string subject;
        public string noteText;
        public string messageType;
        public string dateReceived;
        public string conversationID;
        public int flags;
        public IntPtr originator;
        public int recipCount;
        public IntPtr recips;
        public int fileCount;
        public IntPtr files;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public class MapiFileDesc
    {
        public int reserved;
        public int flags;
        public int position;
        public string path;
        public string name;
        public IntPtr type;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public class MapiRecipDesc
    {
        public int reserved;
        public int recipClass;
        public string name;
        public string address;
        public int eIDSize;
        public IntPtr entryID;
    }

以下是异常的堆栈跟踪。它部分是芬兰语,但您仍然可以从中看到方法名称。

kohteessa System.Windows.Forms.UnsafeNativeMethods.DispatchMessageA(MSG& msg) kohteessa System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData) kohteessa System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context) kohteessa System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context) kohteessa System.Windows.Forms.Application.Run(Form mainForm)

2 个答案:

答案 0 :(得分:2)

您没有一直使用Int64(GetRecipients和GetAttachments也是如此)。我怀疑是否存在问题,但我没有详细介绍。以下是所需的更改。请注意,我使用了一种稍微不同的递增IntPtr的方法,这种方法不易出错。

改为使用Joerg的建议来增加文件附件路径的缓冲区大小

public class MAPI
{
    public bool AddRecipientTo(string email)
    {
        return AddRecipient(email, HowTo.MAPI_TO);
    }

    public bool AddRecipientCC(string email)
    {
        return AddRecipient(email, HowTo.MAPI_CC);
    }

    public bool AddRecipientBCC(string email)
    {
        return AddRecipient(email, HowTo.MAPI_BCC);
    }

    public void AddAttachment(string strAttachmentFileName)
    {
        m_attachments.Add(strAttachmentFileName);
    }

    public int SendMailPopup(string strSubject, string strBody)
    {
        return SendMail(strSubject, strBody, MAPI_LOGON_UI | MAPI_DIALOG);
    }

    public int SendMailDirect(string strSubject, string strBody)
    {
        return SendMail(strSubject, strBody, MAPI_LOGON_UI);
    }

    int SendMail(string strSubject, string strBody, int how)
    {
        MapiMessage msg = new MapiMessage();
        msg.subject = strSubject;
        msg.noteText = strBody;

        msg.recips = GetRecipients(out msg.recipCount);
        msg.files = GetAttachments(out msg.fileCount);

        m_lastError = MAPISendMail(new IntPtr(0L), new IntPtr(0L), msg, how, 0);
        if (m_lastError > 1)
            MessageBox.Show("MAPISendMail failed! " + GetLastError(), "MAPISendMail");

        Cleanup(ref msg);
        return m_lastError;
    }

    bool AddRecipient(string email, HowTo howTo)
    {
        if (!String.IsNullOrEmpty(email))
        {
            MapiRecipDesc recipient = new MapiRecipDesc();
            recipient.recipClass = (int)howTo;
            recipient.name = email;
            m_recipients.Add(recipient);
            return true;
        }
        else
        {
            return false;
        }
    }

    IntPtr GetRecipients(out int recipCount)
    {
        recipCount = 0;
        if (m_recipients.Count == 0)
            return IntPtr.Zero;

        int size = Marshal.SizeOf(typeof(MapiRecipDesc));
        IntPtr blockPtr = Marshal.AllocHGlobal(m_recipients.Count * size);
        IntPtr currentPtr = blockPtr;

        foreach (MapiRecipDesc mapiDesc in m_recipients)
        {
            Marshal.StructureToPtr(mapiDesc, currentPtr, false);
            currentPtr = (IntPtr)((long)currentPtr + size);
        }

        recipCount = m_recipients.Count;
        return blockPtr;
    }

    IntPtr GetAttachments(out int fileCount)
    {
        fileCount = 0;
        if (m_attachments == null)
            return IntPtr.Zero;

        if ((m_attachments.Count <= 0) || (m_attachments.Count > maxAttachments))
            return IntPtr.Zero;

        int size = Marshal.SizeOf(typeof(MapiFileDesc));
        IntPtr blockPtr = Marshal.AllocHGlobal(m_attachments.Count * size);
        IntPtr currentPtr = blockPtr;

        MapiFileDesc mapiFileDesc = new MapiFileDesc();
        mapiFileDesc.position = -1;

        foreach (string strAttachment in m_attachments)
        {
            mapiFileDesc.name = Path.GetFileName(strAttachment);
            mapiFileDesc.path = Marshal.AllocHGlobal(MAX_PATH);
            CopyStringAnsi(mapiFileDesc.path, strAttachment);
            Marshal.StructureToPtr(mapiFileDesc, currentPtr, false);
            currentPtr = (IntPtr)((long)currentPtr + size);
        }

        fileCount = m_attachments.Count;
        return blockPtr;
    }

    void Cleanup(ref MapiMessage msg)
    {
        try
        {
            if (msg.recips != IntPtr.Zero)
            {
                IntPtr currentPtr = msg.recips;
                int size = Marshal.SizeOf(typeof(MapiRecipDesc));

                for (int i = 0; i < msg.recipCount; i++)
                {
                    Marshal.DestroyStructure(currentPtr, typeof(MapiRecipDesc));
                    currentPtr = (IntPtr)((long)currentPtr + size);
                }
                Marshal.FreeHGlobal(msg.recips);
            }

            if (msg.files != IntPtr.Zero)
            {
                IntPtr currentPtr = msg.files;
                int size = Marshal.SizeOf(typeof(MapiFileDesc));

                for (int i = 0; i < msg.fileCount; i++)
                {
                    Marshal.DestroyStructure(currentPtr, typeof(MapiFileDesc));
                    currentPtr = (IntPtr)((long)currentPtr + size);
                }
                Marshal.FreeHGlobal(msg.files);
            }
            m_recipients.Clear();
            m_attachments.Clear();
        }
        catch (Exception e)
        {
            SmtpSender errorSender = new SmtpSender();
            errorSender.SendAutomaticError(e.StackTrace + e.Message, "Virhe mapi sähköpostin lähetyksessä" + MySession.ProjectName + " Käyttäjä:" + MySession.LoginName);
        }
    }

    public string GetLastError()
    {
        if (m_lastError <= 26)
            return errors[m_lastError];
        return "MAPI error [" + m_lastError.ToString() + "]";
    }

    readonly string[] errors = new string[] {  
        "OK [0]", "User abort [1]", "Yleinen virhe sähköpostin lähettämisessä [2]", "MAPI login failure [3]",  
        "Disk full [4]", "Insufficient memory [5]", "Access denied [6]", "-unknown- [7]",  
        "Too many sessions [8]", "Too many files were specified [9]", "Too many recipients were specified [10]", "A specified attachment was not found [11]",  
        "Attachment open failure [12]", "Attachment write failure [13]", "Unknown recipient [14]", "Bad recipient type [15]",  
        "No messages [16]", "Invalid message [17]", "Text too large [18]", "Invalid session [19]",  
        "Type not supported [20]", "A recipient was specified ambiguously [21]", "Message in use [22]", "Network failure [23]",  
        "Invalid edit fields [24]", "Asiakkaalle ei ole määritetty sähköpostiosoitetta.", "Not supported [26]"   
        };


    List<MapiRecipDesc> m_recipients = new List<MapiRecipDesc>();
    List<string> m_attachments = new List<string>();
    int m_lastError = 0;

    const int MAPI_LOGON_UI = 0x00000001;
    const int MAPI_DIALOG = 0x00000008;
    const int maxAttachments = 20;

    const int MAX_PATH = 256;

    enum HowTo { MAPI_ORIG = 0, MAPI_TO, MAPI_CC, MAPI_BCC };

    [DllImport("MAPI32.DLL")]
    static extern int MAPISendMail(IntPtr sess, IntPtr hwnd, MapiMessage message, int flg, int rsv);

    [DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", CharSet = CharSet.Ansi)]
    static extern void RtlMoveStringAnsi(IntPtr pdst, string psrc, IntPtr sizetcb);

    private void CopyStringAnsi(IntPtr intPtr, string str)
    {
        int length = (str.Length + 1) * Marshal.SystemMaxDBCSCharSize;
        RtlMoveStringAnsi(intPtr, str, (IntPtr)length);
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    class MapiMessage
    {
        public int reserved;
        public string subject;
        public string noteText;
        public string messageType;
        public string dateReceived;
        public string conversationID;
        public int flags;
        public IntPtr originator;
        public int recipCount;
        public IntPtr recips;
        public int fileCount;
        public IntPtr files;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    class MapiFileDesc
    {
        public int reserved;
        public int flags;
        public int position;
        public IntPtr path;
        public string name;
        public IntPtr type;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    class MapiRecipDesc
    {
        public int reserved;
        public int recipClass;
        public string name;
        public string address;
        public int eIDSize;
        public IntPtr entryID;
    }
}

答案 1 :(得分:-1)

我注意到,调用MAPISendMail包含msg.files中附件的完整文件名的缓冲区将被mapi服务提供程序(?)覆盖。 提供更长的缓冲区已经解决了这些问题。