2010-01-27 5 views
1

J'essaie d'écrire une application qui surveillera quelques boîtes aux lettres et quand le courrier est trouvé, saisissez des informations sur chaque élément, puis une fois que j'ai une liste des éléments que je peux prendre le actes. Mais peu importe comment je l'approche, je frappe la limite de connexions RPC 255 forcée par Exchange.Exception lors de l'énumération des éléments Outlook en C#

Je suis absolument bloqué quant à ce qui cause l'erreur - autant que je peux voir que j'ai tout attaché dans une méthode et j'appelle Marshal.ReleaseComObject .... J'accepte même la performance hit d'ouverture et de fermeture du gestionnaire Outlook Application lui-même.

Tout conseil serait apprécié massivement ... (je ne peux pas à comprendre pourquoi mon code semble erroné dans l'aperçu ainsi pour des raisons de sécurité, je l'ai mis sur pastebin aussi ... http://pastebin.com/m637eb95)

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using Microsoft.Office.Interop.Outlook; 
using Microsoft.Office.Interop; 
using System.Runtime.InteropServices; 

namespace HandleMailingResponses 
{ 
    class OutlookFolderTableScraper 
    { 
     public List<OutlookItem> GetItemsFromFolder(string folderName) 
     { 
      List<OutlookItem> returnList = new List<OutlookItem>(); 

      Application outlookHandle = new Application(); 
      NameSpace outlookNamespace = outlookHandle.GetNamespace("MAPI"); 
      Folders rootOutlookFolders = outlookNamespace.Folders; 

      outlookNamespace.Logon(null, null, null, true); 

      Folder requestedRoot = enumerateFolders(rootOutlookFolders, folderName); 
      Folders theseFolders = requestedRoot.Folders; 
      Folder thisInbox = enumerateFolders(theseFolders, "Inbox"); 

      Marshal.ReleaseComObject(requestedRoot); 
      requestedRoot = null; 
      Marshal.ReleaseComObject(rootOutlookFolders); 
      rootOutlookFolders = null; 

      string storeID = thisInbox.StoreID; 

      Table thisTable = thisInbox.GetTable("",OlTableContents.olUserItems); 

      //By default each item has the columns EntryID, Subject, CreationTime, LastModificationTime and MessageClass 
      //we can add any of the other properties the MailItem or ReportItem object would have.... 
      Columns theseColumns = thisTable.Columns; 
      theseColumns.Add("SenderEmailAddress"); 

      Marshal.ReleaseComObject(thisInbox); 
      thisInbox = null; 

      outlookNamespace.Logoff(); 
      Marshal.ReleaseComObject(outlookNamespace); 
      outlookNamespace = null; 
      outlookHandle.Quit(); 
      Marshal.ReleaseComObject(outlookHandle); 
      outlookHandle = null; 

      int count = 0; 
      while (!thisTable.EndOfTable) 
      { 
       Row thisRow = thisTable.GetNextRow(); 
       object[] theseValues = (object[]) thisRow.GetValues(); 
       Console.WriteLine("processed {0}",count++); 

       //get the body from this item 
       string messageClass = (string)theseValues[4]; 
       string entryID = (string)theseValues[0]; 
       string body = getItemBody(entryID,storeID, messageClass); 

       returnList.Add(new OutlookItem((string)theseValues[5], (string)theseValues[1], body, messageClass, entryID)); 
      } 



      return returnList; 
     } 

     private string getItemBody(string entryID, string storeID, string messageClass) 
     { 
      Application outlookHandle = new Application(); 
      NameSpace outlookNamespace = outlookHandle.GetNamespace("MAPI"); 
      outlookNamespace.Logon(null, null, null, true); 
      string body; 

      if (messageClass.ToLower().StartsWith("report")) 
      { 
       ReportItem thisItem = (ReportItem)outlookNamespace.GetItemFromID(entryID, storeID); 
       body = thisItem.Body; 
       thisItem.Close(OlInspectorClose.olDiscard); 
       //release this com reference 
       int releaseResult; 
       do 
       { 
        releaseResult = Marshal.ReleaseComObject(thisItem); 
       } while (releaseResult != 0); 
      } 
      else 
      { 
       MailItem thisItem = (MailItem)outlookNamespace.GetItemFromID(entryID, storeID); 
       body = thisItem.Body; 
       thisItem.Close(OlInspectorClose.olDiscard); 
       //release this com reference 
       int releaseResult; 
       do 
       { 
        releaseResult = Marshal.ReleaseComObject(thisItem); 
       } while (releaseResult != 0); 
      } 

      outlookNamespace.Logoff(); 
      outlookNamespace = null; 
      outlookHandle.Quit(); 
      outlookHandle = null; 


      GC.Collect(); 
      GC.WaitForPendingFinalizers(); 

      return body; 
     } 

        /// <summary> 
     /// Iterates through an Outlook.Folders object searching for a folder with the given name 
     /// </summary> 
     /// <param name="rootFolder">An Outlook.Folder object</param> 
     /// <param name="targetFolder"></param> 
     /// <returns></returns> 
     private Folder enumerateFolders(Folders rootFolders, string targetFolder) 
     { 
      Folder returnFolder = null; 
      System.Collections.IEnumerator thisEnumerator = rootFolders.GetEnumerator(); 
      while (thisEnumerator.MoveNext()) 
      { 
       Folder f = (Folder)thisEnumerator.Current; 
       string name = f.Name; 
       if (targetFolder.ToLower().Equals(name.ToLower())) 
       { 
        returnFolder = f; 
        break; 
       } 
      } 
      ICustomAdapter adapter = (ICustomAdapter)thisEnumerator; 
      Marshal.ReleaseComObject(adapter.GetUnderlyingObject()); 
      adapter = null; 
      return returnFolder; 
     } 
     } 

} 
+0

Appelez-vous ce code à partir de plusieurs threads? –

+0

Il me semble que ce serait plus facile si vous utilisiez l'API Exchange 2007 (activée en tant que service Web) ou un simple protocole d'accès aux boîtes aux lettres comme POP3 ou IMAP si le serveur de messagerie cible le prend en charge. J'ai déjà utilisé ces deux méthodes avec aisance auparavant, mais je trouve que programmer des programmes MS Office via COM était une ressource à tout faire. Mais malheureusement, je ne peux pas commenter beaucoup sur le problème spécifique avec ce peu de code ... – ewall

+0

@JP Alioto - le code est appelé à partir d'un seul thread et n'a aucun problème si je supprime l'appel à la méthode qui ouvre l'élément pour obtenir le corps. @ewall - Je me pencherai sur l'API Exchange 07 mais je ne l'ai pas utilisée (ou POP/IMAP) auparavant et je me sens tellement proche de cette solution que j'aimerais la réparer :) –

Répondre

1

J'avais presque les mêmes exigences que la vôtre. l'exemple d'application suivant est vraiment bonne référence:

http://www.c-sharpcorner.com/UploadFile/rambab/OutlookIntegration10282006032802AM/OutlookIntegration.aspx

au sujet de vérifier ces liens "limite d'échange appliqué 255 connexions RPC":
http://www.dimastr.com/Redemption/faq.htm
http://www.outlookcode.com/threads.aspx?forumid=2&messageid=26321

+0

This isn ' T vraiment la réponse mais à la fin je suis allé avec la rédemption. Il a massivement rationalisé mon code et, plus important encore, cela a fonctionné. –

0

Ne pas créer un nouvel objet d'application chaque Lorsque vous appelez la fonction, conservez-la en tant que variable membre privée de votre classe jusqu'à ce que vous n'en ayez plus besoin. De cette façon, vous n'aurez qu'à appeler Namespace.Logon dans votre constructeur.

+0

@McAden - J'ai déplacé l'appel de l'application et de l'espace de noms dans la méthode dans l'espoir que leur fermeture permettrait au système de libérer correctement les objets COM. Si je supprime les appels à Item.Body l'application passera à travers tous les 5000+ éléments donc je suis convaincu que le problème est comment je libère l'objet –

+0

Vous pouvez appeler Marshal.ReleaseComObject sur ces objets si vous les rendez statiques , dans le gestionnaire d'événements sur AppDomain.CurrentDomain.DomainUnload ou tel. AppDomain a sûrement un tel événement. –