none
Reading Appointments runs into error RRS feed

  • Question

  • Dear fellow devs

    We are currently in the process of decomissioning our old Exchange 2003 environment and therefore I need to sync all of the appointments (old and new) to our Database.

    These appointments are saved in a public folder, where all our field service technitians have accss to. Yeah, I know, inefficient solution but we are replacing it as we speak/write/read/whatever.

    So I am writing this program which extract all the data from the appointments I need and then transfer it to an Oracle DB.

    /* Extension by me * nonebusiness@yours.of * Fetches all public appointments from the 2003 Exchange and adds them as journal entries the appointed customer * */ private static void SyncAppointmentsToDB() { OpenOutlookFolders(); // This is the path to the calendar I want to grab the appointments from. Sorry that it's german. MAPIFolder oAppFolder = (MAPIFolder)oNameSpace.Folders["Öffentliche Ordner"]. Folders["Alle Öffentlichen Ordner"].Folders["Kunden"].Folders["Termine"]; // Just two test dates to limit the amount of appointments DateTime dtToday = new DateTime(2014, 11, 24); DateTime dtTomorrow = new DateTime(2014, 11, 25); Items oAppointments = oAppFolder.Items.Restrict("[START] >= '" + dtToday.ToString("yyyy-dd-MM HH:mm") + "' And [START] < '" + dtTomorrow.ToString("yyyy-dd-MM HH:mm") + "'"); // only continue if there are appointments if (oAppointments == null) Log("Kein Termin gefunden"); else { ArrayList aAppList = new ArrayList(); Log("Anzahl Termine:" + oAppointments.Count.ToString()); AppointmentItem oApp = null; try { oApp = (AppointmentItem)oAppointments.GetFirst(); } catch (System.Exception ex) { Log("oApp kein AppointmentItem"); } int iNbTreated = 0; while (oApp != null) { // I added this to prevent known issues iNbTreated++; // force execution of garbage collector every 5 loops if (iNbTreated % 5 == 0) GC.Collect(); // Close and restart Outlook to free ressources if (iNbTreated % 100 == 0) RestartOutlook(); try { aAppList.Add(oApp); oApp = (AppointmentItem)oAppointments.GetNext();

    // if the appointment hasen't got I subject, I just ignore it. THIS IS ALSO THE LINE WHERE THE ERROR MESSAGE POINTS TO! if (oApp.Subject != null) { // This is just the output so I can test and see what I am trying to read. Log("-------------------------------------------------------------"); string[] sShort = oApp.Subject.Split(' '); Log("Kürzel: " + sShort[0]); Log("Kategorie: " + oApp.Categories); Log("Startdatum: " + oApp.Start.ToString("yyyy-MM-dd HH:mm")); Log("Text: " + oApp.Body); Log("Organisator: " + oApp.Organizer); } } catch (System.Exception e) { Log(e.ToString()); aAppList.Remove(oApp); } } Log("Fertig. Anzahl: " + aAppList.Count); } }

    So far so good.

    It actually reads most of the calendar entries, BUT A FEW. And yes, I know that I am excluding those without a subject, but when I do a count there are always some missing and the log file spits out an error message:

    System.NullReferenceException: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt.
       bei DBExchangeSync.Program.SyncAppointmentsToDB() in PROJEECTFOLDER\Projects\DBExchangeSync\DBExchangeSync\DBExchangeSync\Program.cs:Zeile 315.

    I guess I don't have to translate this message, the NullReferenceException says it all.

    Strangely, all the corrupt(?) appoinments are checked at the end of them all, I guess that can't be a coincident.

    But why? I can't see anything special about that specific appointment. Appointments like these are scattered around the calendar with no real clue about what causes them to cause this messasge. I already tried to open and save them to see if they are corrupt or something but the result was the same.

    Now it gets really funny. Not funny "haha" though.

    If I run the same script over a couple of months the error message changes. It's still at the end of the logfile, just like before, and for every other appointment I try to read (a couple of thousand, depends on the time frame of course)
    The first couple of dozen or hundred are ok, but this suddenly changes with this error:

    System.InvalidCastException: Das COM-Objekt des Typs "System.__ComObject" kann nicht in den Schnittstellentyp "Microsoft.Office.Interop.Outlook.AppointmentItem" umgewandelt werden. Dieser Vorgang konnte nicht durchgeführt werden, da der QueryInterface-Aufruf an die COM-Komponente für die Schnittstelle mit der IID "{00063033-0000-0000-C000-000000000046}" aufgrund des folgenden Fehlers nicht durchgeführt werden konnte: Schnittstelle nicht unterstützt (Ausnahme von HRESULT: 0x80004002 (E_NOINTERFACE)).
       bei DBExchangeSync.Program.SyncAppointmentsToDB() in PROJECTFOLDER\Projects\DBExchangeSync\DBExchangeSync\DBExchangeSync\Program.cs:Zeile 314.

    (Sorry again for german)

    Line 314 points to the cast to (ApointmentItem) I am trying around here:

    try
       {
            aAppList.Add(oApp);
            oApp = (AppointmentItem)oAppointments.GetNext();

    I checked the debug, all the other WORKING appointments come along as System.__ComObjects as well and there the cast works perfectly so I can't see anything different to what he has been doing before, but the appointments missing with the first error message are the same as with the latter.

    I have been struggling now for a bit more than a day now and I have to resignate due to missing experience, I suppose.

    I am not sure if my description is sufficient for someone looking into this to understand what my problem is, please let me know if you want more information.

    Thank you and regards,

    Smith

    Wednesday, November 12, 2014 12:01 PM

Answers

  • Hello Smith,

    Most probably the following lines of code fires the NullReferenceException:

     oApp = (AppointmentItem)oAppointments.GetNext();

    The oApp object can be null. In that case all further property calls will cause an exception you get.

    To prevent such issues I'd recommend releasing underlying COM objects instantly. Use System.Runtime.InteropServices.Marshal.ReleaseComObject to release an Outlook object when you have finished using it. Then set a variable to Nothing in Visual Basic (null in C#) to release the reference to the object. You can read more about that in the Systematically Releasing Objects article.

    Also you may find a sample code listed in the How To: Use Restrict method in Outlook to get calendar items article helpful. I didn't get any exceptions in the code on my way.

    • Marked as answer by Smithmc Wednesday, November 12, 2014 1:39 PM
    • Unmarked as answer by Smithmc Wednesday, November 12, 2014 4:24 PM
    • Marked as answer by Smithmc Thursday, November 13, 2014 7:05 AM
    Wednesday, November 12, 2014 12:18 PM

All replies

  • Hello Smith,

    Most probably the following lines of code fires the NullReferenceException:

     oApp = (AppointmentItem)oAppointments.GetNext();

    The oApp object can be null. In that case all further property calls will cause an exception you get.

    To prevent such issues I'd recommend releasing underlying COM objects instantly. Use System.Runtime.InteropServices.Marshal.ReleaseComObject to release an Outlook object when you have finished using it. Then set a variable to Nothing in Visual Basic (null in C#) to release the reference to the object. You can read more about that in the Systematically Releasing Objects article.

    Also you may find a sample code listed in the How To: Use Restrict method in Outlook to get calendar items article helpful. I didn't get any exceptions in the code on my way.

    • Marked as answer by Smithmc Wednesday, November 12, 2014 1:39 PM
    • Unmarked as answer by Smithmc Wednesday, November 12, 2014 4:24 PM
    • Marked as answer by Smithmc Thursday, November 13, 2014 7:05 AM
    Wednesday, November 12, 2014 12:18 PM
  • Thanks for your reply!

    I had a fiddle with the ReleaseComObject, but it just instantly destroys my Object which cannot be used any longer after that.

    According to this link http://msdn.microsoft.com/en-us/library/bb623945%28office.12%29.aspx you cannot use more than 256 Outlook items at a time. Makes sense to clean up, but according to this blog I found on this topic you should not take the  management of ComObject into your own hand and use GC.Collect() instead, which I do in every fifth iteration.

    http://stackoverflow.com/questions/3937181/when-to-use-releasecomobject-vs-finalreleasecomobject

    Where would you use this ReleaseComObject and which object would you release? I tried it with the folder and the oAppointments.

    Thanks again!

    Smith

    / EDIT: Ok, if I release the oApp it finally works!

    Thanks Eugene for pointing me in the right direction!

    • Edited by Smithmc Wednesday, November 12, 2014 1:39 PM
    Wednesday, November 12, 2014 1:27 PM
  • Ok, I know the direction is correct by Eugene, but I now have a new same problem. I can finally ready everything and write it into the Array aAppList. But as soon as I try to read it again using

    foreach (AppointmentItem oAppNew in aApplist)
    {
    
    }

    I get the same message again. I thought I had cleaned it out nicely now

    See Changes:

    do { iNbTreated++; // force execution of garbage collector every 5 loops if (iNbTreated % 5 == 0) GC.Collect(); // Close and restart Outlook to free ressources if (iNbTreated % 100 == 0) RestartOutlook(); if (oApp != null) { try { aAppList.Add(oApp);

    // NEW THINGS HERE! Marshal.ReleaseComObject(oApp); oApp = (AppointmentItem)oAppointments.GetNext(); if (oApp.Subject != null) { Log("-------------------------------------------------------------"); string[] sShort = oApp.Subject.Split(' '); Log("Kürzel: " + sShort[0]); Log("Kategorie: " + oApp.Categories); Log("Startdatum: " + oApp.Start.ToString("yyyy-MM-dd HH:mm")); Log("Text: " + oApp.Body); Log("Organisator: " + oApp.Organizer); Log("Number: " + iNbTreated); } } catch (System.Exception e) { Log("-------------------------------------------------------------"); Log(e.ToString()); aAppList.Remove(oApp); } } } while (oApp != null); Log("-------------------------------------------------------------"); Log("Fertig mit dem auslesen der Termine. Anzahl: " + aAppList.Count);

    // the formentioned Error message appears at this line. foreach (AppointmentItem oAppNew in aAppList) { string[] saShort = oAppNew.Subject.Split(' '); string sShortS = saShort[0]; string sCategory = oAppNew.Categories; string sDate = oAppNew.Start.ToString("yyyy-MM-dd HH:mm"); string sText = oAppNew.Body; string sSqlSelect = "select custcode from TABLE where CUSTCODE='" + sShortS + "'"; OracleCommand oCmd = new OracleCommand(sSqlCount, oConn); oCmd = new OracleCommand(sSqlSelect, oConn); OracleDataReader oRdr = oCmd.ExecuteReader(); while (oRdr.Read()) { Log("Customer ID of " + sShortS + ": " + oRdr["CUSTID"]); } oRdr.Close(); }

    Am I completly missing the concept? Or is it time to knock off and loomk a this again tomorrorw?

    I mean I have created a new AppointmentItem and therefore a new ComObject?

    I see that I am learning new things today, thank you for that!

    Thanks and Cheers,

    Smith


    • Edited by Smithmc Wednesday, November 12, 2014 4:31 PM
    Wednesday, November 12, 2014 4:30 PM
  • Smith,

    After the object is released you can't use it any longer in the code. You need to process found items in the do/while instead of adding them to the array list. Or grab the EntryID value which can be used later for restoring the Outlook objects (see the GetItemFromId method of the Namespace class).

    Wednesday, November 12, 2014 4:48 PM