COMPointer Objects

A COMPointer object represents a pointer to a native Windows COM object. This is a Javascript object that stores a native COM object pointer internally, allowing you to work with the COM interface from Javascript as though it were a normal Javascript object.

Creation

It's a little tricky to think about COMPointer objects because of their dual nature. A COMPointer is a Javascript object that represents a native pointer. The COMPointer construct is designed to make the two appear as one entity as much as possible, but you have to be aware of the two pieces during the creation process.

The first step in the creation process is to create the Javascript portion of the object. You do this the same way you create all other native object types, by calling dllImport.create(), in this case with the type signature of a COM interface pointer:

let pCreateDeviceEnum = dllImport.create("ICreateDevEnum*");

A COM pointer is always a C pointer type, so the type name is always a COM interface name followed by a "*" (for "pointer").

The second step is to create the underlying COM object. The call to dllImport.create() created the Javascript portion of the object, but remember that there's also a native COM object pointer hidden inside that Javascript object. dllImport.create() doesn't create that part. It just sets the hidden internal native pointer inside the new COMPointer object to "null", meaning that there's no actual native COM object associated with it yet.

The reason that dllImport.create() doesn't create the COM object automatically is that you haven't said how to do that yet. There are several ways to create COM objects, so dllImport.create() can't guess at what you intend. COM object creation isn't like C or Javascript object creation where new objects are created with a built-in language keyword like "new". Instead, you have to use COM APIs to create COM objects. There are many such APIs, but they mostly fall into three categories:

In each case, one of the parameters to the API function is the address of a pointer variable that will receive the new interface pointer. That's where our thus-far "empty" COMPointer object - the one we created above via dllImport.create() - comes into play. For this example, we'll use CoCreateInstance() to create our object:

const CLSID_SystemDeviceEnum = "62BE5D10-60EB-11d0-BD3B-00A0C911CE86"; let hr = COM.CoCreateInstance(CLSID_SystemDeviceEnum, null, CLSCTX_INPROC_SERVER, pCreateDeviceEnum);

That function creates a new native COM object, and stores the resulting native pointer in our pCreateDeviceEnum object. Our Javascript variable still contains the same object reference as before, but that object's hidden internal COM pointer now points to a real COM interface rather than a NULL pointer. We can now call the methods defined on the interface, which will invoke the corresponding native COM code.

Note that we took advantage of the special handling in the native COM call mechanism that lets us pass a COMPointer object (pCreateDeviceEnum) to represent two parameters in the function signature. See "IID_PPV_ARGS" below.

Special handling for IID_PPV_ARGS

There's a recurring pattern in COM API functions that create new interface pointers, where the parameter list has two consecutive parameters that specify, respectively, the Interface ID (IID) to use and the address of the variable that receives the new interface pointer. This happens so much in the COM universe that Microsoft created a "helper macro" for C programmers to make the syntax a little more concise: IID_PPV_ARGS. That stands for "Interface ID, pointer to pointer to void arguments".

You see this pattern in many COM API functions, but the canonical example is IUnknown::QueryInterface:

HRESULT QueryInterface(REFIID, void**);

The REFIID is a reference to an IID or Interface ID, which is just another name for a GUID, "globally unique identifier". GUIDs are those 32-character hex strings that identify COM interfaces and classes.

