onenote

OneNote: Do Your Tabs Disappear?

Recently, a customer I work with asked me to investigate why the tabs in their shared OneNote notebooks were disappearing. It is especially prevalent when they moved to OneNote 2016. The cause as it turned out was something called Windows Deduplication. At its simplest, it is a way to reduduplicate files and similar streams of data on a disk. It is a form of compression at its simplest which is to say it is a mechanism to allow you to stick more stuff on a computer or server disk. What we found was the deduplication was corrupting the OneNote index (toc) on the server and each time someone went to make changes to the OneNote file on the server. Tabs would start disappearing for other users when multiple users were editing the files. When we enabled the exception for OneNote files in the data deduplication feature and then moved the OneNote files from one folder to another and then back (which is required to de-optimize the files), the problem went away. To resolve this problem you will likely need your administrator involved:

  1. You will need to set an exception for the OneNote file extensions in Windows Server 2012 Data Deduplication.
    • Here are some details on configuring that with PowerShell. See the “Modifying Data Deduplication volume-wide settings” section.
    • You will need to exclude these extensions: *.one, *.onebin, *.onetoc2
  2. Next, have everyone close OneNote on their systems.
  3. Finally, you will need to “deoptimize” (or, and I love this, de-dedup) the files. To do this, you will create a folder somewhere in the shared folder, move all the files in the OneNote folder to that location, then move them back.

NOTE: Nothing needs to be done on the clients, they will just work the next time the users launch OneNote. You might notice the tabs will still initially appear to be missing, but they will re-index and come back. This might take a few minutes depending on how many tabs there are.

Once completed, you should find that your tabs are no longer disappearing.

jump

Office.js: Jump on!

I am asked the following questions all the time by customers interested in making the jump to Office.js. Therefore, I am providing this FAQ as a primer which usually helps open the door to deeper discussions:

  • What is Office.js exactly?

Office.js is a new way to develop customizations (or add-ins) for Office that are cross-platform. It is developed in JavaScript, runs from a webserver and loads into a content pane or task pane in the document or application.

  • Cross-platform?

Yes. You can currently install or use an Office application (Word, Excel, PowerPoint and Outlook), on any of these platforms: Windows, Mac, iOS, Android (soon) or Web. There are some restrictions on what is supported and how much of the toolset is available per platform. See this article: Office Add-in host and platform availability

  • Is Visual Basic for Macros (VBA), Visual Studio Tools for Office (VSTO), COM Add-ins or Macros going away then?

No. Office.js is just another development platform for Office. There are no plans to deprecate any of the other programmability options for Office. Office.js was designed with the Cloud First, Mobile first vision at Microsoft and expands customizations across multiple platforms. It can be thought of as add-ins that you write once, run anywhere.

  • I have an existing COM add-in. Do I need convert it to Office.js?

No. Unless of course you want your add-in to work across multiple platforms. However, it is important to consider what your add-in is doing. The current application programming interfaces (APIs) for Office.js are more limited. While they are increasing in capability every day, they are still not on parity with COM, VBA or VSTO.

  • Can I convert my add-in in Visual Studio?

No. This is because the new Office.js developer platform is completely new. There are no conversion tools. If you want to make the move to Office.js, you will likely need to start from scratch. However, if you have properly separated your backend logic from application interfaces, you might be able to reuse some of your existing code as a Web Service.

  • How do determine if my existing COM add-in can be rewritten in Office.js?

This is harder to answer. The short answer is you try to create it in the new platform and see how much you can get to function. Longer answer, there are a couple of approaches:

  • You rewrite the entire thing in the new platform and where some things do not translate or work the same, you change the requirements to meet the new platform restrictions, such as changing the workflow, number of features or expected results.
  • You write as much as you can in the new platform and keep building on it as the platform continues to take shape. Whatever you cannot accomplish in Office.js you write as a VSTO add-in.
  • You provide a more limited Office.js version for the non-Windows based platforms you support but still use your COM or VSTO add-in on Windows.
  • How do I develop an Office.js add-in?

