title: Outlook Add-in: Am I not aware of some basic, conceptual theories of C# programming?
body: <p>Win 10 - VS 2019 - Office365 - Exchange Online</p>
  <p>My first post - woo hoo!!!  (Although I have been lurking forever.)</p>
  <p>I am writing my first Outlook Add-in with my first experience with C#.  I have been hacking on computers since the 1980s and have had experience with about 10 different languages but am very new to this C# and .NET environment.</p>
  <p>What I have will build without complaint and works as intended - most of the time.</p>
  <pre><code>namespace RoomReservations
  {
      public partial class ThisAddIn
      {
          private Outlook.Inspectors inspectors = null;
          private Outlook.UserProperty objUserPropertyEventId = null;
  
          private void ThisAddIn_Startup(object sender, System.EventArgs e)
          {
              inspectors = this.Application.Inspectors;
              inspectors.NewInspector += new Microsoft.Office.Interop.Outlook.InspectorsEvents_NewInspectorEventHandler(Inspectors_NewInspector);
          }
  
          private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
          {
              // Note:      Outlook no longer raises this event. If you have code that 
              //              must run when Outlook shuts down, see https://go.microsoft.com/fwlink/?LinkId=506785
          }
  
          void Inspectors_NewInspector(Microsoft.Office.Interop.Outlook.Inspector Inspector)
          {
              #pragma warning disable IDE0019 // Use pattern matching
              Outlook.AppointmentItem appt = Inspector.CurrentItem as Outlook.AppointmentItem;
              #pragma warning restore IDE0019 // Use pattern matching
              // is it an AppointmentItem
              if (appt != null)
              {
                  // is it a meeting request
                  if (appt.MeetingStatus == Outlook.OlMeetingStatus.olMeeting)
                  {
                      appt.Save();
  
                      // save EventId as UserProperty
                      Outlook.AppointmentItem mtg = null;
                      mtg = (Outlook.AppointmentItem)Inspector.CurrentItem;
                      mtg.MeetingStatus = Outlook.OlMeetingStatus.olMeeting;
                      if (mtg != null)
                      {
                          if (mtg is Outlook.AppointmentItem)
                          {
                              string strEntryId = mtg.EntryID;
  
                              Outlook.UserProperties objUserProperties = mtg.UserProperties;
                              objUserPropertyEventId = objUserProperties.Add(&quot;MeetingEntryId&quot;, Outlook.OlUserPropertyType.olText, true, 1);
                              objUserPropertyEventId.Value = strEntryId;
                          }
                      }
                      if (mtg != null)
                          Marshal.ReleaseComObject(mtg);
                  }
                  if (appt != null)
                      Marshal.ReleaseComObject(appt);
              }
              // define event handler for ItemSend
              Application.ItemSend += new Outlook.ApplicationEvents_11_ItemSendEventHandler(Application_ItemSend);
          }
  
          public void Application_ItemSend(object Item, ref bool Cancel)
          {
              // use EventId to identify current meeting request
              var app = new Microsoft.Office.Interop.Outlook.Application();
              var ns = app.Session;
              Outlook.AppointmentItem meeting = ns.GetItemFromID(objUserPropertyEventId.Value);
  
              // is ItemSend a request or a cancel
              if (meeting.MeetingStatus != Outlook.OlMeetingStatus.olMeetingCanceled)
              {
                  // ItemSend is a request
                  MessageBox.Show(&quot;Not Cancelled.&quot;);
                  if (meeting is Outlook.AppointmentItem)
                  {
                      if (meeting.MeetingStatus == Outlook.OlMeetingStatus.olMeeting)
                      {
                          // if a location was provided
                          if (meeting.Location != null)
                          {
                              Cancel = true;
                              bool blnTorF = false;
                              Outlook.Recipient recipConf;
                              // has the user included &quot;JHP - Room Reservations&quot; for placement
                              // on the shared calendar for all conference room availability
                              foreach (Outlook.Recipient objUser in meeting.Recipients)
                              {
                                  if (objUser.Name == &quot;JHP - Room Reservations&quot;)
                                      blnTorF = true;
                              }
                              // add &quot;JHP - Room Reservations&quot; if not already included
                              if (blnTorF == false)
                              {
                                  recipConf = meeting.Recipients.Add(&quot;JHP - Room Reservations&quot;);
                                  recipConf.Type = (int)Outlook.OlMeetingRecipientType.olRequired;
                                  if (recipConf != null)
                                      Marshal.ReleaseComObject(recipConf);
                              }
  
                              // add reservarion to specific conference room calendar
                              String strLocation = meeting.Location;
                              Outlook.Recipient recipRoomUser;
                              if (strLocation.Contains(&quot;142&quot;))
                              {
                                  recipRoomUser = meeting.Recipients.Add(&quot;conference142@jhparch.com&quot;);
                                  recipRoomUser.Type = (int)Outlook.OlMeetingRecipientType.olRequired;
                              }
                              if (strLocation.Contains(&quot;150&quot;))
                              {
                                  recipRoomUser = meeting.Recipients.Add(&quot;conference150@jhparch.com&quot;);
                                  recipRoomUser.Type = (int)Outlook.OlMeetingRecipientType.olRequired;
                              }
                              if (strLocation.Contains(&quot;242&quot;))
                              {
                                  recipRoomUser = meeting.Recipients.Add(&quot;conference242@jhparch.com&quot;);
                                  recipRoomUser.Type = (int)Outlook.OlMeetingRecipientType.olRequired;
                              }
                              if (strLocation.Contains(&quot;248&quot;))
                              {
                                  recipRoomUser = meeting.Recipients.Add(&quot;conference248@jhparch.com&quot;);
                                  recipRoomUser.Type = (int)Outlook.OlMeetingRecipientType.olRequired;
                              }
  
                              // send request
                              meeting.Recipients.ResolveAll();
                              meeting.Send();
                          }
                      }
                  }
              }
              else
              {
                  // ItemSend is a cancel
                  MessageBox.Show(&quot;Meeting is cancelled.&quot;);
              }
          }
  
          #region VSTO generated code
  
          /// &lt;summary&gt;
          /// Required method for Designer support - do not modify
          /// the contents of this method with the code editor.
          /// &lt;/summary&gt;
          private void InternalStartup()
          {
              this.Startup += new System.EventHandler(ThisAddIn_Startup);
              this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
          }
          #endregion
      }
  }
  </code></pre>
  <p>When working correctly, a meeting request will put 4 separate entries on 4 separate calendars.  A meeting cancel will remove 4 separate entries from 4 separate calendars.  The erratic behavior that occurs for a request includes placing the meeting event on only 2 calendars.  Unusual behavior that occurs for a cancel includes deleting the meeting event from only 2 calendars.  Or the meeting cancel has to be sent out 3 or 4 times before the meeting event is removed from all calendars.  But then, I send another meeting request without changing any code and it is placed on all 4 calendars, as expected.  And a cancel of that request will remove it from all 4 calendars on the first try.  This odd behavior is common within our test group of three users, each running the identical add-in.</p>
  <p>I feel like I understand how the native functions [if, foreach, etc.] work.  But I don't know what I don't know.  I have added some housekeeping [Marshal.ReleaseComObject()].  I'm not sure if it is everywhere it needs to be.  I'm not sure if there are other means of housekeeping I am not aware of that could be causing this behavior.  I am having a difficult time understanding why this will work, then not work, then work - all without changing any code.</p>
  <p>Thanks for any leads that point in the direction of discovery,
  Chris</p>