OLE Automation Objects

OLE Automation is a Windows feature that lets scripting languages access system functions and features in other applications. This is also known as "Scripting Objects", but that's a vague term that can mean other things as well. Automation was created originally for the Visual Basic language, and is most often used in VB. Windows Power Users will be familiar with this from working with Visual Basic scripts in programs like Excel, Word, or WScript. If you've done any work creating Visual Pinball tables, you might have used automation objects in VP's Visual Basic scripts.

In Javascript, you create an OLE scripting object like this:

let fs = createAutomationObject("Scripting.FileSystemObject");

If you're a Visual Basic user, it might be helpful if we mention that this is the equivalent of VB's CreateObject() function.

The object name in quotes is the "class name" of the object you wish to create. This isn't defined by Javascript; it's defined by whatever application or system facility provides the automation object. Windows provides a set of pre-installed "Scripting" objects, including "Scripting.FileSystemObject", which provides functions to access files and folders on the local hard disks. Many other objects come from separate applications, such as Excel or Word.

If the named class is installed on your system, createAutomationObject() returns a Javascript object that provides the set of methods and properties defined by the system object. You can now simply call methods on the returned object to access the system features provided by the object.

// list the files in the Windows folder for (let file of fs.GetSpecialFolder(0).Files) console.log(file.Name);

One common Windows OLE object that you shouldn't use is MSXML.XMLHTTP (or any of its several versions). You should use PinballY's HttpRequest instead, as it has provisions for asynchronous event handling that can't be easily reproduced with the generic Automation facility.

"Variant" arguments

Many Automation objects work in terms of "Variant" variables, which is a special object formatted the same way that Visual Basic represents its variable values. (Microsoft originally designed Automation around Visual Basic's needs, so the whole system is very VB-centric.) When you call a method on an automation object, any Javascript arguments you pass to slots expecting Variant values have to be converted to Variant before being passed to the external object.

Javascript will perform the conversion to Variant automatically if you use regular Javascript values as arguments, so in most cases you can just call methods on these objects without worrying about special data types.

There's a snag, though. The automatic conversion can't always figure out which type the callee expects, so it always uses a standard conversion from each Javascript type to a suitable corresponding Variant type. For example, if you pass a Javascript number value as an argument, it will always be converted to a Variant "double", which is the VB equivalent of a Javascript number. The snag is that some automation methods are picky about the exact types they receive as arguments, and will return an error if you pass the "default" type used in the standard conversion from Javascript.

In such cases, you can solve the mismatch by explicitly creating your own Variant variable, and setting it to the expected type. You can do this as follows:

// create a Variant variable and set its "short int" value let v = new Variant(); v.iVal = 7; // now pass "short int" 7 to an automation method let x = createAutomationObject("Example.Object"); x.RequiresShortInt(v);

See Variants for details on the special Variant type.

Properties

Some OLE Automation objects have "properties". These work a lot like Javascript properties: they're named slots in the object where you can read and/or write values. The syntax in Javascript for operating on Automation object properties usually looks exactly like the normal syntax for accessing properties of Javascript objects. For example, the standard Windows scripting object Scripting.Dictionary has a property called CompareMode that lets you specify how values are compared:

let dict = createAutomationObject("Scripting.Dictionary"); dict.CompareMode = 1;

Okay, that's straightforward enough, but why did we say you "usually" use the normal Javascript syntax? That's because there are cases where you can't use the normal syntax. Specifically, you can't use the normal assignment syntax for properties with arguments.

What's a "property with arguments"? It's a property that doesn't represent just a single value, but represents multiple values, indexed by one or more additional arguments. Conceptually, it's like an array variable. For example, the Scripting.Dictionary object has a property called Item that acts like an array of values that can be indexed by numbers, strings, dates, or anything else. In keeping with the conceptual similarity to arrays, Visual Basic lets you access these properties with syntax exactly like array variables:

' NOTE! This is a VISUAL BASIC example, NOT Javascript! ' "Item" is a property of the Scripting.Dictionary object ' that can take arguments, so we access it like an array. dict.Item("red") = 72 MsgBox "The 'red' value is " + dict.Item("red")

Now, given that these sorts of properties are meant to resemble arrays conceptually, and given that you access them in Visual Basic exactly as though they were arrays, it might seem like the logical translation to Javascript would be treat them as arrays there as well, by changing the Visual Basic parentheses to Javascript square brackets. Unfortunately, that won't work:

// Javascript equivalent of the above? NO! This won't work!!! dict.Item["red"] = 72; // WRONG!!! alert("The 'red' value is " + dict.Item["red"]); // WRONG!!!

The problem is that these properties aren't actually arrays. What's really going on under the covers is that the language engine (Visual Basic or Javascript) has to make method calls to the OLE Automation object to read or write the properties. Visual Basic hides that, by making it look like ordinary array access, but it isn't really; it's really a method call to the object.

So how do we translate this to Javascript? The reading part is easy: you just write it exactly like in Visual Basic:

alert("The 'red' value is " + dict.Item("red"));

The writing part is a little trickier. Javascript doesn't let us use anything resembling the Visual Basic assignment syntax. Instead, we look to the C++ world, where Microsoft developed a naming convention to deal with the same mismatch. Microsoft's convention for C++ is to create a method with the prefix put_ followed by the property name. This means "put a value in this property". We do the same thing in Javascript. Here's how you call it:

dict.put_Item("red", 72); // equivalent to VB's dict.Item("red") = 72

The Javascript engine automatically creates a put_ method for each Automation object property that requires parameters, so there's nothing extra you have to do to create these. They're automatically present when necessary. All you have to do is remember to call the method with the put_ prefix when you want to assign to a property that takes arguments.

To keep the syntax simple for the more common case of ordinary properties, a put_ method is only created when a property takes extra arguments. Ordinary properties with no parameters can be both read and written using the original property name, as in the CompareMode property example at the start of this section.

OUT parameters

Some automation object methods take "Out" parameters, which are arguments that the callee uses to return data to the caller.

Javascript has no direct equivalent of "Out" parameters. The data transfer in Javascript is always one-way, from caller to callee. When calling an automation method that returns information through an "Out" parameter, therefore, we have to do something a little unusual.

Specifically, we use Variant objects. If an automation method passes information back through an Out parameter, simply pass a Variant to the method for that parameter. On return, the Variant will be modified to contain the returned information.

let v = new Variant(); autoObj.FuncThatChangesArgument(v); // v now contains the updated value

The Javascript engine doesn't require Variants for OUT arguments; you can use any other value type instead. But passing a Variant is the only way to retrieve the updated value if the callee changes the OUT parameter. If you don't use a Variant, any update to the value will simply be discarded.

Limitations

Unsupported types: The Automation system doesn't currently support calling methods taking the parameter types listed below. The parameter types are specified in the IDispatch COM interface provided by the automation object's native code, so this isn't a question of the argument values you pass from Javascript code; it's a fixed feature of each automation object's method interfaces. Functions that require unsupported argument types will throw a Javascript error to that effect if you attempt to call them.

Event handling: There's no built-in provision for the COM "event sink" system, which is used in some of the more complex automation objects to send asynchronous events back to the host program. Objects with event callbacks therefore can't easily be used, or at least can't easily be used with their full event systems.