There are a number of editors you can use. These are web based so technically you can use any platform you want. I suggest using Visual Studio where you have templates the provide you with predefined templates for Word, Excel, PowerPoint and Outlook Web Add-ins. Also, the Publishing process is much simplified in my opinion with the Visual Studio tools.

  • How do Office Web Add-ins work?

Office Web Add-ins are web pages. You publish the web pages to a web server and the entire solution will load and run from there. As part of developing the add-in, you will create an XML file called a manifest. The Manifest in simplest terms will define the name of your add-in, a unique ID for your add-in, its version, a description, any ribbon commands and the URL to the website. In Office you can then add the manifest and this will register the add-in in your Office application and load the ribbon buttons defined. When the user clicks the ribbons the manifest will tell Office what URL to load and then the add-in will appear in a content pane or task pane.

  • What if I have more questions?

Please ask in the comments section and I will work to continue to expand this FAQ.

facepalm

Office.js: A Face palm moment with Outlook Client in Compose Mode

I have been working with Outlook Web Add-ins  and Compose Message integration for Office Outlook Web Access for a while now. That is, Outlook add-ins that present themselves when the user is composing an email. This scenario is not seemingly as common. But one of my customers has been completely gung-ho about getting all of their COM add-ins converted to the new Office.js model. Some of their solutions will be able to make it there, and some will not. We are finding some roadblocks with mail compose apps.

One area that is currently failing is setting X-headers on an email item. I developed a JavaScript library to assist with this: easyEws. I blogged about it here. The one specific EWS SOAP call my customer wants to use from that library is this one:

updateEwsHeader: Updated the named x-header in the message.

This function works great in two conditions:

  • Outlook Web Access (or Office 365 Outlook Online), and
  • Outlook client – Online mode only

If you use Outlook client (Outlook 2016) in cached mode, this call will seem to work (because it does), but the x-headers will be missing when the user presses “Send.” The problem is that while the currently composed item is saved to the drafts folder and the custom x-headers are written to it via the EWS call (server side), the current Outlook Inspector/session is NOT updated. The cached Drafts folder may take (depending on network latency) anywhere from 1 to 3 minutes to be updated, but regardless the item in the current inspector session will NOT ever be updated. So when the user presses SEND, the item in the drafts folder will be overwritten with one that does not contain the custom x-headers and then sent.

There is no way around this limitation. And I have confirmed this with the Exchange and Office.js teams that this is a limitation of cached mode in Outlook. If you have a need for custom x-headers with an Outlook add-in your only option for now is to create (or keep your existing) COM/VSTO add-in.

If you are running into this limitation, please let me know by commenting below.

choice

Testing Office.JS: The Power of Choice

You know, sometimes the way you want to do things is not necessarily the right way. And even then the right way makes life so hard you want to cry. With Office.JS I recently blogged about “a way” you can test multiple sprints of your add-in in your development environment: Testing a Web Add-in with Multiple Manifest. In this article I explain a method of creating multiple manifests to point to each “version or sprint” of your add-in. Recently, I found that one of my customers threw a wrinkle into that idea. Not that creating multiple manifests was impossible, but they were needing two conflating factors:

  1. Each sprint points to a different folder of HTML/JS.
  2. And they needed to use query string parameters (like this post) to load one of maybe 20 different configuration settings.

With up to 20 configurations per sprint and lets say 4 sprints, you end up with 80 manifest files. So, this is untenable. You need more choices, then enter necessity… wlEmoticon-hotsmile.png

So, lets say you need to test multiple configurations and multiple sprints. Here is how you will set this up:

  • You will publish each sprint of your Web Site Project  into a different folder in IIS. Like this:
