2010-11-16 3 views
4

J'ai problème protégé par la classe MAPI ci-dessous. (Source d'origine http://www.codeproject.com/KB/IP/SendFileToNET.aspx)a tenté de lire ou d'écrire la mémoire

Lorsque les utilisateurs tentent d'envoyer des e-mails en utilisant la SendMailPopup méthode du programme e-mail ouvre correctement mais après la fenêtre de courrier ferme le programme se bloque parfois avec le message: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

Je soupçonne que le bogue est causé par la méthode de nettoyage et je réussi à réduire la fréquence des accidents en utilisant Int64 pour stocker les pointeurs au lieu d'int utilisés dans l'original version.

Mise à jour: Apparemment, la méthode de nettoyage ne provoque pas les exceptions, car le programme se bloque même quand il est mis en commentaire. , donc la seule cause possible est la méthode MAPI32.ddl sendmail. Probablement quelque chose de mal avec les pointeurs passés à lui en tant que paramètres.

Les utilisateurs utilisent des systèmes avec des processeurs 64 bits et winxp 32 bits.

Mise à jour:

Les solutions fournies dans ce fil n'abaissaient la fréquence des accidents, mais ne résout pas complètement le problème. La seule solution qui a fonctionné à 100% était d'écrire une petite application console en utilisant C++ qui a réellement fait les appels MAPI. Notre application .NET s'est interfacée avec mapi en allumant l'application console et en lui transmettant des données en utilisant des arguments.

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; 
    } 

Ci-dessous figure la pile de l'exception. C'est en partie en finnois, mais vous pouvez toujours voir les noms des méthodes.

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)

Répondre

2

Vous n'avez pas utilisé Int64 consistantly (getRecipients et GetAttachments ainsi). Je soupçonne que le problème réside dans le problème, mais je ne l'ai pas examiné en détail. Voici les changements requis. Notez que j'ai utilisé une méthode légèrement différente d'incrémentation d'un IntPtr qui est un peu moins sujette aux erreurs.

Modifié pour utiliser la suggestion de Joerg d'augmenter la taille de la mémoire tampon du chemin de pièce jointe.

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

Voici une version "légèrement" plus intéressante. Notez qu'il existe de nombreux changements mineurs que vous devriez jeter un coup d'œil. http://pastebin.com/5P0bYEJj – Tergiver

+0

Cela a considérablement réduit le nombre d'exceptions, mais l'exception survient parfois. – Jargo

+0

@Jargo: J'ai modifié le code ci-dessus pour incorporer la suggestion de Joerg concernant l'augmentation de la mémoire tampon pour le chemin de la pièce jointe. – Tergiver

-1

J'ai remarqué que l'appel MAPISendMail le tampon (s) contenant le nom complet de la pièce jointe (s) dans msg.files sera écrasé par le fournisseur de services MAPI (?). Fournir un tampon plus long a résolu ces problèmes.

+0

Comment augmentez-vous la taille de la mémoire tampon mapi? – Jargo

Questions connexes