Ripsaw COM Interface, First Pass

In this installment of the Ripsaw article series we’ll finally get to write some code. We’ve already gotten a pretty good idea about how we want to implement the core Ripsaw library, so now we’re going to define enough of the COM interface that we can create a simple test script that will eventually be used to exercise the library.

To be callable from script, the object’s interface should be OLE-automation compatible, so we’ll declare it as such in our IDL:

The interface is also derived from IDispatch to support late-binding, which means that scripting languages can discover the methods and properties of the interface at runtime.

Before we can write the test script we’ll need decide what basic methods a Ripsaw object needs to implement. A single Ripsaw object will represent a connection to a file, and the object will fire events whenever the file changes so that listeners can take an action based on the update (change the display, parse the update, etc.). At a minimum, then, we need to be able to open a file and close a file.

The Open method will accept a path to a file and return a Boolean indicating the success or failure of the method call. The Close method will close any existing connection, and we won’t bother to check for a return code from that method.

Now, it would probably be useful to be able to query a property of the object for the name of the file to which it is connected, and perhaps another property to check if the object is currently opened or closed. Let’s add those properties:

Okay, not bad so far. We can open a file, query for the status, query for the name, and close the file. That’s no good if we can’t catch events fired by the object. We need to define a dispinterface that specifies the events that the object can fire at its clients:

The Open event fires when a file is opened successfully, of course. Close fires when a file is closed, and Update naturally fires when the file changes. The Update notification will also pass along the data that was gathered from the last file update.

Okay, based on those interfaces, our script might look like this:

First of all, I’ll explain what all this means for those of you that are new to JavaScript — err, JScript — under Windows Script Host (WSH). The first line creates a COM object based on its ProgID, or programmatic identifier. This is a string that resolves, through the registry, to a GUID that uniquely identifies a COM library that provides the requested functionality.

The second parameter to CreateObject tells the scripting engine to wire events provided by the object to functions that start with the specified prefix. In our script, we want to handle the Open, Close, and Update notifications, so we created functions named ripsaw_Open, ripsaw_Close, and ripsaw_Update to catch these events. Since they all start with the “ripsaw_” prefix, the script engine will call them when any of the corresponding events are fired by our object.

If you looked carefully at the script, you noticed that I inserted a comment: “Uh-oh.” That’s because the script has no way of sitting and waiting for an update other than looping in a tight loop, and that is almost never a good idea. The script would waste CPU time doing nothing while we wait for the file to be updated. In other languages we have access to message loops, WaitForSingleObject, and other means of yielding CPU until an event occurs, but not in WSH. We need something better. What we’ll do is add a method to our object which, when called, will do absolutely nothing until an update occurs. We’ll call it WaitForUpdate.

The first parameter is a timeout, in milliseconds, so that the function will return if no updates arrive in the specified amount of time. We’ll take a page from Win32 and say that a value of 0xFFFFFFFF will tell the method to wait forever. If the timeout expires, the method will return a null BSTR; otherwise, the return value will be the new data from the last file update. We’ll modify the heart of our script to call the new method:

In a real script we’ll probably call WaitForUpdate in a loop, but for now we’ll just wait for one update before exiting.

Generalizing the Interface

This looks good for files, but perhaps later on we’d like to extend Ripsaw to listen to other sources of events, like the Windows Event Log (the first Ripsaw actually did that for a while, but I backed out the feature to focus on files). It would be a good idea to go ahead and factor out the common methods and properties into a base interface that could be extended by other event sources.

Later on, if we define an interface for listening to event logs, we’ll derive that interface from IRipsawObject and define an Open method specific to event logs. We’ll also assign a special ProgID to event log objects so our client applications can create them:

Next Steps

Now we have an interface, and we have a script to exercise that interface. What we need next is some real code to implement the interface and a binary that the script can load and run. We’ll start on a preliminary implementation in the next article. Until then, you can download the IDL and the script file and look them over. Please leave a comment with any suggestions you may have.


One Comment

Leave a Reply

Your email address will not be published. Required fields are marked *