folder
Different sprints in separate folders
  • Then, you will create a new HTML page. I called mine Launcher.html. This page will contain a submit button and two drop-down select controls:
    • One for sprints
    • One for configs
  • Next, you will modify your manifest. You can add a new button called “Sprint Testing” for example and have it launch: Launcher.html.
  • In the Launcher.JS, populate those dropdowns. You can populate these hard coded into the HTML, from your client side JS or call your service controller. In my example, I call my service controller.
  • When the user clicks the submit button after choosing the desired settings, you will build the URL to the proper sprint folder and then append the config values to the current query string (that last item is critical so that your sprint Compose/Read page will get the important Office related query string values passed to it). You will see I do this below by replacing my “launcher.html”, replacing it will the new path, then append the config setting:
url = window.location.href.replace("launcher.html", sprint + "/ComposeMessage.html");
url += "&Config=" + config;
  • Once done your code will perform a location.replace() on the url and you will have your proper sprint and configuration loaded. You will only have a single manifest file and you will be able to test/regress test as much as needed with different sprints and configurations.

Here is what the HTML page I created looks like when loaded:

launcherpane

 

Here is the HTML:

<!DOCTYPE html>
<html>
<head>
    <title>Select Which Sprint to Launch</title>
    <meta charset="utf-8" />
    <script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js" type="text/javascript"></script>
    <script src="Scripts/jquery-3.1.1.js" type="text/javascript"></script>
    <script src="launcher.js" type="text/javascript"></script>
</head>
<body>
<h3>Please select the sprint you wish to launch:</h3>
<select id="selectSprintList">
</select>
<h3>Please select the configuration:</h3>
<select id="selectConfigList">
</select>
    <button id="launchSprintButton">Submit</button>
</body>
</html>

Here is the code behind for the page:

