RPA Part 1 — Sharing Word/Excel between objects in Blue Prism

It’s quite common for Blue Prism developers to use MS Word VBO or similar to interact with Word instances in workflows. However, a problem arises when we want to extend the object with some custom functionality that utilizes Word COM object. The issue is that MS Word VBO doesn’t expose the Word instance and we can only use the handle. Due to that, Blue Prism developers often extend this object and add actions directly in it. This breaks many principles and is very nasty. Let’s see why and how to fix that.

Why extending MS Word VBO is not the best idea

Nothing stops us from extending the object. However, this is not ideal because:

  • IT administrators become scared of updating the object since it is hard to migrate the newly added actions
  • We stop understanding what’s in the object. Apart from new actions, someone may have actually modified existing ones and broken the object. We can’t trust it anymore
  • This breaks Open Closed principle – objects should be open for extension but closed for modification
  • MS Word VBO is written in VB.NET. If we want to use C#, we can’t do that as we can’t mix languages inside an object
  • It’s hard to share functionality between projects. Ideally, we’d like to be able to share our actions between projects and separate them from actions that we don’t want to share. This is much harder if we put these actions directly on the object
  • Actions are much further from the business context. If we need a specific action for one of our processes, then it’s probably better to keep the action with other actions in the process-specific object

There are probably many more reasons to avoid that. Generally, apart from technical limitations (like being unable to use C#), this is just a matter of best practices and good design.

Why it’s hard to put actions outside of the object

First and foremost, Word instance is a COM object. Without going into details, it’s an object that is globally visible in the operating system and exposes many APIs. You can call this object from Blue Prism, Visual Basic for Applications, PowerShell, Visual Basic Script, C#, Java, and much more.

However, actions in Blue Prism objects cannot return any type they want. They can deal with numbers, texts, collections, and some other things. However, they can’t return an arbitrary object type. This means that the action cannot return a Word instance. To work that around, MS Word Object returns a handle which is an integer (a number). So when we call MS Word.Create Instance, then the method returns a number that identifies the word instance held behind the scenes. If we then call a method like MS Word.Open, the MS Word Object must find the actual Word instance based on the number.

Technically, it works in the following way. When creating an instance, this is the code that is executed:

We create an instance of the Word object, and then we get the handle for it. This is GetHandle:

We first check if the Instance is empty. It is not, as we just created it. We then check if the instance is already in the InstanceMap collection that holds all the Word instances we already created. At this point, it is not there. In that case, we simply iterate over all numbers from 1 going up, and then we find the first unused number. We add the instance to the HandleMap with key equal to 1, and then we return 1 as the handle that the user will use later on.

Let’s now say that you call Open. This is what it does behind the scenes:

This calls GetInstance which looks like this:

We just check if the handle is in the HandleMap and then store it in the CurrentInstance. So this is how we get the Word instance back from the integer passed by the user.

To use the Word instance in some other object, we would need to access the Word instance. However, we can’t just return it from the action as Blue Prism doesn’t support returning any type. We could also try to get access to HandleMap and then extract the instance. This is doable but far from straightforward. However, we can use some clever tricks to get access.

One of possible workarounds – truly global variable in Blue Prism

The idea is to create a global dictionary for sharing any data between any objects in Blue Prism process. The solution will work like this:

  1. We create a code block that creates a globally-accessible dictionary storing string-object pairs
  2. We add one action to MS Word VBO to put the Word COM instance in the dictionary based on the handle
  3. In some other object (like our custom MS Word Object), we extract the com instance from the globally-accessible dictionary and use it accordingly

Let’s see that in action.

First, let’s create an object CustomWordObject.

We need these two external references added:

and

You may need to adjust the path to the Word dll based on your Word version.

Now, let’s create an action named CreateGlobalDictionary with the following content:

Specifically, there is a code block that does the following:

We use some .NET magic to dynamically create an assembly named GlobalAssemblyForSharingData, add a type named GlobalType to it, add a global field named GlobalDictionary, and initialize it accordingly.

Next, let’s add the action to MS Word VBO named ExportWord with the following content:

Let’s see the code in detail:

We list all the assemblies, then we find all the types, then we find the type with the global dictionary. Next, we get the dictionary and put the COM object in it. Finally, we return the identifier.

With the regular actions on the MS Word Object, we need to identify the Word instance by using an integer handle. For our custom actions, we will use a string identifier that serves the same purpose. Just like the regular actions accept handle and other parameters, we’ll accept the same with only a different identifier type.

Lastly, we create a new action in our custom object. Let’s say that we would like to show the instance of the Word application. This is the action:

And here is the code specifically:

You can see that it’s the same code for obtaining the dictionary as before. We list all the types, we then find the dictionary and extract the COM instance based on the identifier obtained when exporting the instance. We then cast the object to the known interface and then simply change the property to show the Word application.

This is how we would use it in the workflow:

You can see that we first open the Word instance and open the file using the regular Word object. We then call CreateGlobalDictionary and ExportWord to export the instance. Finally, we call ShowWord from our custom object. This way, you can add any action in your business object and not touch the Blue Prism’s stock object anymore.

Further improvements

It’s worth noting that this solution provides a truly global dictionary for sharing any data between any objects. Nothing stops us from sharing other things. Just don’t abuse the mechanism.

It’s also worth noticing that there is a big code duplication. We could put it in a NuGet package to reuse the code easily.