easyEws v1.0.7 is now released

This has been slow coming, but I have just released v1.0.7 of easyEws. As per usual, it is on NPM and on GitHub. You can get the latest with the command:

npm update easyews

Or, you can install it into your solution with the command:

npm install easyews

In this version the JSDOC information is now full incorporated so you get great inline help with each command. I enhanced the library so that it works better with inline help and so I changed how the class is setup and exposed. I have updated the README.md on GitHub, but I still am working on examples and such, that will be done later. And I added new functionality, a moveItem command which can be used to move any item from one folder to another. This was a added due to feedback I received on LinkedIn.

Please feel free to reach out to me with any questions or suggestions.

 

OfficeJS.dialogs Updated (v1.0.5)

I have been working hard on my OfficeJS.Dialogs library and just published version 1.0.5. You can get it from NPM and GitHub. See my previous post for more information on how to do this.

I have added a few new features:

  • A simple Alert.Show() method that displays a simple OK box. For those times you want to just simply pop up a quick notification to the user.
  • A Progress.Show() that displays a progress bar. This allows for you to show the progress bar and then issue Progress.Update() to move the progress bar along. When you are done you call Progress.Complete().
  • A Wait.Show() dialog that will show an indeterminate spinner. This form will remain up until you issue a Wait.CloseDialog().
  • New UpdateMessage() and Update() methods were added to the MessageBox. This was done to allow you to quickly ask a lot of questions of the user in one instance of the dialog, without giving the user back to the application for a second while the new dialog is rendered. UpdateMessage() will just update the message but keep all the buttons the same, but you will specify a new callback. Update() will allow you to fundamentally change all the settings the MessageBox (buttons, icon, caption, text and all), plus a new callback function.
  • Behind the scenes I made some improvements/bug fixes:
    • If you try to show two dialogs too quickly, nothing will happen. So I added a half-second delay between dialog displays to make sure you never get an overlap.
    • You will get an error message in your callback if more than one dialog is attempted to be opened at once.
    • “Window Messaging” has been setup with Progress and MessageBox to allow the parent and the dialog to pass messages back and forth. It involves using setTimeout().

For those interested in the last item, here is what that look like:

        /**
        * Handles messages coming from the parent
        */
        function startMessageHandler() {
            setTimeout(function() {
                var message = localStorage.getItem("dialogMessage");
                localStorage.setItem("dialogMessage", ""); // clear the message
                if(message !== undefined && message !== null && message != "")
                {
                    var msg = JSON.parse(message);
                    if(msg.message == "update") {
                        // update the form
                        updateForm(msg.settings);
                    } else if(msg.message == "close") {
                        // do nothing special here
                        return; // stops the message pump
                    } else if(msg.message == "progress") {
                        if(msg.settings.Number > 100) return;
                        $("#bar").prop("value",msg.settings.Number);
                    }
                }
                startMessageHandler(); // call again
            }, 0);
        }
/**
* Handles messages coming from the parent
*/
function startMessageHandler() {
setTimeout(function() {
var message = localStorage.getItem("dialogMessage");
localStorage.setItem("dialogMessage", ""); // clear the message
if(message !== undefined && message !== null && message != "")
{
var msg = JSON.parse(message);
if(msg.message == "update") {
// update the form
updateForm(msg.settings);
} else if(msg.message == "close") {
// do nothing special here
return; // stops the message pump
} else if(msg.message == "progress") {
if(msg.settings.Number > 100) return;
$("#bar").prop("value",msg.settings.Number);
}
}
startMessageHandler(); // call again
}, 0);
}

view raw
setTimeout.js
hosted with ❤ by GitHub

Here is an example of how to use the Progress dialog. The method signature is like this:

Progress.Show( [message], [start], [max], [completeCallback], [cancelCallback] )

  • The message is the message the user see’s when the dialog is opened.
  • The start is the number you want the progress bar to start at. Usually this should just be zero (0).
  • The max is the number you want the Progress bar to end at. Usually this should be 100. But it can be any number you want. If you have 5 steps to perform in the background while this dialog is up, you can set this to 5.
  • The completeCallback is your callback function to be called when your code calls the Progress.Compelte().
  • The cancelCallback is what gets called when the user presses the Cancel button on the form.

