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
This content is no longer actively maintained. It is provided as is, for anyone who may still be using these technologies, with no warranties or claims of accuracy with regard to the most recent product version or service release.
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/en-us/library/ms950396.aspx. 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/en-us/library/ms950396.aspx.
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.