Sometimes you have multiple add-ins and you need to facilitate communication between them. For example, a common scenario I have heard is that you have:
A Content Add-in that displays something like a graph or an organization chart.
A Taskpane app that allows you to manipulate settings, upload and download data from a backend web service.
You need to be able to facilitate communication between the two so that when updates happen to one add-in, the other receives those updates. I recently worked on a proof of concept that helped prove how this can be done.
The solution is to use the Document as a communication medium. In the particular case we used CustomXMLParts in the document. Here is how it would work:
One add-in would need to send an update to the other, so it would write a CustomXMLPart with a specific namespace and a “context” (basically, I am the TaskPane communicating) to the document.
Both add-ins will have a window.setInterval() thread running to check the documents for CustomXMLParts in that given namespace.
The timer on the Content Add-in would fire, find the new customXMLPart from the taskpane, read the contents and then update itself as needed and finally, delete the CustomXMLPart.
Here is the code for the Content Add-in to look for the message from the TaskPane:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Next, here is the code in the Task Pane Add-in that will send the message for the content add-in to read:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This is a common problem in Outlook. You might have tried to override the Ribbon settings for Print in Outlook to find that your code never gets run when the user clicks Print.
There is also not any events in the Outlook object model to detect Print either. So if you need to detect the user pressing the print button, you are out of luck.
While it is still not possible to detect the print button being pressed, you can at least detect when the user has selected the Print tab on the backstage.
The following code uses a background thread and a series of Windows API calls to FindWindow/FindWindowEx to detect when the Print tab on the backstage is opened:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
I have made a few minor updates to easyEws. First, was a fix for distribution lists with an “&” in the name. The second is a few minor JSDoc updates for better linting. And finally, I changed a couple forEach loops to traditional for loops for performance reasons.
I have published the update to NPM and to my GitHub.
Please let me know if you have any issues or questions.
If you have followed my blog or the Microsoft Office Developer Blogs enough, you have heard over an over again how Office Web Add-Ins are truly cross-platform.
In working on a proof-of-concept I created a simplified Word Redactions add-in that is truly cross-platform. I have shared the entire project on my GitHub repository, so you can see how it works.
Here are the screenshots to prove it runs on every platform supported:
I find myself frequently converting Office Web Add-in projects I originally created in VS2019/2017/2015 over to VS Code. This is because VS Code has become my preferred development platform for everything Web based.
There is a trick to this and a few things you need to know:
Your project in VS2019 (et. al.) has a completely different structure, so once you move things around, you need to change some references.
If you use the Yeoman generator to create the shell of your new project, you need to know a little about webpack.
So, in the following sections we will cover what you need to do:
Copy over all the files
Update the references
Add web pack commands
Copy Files
Run “yo office” and create a default taskpane add-in, with JavaScript and for Outlook. This will create your baseline project. You will open this project and delete these files:
.\src\taskpanes\taskpane.html
.\src\taskpanes\taskpane.js
.\src\commands\commands.html
.\src\commands\commands.js
.\assets\*.* (delete everything in there)
In your VS2019 project and then open your Manifest (highlighted in the image below). Select All and Copy everything from the manifest.
Open your shell project in Visual Studio Code, open the manifest.xml and paste. Later we will update the references.
Next, copy the VS2019 FunctionFile.html and FunctionFile.js files to the .\src\commands folder in VS Code, then rename both: commands.html, commands.js
Next, copy your VS2019 taskpane.html and taskpane.js to the taskpanes folder in VS Code.
If you are like me and also have dialogs, you can create a .\src\dialogs folder in VS Code and then copy the dialog.html and dialog.js from VS2019 to VS Code.
Be sure to copy over any other supporting scripts you have. I created a folder called .\src\common in VS Code and copied those JS files from VS2019.
Next, anything you have in the Content folder in VS2019 you need to copy to VS Code .\assets folder.
That is it for the copy part. Now for the manifest part.
Update the Manifest
At this point you need to update the manifest.
Open the manifest.xml in VS Code and Press CTRL+H to open the Find/Replace dialog. Type in ~remoteAppUrl and replace it with https://localhost:3000. Then replace all instances.
Next and this part is a bit more tricky, but you need to look at every single html pointer URL and verify that it points to the ROOT. Meaning if it was [~remoteAppUrl/Functions/FunctionFile.html] and then you did the Find/Replace, it became [https://localhost:3000/Functions/FunctionFile.html]. You need to update this to be just: https://localhost:3000/FunctionFile.html. This is because when you compile your project with Web Pack, it places everything in the root.
Again, this is tricky, but every one of your base URL’s for images will need to be updated to point to the correct image in the ./assets folder.
Once you have completed, you can press CTRL+~ to open the command console and type: npm run validate. This will tell you if there is anything wrong with your manifest.
Update the HTML files
Next you need to open up each of your HTML files: dialogs.html, commands.html (was called FunctionFile.html), and taskpane.html. etc. In each of these you will find a script that points to a respective JavaScript file and possibly other source files you copied over to the .\src\common folder. Remove all those script tags. Leave only the tags that point to online CDN’s. We will get to this in one of the following sections, but essentially Web Pack will put all the references in for you automatically at compile time.
Also, any images that you are referencing in your HTML with the <img> tag will need to be updated to point to the proper file in the ./assets folder.
Update the Commands.js (formally FunctionFile.js)
Now we need to get everything ready for Web Pack and that is where we start getting into some code. I will cover Web Pack in another post soon, but what it uses is a global namespace for the functions you call. So you MUST add this code to the bottom of the JS file:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
In your manifest you have specified the functions that will be called when the user clicks on a Ribbon button or Events that will fire such as OnSend in Outlook. Find those function and made sure they are added to the global namespace as shown.
Also, at the top of EVERY single JS file in your project you will need to add the following line:
/* global global, self, window, Office, console, myClass*/
When you start to edit these files in VS Code, there will be a whole lot of “red squigglies” for undefined items in the global space. For each of those that the syntax highlighter defines as such, you will add it to the global tag at the top of the file. Like this:
Undefined object
Update the Common JS Files
For the .\src\common files any function you want to be available in the global namespace, meaning you call it from one JS file to the next, you will want to add the same section above and make sure the function is specified.
NOTE: You will also want to do this for any global constants or vars that you want visible as well.
Update the Web Pack Config
Finally, we have to update the webpack.config.js. This is probably the most tricky part. What you need to do is locate the values for {entry} and make sure that every JS file in your project is represented:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
NOTE: This is critical to get correct and to include all JS file because if you do not do this, it will not work.
Now that you have all the entries in place you need to update the {plugins} section. You might need to create an extra module or two as well. By default you have one for taskpane and commands, but my example will show dialogs as well:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Note how in each plug-in (the HTML file), there are “chunks.” These chunks are the references to each script tag that will be added at compile time. It is VERY important to make sure that you reference each file as expected.
Ready to Test
At this point you should be ready to test. If you manifest validated, you can now just press CTRL+~ to open the command console and type npm start.
If your solution successfully sideloads in Office, you should verify that your commands, events and taskpanes all function. If they do not here are some common issues:
You did not properly reference your JS file in the webpack.config.js. Either defined as an {entry}, or as a chunk on a {plugin}.
You did not add the required code at the bottom of the JS file for your functions to be defined in the global namespace, or you forgot to add one or more.
Your manifest is not pointing to the right file. Remember, once Web Pack compiles your code, everything is in the root, except for the assets.
That is a lot, I am sure I missed a few things even though this post took me a while to write. If you have any issues with porting over your project and find there are some missing steps or something I can clarify more, please let me know.
If you are just getting started with OfficeJS, then this post is for you. I will assume you have some understanding of what Office Web Add-ins are. However, there is one very basic point I will demonstrate in this tutorial:
An Office Web Add-In is simply a web site and an XML “description” file. I detailed this in this blog post. And we are going to build on that.
So, you are brand new to OfficeJS, and want to get started. The first thing you need to do is get your development environment up and running. There is a script for that:
NOTE: For more information on this script, please read my blog post.
Next, you will need to pull down the Web Add-in Side Loader tool. Keep this ZIP on your desktop. This will be used later in the steps below to make it really easy for you to install the Add-in to Excel. However, if you prefer to do the steps manually, see the link below. For more information on this tool, please see my blog post and/or the GitHub repository.
Now that you have the development environment setup, lets build your very first, super basic Excel add-in:
Create a folder on your desktop called “MyFirstAddIn”
Open Notepad and copy/paste the code from HERE, then click File, Save, name it “manifest.xml”, change the type to “All Files *.*”, browse to folder on your desktop, and click Save.
NOTE: In this step you created the manifest file. This file will be used to “install” the add-in into Excel. Essentially, it will tell Excel where the web page is for the task pane.
Close Notepad, then open it again. Copy paste this code HERE, then click File, Save, name it “home.html”, change the type to “All Files *.*”, browse to the folder on your desktop, and click Save.
NOTE: In this step you created the primary file for website. This one file contains both the HTML and JavaScript needed to make the taskpane load in Excel.
Now you need to side load the manifest and you do that by first opening the ZIP file on your desktop: Set-WebAddin (v1.0.0.0).zip and extracting the Set-WebAddin.exe to the “MyFirstAddIn” folder.
Press Window+R to open the run dialog, and type CMD and press enter.
NOTE: This will open the Windows Command Prompt which you will use to sideload the add-in and run it.
NOTE: The word “sideload” is a fancy way to say install it for only your instance of Excel. It is also known as Developer Sideloading.
Change the directory to the folder on your desktop with this command:
NOTE: In this step you have sideloaded the add-in to Office. This essentially tells Office you have an added an Excel add-in via the information in the manifest file which, when loaded tells Excel everything it needs to know about it, including where to find it. If you prefer to do the sideloading steps manually, you can follow them here.
Now you are almost ready. Type this last command (that is a dot/period at the end, very important):
http-server .
NOTE: The above steps start a local the http-server on your computer so that it will serve the webpage in the browser from the folder (.) you are current inside. You can now test this by clicking this link.
You are now ready to go. Simply open Excel and a new Blank Workbook. Then on the Insert tab, click the down arrow next to My add-ins and click BasicAddin. It should look like this:
The BasicAddIn in the Developer Add-ins menu
Your add-in Task Pane should load.
Click in cell A1, type “Hello World!”, and then in your Task Pane, click the “Click here” button and you should see “Hello World!” in the bottom of the page.
Once you are done, you can press CTRL+C in the command prompt window to shutdown the server and then to uninstall the add-in you can use this command:
And that is it. There are three things to note here:
You created your first web add-in.
You did not use VSCode or any Integrated Development Environment. You did this only with Notepad.
But as an added bonus – you have also installed VSCode and the tools you need to start doing more complex things with Office Web Add-ins.
JavaScript, HTML, Web, CSS, front-end, back-end, web server, ports, local hosts… some, if not all of these are new terms if you are just getting into this new world of the Web and Web add-ins. But it does not have to be that complicated. As I referred to earlier, the following posts should be your next two stops on your journey:
In this latest release I have incorporated my first community pull. A big thank you to Vijay Samtani for adding the sendMailItem. This new addition allows you to create a message to multiple recipients and with zero to many attachments.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Recently, I had a project I was working on where we needed to be able to notify the user of a possible problem during a long running process. The results would not be ruined, or wrong, just likely incomplete. In this particular case we were collecting several pieces of information and sometimes one of those pieces were not available (say, for example the server was down, the file was missing, or it took too long). We wanted to let the user know the process had encountered an issue, but not stop it. Yet we wanted to also give them the option to retry it. So we presented an Abort, Retry, Cancel dialog like this one:
The problem is that with a traditional MessageBox the entire process would be frozen. If this happens immediately after the user heads off to lunch, when they get back there would be no report, no data and most of the process would not have been run yet, for example.
What I needed was for the process to continue, but to allow the user to be able to take action before the process completed on anything that was found. For example, they see this error above and think: “oh crap, I forgot to copy over the report file.” So they put the correct file in place and hit “Retry.”
Therefore, I created the AsyncMessageBox class:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Under the covers this uses a traditional MessageBox, but on background thread. You need to attach to the MessageBoxClosed event before you make the AsyncMessageBox.Show() call if you want to get the result. Here is how you use this method:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
If you are new to Office Web Add-in Development, then this post is for you. Even if you are not new and you need to setup a new development environment, this post is for you too.
Now, as far as manuallysetting up a new Office web add-in development environment, there are a few sources for helping you get started. There is the official guide here:
NOTE: Mine is a tad more complicated, but essentially walks you through setting up for full fledged development, including source control with Git. That article was actually more for me when I wrote it.
BUT WAIT, THERE’S MORE…
However, after a co-worker and fellow Office Developer (Marty Andren) and I got to talking about this topic, I had an idea to create a script essentially automating the entire process. That was how the Microsoft Office Development Setup script, or MODES was born. The script is posted in the following repository:
The MODES script is provided as-is for folks who need to setup an Office development environment. It is a simple run it and done. Once you have completed this script you will have:
NOTE: This also installs http-server, a simple local web server that I hope to use in a forthcoming blog post that expands on a previous post demonstrating how easy it is to build a Web Add-ins from scratch.
NOTE: The script installs everything as local user, so there is no need for administrative permissions.
On the MODES GitHub post, you can follow the README directions to download and run the PowerShell script. If you are unfamiliar with PowerShell, you can just follow the steps and should walk you through what you need to do fairly quickly and easily.
If the above script cannot be run on your system (as a script), you can try to perform the steps manually by Copy/Pasting the following GIST into a PowerShell window and pressing enter:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
This GIST is a “minified” version of the script on GitHib (no comments, no output and some lines are combined). So if you run this GIST, you will need to do give it time to complete as there is no feedback.
For this latest release, I have added one handy new function getParentId(). This function uses the child message “InReplyTo” field to locate the parent message with the same corresponding “InternetMessageId” field. If found, you will get the Id of that parent message. If not found, you will get NULL.