The "void**" is a "pointer to a pointer to void". That's a double level of pointer indirection, which is awfully hard to visualize if you're used to Javascript (and frankly, it's not much easier for most C programmers). All you really have to know about it here is that it's an "out" parameter that will receive a pointer on return to a COM interface.

In C and C++, the IID_PPV_ARGS macro we mentioned lets you call functions with REFIID,void** pairs using a single COM interface pointer variable. The reason this is possible is that a COM pointer variable is declared with its GUID, so the one variable contains both pieces of information needed by the function. Here's how the call might look in C++:

IPersistStream *ips; if (SUCCESS(obj->QueryInterface(IID_PPV_ARGS(ips)))) ...

The Javascript-to-native calling mechanism has special handling that simulates the IID_PPV_ARGS macro. But in this case, you don't even have to use the macro; the Javascript engine recognizes the pattern automatically from the function arguments.

Here's the rule:

So in Javascript, the equivalent of the C++ example above is even simpler:

let ips = dllImport.create("IPersistStream*"); let hr = obj.QueryInterface(ips);

Calling COM methods

Once you've created the Javascript COMPointer object via dllImport.create(), and populated the internal COM pointer via CoCreateInstance(), QueryInterface(), or some other COM API, you can call the COM interface's methods directly, as though they were ordinary Javascript methods on the COMPointer object.

const CLSID_SystemDeviceEnum = "62BE5D10-60EB-11d0-BD3B-00A0C911CE86"; let pCreateDevEnum = dllImport.create("ICreateDevEnum*"); let hr = COM.CoCreateInstance(CLSID_SystemDeviceEnum, null, CLSCTX_INPROC_SERVER, pCreateDevEnum); let pEnumMoniker = dllImport.create("IEnumMoniker*"); hr = pCreateDevEnum.CreateClassEnumerator("33D9A762-90C8-11d0-BD43-00A0C911CE86", pEnumMoniker, 0); for (;;) { let pMoniker = dllImport.create("IMoniker*"); if (pEnumMoniker.Next(1, pMoniker, null) != 0) break; }

AddRef and Release

In most cases, you shouldn't call AddRef() or Release() on COMPointer objects. The reference counting should be automatic for most use cases, so you shouldn't have to worry about these the way you do in C/C++ code.

The basic COM rules require that the constructor of an object - CoCreateInstance(), QueryInterface(), etc - will always add a reference count on behalf of the caller. So you don't have to make your own AddRef() call when creating a new object or retrieving an interface.

The COMPointer object itself automatically calls Release() when the Javascript object is deleted by the garbage collector. So you don't have to call Release() when you're done with the object; just let Javascript clean it up automatically, like any other Javascript object.

In fact, it's important that you don't call Release() on a COMPointer object yourself, unless you made a matching call to AddRef() earlier. You must exactly balance each call you make to AddRef() with a corresponding call to Release(). But you musn't call Release() more times than you called AddRef() on the same object, because that could cause the underlying COM object to be deleted while the COMPointer object is still holding its reference to it, which could result in memory corruption and crash the program.

If you want to explicitly free the underlying COM object at a particular point in time, rather than waiting for Javascript to delete it as part of the normal automatic garbage collection procedure, use COMPointer.clear().

Methods

The methods on a COM pointer object are defined by the COM interface, and have the same names as the members of the interface. You call these like ordinary Javascript methods:

let p = dllImport.create("IMyInterface*"); let other = dllImport.create("ISubInterface*"); let hr = CoCreateInstance(CLSID_MyClass, null, CLSCTX_INPROC_SERVER, p); hr = p.QueryInterface(other);

The Javascript COMPointer class adds some additional class methods. These are defined on the class rather than the individual instance objects, to avoid any possibility of naming conflicts with methods defined in COM interfaces.

COMPointer.clear(comPointer): Releases the Javascript reference on the underlying COM object, and sets the internal hidden pointer in the COMPointer object to null. You can call this to explicitly release the underlying COM object at a particular point in time, so that the native object can be deleted if it's no longer needed by any other callers.

You don't normally have to call this method, because the COMPointer object automatically releases its reference on the COM object when the Javascript garbage collector deletes the COMPointer object itself. In most cases it's simpler to let Javascript handle this automatically. The only drawback is that the garbage collector's timing is unpredictable, so a COM object might stay around for a while after you're done with it if you wait for automatic collection. It might be desirable in some cases to explicitly release a COM object as soon as you know you're done with it, such as when the object holds a system resource that shouldn't be held longer than necessary.

COMPointer.isNull(comPointer): Returns true if the internal native COM interface pointer in the object is null, false if it's populated with a live COM object instance.