MSHTML Methods: execCommand

Scripting Syntax

bSuccess = object.execCommand (sCommand, bUserInterface, vValue);

Object

The execCommand method applies to the document and TextRange objects and the controlRange collection.

Arguments

The required sCommand argument names a command to execute. There are many supported commands.

The optional bUserInterface argument is true to have the command prompt the user. The default is false. Some commands do not respect this argument.

The optional vValue argument provides input for the command. Interpretation varies with the command. The default is to have no input. Of course, some commands do not succeed without input (or do nothing useful).

Return Value

The method returns true for success, else false.

Behaviour

The execCommand method provides scriptable access to the object’s IOleCommandTarget functionality, specifically to call the Exec method.

The case-insensitive command name given as sCommand translates to a numeric command ID in the MSHTML command group represented programmatically by CGID_MSHTML. Even within this command group, only a small subset of the commands that are supported through the IOleCommandTarget interface are supported for scripting.

The three implementations each have preparatory work of their own and then some in common. The variations are presently beyond the scope of these brief notes except to note that failure in any of these preparations causes the document and controlRange implementations to throw a runtime script error, as opposed to having execCommand return false. Two preparations are common to all three implementations. First, the execCommand method fails if the given command name is not supported for scripting. Second, the following commands fail (subject to the notes on coding errors, below) if they cannot get clipboard access before calling Exec:

The Exec method is called with execution options constrained to OLECMDEXECOPT_PROMPTUSER and OLECMDEXECOPT_DONTPROMPTUSER, depending on whether bUserInterface is true, and with no provision for receiving any output. The success or failure of calling Exec becomes success or failure of the execCommand method.

What happens in the call to Exec varies with the command and is anyway left for the different topic of MSHTML’s IOleCommandTarget functionality.

Demonstrations

The demonstration script that follows prompts for a command name which is then fed as the sCommand argument to the document object’s implementation of execCommand, with the remaining arguments left to default. This is simple but suffices for several demonstrations: to confirm that some undocumented commands, such as “Open”, have non-trivial implementations; to test the effect of the Internet Security setting “Allow Programmatic clipboard access” on the commands that are listed above as requiring clipboard access; and also to see that passing a genuinely unsupported command is more serious than it ought to be.

There is a demonstration here. To see it, please enable scripting and then refresh this page.

function Test_document_execCommand ()
{
    var cmd = prompt ("Enter the name of an MSHTML command to execute as a test:", "");
    if (cmd == null) return;

    var result = document.execCommand (cmd);
    alert ('execCommand ("' + cmd + '") for the document returned ' + result);
}

A second demonstration is the same but for a TextRange object created for the document’s first button (since buttons are among the objects that support the createTextRange method and we already have at least one button because of the demonstration). Note that unsupported commands sent to this object do not cause runtime script errors.

There is a demonstration here. To see it, please enable scripting and then refresh this page.

function Test_TextRange_execCommand ()
{
    var cmd = prompt ("Enter the name of an MSHTML command to execute as a test:", "");
    if (cmd == null) return;

    var range = document.getElementsByTagName ("BUTTON") [0].createTextRange ();
    if (range == null) return;

    var result = range.execCommand (cmd);
    alert ('execCommand ("' + cmd + '") for a TextRange returned ' + result);
}

Coding Errors

That the document and controlRange implementations so readily cause runtime script errors, including in cases for which the TextRange implementation returns an orderly failure, is presumably not deliberate but might be. Even if it is deliberate, there is one coding error for certain. To assess, you must know that although execCommand has the scripting syntax given above, the actual method as coded in MSHTML has a prototype more like

HRESULT
classname :: execCommand (
    PWSTR sCommand,
    VARIANT_BOOL bUserInterface,
    VARIANT vValue,
    VARIANT_BOOL *pbSuccess);

To fail for scripting purposes, the actual method must succeed, i.e., must return S_OK (zero) as its HRESULT. The failure is returned indirectly, by having stored VARIANT_FALSE at the address given by the extra argument pbSuccess. In the MSHTML code when classname is CDocument or CAutoTxtSiteRange, early failures that are represented internally by a non-zero HRESULT are instead handled by returning this HRESULT. When this gets back to the script engine, the interpretation is not that the method has failed but that it has not worked at all, hence the runtime script error.

To misunderstand all this is also to risk a converse error, namely of returning a random success. To return success for scripting purposes, it is not enough to return a successful HRESULT. It is also required that VARIANT_TRUE be returned indirectly via the extra argument. The risk is of returning S_OK without having stored either VARIANT_TRUE or VARIANT_FALSE via the extra argument. The caller may or may not have initialised the intended storage. If not—and the internal MSHTML routine which translates the IDispatch parameters to the particular types of arguments required for the execCommand method does not— then because all non-zero values count as true, the likely result is that the function will be seen to have succeeded, but by accident not design.

This is in fact what happens when execCommand does not proceed to Exec because clipboard access is unavailable. This case is described above as failure, on the presumption that this must be the intention, but the reality is that the document and controlRange implementations do not actually set success or failure.

The flow of the code is different when classname is CAutoRange (representing the TextRange object). Except if the mechanism of translating dispatch parameters to method arguments has gone wrong, the internal HRESULT is always converted to success or failure via the extra argument, and so there are no runtime script errors. However, this has its own coding oversight, because in the case where clipboard access is required but unavailable, the internal HRESULT is S_OK and the early exit is returned as a well-defined success.