Monday, March 16, 1998 at 10:19:02 AM PacificBierman's COM Design
Following up on last week's discussion around Reaching for COM Nirvana.To all of you who were kind enough to respond and help in our COM efforts, I've put together the following rough specification. Any comments, suggestions, or all-out mortar shells are completely welcome.
What we are doing is OLE Automation, not just raw COM). The key to this is the iDispatch interface that allows for "blind" invocation of a routine. It also allows for "put" and "get" of properties (member variables, or in our case even odb variables). The advantages of this interface is that it allows us to have a clean single interface, that is easy to use from Visual Basic, J++, and in ASP pages. The disadvantages is that it is slower than a direct call because of having to create the necessary structures to pass and the additional cross process call to GetIDsOfNames (necessary in the iDispatch interface), and it is harder to deal with when writing the client in C or C++ since the coder has to create the calling structure by hand.
UserLand.Frontier.Comobject is the "friendly" name for our primary iDispatch interface. Within that we will have a "coClass" called "Frontier" within that will be the interface IFrontier which inherits from IDispatch.
The methods within IFrontier that we will implement is GetIDsOfNames, Invoke (those two are the standard two as defined by IDispatch) and a method GetObject. A user will do a coCreateInstance on our object to get a pointer to our IFrontier interface. They would then use the GetObject method to specify a path in the odb and get back a new object (also of the IFrontier interface) that was specific to that address in the odb.
For design reasons within Frontier we will not be exposing the entire odb to this interface, instead everything through this interface will be relative to the "user.com" table within the root, the name prefix "user.com" will be obtained from the root one time on startup from system.misc.adrComTable. The user would then use that new object to invoke a script or set or get a "property" i.e. element within the odb. In Visual Basic, this would look something like the following:
Dim f as New Frontier Dim mytable as Object Dim i as Integerset mytable = f.GetObject ("myTable.myFirstSubTable") mytable.countPref = 10 i = mytable.countBy (12)
We are using the GetObject method because of its parallels with Frontier and getting the address of a table (mytable = @myTable.myFirstSubTable).
Nitty Gritty
//base IDL definition// Interface IFrontier [ object, uuid(XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX), helpstring("Frontier COM Server Interface"), oleautomation ] interface IFrontier : IDispatch { import "oaidl.idl";
HRESULT GetObject ([in] BSTR path, [out, retval] IDispatch * newobj); };
Implementation of GetIDsOfNames
Since we have no way to know all the names within the user.com table, any given object when requested for a name will build an internal array that will contain that name and then return the index position of that name as the "DISP ID". This ensures for any single instance of an object that the same ID will be returned for a specific name. It will also mean that there is NO guarantee that the ID will be the same the next time you create that object!
Note: for performance - no checking is done until Invoke to validate the path or name within the odb.
Are there any known issues in not returning the same ID across different creations of an object?
Implementation of GetObject
This will simply do a new object specifying the "path" so that object has that as a member variable.
Implementation of Invoke
This is the key procedure for doing anything within the IDispatch interface. For script calls (i.e. DISPATCH_METHOD) the full "name" will be built by taking "user.com", then appending the member variable that contains the path, then appending the final name which is in the array and indexed by the DISPID passed into Invoke. Then the internal Frontier procedure will be called to build a call tree to that script with the parameters specified in the pDispParams field of Invoke. NOTE: Frontier does not support named parameters. Gets and puts (i.e. DISPATCH_PROPERTYGET, and DISPATCH_PROPERTYPUT) will build the "name" in the same fashion as above then use the ODB call backs to get or get that field in the odb.
If the name specified does not exist on the PUT, should we automatically create that odb entry?
What do we return when a GET is done on an object that can not be sent via COM such as a WPText or Outline element?
There is a concept of collections within the OLE Automation world. While not for the very first cut, this may be a good way to support our record and list types across COM.
Am I missing anything? Does this make sense to people?
Comments and suggestions welcome and appreciated, and again many thanks to all those who have helped get us this far.
Robert Bierman
bierman@scripting.com
This page was last built on 3/16/98; 10:45:19 AM by Dave Winer. dave@scripting.com