By itself, this will do nothing. You will have to call the Progress.Update() command in order to move the progress bar, or update the message to the user. Here is the method signature for the Update method:

Progress.Update( [amount], [message] )

  • The amount is how much you want the progress bar to move. If you do not specify an amount, an amount of 1 is assumed.
  • The message is a new message to provide the progress bar. If you want to update the message and do not want to increment the progress bar, specify an amount of zero (0).

Once you are all done with the Progress dialog, you issue a Progress.Complete() call. There are no parameters to it. Once called, your completeCallback in the Progress.Show() call will then be executed.

Here is an example:

// reset first to make sure we get a fresh object
Progress.Reset();
// display a progress bar form and set it from 0 to 100
Progress.Show("Please wait while this happens...", 0, 100, function() {
    // once we are done - when your code
    // calls Progress.Complete()
    Alert.Show("All done folks!");
  }, function() {
    // this is only going to be called if the user cancels
    Alert.Show("The user cancelled");
});
doProgress();

function doProgress() {
  // increment by one, the result that comes back is
  // two pieces of information: Cancelled and Value
  var result = Progress.Update(1);
  // if we are not cancelled and the value is not 100%
  // we will keep going, but in your code you will
  // likely just be incrementing and making sure
  // at each stage that the user has not cancelled
  if(!result.Cancelled && result.Value <= 100) {     setTimeout(function() {       // this is only for our example to       // cause the progress bar to move       doProgress();     },100);   } else if(result.Value >= 100) {
    Progress.Compelte(); // done
  }
};
// reset first to make sure we get a fresh object
Progress.Reset();
// display a progress bar form and set it from 0 to 100
Progress.Show("Please wait while this happens…", 0, 100, function() {
// once we are done – when your code
// calls Progress.Complete()
Alert.Show("All done folks!");
}, function() {
// this is only going to be called if the user cancels
Alert.Show("The user cancelled");
});
doProgress();
function doProgress() {
// increment by one, the result that comes back is
// two pieces of information: Cancelled and Value
var result = Progress.Update(1);
// if we are not cancelled and the value is not 100%
// we will keep going, but in your code you will
// likely just be incrementing and making sure
// at each stage that the user has not cancelled
if(!result.Cancelled && result.Value <= 100) {
setTimeout(function() {
// this is only for our example to
// cause the progress bar to move
doProgress();
},100);
} else if(result.Value >= 100) {
Progress.Compelte(); // done
}
};

view raw
ProgressSample.js
hosted with ❤ by GitHub

That example also uses the new Alert dialog. This one is very simple:

Alert.Show ( [message] )

This next example uses the new Wait dialog, which is much simpler to implement. Here is the method signature:

Wait.Show( [message], [showCancel], [cancelCallback] )

  • The message is the message you want to show the user. If you specify null, it will appear as simply “Please wait…”
  • The showCancel flag if set will allow the user to see a Cancel button.
  • The cancelCallback function is only valid if the showCancel option is true. When the user presses cancel, this function gets called.

When you are ready to close the Wait dialog, you issues a Wait.CloseDialog(). Here is an example:

  Wait.Show(null, true, function() {
    Alert.Show("The user cancelled.");
  });
  setTimeout(function(){
    Wait.CloseDialog();
    Alert.Show("Done!");
  }, 15000);
Wait.Show(null, true, function() {
Alert.Show("The user cancelled.");
});
setTimeout(function(){
Wait.CloseDialog();
Alert.Show("Done!");
}, 15000);

view raw
WaitDialogSample.js
hosted with ❤ by GitHub

If you have some suggestions for some things you would like to see added to this library, please add a comment below or reach out to me on LinkedIn or Twitter. Some ideas I will be working on:

  • Allow you to call another dialog type without having the close the dialog.
  • A selection dialog, where you have a dropdown list of a listbox where you wan select (or multi-select) items.
  • An option to resize forms.
  • An option to use the message handler in your own custom form – minimal code

Delay Loading Outlook Add-ins

A customer I work with encountered an issue where a specific add-in was causing Outlook to lose its network connection. Essentially, we were unable to get the “Click here to view more on Microsoft Exchange” (in cached mode) to light up. Here is what we saw:

connect error

Here is what we wanted to see:

connected

It was always grayed out and no mater what we did in Outlook with the connection state, it never same back.

After a lot of troubleshooting we found one particular in-house add-in was causing the problem. Oddly, everything worked great with the same add-in in previous versions of Outlook, but in Outlook 2016, we started seeing this problem. Therefore, we knew it had to be a change made in Outlook 2016. What we found is that Outlook 2016 had been greatly reconfigured in the startup code to optimize network connections as it now connects to the cloud (Office 365). So we started working with the product team on identifying the root cause and in the end we were unable to find a solution (in time). My customers deployment was delayed.

As such, I had to come up with a workaround. We found that if the add-in was not loaded when Outlook started, but was manually enabled after Outlook  launched, the problem would NOT occur. This got me thinking: What if I created an add-in that loaded add-ins AFTER Outlook was done loading all other add-ins?

The Outlook Delayed Loading of Add-ins for the Enterprise was born. By the way, that name is credited to my customer. The catchy acronym stuck: D-LAME Add-in. wlEmoticon-disappointedsmile.png

I have posted the project for it here on GitHub here:

https://github.com/davecra/DLAME

You will need to load it into Visual Studio and compile it and then sign it with a certificate on your own. The code is provided AS IS. The README.md on the page explains the installation, configuration and usage of the a add-in once you have it ready for deployment. Some key points:

  • You will want to make sure it is not disabled by setting the Resilience policy key for the add-in.
  • You will want to move the add-in(s) you wish to delay from HKLM to HKCU registry locations.
  • You can load DLAME as either HKCU or HKLM. The suggestion is HKLM.

So, what can you use this for? Well, it turns out this add-in has a lot of uses and as I have started discussing it with other support folks at Microsoft, several use cases came out:

  1. You have a lot of add-ins that you need to have loaded with Outlook. They keep getting disabled by Outlooks resiliency feature, so you add policy settings to prevent them from being disabled, but now Outlook takes forever to launch. You can now set only DLAME to be resiliency policy blocked and then delay load all your other add-ins.
  2. Because it is a .NET/VSTO add-in, added to the above scenario, you can have all your VSTO add-ins load after Outlook has completed loading all other add-ins.
  3. Because the loading occurs on a background thread, the user will see Outlook fully load and then will start to see the other add-ins load (Ribbons and buttons appearing) after they are able to see their inbox and start reading/selecting items.

Bottom line, this add-in is useful for helping an enterprise manage their add-in without impacting the loading of Outlook or user productivity.

However, there is ONE major caveat. You will need to thoroughly test your add-ins because some add-ins might not like being loaded AFTER the fact. Technically, I have not found any that behave this way, but there could be some that register to certain events (like Application_Load and NewExplorer) that will not get fired if loaded after Outlook is already fully loaded.

Using Outlook.Items.Restrict()

Recently, I was working on an Outlook Visual Studio Tools for Office (VSTO) solution and encountered a problem with unexpected results with the Restrict() method. What I was doing was trying to locate all the appointments (meeting and recurrence) that occurred today. The Restrict command was returning items where the Start and End date properties that were from 2000, 2001, 2010, etc. I quickly deduced that it was also returning recurring items that were dated back to the first occurrence. Here is what my code looked like:

///
<summary>
/// EXTENSION METHOD
/// Get all the appointments between the given date times
/// </summary>

/// <param name="PobjItems"></param>
/// <param name="PobjStart"></param>
/// <param name="PobjEnd"></param>
/// <returns></returns>
public static List<Outlook.AppointmentItem> FindAppointments(this Outlook.Items PobjItems,
                                              DateTime PobjStart,
                                              DateTime PobjEnd)
{
   try
   {
      List<Outlook.AppointmentItem> LobjReturn = new List<Outlook.AppointmentItem>();
      string LstrCriteria = "[Start] >= \"" + PobjStart.ToString("g") + "\" AND [End] <= \"" + PobjEnd.ToString("g") + "\"";
      // we want to find the oldest first. This way we do
      // not get stuck on all day appointments that are conflicting
      // with the current value.
      PobjItems.Sort("[End]");
      PobjItems.IncludeRecurrences = true;
      PobjItems = PobjItems.Restrict(LstrCriteria);
      object LobjItem = PobjItems.GetFirst();
      // chcek the appointments or meetings against the
      // the rules and set Skype status
      Outlook.AppointmentItem LobjAppt = null;
      do
      {
         if (LobjItem is Outlook.MeetingItem)
         {
            LobjAppt = ((Outlook.MeetingItem)LobjItem).GetAssociatedAppointment(false);
         }
         else
         {
            LobjAppt = (Outlook.AppointmentItem)LobjItem;
         }
         // as long as we are not null
         if (LobjItem != null)
         {
            LobjReturn.Add(LobjAppt);
            LobjItem = PobjItems.GetNext();
         }
      }
      while (LobjItem != null);
      // done
      return LobjReturn;
   }
   catch (Exception PobjEx)
   {
      PobjEx.Log(false);
      return null;
   }
}
/// <summary>
/// EXTENSION METHOD
/// Get all the appointments between the given date times
/// </summary>
/// <param name="PobjItems"></param>
/// <param name="PobjStart"></param>
/// <param name="PobjEnd"></param>
/// <returns></returns>
public static List<Outlook.AppointmentItem> FindAppointments(this Outlook.Items PobjItems,
DateTime PobjStart,
DateTime PobjEnd)
{
try
{
List<Outlook.AppointmentItem> LobjReturn = new List<Outlook.AppointmentItem>();
string LstrCriteria = "[Start] >= \"" + PobjStart.ToString("g") + "\" AND [End] <= \"" + PobjEnd.ToString("g") + "\"";
// we want to find the oldest first. This way we do
// not get stuck on all day appointments that are conflicting
// with the current value.
PobjItems.Sort("[End]");
PobjItems.IncludeRecurrences = true;
PobjItems = PobjItems.Restrict(LstrCriteria);
object LobjItem = PobjItems.GetFirst();
// chcek the appointments or meetings against the
// the rules and set Skype status
Outlook.AppointmentItem LobjAppt = null;
do
{
if (LobjItem is Outlook.MeetingItem)
{
LobjAppt = ((Outlook.MeetingItem)LobjItem).GetAssociatedAppointment(false);
}
else
{
LobjAppt = (Outlook.AppointmentItem)LobjItem;
}
// as long as we are not null
if (LobjItem != null)
{
LobjReturn.Add(LobjAppt);
LobjItem = PobjItems.GetNext();
}
}
while (LobjItem != null);
// done
return LobjReturn;
}
catch (Exception PobjEx)
{
PobjEx.Log(false);
return null;
}
}

view raw
broken.cs
hosted with ❤ by GitHub

What I created is an extension item off the Outlook.Items object that allows me to easily restrict them to appointments found between two given dates. This is actually a very useful extension method. If it worked the way I expected it to. What I found out is that in order to get recurring items that occur today which have todays date in the Start and End date properties, you MUST sort the results by the [Start] date. You cannot sort by the [End] date as I had done. Almost exactly the same, here is the fixed function:

///
<summary>
/// EXTENSION METHOD
/// Get all the appointments between the given date times
/// </summary>

/// <param name="PobjItems"></param>
/// <param name="PobjStart"></param>
/// <param name="PobjEnd"></param>
/// <returns></returns>
public static List<Outlook.AppointmentItem> FindAppointments(this Outlook.Items PobjItems,
                                              DateTime PobjStart,
                                              DateTime PobjEnd)
{
   try
   {
      List<Outlook.AppointmentItem> LobjReturn = new List<Outlook.AppointmentItem>();
      string LstrCriteria = "[Start] >= \"" + PobjStart.ToString("g") + "\" AND [End] <= \"" + PobjEnd.ToString("g") + "\"";
      // FIX: we must use [Start] in the Sort to get the right recurrence
      // for todays date...
      PobjItems.Sort("[Start]");
      PobjItems.IncludeRecurrences = true;
      PobjItems = PobjItems.Restrict(LstrCriteria);
      object LobjItem = PobjItems.GetFirst();
      // chcek the appointments or meetings against the
      // the rules and set Skype status
      Outlook.AppointmentItem LobjAppt = null;
      do
      {
         if (LobjItem is Outlook.MeetingItem)
         {
            LobjAppt = ((Outlook.MeetingItem)LobjItem).GetAssociatedAppointment(false);
         }
         else
         {
            LobjAppt = (Outlook.AppointmentItem)LobjItem;
         }
         // as long as we are not null
         if (LobjItem != null)
         {
            LobjReturn.Add(LobjAppt);
            LobjItem = PobjItems.GetNext();
         }
      }
      while (LobjItem != null);
      // done
      return LobjReturn;
   }
   catch (Exception PobjEx)
   {
      PobjEx.Log(false);
      return null;
   }
}
/// <summary>
/// EXTENSION METHOD
/// Get all the appointments between the given date times
/// </summary>
/// <param name="PobjItems"></param>
/// <param name="PobjStart"></param>
/// <param name="PobjEnd"></param>
/// <returns></returns>
public static List<Outlook.AppointmentItem> FindAppointments(this Outlook.Items PobjItems,
DateTime PobjStart,
DateTime PobjEnd)
{
try
{
List<Outlook.AppointmentItem> LobjReturn = new List<Outlook.AppointmentItem>();
string LstrCriteria = "[Start] >= \"" + PobjStart.ToString("g") + "\" AND [End] <= \"" + PobjEnd.ToString("g") + "\"";
// FIX: we must use [Start] in the Sort to get the right recurrence
// for todays date…
PobjItems.Sort("[Start]");
PobjItems.IncludeRecurrences = true;
PobjItems = PobjItems.Restrict(LstrCriteria);
object LobjItem = PobjItems.GetFirst();
// chcek the appointments or meetings against the
// the rules and set Skype status
Outlook.AppointmentItem LobjAppt = null;
do
{
if (LobjItem is Outlook.MeetingItem)
{
LobjAppt = ((Outlook.MeetingItem)LobjItem).GetAssociatedAppointment(false);
}
else
{
LobjAppt = (Outlook.AppointmentItem)LobjItem;
Debug.Print(" >>>>>>>>>>> " + LobjAppt.Subject);
}
// as long as we are not null
if (LobjItem != null)
{
LobjReturn.Add(LobjAppt);
LobjItem = PobjItems.GetNext();
}
}
while (LobjItem != null);
// done
return LobjReturn;
}
catch (Exception PobjEx)
{
PobjEx.Log(false);
return null;
}
}

view raw
FindAppointments.cs
hosted with ❤ by GitHub

Outlook Export Calendar to Word Add-in

I have been working with a number of customers over the years that come from the world of Lotus Notes. And one of the areas they often complain about with regards to Outlook is the calendar printing options. There are certain things you just cannot do in Outlook from the printing perspective that leaves them wont for more.

So, I have been working over the years on this add-in. This has actually gone through a few iterations – the current version 1.2.0.9 is the most recent and most fully-featured version.

The full source code is on GitHub, here. It is totally open source and free to use, modify, etc. Here is what it can do:

addin

  • Printing calendars not available in Outlook by default.
  • The ability to create your own custom calendar
  • The ability to combine calendars for multiple people at once:
    • Displaying only overlapping schedules on the same calendar
    • Displaying all meetings including overlapping meeting
  • The ability to export in daily, weekly, by-weekly, tri-weekly or monthly formats.

The exact details on customization, installation and usage are all covered in the user guide, here.

Developing an On-premises Web Add-in

A customer I was working with wanted help developing a fully on-premises Outlook web add-in. By this, they wanted no part of it to reach out to the Internet (Azure or Office 365). They wanted:

  • To connect to their internal Exchange server
  • An internal IIS website
  • And no references to the Internet (including the Office.js).

This is the topology we are trying to achieve:

topology

If you have developed an Office Web Add-in lately, you find it is inherently biased to the Internet. Even the samples and solutions provided assume Office 365/Exchange and Azure websites. In a default, new Visual Studio solution, the links to the Office.js libraries and stylesheets are all pointing to the web. And, so as you might expect, there are some challenges to getting it to work on-premises only.

This posting covers what you must do to get such a solution to work, including getting past some pitfalls.

  1. First, you have to download the Office.js files locally. And especially for Outlook because the Office.js files that are provided by default in your solution folder (“for offline debugging” as part of VS2015U3 or earlier) are missing some features to work with specific builds of Outlook 2013 and Outlook 2016. You will run into some strange “type” missing and “Office not defined” errors if you forget this step.
  2. Once you have downloaded the Office.JS files, you will delete all the files under the Scripts\Office\1 folder and copy in the contents you downloaded in step 1.
  3. Next, find all your HTML pages where you have the following reference:
    <script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js" type="text/javascript"></script>;
    
  4. Comment out that line and add the following two lines:
    <!-- <script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js" type="text/javascript></script> -->
    <script src="../../Scripts/Office/MicrosoftAjax.js" type="text/javascript"></script>;
    <script src="../../Scripts/Office/1.1/office.js" type="text/javascript"></script>;
    
  5. Once you have developed your solution, you must setup your IIS server. In general here is what you must do:
    • The IIS Server must have ASP.NET installed, it must have .NET4 installed and you must have the Web Application role enabled.
    • Open IIS Manager
    • Create a site, and figure the folder path
    • Convert the site to an Application
    • Apply an SSL certificate that is already trusted on all your client computers or that has a root certificate authority that is trusted on all your client computers. If you browse to your site using HTTPS and you get a RED warning about an untrusted site, then the certificate is not trusted or properly setup.
  6. Next, and this was a major issue to troubleshoot, your Exchange Web Services certificate cannot be expired. If it is, any EWS call you make will return “succeeded” but will be blank – missing data. Digging into the logs you might find an error: “ErrorInvalidClientAccessTokenRequest” The Microsoft Exchange Server Auth Certificate that is used for OAuth needs to be updated. To do this you have to be logged into the Exchange server as administrator:
    • Run this cmdlet to identify the thumbprint of the certificate being used for OAUTH:Get-AuthConfig | FT currentcertificate*
    • Run this cmdlet to identify the thumbprint of certificates used for other Exchange servers (IIS, SMTP, etc.):Get-ExchangeCertificate | Fl *thumb*
    • Run these cmdlets to configure Exchange to use the valid certificate (copy/paste the thumbprint from the Get-ExchangeCertificate output):$today = Get-Date
      Set-AuthConfig -NewCertificateThumbprint newthumbprint -NewCertificateEffectiveDate $today -Force
    • Run this cmdlet to make sure the changes are published to the environmentSet-AuthConfig -PublishCertificate
    • Run this cmdlet to verify the certificate thumbprintGet-AuthConfig | FT currentcertificate*,previouscert*
  7. Now you can deploy your solution to IIS, update your manifest to point to the IIS server (do not forget the HTTPS) and then install it on the Exchange Server Control Panel (ECP) under the Organization / Add-ins option as a mandatory add-in.
  8. Finally, and this last bit is important: IF USING OUTLOOK 2013 or OUTLOOK 2016, YOU MUST BE LOGGED INTO WINDOWS ON THE SAME DOMAIN AS YOUR EMAIL. I know, I know… for some folks this sucks. I have reported this to our product team and they are looking into it. If you are not logged into the same domain controller as your email address is registered, you will not see the advertised add-in. It will load in Outlook Web Access (OWA), but will not appear in Outlook 2013/2016. The exact cause of this problem is unknown, but hopefully it will be addressed in a future version of the product (Exchange or Outlook or both).

Setting up for 100% on-premises is difficult, but it CAN be done. There are a lot of steps, but if you follow the above prescription, you should get it to work. In time, I hope to see this process get easier. But in an online world where Microsoft Office 365 and Azure are main focus, “old fashioned” on-premises solutions are going to require a little more elbow grease.

NOTE: This entry was contributed to by Arthel Bibbens (MSFT) / Exchange PFE. You can follow his posts on this topic here:

 1-GetOrgConfig Administering Office Add-ins within Exchange 2013 and Exchange 2016

blogs.technet.microsoft.com

I recently worked with a developer to deploy an Office add-in within an Exchange 2013 on-premises environment. This project highlighted a capability of Exchange and Outlook that is a huge shift in the way mail add-ins are developed, deployed, and maintained. Let’s take a look at the key components of Exchange 2013 that support this…

 

EWSEditor

The EWSEditor tool has been around a while and is managed/developed by some of my co-workers that sit on the Outlook Developer Support Team. As I have been developing more and more Office Web Add-ins for Outlook, I have found knowing and using EWS to be a very important skill.

EWSEditor helps in this regard. It is a very powerful, full featured EWS test bed. To get started you download this and extract the contents of the ZIP to a folder. From there you launch it and from the File menu, click Next Exchange Service enter in your email address and then select password and click Ok. Then viola, you are connected:

ewsCapture1.PNG

Once connected, you can start browsing your mailbox using EWS. To get to your Inbox, for example, you select TopOfInformationStore and then select Inbox:

ewsCapture2

From there you can go to different folders and look at the items, properties, and values stored in your mailbox. It is quite handy to understand how these things are structured and stored.

Next, you can click  Tools, EWS Post and test your EWS skills. What I did was entered in my information to connect to my server and filled it in as such:

ewsCapture5

I then entered the following XML:


<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<t:RequestServerVersion Version="Exchange2010_SP2" />
</soap:Header>
<soap:Body>
<m:GetFolder>
<m:FolderShape>
<t:BaseShape>AllProperties</t:BaseShape>
</m:FolderShape>
<m:FolderIds>
<t:DistinguishedFolderId Id='inbox'/>
</m:FolderIds>
</m:GetFolder>
</soap:Body>
</soap:Envelope>

And when I hit run, I got a response you see above. It is that simple. And there are also lots of examples as well. If you click Load Example you will see a lot of XML SOAP requests you can test with:

ewsCapture4.PNG

Download it and give it a try.

 

 

Getting Appointments on a specific date from Outlook

I was working on proof of concept Outlook add-in for a customer when I ran into  series of distressing exceptions trying to access specific recurring appointments from the calendar. There seems to be a great many ways to get a list of appointments for a specific date, but you may find the .Start date of these vary wildly. I went down a path using GetRecurrencePattern().GetOccurrence(DateTime.Now), and I got this exception a LOT:

“You changed one of the recurrences of this item, and this instance no longer exists. Close any open items and try again.”

After doing some more searching, I found that I was going about it all wrong. Now, I will be the first to say that I am not the strongest in the Outlook Object Model. Excel, Word and especially PowerPoint are by bread and butter. But sometimes, Outlook can be just downright confusing. Disappointed smile

In the end, this is what I came up with to get a list of all the appointments on a given date in a specific users calendar that you select from the Address list:

Outlook.Recipient LobjRecipient = null;
// crete a select names dialog
Outlook.SelectNamesDialog LobjSnd = MobjOutlook.Session.GetSelectNamesDialog();
// limit to the TO box
LobjSnd.NumberOfRecipientSelectors = Outlook.OlRecipientSelectors.olShowTo;
LobjSnd.AllowMultipleSelection = false; // there can be only one
LobjSnd.Display(); // display it
// do we have resolved names
if (!LobjSnd.Recipients.ResolveAll())
{
LobjRecipient = null; // NO
return; // exit out
}
else
{
LobjRecipient = LobjSnd.Recipients[1]; // yes
}
LobjSnd = null;
// open the shares Calendar folder
Outlook.MAPIFolder LobjFolder = MobjOutlook.ActiveExplorer().Session.GetSharedDefaultFolder(
LobjRecipient, Outlook.OlDefaultFolders.olFolderCalendar)
as Outlook.MAPIFolder;
// get all the items
Outlook.Items LobjItems = LobjFolder.Items;
LobjItems.Sort("[Start]"); // sort the items by start date
LobjItems.IncludeRecurrences = true; // be sure to include recurrences
string LstrDay = DateTime.Now.ToShortDateString(); // today
// set the find string to today 0:00 to 23:59:59
string LstrFind = "[Start] <= \"" + LstrDay + " 11:59 PM\"" +
" AND [End] > \"" + LstrDay + " 12:00 AM\"";
// find the first appointment for the day
Outlook.AppointmentItem LobjAppt = LobjItems.Find(LstrFind);
while (LobjAppt != null)
{
// ...do your thing here...

// get the next item
LobjAppt = LobjItems.FindNext();
}

Outlook Contact Category Correction Tool

When it rains it pours Outlook issues. A few weeks ago I was working with a customer that was having an issue where a small handful of users were loosing the custom categories they applied to their categories. The problem – based on my experiences – seems to be caused by having iCloud sync for contacts turned on in the iPad, but also pulling the contacts from your Exchange account. At some point I think a restore happens or some automatic sync correction ends up wiping the categories away also updating all the contacts in Exchange, therefore Outlook…

In the end, it is ALWAYS good to have a backup of you contacts. and as it turns out this customer did – albeit they were 6 months old. The problem was that the “decategoried” contacts they had in Outlook were fairly updated with notes, changed e-mail and phone numbers, versus the properly categorized backup’s that were 6 months old. Enter the tool…

 

image

While on-site I developed the tool to resolve this problem for their users. It might be useful for others, so that is why I posted it on CodePlex:

https://olcontactcorrecttool.codeplex.com/

But, I think it is also a good example/sample of how to access Outlook from an external application, iterate through a folder and compare items in Outlook. So the source code is available too.

Add-in to Check for Double Resource Booking

I was working with a customer today that had an issue where one some occasions they were double-booking, triple booking or even booking up to 30 conference rooms for the same meeting. They were looking for a way to prevent this from happening because once booked and auto-accepted by the room, they were unable to “cancel” without an administrator. So, I wrote them some code that essentially does a check to make sure if they have more than one resource/room scheduled for a meeting it will prompt them to ask them if they are sure they want to do this. The trick – in this sample – is to put all the resources you care about into a text file called “MeetingRooms.txt” in the installation folder. It will read it into memory and then check to make sure no more than one is scheduled or it will stop the user from making the mistake.

You will need to start off creating a Visual Studio 2010 / Outlook 2010 / .NET 4.0 Add-in…Here is the code:

public partial class ThisAddIn
{
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
// attach to the Item Send Event
Application.ItemSend += new Outlook.ApplicationEvents_11_ItemSendEventHandler(Application_ItemSend);
}

void Application_ItemSend(object Item, ref bool Cancel)
{
// try to cast to a Meeting Item
Outlook.MeetingItem meet = Item as Outlook.MeetingItem;
if (meet != null) // is it a meeting item - not null
{
// load the list of conference rooms from the install folder
List<string> rooms = new List<string>();
string path = System.Reflection.Assembly.GetExecutingAssembly().Location + "\\MeeingRooms.txt";
StreamReader sr = new StreamReader(path);
while (!sr.EndOfStream)
{
// save as lowercase -- we will compare this way
rooms.Add(sr.ReadLine().ToLower());
}

int cnt = 0;
// no loop through all the recipients
foreach (Outlook.Recipient r in meet.Recipients)
{
// if the recipient is in the List of rooms we count it
if (rooms.Contains(r.Name.ToLower()))
cnt++;

// now if we get more than one -- we have a problem
if (cnt > 1)
{
// notify the user they are about to send a meeting request
// that will book more than one room
DialogResult result = MessageBox.Show("You have more than one conference room on " +
"this meeting request. \n\n" +
"Are you sure you want to continue?", "Resource Conflict",
MessageBoxButtons.YesNoCancel, MessageBoxIcon.Hand);
// if the user hits cancel or no, we will
// cancel the send
if (result != DialogResult.Yes)
Cancel = true;
else
// otherwise, allow it
Cancel = false;

// exit for loop
break;
}
else
{
Cancel = false;
}
}
}
else
{
Cancel = false;
}
}

You need the following using statements:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Outlook = Microsoft.Office.Interop.Outlook;
using Office = Microsoft.Office.Core;
using System.Windows.Forms;
using System.IO;