Script Components
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.
Net Assets
Integrating Client- and Server-side Scriptlets
By Peter Vogel
It's a common assumption that if you're creating an
object, you'll have to compile the code you write. Microsoft's scripting
technologies turn that assumption on its head. With scripting
technologies, you can create components with Notepad. Once you've written
your code, you simply save the resulting file and you've created a
component that can be used from Visual Basic, Active Server Pages, or any
other COM development tool.
Although they can be used from any development tool,
script components really shine in a Web-development environment. As I'll
show you in this article, you can use script components to create the
equivalent of ActiveX controls for Internet Explorer by using DHTML
scriptlets. An even more powerful technology is Remote Scripting, which
allows code executing in the client's Web browser to reach back through
the Internet and run a component residing on a Web server. Remote
Scripting, which works in both Netscape Navigator and Internet Explorer,
helps break down the barriers between client- and server-side processing
in Web applications.
DHTML Scriptlets
The simplest objects to create with script are DHTML
scriptlets, which can be used in Internet Explorer. A DHTML scriptlet is a
kind of ActiveX control created from script and HTML. However, because
script code doesn't operate "out of the sandbox," it doesn't violate the
rules about interacting with the computer that an ActiveX Control does. As
a result, no security adjustments have to be made to the browser to use
DHTML scriptlets. Effective use of scriptlets can lower the costs of doing
more client-side processing by creating reusable objects that can be used
on any Web page. Client-side scripting makes your pages more interactive
(no more trips to the server just to display an error message) while
reducing the load on your server.
A DHTML scriptlet is simply a text file that can include
any combination of HTML, DHTML, and script. You can choose to expose some,
or all, of the script routines that make up this file, as either methods
or properties, by prefixing the name with the word "public." The result is
a file that, when included in a Web page, produces an object that can be
used by the code on the Web page. If your text file includes HTML, the
object also has a user interface that will be incorporated into the Web
page that uses the scriptlet. In FIGURE 1, for instance, you can't tell
that the TextBox with its prompt is part of a scriptlet, while the command
button is part of the page that is hosting the scriptlet.
FIGURE 1: A Web page displaying a scriptlet.
The code in FIGURE 2 defines the simple scriptlet shown
in FIGURE 1. The scriptlet consists of a TextBox and a routine to validate
the contents of the TextBox. Because the method name has the prefix
"Public_", the routine will appear as a method of the object defined by
the code in FIGURE 2.
<HTML>
<BODY>
Please Enter Your E-Mail Address:
<INPUT Type=Text Name=txtEMailAddress Id=txtEMailAddress>
<SCRIPT Language=VBScript>
Function Public_CheckEMailAddress()
CheckEMailAddress = True
If Instr(document.all.txtEMailAddress.value,"@") = 0 Then
Public_CheckEMailAddress = False
End If
End Function
</BODY>
</HTML>
FIGURE 2: Code for the scriptlet shown in FIGURE 1.
You add a scriptlet to a page as you would an ActiveX
control or Java applet - by using the <OBJECT> tag. Assuming that
the scriptlet in my previous example was in a file named MyScriptlet.HTM,
I could add it to a Web page using the following HTML:
<OBJECT Id=GetEMail Type=text/x-Scriptlet
Style="HEIGHT: 100px;LEFT: 0px; TOP: 0px; WIDTH: 100px">
<PARAM Name="URL"
Value="http://MyServer/MySite/MyScriptlet.HTM">
</OBJECT>
When the browser sees the < OBJECT> tag, it starts
to download a program. In this case, the program being downloaded is a
scriptlet, as indicated by the < OBJECT> tag's Type attribute. The
< PARAM> tag provides the browser with the location of the scriptlet
to be downloaded. This < OBJECT> tag's Id attribute then assigns the
name GetEMail to the object, once it's downloaded and ready to use
on the client.
Because the < OBJECT> tag that invoked the
scriptlet assigned it the name GetEMail, I can call the scriptlet's
CheckEMailAddress function from the page that's hosting it, like
this:
bolResult = document.all.GetEMail.CheckEMailAddress()
There is no need to use New or CreateObject to load my scriptlet
object - the < OBJECT> tag takes care of that. Effectively, the <
OBJECT> tag creates a COM object from the text in the HTML page
MyScriptlet.HTM.
Properties and Events
To create a property in a DHTML scriptlet, you add the
prefixes "Public_Get_" and "Public_Put_" to the name of the property.
These properties identify the routines that will act as the Get and Put
routines for the property. Each time a program reads the scriptlet's
property, the Get routine will run. Each time a program changes the value
of the scriptlet's property, the Put routine will be run.
The following code, for instance, defines an
EMail property that retrieves and updates the value of the
scriptlet's txtEMailAddress
TextBox. I've created this property by writing a Put routine that updates
the TextBox with whatever value is passed to it, and a Get routine that
(like a function) returns the value of the TextBox:
Sub Public_Put_EMail(strAddress)
Document.all.txtEMailAddress.Value = strAddress
End Sub
Sub Public_Get_EMail()
Public_Get_EMail = document.all.txtEMailAddress.Value
End Sub
With the preceding code added to MyScriptlet.HTM, my
scriptlet would have a property named EMail to go with its CheckEMailAddress property. The
property could be used from the code in the hosting page like this:
document.all.GetEMail.EMail = "peter.vogel@phvis.com"
Msgbox document.all.GetEmail.EMail
In addition to simple data types, such as strings and
numeric values, you can use the Get and Put routines to return and accept
objects from your scriptlet.
Events can be fired from your DHTML scriptlet by using
the RaiseEvent method of the External object. The
External object is provided by Internet Explorer, and is accessed
through the External property of the Document Object Model's
Document object. The RaiseEvent method accepts two
parameters: the name of the event, and a reference to an object
(typically, the object that triggered the event).
The following code, used inside a scriptlet, would fire
an event when the edit on txtEMailAddress fails. The code
passes a name for the event (BadEMail) and a reference to the
EMailAddress TextBox to the hosting page:
<SCRIPT Language=VBScript>
Function Public_CheckEMailAddress()
Public_CheckEMailAddress = True
If Instr(document.all.txtEMailAddress, "@") = 0 Then
Window.External.RaiseEvent _
"BadEMail", document.all.txtEMailAddress
Public_CheckEMailAddress = False
End If
End Sub</SCRIPT>
To catch the events fired by RaiseEvent, the
hosting page must implement an OnScriptletEvent routine. This
routine will be called for all events fired by the RaiseEvent method in a scriptlet.
In a page that hosts a scriptlet, you would add this code to catch events
from the scriptlet GetEMail:
Sub GetEMail_onScriptletEvent(strEName, objEObject)
If strEName = "BadEMail" Then
MsgBox objEObject.Value & _
" must have an @ sign to be a valid address."
End If
End Sub
The name of the event routine uses the same convention
as Visual Basic: the Id attribute of the scriptlet's < OBJECT> tag,
followed by the name of the event (OnScriptletEvent), joined by an
underscore. The two parameters passed to the routine are the same ones
passed by the RaiseEvent method: the name of the event, and a
reference to the object that fired the event. In this example, the code
checks the name of the event, and, if it's "BadEMail," displays the
current value of the TextBox and a message:
Sub GetEMail_OnScriptletEventl(Ename, EObject)
If Ename = "BadEMail" Then
MsgBox EObject.Value & _
" is not a valid e-mail address."
End If
End Sub
Server Scriptlets
Not only do DHTML scriptlets not have to be compiled,
they don't even need to be registered. Instead, the Web page finds the
scriptlet file using the information in the < PARAM> tag. You can,
however, create script components that can be registered.
When a component is registered, it acts more like a
compiled component. A program that uses a registered script component will
load the component the same way a compiled component is loaded: by using
the New or CreateObject keywords. The New and CreateObject
keywords are passed the object's ProgID (e.g. ServerComponent.Object), so the
necessary registry entries for the script component must be made in the
Windows registry to support finding the object by its ProgID.
As an example, script components designed to be run on
the server from the script code of an ASP page need to registered so they
can be accessed using the CreateObject method of the ASP
Server object. To create a component that can be registered, you
need to add some XML tags to the top of the file containing your
component's code. This file should have the extension .wsc, for Windows
Script Component. This XML contains code to define an object that has a
LogFileName property, a FileOpen method, and an OpenFailed event, as shown in
FIGURE 3.
<package>
<comment>
Author: Peter Vogel
Date: 99/01/04
</comment>
<component id="MyScriptComponent">
<registration
ProgID="MyScriptComponent.AuditLog"
deScription="A sample object"
version="1.0"
CLSID="{bcde9140-5068-11d3-a8c8-00107a901a5f}"/>
<public>
<property name="LogFileName"/>
<method name="WriteLog"/>
<event name="OpenFailed"/>
</public>
<SCRIPT language="VBScript">
Dim LogFileName
Function WriteLog(strMessage As String)
On Error Resume Next
Set objFSys = _
Server.CreateObject("Scripting.FileSystemObject")
Set objFile = objFSys.OpenTextFile(LogFileName, 8, , 0)
Set objStream = objFile.OpenAsTextStream(8)1
objStream.Write strMessage
objFile.Close
If Err > 0 Then
FireEvent "OpenFailed"
End If
End Function
</SCRIPT>
</component>
</package>
FIGURE 3: An XML file that defines an object with a
LogFileName property, a FileOpen method, and an OpenFailed event.
If the amount of XML in FIGURE 3 seems daunting, don't
be dismayed. Microsoft provides the Script Component Wizard, which will
generate most of the XML entries for you (see FIGURE 4). The GUID that
appears in the CLSID tag can be generated using two other tools from
Microsoft: GUIDGen.exe (a Windows program), or UUIDGen.exe (a DOS
command-line tool). All three can be downloaded from http://msdn.microsoft.com/.
FIGURE 4: Microsoft's Script Component Wizard.
Registering a Script Component
Assuming you've installed Internet Explorer 5.0,
registering your script component is easy. All you need to do is
right-click on the .wsc file that contains your script component code, and
select Register from the popup menu (see FIGURE 5).
FIGURE 5: Registering a script component.
You can also register a component using the version of
Regsvr32.exe that ships with the script component package. This version of
Regsvr32 will accept a URL that points to your component:
regsvr32 file:\\c:\AuditLog.wsc
If you're installing your component on a computer that
doesn't have the latest version of Regsvr32, you can register your
component through the scripting run-time DLL (which must be registered on
the computer for your component to work). This example registers the
scripting run-time engine, then uses it to register the component:
regsvr32 scrobj.dll
regsvr32 scrobj.dll /n /i: file:\\c:\AuditLog.wsc
You can even generate a type library for your component.
If you do, your component will support the IntelliSense drop-down lists
when you use it from a full-featured development tool, such as Visual
Basic. To create a type library, select Generate Type Library from the
file's popup menu. This will create a file named Scriptlet.tlb, which you
should rename to match your .wsc file.
You can also generate a type library from within your
script component by using the scriptlet.TypeLib object. In my
sample XML code, the <registration> tag appears as a single tag.
However, the <registration> tag can also be used as a paired tag. As
a paired tag, <registration> can enclose script code that will be
executed when the component is registered (see FIGURE 6). If you place a
routine named Register inside the <registration> tag, the
routine will be run as part of registering your component. The code in
FIGURE 6, for instance, will create a type library named
MyScriptComponent.tlb as the component is registered (again, the GUID can
be generated using either GUIDGen or UUIDGen).
<registration
ProgID="MyScriptComponent.AuditLog"
CLSID="{bcde9140-5068-11d3-a8c8-00107a901a5f}">
<SCRIPT language="VBScript">
Function Register()
Set gtl = CreateObject("Scriptlet.TypeLib")
gtl.Path = "c:\MyScriptComponent.tlb"
gtl.GUID = "{a1e1e3e0-a252-11d1-9fa1-00a0c90fffc0}"
gtl.Name = "ScriptComponents"
gtl.Write
End Function
</SCRIPT>
</registration>
FIGURE 6: Placing the Register routine inside the
<registration> tag will run the routine as part of registering the
component.
Once it's been registered, a script component can be
used like any other COM component. The following code, in an ASP page,
loads the sample script object I've been using in my example, sets the
name of the audit log through the LogFileName property, and writes
a message using the WriteLog method:
Set objLog = _
Server.CreateObject("MyScriptComponent.AuditLog")
objLog.LogFileName = "c:\AppLog.txt"
objLog.WriteLog Date()
Set objLog = Nothing
Remote Scripting
The real power of scripting comes with Remote Scripting.
With Remote Scripting, you can have client-side code, running in the
browser, execute a component that runs on your server. Because the
component is executing on your server, it has the ability to access
databases, or other server-side resources, and return results to your
client. And, if you thought the XML required for a registered script
component looked ugly, you'll be glad to know that an object accessed
through Remote Scripting doesn't need to be registered.
While you need to be running IIS 4.0 or better on your
server, you should be able to use any browser with Remote Scripting
because the underlying technology is implemented using a Java applet. If
you intend your client-side code to run in a browser other than
Internet Explorer, however, you should be writing your code in JavaScript.
However, I'll stick to VBScript for the examples in this article.
Before you begin to use Remote Scripting, you need to
download some support files (and some updates to IIS) from the Microsoft
scripting site at http://msdn.microsoft.com/scripting.
The support files include three items:
- RS.HTM: This file contains client-side routines required by Remote
Scripting.
- RS.ASP: This file contains the server-side routines.
- RSProxy.class: A Java applet that the routines in RS.HTM will
download to your client. It also handles communication with the
server-side component.
The following examples assume these files have been
placed in a directory named ScriptLibrary immediately under the root
directory for your Web site. If you're using Visual InterDev as your
development tool, this directory is automatically created for you as part
of starting a new Web project. In the examples, I'll first walk you
through what you need to add to your server-side component, and then look
at what's required in the client-side page.
On the Server
Once you've set up the directory and placed the support
files in it, you can create a file to hold your server-side object. Your
server-side code must be in a file with an ASP extension. Because this
file never goes to the browser, it shouldn't include any HTML. The file
must, however, include the following lines, which incorporate some
standard routines into your file:
<%@ Language=JScript %>
>!--#INCLUDE FILE="../_ScriptLibrary/RS.ASP"-->
With the required plumbing taken care of, you can add
any functions or subroutines you want to use to define methods. The
process of defining methods is slightly more complicated than with DHTML
scriptlets, or server components. First, you must create a class using
VBScript 5.0's Class statement. You then place your object's
routines inside the Class declaration.
With the class defined, you must then make it available
to the clients that call your server-side object. You do this by creating
a variable named public_description, and setting it to point to a
new instance of your class. Finally, you must initialize Remote Scripting
by calling the RSDispatch routine that was added to your page from
RS.ASP. This example creates a class named DBManager with a method
named MakeConnection, as shown
in FIGURE 7.
<SCRIPT Language=VBScript RunAt=Server>
Dim public_deScription
Set public_deScription = New DBManager
RSDispatch
Class DBManager
Public Sub MakeConnection()
Set conn = _
Server.CreateObject("ADODB.Connection")
conn.Open "File Name=c:\MyDatabase.UDL"
Set Session("Connection") = conn
End Function
End Class
</SCRIPT>
FIGURE 7: This example creates a class named
DBManager with a method named MakeConnection.
On the Client
The Web page that will use your server-side object needs
to initialize Remote Scripting. The process is similar to what you saw on
the server. First, a <SCRIPT> tag adds some standard routines to
your Web page from the support file RS.HTM. This <SCRIPT> block will
cause the routines in RS.HTM to be included in your Web page:
<SCRIPT Language="JavaScript"
src="../_ScriptLibrary/RS.HTM">
</SCRIPT>
Your next <SCRIPT> block must call the
RSEnableRemoteScripting routine in the RS.HTM page to initialize
Remote Scripting on the client:
<SCRIPT Language="text/vbScript">
RSEnableRemoteScripting "_ScriptLibrary"
</SCRIPT>
With those entries made, the client-side program can
then create the server-side object using the RSGetASPObject
function. RSGetASPObject accepts a single parameter: the URL to the
ASP page containing your server component. After the object has been
created, calling its methods and properties works the same way as any
other object.
The following code, for instance, creates a server-side
object from a file named MyObject.ASP, then calls the object's
MakeConnection method:
Set objConn = RSGetASPObject("MyObject.ASP")
objConn.MakeConnection
If a server component's method returns a value, your
client-side code must accept an object from your server-side object. This
is the Call object, and its return_value property will
contain the value returned from the server-side method. As an example, the
following code accepts a Call object from the
GetCustomerName method and displays the result:
Set objCall = objConn.GetCustomerName(strCustomerNumber)
Msgbox "The customer name is " & objCall.return_value
You can skip using RSGetASPObject by using RSExecute. RSExecute
accepts the URL to the object, the name of the method, and an optional
list of parameters. Calling a GetCustomerName method using
RSExecute would look like this:
Set objCall = RSExecute("../MyObject.ASP", _
"GetCustomerName", "strCustomerNumber")
In addition to result_value, the Call object has a number of
useful properties:
- Status: You should check this after each RSExecute call. A value of -1
indicates that the call failed, while 0 indicates successful completion.
- Message: This property contains either "Completed" for
successful calls, or an error message for unsuccessful calls.
- Data: Contains XML information from the object about any
error condition.
Other properties and methods of the Call object
relate to calling server objects asynchronously, which can only be done
using JScript. Using Remote Scripting can create a security hole on your
server. One of the disadvantages of client-side code is that it's visible
to any user through the View | Source menu selection on their browser.
This allows users browsing your code to see how your server-side objects
can be accessed from client-side code. With that information, any user
could create a page that would invoke your server-side script component.
There are three steps you should take to ensure that
your Remote Scripting component is safe. First, you'll want to ensure that
your server component can't be used to harm your site or access
information you don't want distributed. A component limited to retrieving
catalog information probably wouldn't be a risk to any site, for instance.
Second, you can package your calls to your server-side objects in a DHTML
scriptlet. Scriptlet code isn't visible from View | Source, making it more
difficult for others to see how your script works. Finally, Microsoft's
Script Encoder, also available on Microsoft's scripting site, allows you
to encrypt your script code.
Conclusion
I've really just scratched the surface of the scripting
technologies here. I haven't discussed the Windows Script Host, for
instance, which lets you run scripts from your Windows desktop.
Effectively, with the Windows Script Host, scripting provides Windows with
a batch language. And, although I've used VBScript in my examples here, I
haven't shown you how to use any of the other scripting languages,
including JScript (Microsoft's version of JavaScript) or PerlScript. Nor
did I discuss DHTML Behaviors, which let you add new methods, properties,
and events to existing HTML tags. There's a wealth of tools in this
technology and you can get more information on all of them (and download
the necessary support files) from the Microsoft scripting technologies
site at http://msdn.microsoft.com/scripting.
Peter Vogel (MBA, MCSD) is a principal in PH&V
Information Services, specializing in system design and development for
VBA-based systems. PH&V is involved in creating intranet- and
component-based applications. Peter is also the Editor of the Smart Access
newsletter (the authority for in-depth technical information for Microsoft
Access developers). Peter also wrote The Visual Basic Object and Component
Handbook for Prentice Hall from which this material is, in part, drawn. He
teaches Access, Visual Basic, and database design for Learning Tree
International and wrote their Web application development course. His
articles have appeared in every major magazine devoted to VB-based
development and in the Microsoft Developer Network Libraries. Peter also
sits on the editorial advisory board for the IT Consultant newsletter.