2010-11-16 103 views
4

我有下面的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的系统。

更新:

在这个线程提供的解决方案确实降低死机的频率,但并没有完全解决问题。唯一能解决问题的解决方案是编写一个使用C++的小型控制台应用程序,实际上可以调用MAPI。我们的.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

你没有使用的Int64 consistantly(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; 
    } 
} 
+0

这是一个'稍微'更有趣的版本。请注意,您应该查看一些很小的更改。 http://pastebin.com/5P0bYEJj – Tergiver 2010-11-16 17:37:47

+0

这大大减少了异常的数量,但有时还会出现异常。 – Jargo 2010-11-29 12:43:38

+0

@Jargo:我修改了上面的代码,将Joerg关于增加文件附件路径缓冲区的建议结合起来。 – Tergiver 2010-11-29 15:27:30

-1

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

+0

你如何增加MAPI缓冲区大小? – Jargo 2010-11-19 07:43:42

相关问题