/// <reference path="Scripts/jquery-3.1.1.js" />
(function () {
    'use strict';

    Office.initialize = function (reason) {
        $(document).ready(function (reason) {
            /// Upon load connect to the server and request
            /// the sprints so that we can fill the select
            /// field on the form.
            makeAjaxCall("GetSprints", null, function (response) {
                /** @type {String} */
                var data = response.Message.toString();
                /** @type {String[]} */
                var results = data.split(",");
                $.each(results, function (index, value) {
                    $("#selectSprintList").append("
<option>" + value + "</option>");
                })
            }, function (error) {
                $(document).append("

" + error + "

");
            });

            makeAjaxCall("GetConfigs", null, function (response) {
                /** @type {String} */
                var data = response.Message.toString();
                /** @type {String[]} */
                var results = data.split(",");
                $.each(results, function (index, value) {
                    $("#selectConfigList").append("
<option>" + value + "</option>");
                })
            });

            $("#launchSprintButton").click(function (event) {
                /// The user clicked the submit button. Build the URL
                /// from the selection in the select control and
                /// change the location
                var sprint = $("#selectSprintList").val();
                var config = $("#selectConfigList").val();
                var url = "";
                if (sprint != null && sprint != "" &&
                    config != null && sprint != "") {
                    // replace the launcher page with the proper sprint folder location,
                    // but be sure to keep all the query string values - to pass on
                    // to our OfficeJS page. However, add our one config setting to the end
                    url = window.location.href.replace("launcher.html", sprint + "/ComposeMessage.html");
                    url += "&Config=" + config;
                    // replace the url
                    location.replace(url);
                }
            });

        });
    }
})();

// Helper function to call the web service controller
makeAjaxCall = function (command, params, callback, error) {
    var dataToPassToService = {
        Command: command,
        Params: params
    };
    $.ajax({
        url: 'api/Default',
        type: 'POST',
        data: JSON.stringify(dataToPassToService),
        contentType: 'application/json;charset=utf-8',
        headers: { 'Access-Control-Allow-Origin': '*' },
        crossDomain: true
    }).done(function (data) {
        callback(data);
    }).fail(function (status) {
        error(status.statusText);
    })
};

And here is the code for the controller, now, I cheated and just returned a string value for each call, but this at least help you get the idea of how to build your own list for sprints and configs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;

namespace OutlookLauncherDemoWeb.Controllers
{
    public class DefaultController : ApiController
    {
        ///
<summary>
        /// Service Request - incoming
        /// </summary>

        public class WebServiceRequest
        {
            public string Command { get; set; }
            public string[] Params { get; set; }
        }

        ///
<summary>
        /// Service Response - outgoing
        /// </summary>

        public class WebServiceResponse
        {
            public string Status { get; set; }
            public string Message { get; set; }
        }

        [HttpPost()]
        public WebServiceResponse Values(WebServiceRequest request)
        {
            WebServiceResponse response = null;
            switch (request.Command)
            {
                case "GetSprints":
                    return getSprints();
                case "GetConfigs":
                    return getConfigs();
            }

            response = new WebServiceResponse();
            response.Message = "Unknown command";
            return response;
        }

        private WebServiceResponse getConfigs()
        {
            WebServiceResponse response = new WebServiceResponse();
            response.Message = "Config1,Config2,Config3,Config4,Config5,Config6,Config7,Config8";
            return response;
        }

        private WebServiceResponse getSprints()
        {
            WebServiceResponse response = new WebServiceResponse();
            response.Message = "sprint1,sprint2,sprint3,sprint4";
            return response;
        }
    }
}

The idea here is that this add-in is a baseline for yours. You can build these components into a debug build of your add-in and it will call into each separate folder/sprint where you have posted previous versions.

I have published the full source up on GitHub: https://github.com/davecra/OutlookLauncherDemo

Automating Excel and Add-ins Don’t load

There is a common problem I see when automating Excel from an external .NET application:

You will launch Excel, open a workbook, perform work, and then try to close Excel and find that it remains in memory. You might even have code in your solution to try to re-use existing instance of Excel and find that it uses one of these “zombie” instances that do not have any of the add-ins loaded… and maybe an add-in your code relies on.

The issue is that Excel is not able to close because  the primary thread of your application is holding on to a COM reference. Even adding Marshal.ComReleaseObject() and GC.Collect() like this probably will not improve the situation either:

From my experience, and from what I have gathered is that until the thread is stopped, the COM reference will be locked. Not sure where the issue lies (EXCEL, COM, or .NET), but I have found a workaround to this. What you have to do is terminate your thread. But because you do not want to terminate your application, you need to create a NEW thread. So the following pattern is what I have found works:

 

 

JSDoc – Wow, I just learned something cool

As I start to delve deeper into the world of Office.JS and JavaScript, I am having some fits with how things are done and how things are implemented. Besides getting used to a whole new load of terms, finding out that there are more frameworks than there are developers (ok, maybe that is a small exaggeration), or that JavaScript is not “really” object-oriented (and that’s “ok”) is just nearly too much to handle.

Then there is one of my biggest complains with JavaScript is it use of types. You can get in a lot of trouble here and you can even break JavaScript from its root if you really prototype something wrong (see monkey patching). Additionally, scope can be an issue. And then there is Visual Studio 2015 doing it’s best to try to help you with types, but then it fails you. For example, you type “something(dot)” and you expect IntelliSense to come to the rescue – and low and behold, you get what I like to call: “The yellow bangs of hell.“(tm) wlEmoticon-hotsmile.png

yellow-bangs-of-hell.png

I have been coding with this problem thinking I am all alone in this world screaming every time an object, string, or number fails at IntelliSense. Then on an internal Microsoft email thread where the virtues of TypeScript and JavaScript were being contemplated, a whole world was opened before my eyes. Enter JSDoc. Turns out there is an entire specification around this. And even better – Visual Studio 2015 support it. And it is pretty easy to use. Plus type definition is just the tip of the iceberg, there is a LOT more for me to lesrn. But for now, here is an example of how to define a few common types I frequently use:

/** @type {XMLDocument} */
var xmlDoc = $.parseXML(ewsResult.value);
/** @type {string} */
var theString;
/** @type {number} */
var myNumber;
I have started to use this very recently in my proof of concepts I work on with my customers and I think I just increased my productivity by 50% (or more). Holy cow. wlEmoticon-winkingsmile.png