This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.
Server Scriptlets
n the January 1998 installment of this column, I presented Dynamic HTML (DHTML) scriptlets as the forefront of scripting technology. A few months later much has changed, and even more evolution can be expected in the immediate future. The spotlight this month is on server scriptletsfull-fledged COM components composed of script code. These components expose an effective COM interface to the development environment in which you're working.
Despite the similarity in name, there are a number of
basic differences between server scriptlets and DHTML
(client-side) scriptlets. Actually, DHTML scriptlets are a special case of a more general category of script components called server scriptlets. The word "server" here means that the object acts as a server; it does not refer to where the code actually executes. In my opinion, the prerelease name for server scriptlets, COM scriptlets, more clearly identifies what server scriptlets really are: script-based COM components.
In this column, I'll first discuss the main points that make DHTML scriptlets and server scriptlets different, and how each is useful to developers. Second, I'll provide in-depth coverage of the features that further simplify the process of creating COM components using script languages.
Script-based COM Components
A server scriptlet is a COM object written in JScript, VBScript, or any other script language with a parsing module that is compatible with the ActiveX® Scripting engine specification. Functionally, a server scriptlet is no different from the generic COM server you can write using a variety of languages and development tools. Hence, a server scriptlet has much more in common with a Visual Basic COM server than with a DHTML scriptlet, despite the similarity in names. A server scriptlet provides certain services (making it a server), and is composed of script and XML code (which makes it a scriptlet).
There are a number of key differences between DHTML scriptlets and server scriptlets. First, a server scriptlet is not an HTML page, but a XML document. Next, a server scriptlet cannot support a user interface, and is identified through a 128-bit CLSID rather than by name. Despite this, server scriptlets require some information to be entered in the system registry. In principle, a server scriptlet can implement any COM interface that has a special module called an interface handler. (I'll discuss this in detail later.) A DHTML scriptlet is a Web page that exposes functions, whereas a server scriptlet is a real automation server that is callable from automation clients.
Since server scriptlets can't have a user interface, you can write them only for internal calculation or data retrieval purposes. One ideal use of server scriptlets is the implementation of business logic modules in n-tier application. Since they work as completely transparent COM objects, you can call them from any client or server module.
The source code available with this article includes a server scriptlet written in JScript and a couple of test applications that utilize it. To demonstrate the flexibility of server scriptlets, I've written two test programs, one in Visual Basic and one in Borland Delphi 3. Yes, Delphi 3. What better way to illustrate COM's language neutrality than a demonstration of how server scriptlets are perfectly supported by a variety of COM-compliant programming languages?
Getting the Server Scriptlet Package
The server scriptlets package is available at http://msdn.microsoft.com/scripting/. Once you've downloaded and installed it, you'll have a wizard, a DLL file, some HTML pages with an overview of server scriptlets technology, and some updates to your registry that add shell-level support for new .sct files (the default extension of server scriptlets). The wizard helps you write both DHTML and server scriptlets. It also provides a comfortable user interface for specifying the various methods and properties of the component, and qualifying information such as CLSID, version number, and name.
The core of the server scriptlets package is a DLL file called scrobj.dll, which is installed in the directory you choose during the setup. The role of scrobj.dll will become clear later in the article. For now, let's just say it's a runtime engine that interprets and executes the XML code of a server scriptlet. It's also the junction point for communication with external automation clients.
Overview of a Server Scriptlet
Basically, a server scriptlet is an XML document. It supports a few new tags and should avoid the typical tags of a HTML document, such as <HTML> and <BODY>. The only point of similarity between DHTML and server scriptlets is the script code block, which is defined by the <SCRIPT> tag. A given piece of script code can be a DHTML or server scriptlet depending on the type of layer you add.
The easiest way to write a server scriptlet is by using the Microsoft® Scriptlet Wizard mentioned earlier. The wizard will guide you through the various steps of defining the component by prompting you for registration information, code preferences, and a list of properties and methods. Figure 1 shows the first page of the wizard, which produces a text file with an .sct extension. The syntax is based on XML, an extensible markup language that includes the syntax of HTML. For more information about XML, refer to the Internet Client SDK documentation.
|
Figure 1: Microsoft Scriptlet Wizard |
The source code for a server scriptlet is composed of four main tags: <SCRIPTLET>, <REGISTRATION>, <IMPLEMENTS>, and <SCRIPT>. The <SCRIPTLET> tag is similar to the <HTML> tag and delineates all the XML source code for a scriptlet. Since a server scriptlet describes a COM object, it needs to store some identification data in the system registry. This information is found in the <REGISTRATION> block. By design, COM servers implement a certain number of interfaces. The interfaces supported by the server scriptlet are listed in the <IMPLEMENTS> section.
However, there are a few points worth clarifying about the <IMPLEMENTS> section. First, as you probably know, every COM server must implement the IUnknown interface. Most COM servers also add IClassFactory and IDispatch. These common interfaces do not need to be listed in the <IMPLEMENTS> section of a server scriptlet. Server scriptlets use a runtime module (contained in the previously-mentioned scrobj.dll) that provides a default implementation for a COM object's basic tasks, including registration and dynamic instantiation. The runtime module in the next generation of COM will let programmers extend the standard
behavior if needed. For more information on future developments
like COM+, check out Steve Zimmerman's Extreme C++ column in this issue.
Turning back to server scriptlets, the <IMPLEMENTS> block should enumerate the interfaces implemented by the component through script code. In the current beta, a server scriptlet can only be an automation object, and must implement the IDispatch or IDispatchEx interface. On a conceptual level, you create automation objects by defining their methods and properties. But to implement these interfaces you must write helper functions that have little to do with the methods and properties you want to expose. The runtime engine saves you from having to write code for functions, such as GetIDsOfNames or Invoke, that are technically part of the IDispatch interface.
Server scriptlets require programmers to code only what they really need, avoiding the quirky details of low-level COM programming. Automation objects require only the code for the methods and the properties. In general, you'd specify in the <IMPLEMENTS> section only the information needed by the runtime engine to expose a COM automation object to clients. Figure 2 presents a bare-bones server scriptlet, with a method called Method1 and a property named Property1.
Architecture of a Server Scriptlet
A server scriptlet is a COM object. Its only point in common with DHTML scriptlets is the internal code that provides the actual behavior of the component. This code is stored into a <SCRIPT> block and can be written using any recognized scripting language. Server scriptlets are just XML-based files with an .sct extension, but they appear to clients as COM objects thanks to the runtime module (scrobj.dll). Figure 3 illustrates the architecture of a server scriptlet.
|
|
Figure 3: Server Scriptlet Architecture
|
The runtime module exposes an automation interface to client applications in the traditional COM way: through IDispatch. This lets external modules invoke the documented functions of the interface. When an application calls an object, it uses the object's ProgID for identification. The ProgID is matched to a system registry entry which stores the path of both the runtime engine and the .sct file that provides
the behavior.
Scrobj.dll exposes all the interfaces listed in the <IMPLEMENTS> section of the server scriptlet file. As mentioned earlier, the current beta of the server scriptlet technology only supports the automation interface. In the months to come, you should see support for other COM interfaces. To implement these additional interfaces, you'll need a more flexible and extensible runtime engine. I'll discuss this more later.
Each time an external application calls one of the exposed automation methods, scrobj.dll loads the proper piece of script code from the linked .sct file. The link between the .sct file, scrobj.dll, and the external application is established through the system registry, and conforms to all the standard COM conventions.
Writing Your First Server Scriptlet
The purpose of server scriptlets is to provide functionality through COM automation. Because of this, server scriptlets look very different from DHTML scriptlets at runtime. The most obvious difference is in the user interface. A server scriptlet is a COM object designed to be run in a server environment and is not meant to provide graphics or visual effects. If you want to create something that looks like an ActiveX control, you should use a DHTML scriptlet instead. Future implementations of server scriptlets will provide support for any COM interface, including those commonly used to embed
components in COM-aware containers. When this comes
to pass, you will be able to write true ActiveX controls with
a script language.
DHTML and server scriptlets also differ in how callers refer to them. DHTML scriptlets are identified by name, while server scriptlets follow the convention for COM serversthey are identified by CLSID and ProgID. This information is specified in the <REGISTRATION> tag together with the version number and a description.
I stated earlier that DHTML and server scriptlets could share the same core code. Let's see how this is possible (even if this isn't the preferred use of server scriptlets). The following code is a minimal DHTML scriptlet that calculates the factorial of a number:
|
<html>
<script language=JScript>
var public_description = new Factorial();
function Factorial() {
this.Calculate = Calculate;
}
function Calculate(num) {
if( num < 2 ) return 1;
return num*Calculate(num-1);
}
</script>
</html>
|
You can use this same block of script code as the basis of a server scriptlet. This is demonstrated in Figure 4. If you compare the previous listing with the one shown in the figure, you'll notice that the server scriptlet doesn't include the public_description variable. The DHTML scriptlet uses this variable to keep track of the public functions exposed by the component. (For more information, see my Cutting Edge column in Microsoft Interactive Developer, January 1998.) There's no need for a variable like this in a server scriptlet since the automation interface detailed in the <IMPLEMENTS> tag serves the same purpose. However, if you add a public_description variable to a server scriptlet, you can also use it as a DHTML scriptlet.
The <REGISTRATION> block includes the following information:
|
<Registration
Description="Factorial"
ProgID="Factorial.Scriptlet"
Version="1.00"
ClassID="{4ac6e5c0-9f18-11d1-83d1-f49604c10000}">
</Registration>
|
This code snippet should be basically self-explanatory. Note that you must generate a new CLSID for each new component (as you would for any COM-based programming), and that the ProgID string is the name you
must pass to CreateObject or similar functions to create a new instance of the server. New CLSIDs can be obtained via special utilities like guidgen.exe, which is included in Microsoft Visual C++®, or generated by the Scriptlet Wizard.
Implementing the Automation Interface
When you implement an automation object, the <IMPLEMENTS> block lets you define the various methods and properties the component will make available to external callers. In the future, the same block will be used differently when you're not creating automation objects. While the actual
syntax is different, the <IMPLEMENTS> block stores the same information as the public_description variable in a DHTML scriptlet.
|
<implements TYPE=Automation ID=Automation>
<method NAME=Calculate>
<PARAMETER NAME=num/>
</method>
</implements>
|
The attribute called TYPE specifies the name of the COM interface handler I'll be using (more on this later). The <METHOD> block contains both the public and the internal name of the method. The attributes for these are called NAME and INTERNALNAME, respectively. The former is the name by which external callers can invoke the function. The latter is the name of the internal procedure that provides the required behavior. If INTERNALNAME is missing, then the runtime module assumes that the function is implemented through a routine with the same public name. Any parameters the method may need must be listed using the <PARAMETER> attribute. Note the use of the XML syntax that lets you replace the endtag with a final slash. In the previous example, I'm using
|
<PARAMETER NAME=num></PARAMETER>
|
This shortcut can be used only if the tag doesn't include child tags. A similar syntax is allowed for properties.
|
<implements TYPE=Automation ID=Automation>
<property NAME="Text" INTERNALNAME="mText"/>
</implements>
|
In this case the property "Text" is implemented through a direct variable that can be read and written without limitation. The name of the variable can be specified via the INTERNALNAME attribute. If left unspecified, it is assumed to be the same as the property itself, meaning you can't control how it is read and/or written.
However, this is not the only approach you can take to define a property; you can provide Get and Put functions for the property to gain control over its reading and writing. Of course if you do this, omitting the Get function makes the property write-only, and omitting the Put routine transforms the property in a read-only attribute.
|
<property NAME="Text">
<GET INTERNALNAME="DoGetText"/>
<PUT INTERNALNAME="DoPutText"/>
</property>
|
The INTERNALNAME attribute lets you specify the name of the Get or Put function. The default is the public name of the property prefixed by get_ or put_.
There's an evident similarity between this and the public_description object used by DHTML scriptlets. Briefly, let's see how to express the same properties and methods through a public_description.
|
this.get_text = DoGetText;
this.put_text = DoPutText;
this.Calculate = Calculate;
|
In describing the public interface of a server scriptlet, I've talked about properties and methods, but not events. In the COM world, events are implemented via specialized interfaces like IConnectionPoint, which are not yet supported in the server scriptlet runtime. Events in DHTML are part of the object model, and a DHTML scriptlet can raise events using this infrastructure. Nothing of the kind exists yet for server scriptlets.
Once you've entered the registration information and defined the public automation interface, you should make sure that the <SCRIPT> block of the server scriptlet includes entries for all the procedures referred to in the <IMPLEMENTS> section. You can use any script language you like, but mixing two or more could slow the component's performance because the runtime module will have to continuously switch between parsers.
Registering a Server Scriptlet
Before you can run and test a server scriptlet you need to register it, just as you would any other COM server. You can do this with the utility called regsvr32.exe that comes with Visual Studio, but the server scriptlet package installs its own registration utilityan early version of the one that is scheduled to ship with Windows NT® 5.0 and Windows® 98.
If you use the older version, you'll need to specify some additional switches on the command line:
|
regsvr32 scrobj.dll /n /i:MySS.sct
|
The updated regsvr32.exe accepts a simpler syntax:
|
When you install the server scriptlet package, it modifies the registry to add an item called "Register" to the context menu of .sct files linked to regsvr32. I manually added an Unregister item to the context menu. Future releases of the package may include this. To unregister a server scriptlet, launch regsvr32 with the /u option on the command line.
Registration for COM servers is usually accomplished by calling DllRegisterServer or DllRegisterServerEx. For server scriptlets, the runtime module exports the function, which reads the <REGISTRATION> block data from the specified .sct file and saves it in the registry.
Figure 5 shows how the information is stored in the registry. The layout is basically the same as for any COM server. The InprocServer32 key stores the name of the runtime module, the ProgID key stores the ProgID string, and the new ScriptletUrl key holds the .sct file name.
|
|
Figure 5: Information Stored in Registry Editor
|
Figure 6 displays two dialog boxes you may see when attempting to register your server scriptlet. The second window appears when registration succeeds. You'll see the first one under certain error conditions. I can't say exactly what the error code means, but it occurs in at least two cases: when you try to reregister a server scriptlet, and when the source code of the server scriptlet includes HTML-specific tags. If you change a server scriptlet's registration information and need to reregister it, you must first unregister it. Since a server scriptlet is a XML document,
not an HTML document, by design scrobj.dll only registers server scriptlets that are not HTML documents. Only the server scriptlet's XML schema is parsed, meaning the contents of any <HTML> or <BODY> tags are ignored.
|
|
Figure 6: Registering a Server Scriptlet
|
Interface Handlers
The server scriptlet architecture includes a type of module called an interface handler, which is a COM server that implements one or more interfaces. As mentioned, this first beta of server scriptlets offers only one interface handler: the automation handler that is part of scrobj.dll. Figure 7 illustrates the role of interface handlers.
|
|
Figure 7: Interface Handler
|
In the <IMPLEMENTS> section you declare all the COM interfaces whose functions you're implementing in the .sct file. In addition to this, you need a layer of binary code that provides a uniform COM-compliant interface to external callers. This codecalled an interface handleris necessary for each interface you want to implement in your server scriptlet. In the near future, you'll be able to write your own handlers. As of this writing, various teams at Microsoft are still working to formalize a general mechanism for creating custom handlers that can communicate between the server scriptlet and the client.
The ability to create interface handlers will have some interesting consequences. First, you'll no longer be limited to creating worker servers; you'll be able to support interfaces that require a UI, such as those needed for an ActiveX control. Second, it will let you do literally everything with script code. Using only script code, you will be able to manage compound files and desktop shortcuts, and
even implement shell extensions or namespace extensions.
Using a Server Scriptlet in Desktop Applications
To use server scriptlets in end-user applications, just use your development tool to create a new COM object. Let's consider a server scriptlet that accesses a database through the ActiveX Data Objects (ADO) interface and returns an array containing the records. The source code is in Figure 8.
|
|
Figure 9: DHTML and Server Scriptlets
|
The server scriptlet exposes three properties that fully define the data source: Database, Table, and Field (see Figure 9). The method GetData reads in the data and returns the specified recordset in the form of an array. This server is uniquely identified by the ProgID and the CLSID.
|
ProgID="ADOScriptlet.Scriptlet"
ClassID="{5ac6e5c0-9f18-11d1-83d1-f49604c10000}"
|
To create a new instance of this object you can call CreateObject if you're using Visual Basic or VBScript, and CreateOleObject if you're working
with Delphi. With JScript you need to use this syntax:
|
obj = new ActiveXObject( "ADOScriptlet.Scriptlet" );
|
If you correctly registered your server scriptlet, then the ProgID points to the CLSID, and in turn to the actual path of scrobj.dll and the .sct file (see Figure 5 for a snapshot of the registry).
|
Figure 11: Server Scriptlet from Visual Basic
|
Using the server scriptlet from a Visual Basic-based program is also easy. Figure 10 shows the code that generates the application shown in Figure 11. The first beta of the server scriptlet package has a nasty bug that curiously occurs only with Visual Basic. You always need to pass an argument to a method, even if it doesn't take any. You can't call
|
Set a = g_sctADO.GetData("")
|
Hopefully, this bug should be fixed in later betas.
When I examined DHTML scriptlets (see Cutting Edge in Microsoft Interactive Developer, January 1998), I briefly discussed the Microsoft Scriptlet Control that lets you host these components in desktop applications. This control only works with Visual Basic 5.0 and Microsoft Internet Explorer 4.0; it can't be used with Visual C++. The control is also unusable in Delphi, though it succeeds in creating a valid wrapper. These problems arise from the lack of a sufficiently uniform and general layer of COM-compliant code.
|
|
Figure 12: Server Scriptlet with Delphi 3
|
Fortunately, these problems don't occur with server scriptlets. In fact, you can even use server scriptlets with Delphi 3, as demonstrated in Figure 12.
Summary
Server scriptlets make writing automation servers a snap, and the technology will become even more powerful when interface handlers are supported. Perhaps the most compelling use of server scriptlets, however, will be with ASP. Server scriptlets were conceived as a way to help ASP developers create IIS-compatible components.
This month's source code consists of four different projects: the factorial scriptlet, plus three demo applications that use the ADO scriptlet with HTML pages, Visual Basic, and Delphi 3. Remember to register the server scriptlets before using the demonstration projects. To do so you need the server scriptlet package available from http://msdn.microsoft.com/scripting/.
|
From the May 1998 issue of Microsoft Interactive Developer.