Windows Script Components |
Windows Script Components (WSC), formerly known as Scriplets, is a technology
for developing powerful COM components in an easy fashion. WSC's can be authored
in any scripting language that has implemented the ActiveX Scripting Interfaces,
which means that units of PerlScript code can be encapsulated as Windows Script
Components. The Component Object Model (COM) is a language-independent and
object-oriented programming model. It is not, however, a programming language,
and it does not demand that a new style of programming be learned. It is a
binary standard that enables software to be implemented as units called
components. The component can be authored in any programming language or
scripting language provided that the language supports the standard defined by
the COM. After the binary unit has been built, it can communicate with other
units that were produced in any other language, on the same machine or on remote
machines provided that the units were written as COM components. COM is both a specification and an implementation. And being both a
specification and an implementation, the COM defines a standard for how the
components are created and how they communicate - namely as objects. As a
result, the specification solves the implementation issues of the following key
issues: When wondering what makes up a component, recall that COM is an
object-oriented programming model that requires every component to be
implemented as an object, so from this we know that we will be talking about
classes, methods, properties, and such entities that belong in an
object-oriented programming model. However, let’s start at the bottom level
and move our way up from interfaces, to classes, and to objects.
Windows Script Components (WSC)
Be warned, reader, a lengthy introduction to the Component Object Model (COM)
stretches until the topic of Windows Script Components. Please move directly to
the topic of Windows Script Components if you feel that you understand the COM;
the COM is essential to WSC, thus it is necessary to cover the fundamentals of
the COM.
The Component Object Model
As many see it, the Component Object Model is elegant and powerful. The basic
yet ingenious programming model provides the foundation for
application-development. The power resides in that everything built on the same
foundation can communicate with each other without any restrictions such as
programming languages or platforms. When developing an application devoted to
COM, components should encapsulate every functionality that the application
requires. A task performed by the application would be executed by a component,
and these components would be reused in other and future applications. For
example, a Perl component can contain the process of sending an email or
performing a series of regular expressions on text. The component can then be
used to provide functionality for an Active Server Pages application or a
Windows desktop application.
In summary, although the language in which a component is generated may vary
from component to component, a binary standard overcomes the limitation of
programming in different languages. It does not care about anything else but the
binary form of the machine code that the once source code was turned into and
the fact that is implemented into COM. In this sense, the component is not a
regular script or executable, but a black box that performs one task and returns
the projected result. Multiple languages can be used for one application because
COM provides a binary standard. A binary standard also results in
platform-independence provided that multiple platforms support COM. Units of
code that are used in COM are known as components, and on Windows a component is
normally file with the extension .dll; however, there are offshoots such as
Windows Script Components, which have the extension .wsc. The purpose of the
component is to perform the one task that it has been programmed for, and
through COM, it can communicate with other components, languages, and platforms.
A Standard And An Implementation
Although the parts above are involved in the specification of how to implement
components, the programmer normally does not have to worry much about them. A
software development tool that holds high class will create a skeleton for a
component, and leave it up to the programmer only to plug in the code in the
right places. Thanks to this, the code itself can be developed, debugged, and
experimented with as usual. And then when the code is finished, it is placed in
the skeleton, the right buttons are clicked, and the component pops out.
The Parts of A Component
COM Interface
When the component is implemented, it exposes its features through one or more
interfaces. The interface is a specification of a collection of methods and
properties that are related to each other, so when a component is called, it
must be called through one of its interfaces. In general, this means that an
interface relates to an operation such as verifying a credit-card number or
sending an email. Because the component may expose several interfaces and the
method-calls pass by way of the correct interface, the interface that is called
must be identifiable. It is a fact that names easily clash, and especially with
a high pace of component development, so another solution is provided. An
interface uses a globally unique identifier (GUID) which is a 128-bit integer.
And through the GUID, the interface is identified to the COM.
COM Class
Next, the COM class implements, or maybe better 'inherits', one or more COM
interfaces. A Class ID (CLSID) uniquely identifies the COM class, and while the
interfaces are at the base level of the COM architecture, the COM class is on
top of the interfaces - one step close to the application. The main purpose of
the COM class is to generate, or instantiate, the COM object that represents the
functionality of the unit of code.
COM Object
As previously mentioned, this part of COM simply is the instance of a COM class,
or the entity from which all the features of the COM class are accessed.
COM Component
In the midst of everything, where does the component fit into the picture? It
can be easily summarized as that the component is used to include a COM class by
identifying it by the CLSID, create an instance of the COM class as the COM
object, and then let the application make use if the COM object on the machine.
Scripting Languages and Automation Automation, formerly OLE automation,
makes it easier for the masses to access COM object servers such as components.
In terms of a scripting language, the only way for a COM component to make its
features available is through Automation. Automation is one level higher up than
COM, in other words one step closer to the application, and it is the technology
that lets software packages such as Microsoft Office expose its functionality in
an object-model for scripting languages that support Automation.
In the same manner as large applications like MS Office are exposed, smaller COM
components are made available to a scripting language through Automation, as
well. Without it, the scripting language would necessarily have to know all the
interfaces of an object that it wants to use before calling it - too much
information to build into a language.
Instead of bloating a language with junk, COM defines the standard for accessing
COM object servers: Automation. In turn, Automation makes sure that a standard
interface that allows, in our case, script-access to the component is always
available for object-access. This standard interface used in Automation is
called a dispatch interface, dispinterface, or automation interface.
The Dispatch Interface
A dispatch interface is somewhat different than the standard COM interface.
Methods are associated with dispatch IDs (dispids), and methods have been
provided for reading and writing the values of properties - the access to the
data members in the underlying data structure.
The standard dispatch interface for automation is called IDispatch. Through
IDispatch, a component can expose as much as it wants to expose. Then in order
for Perl to gain access to components, the interpreter needs to support an
object-oriented programming model, have the ability to call methods through
IDispatch, know how to return errors, and be able to destroy objects.
A method that belongs to an interface of a COM component is retrieved by the
dispatch interface and in Perl this can occurs by what is known as late binding.
A dispatch method called GetIDsOfNames is called with the name and the
parameters of the method of the COM interface that is called. The dispatch ID of
the method is returned. Next, the Invoke-method of IDispatch is called with all
the processed information returned from GetIDsOfNames, and from that call the
result is returned in an output parameter. This method is called late binding,
and there are two things to notice about it:
When not using late binding, the application already is aware of all the
dispatch IDs and does not call GetIDsOfNames at all. This is called early
binding since the information needed for using the components have been built
into the application and are used at run-time.
Perl uses late binding, and the process of late binding is simple. First, a
moniker is required, which is a name that uniquely identifies the COM object.
The moniker is then used to locate the object, which next is either in running
state or put into running state. When in running state, the server application
can access the interfaces of the COM object, and when that is completed, an
interface pointer is returned to it. The dispatch interface that makes COM
objects accessible to a scripting language is implemented as a COM interface
that uses IDispatch. And the IDispatch functions of this interface only calls
the methods that are laid out in the COM interface, and these two interfaces are
called dual interfaces because an application that knows only early-binding can
access it while another language that knows only late-binding can access it, as
well.
Although the implementation of all of the above hidden to Perl, there are some
things to know. In most cases, Automation will accept and convert scalar data
into the correct variant-type. However, it might be necessary to specify the
type of data, and to solve this issue and convert Perl data types to the
variants used.
In Perl, Automation and creation of Automation objects is provided by the
Win32::OLE module, Furthermore, Variant data types can be converted by the
Variant() method or the OLE module although Perl seamlessly takes care of most
of the conversion needed.
VT_EMPTY
No Value
VT_NULL
Null Value
VT_I2
2-byte integer
VT_I4
4-byte integer
VT_R4
4-byte real value
VT_R8
8-byte real value
VT_CY
Currency
VT_DATE
Date
VT_BSTR
Binary string
VT_DISPATCH
Automation object
VT_ERROR
Error code
VT_BOOL
Boolean value
VT_VARIANT
Variant
VT_UNKNOWN
IUknown Pointer
VT_UI1
Unsigned 1-byte character
VT_BYREF
Describes the data as passed by reference
VT_ARRAY
An OLE Safearray
Creating Windows Script Component's
When it comes to writing a WSC, it is a surprisingly quick and easy process. In
fact, anybody who has written an ASP page with some script commands and HTML
will have completed a WSC in less than five minutes. And although .wsc files are
Extensible Markup Language (XML) files, no previous experience with XML is
necessary in order to build a successful component.
Ten Easy Steps
In order to create a WSC, you can use the Windows Script Component Wizard. The
wizard will produce a valid skeleton for your WSC, and all you will have to do
is enter your PerlScript. Let's create a simple WSC that can be instantiated
within a script and holds the basic functionality of being passed a string and
retuns the reversed string, so follow me on these steps.
Implementing the PerlScript
Following the simple steps above will create a nice skeleton to hold your code.
With the exception for the classid, which is unique each time one is generated,
it will look as illustrated.
<?xml version="1.0"?>
<component>
<registration>
description="Easy"
progid="Easy.WSC"
version="1.00"
classid="{74bb1ba9-2e69-4ad6-b02c-c52f3cbe153b}"
</registration>
<public>
<method name="SayHello">
</method>
</public>
<script language="PerlScript">
<![CDATA[
]]>
</script>
</component>
As seen above, you have a Window Script Component that is an XML file. The first
declaration of the WSC enables strict XML. In that mode, the elements and
attributes are case-sensitive, and attribute values must be enclosed within
single quotes or double quotes. You may omit the XML declaration on top of the
document and it will not be compiled as strictly, but in these examples, we will
stick with XML conformity and leave the declaration in each document. Note: XML
elements is that they, like HTML, have tag pairs like
"<registration>" and "</registration>".
Secondly, you have a component element. This element is used to enclose each
component. You will place one at the beginning, and one at the end. As an
exception to the rule, there is an element that has a higher priority than the
component, and that element must be used whenever you keep more than one
component in your WSC file. Is is the package element. It will as a single
element enclose all components; however, as mentioned, it is not required when
you have one component within the file.
Next, the registration element contains the information about your component
such as the progid, classid, description, and version number. Description is a
string in which you can write a short abstract summary the funcitonality of your
component. The progid is used by the program which creates an instance of your
component, and the version number should be incremented if you release a new
version of your component. The version number can also be appeneded to the
progid as as Easy.WSC.1.00 when creating the instance of your component.
After the registration element, the data and functionality that the component
expose are defined. The public element will hold properties, methods, and
events. We declare a method by the name "SayHello" and then skip on
down to the script-elements. As you can tell, there is no source code, so we
need to fill that out. In the empty space, enter the following:
sub SayHello {
my($param) = shift @_;
return reverse($param);
}
Finally, your script file will look like this:
<?xml version="1.0"?>
<component>
<registration>
description="Easy"
progid="Easy.WSC"
version="1.00"
classid="{74bb1ba9-2e69-4ad6-b02c-c52f3cbe153b}"
</registration>
<public>
<method name="SayHello">
</method>
</public>
<script language="PerlScript">
<![CDATA[
sub SayHello {
my($param) = shift @_;
return reverse($param);
}
]]>
</script>
</component>
Registering the Component
Now, it's time to register the component on the system so that it can be used.
There are two ways to do this and we assume that the file is saved as
c:\easy.wsc.
Either one of the above methods for registering a component should notify you
upon success or failure. After registering the component, you can use it from
within Active Server Pages by authoring a small script.
<%@Language=PerlScript%>
<%
$obj = $Server->CreateObject('Easy.WSC');
$retval = $obj->SayHello("Hello World");
$Response->Write($retval);
%>
WSC with Properties and Notification
Next, let's look at how to get a few properties included in the component, too.
The following example will display how to read and write properties. It also
includes comments, and, in addition, a custom subroutine is run when the
component is registered and unregistered. What will happen is that when the
component has been registered, a message box pops up with the text "Windows
Script Component says: First.WSC has been registered!" and a similar
tailored message when the component is unregistered by either oone of the ways
previously shown. So, open up the Windows Script Component wizard, again, and
this time enter the read/write property "YourName", and the method
"SayHello." The property is a global variable, and it internally is
read and written by subroutines that implement the functionality needed for
performing the given operation. The property can either use "get" and
"set" attributes that point to the mentioned subroutines or there can
be separate "get" and "set" elements as in the example.
Their values point to internal Perl subroutines that do their thing on the
property. It is a simple example, and it is as follows.
<?xml version="1.0"?>
<component>
<registration>
description="First"
progid="First.WSC"
version="1.00"
classid="{d0ccb637-bd0c-4c90-a4bd-7473f499d35a}">
<comment> This makes the messagebox pop up on
registration and unregistation </comment>
<script language="PerlScript">
<![CDATA[
use Win32;
sub register {
Win32::MsgBox('Windows
Script Component says: First.WSC has been registered!');
}
sub unregister {
Win32::MsgBox('Windows
Script Component says: First.WSC has been unregistered!');
}
]]>
</script>
</registration>
<comment> The methods and properties to expose to the data consumer
</comment>
<public>
<property name="YourName">
<get
internalName="hiddenGetProperty"/>
<put
internalName="hiddenSetProperty"/>
</property>
<method name="SayHello">
</method>
</public>
<comment> The code that implements the functionality of the component
</comment>
<script language="PerlScript">
<![CDATA[
use vars qw($YourName_Property);
sub hiddenGetProperty {
return $YourName_Property;
}
sub hiddenSetProperty {
my $param = shift;
$YourName_Property = $param;
}
sub SayHello {
return "Hello
$YourName_Property!";
}
]]>
</script>
</component>
XML Element Reference
Windows Script Components use XML to mark up the definition of the component and
what is exposed by the component. Listed below are the elements that are valid
XML elements for use within WSC components..
The Component Element
The component element is used to define the beginning and the end of the
components. It encapsulates all other WSC tags as illustrated.
<component>
.
.
.
</component>
You can also set a boolean value of true (1) or false (0) for error checking or
debugging by using <? component error="true"
debug="true" ?>
In case your file will contain more than one component, you use a <component
id=componentID>
element for each, and you are required to then enclose
all components within a <package> element.
<package>
<component id="ComponentA">
.
.
.
</component>
<component id="ComponentB">
.
.
.
</component>
</package>
The default value for the component ID is ComponentCoClass, and when you define
your own, either to identify the components or for generating a type library,
the name must be unique and it must begin with a letter and contain no spaces.
When using more than one component within a package, you can create instances of
the other component within the current component by calling the createComponent(componentID)
function.
The Registration Element
<registration> contains the necessary information in order to successfully
register the component as a COM component, and it has two ways of writing.
Syntax:
or
<registration
progid="progID"
classid="GUID"
description="description"
version="version"
[remotable=remoteFlag]
/>
<registration
progid="progID"
classid="GUID"
description="description"
version="version"
[remotable=remoteFlag]
>
<script>
(registration and unregistration script)
</script>
</registration>
Most of the element attributes have been discussed in the introduction to the
COM. However, remotable, which is optional, specifies if the component can be
instantiated using DCOM. Its value can be true or false.
The Public Element
The public element implements Automation, formerly known as OLE Automation, and
within the public elements you define the properties, methods, and events that
the component exposes after it has been registered. This is done using the
property, method, and event elements.
<public>
<property name="myProperty"/>
<method name="myMethod"/>
<event name="myEvent"/>
</public>
The Property Element
Property declares a property exposed by the component. Syntax:
The name of the property is what it will be exposed as, which needs to be the
same name as the global variable used to represent the proeprty. In contrast,
you can set an internalName attribute and run the property under another name
within the <script> elements. You may also use the following syntax if you
wish to implement the properties as subroutines that calculate the value of the
property.
<property name="myProperty"
[internalName="propertyScalarVariable"] />
<property name="myProperty"
get="getSubroutineNamet" put="putSubroutineName"/>
or
<property name="myProperty">
<get [internalName="getSubroutineName"]
/>
<put [internalName="putSubroutineName"]
/>
</property>
Put indicates write-permissions while get indicated read-permissions. A
combination of the both as seen above indicated read/wrote permissons.
The Method Element
The method elements define the methods that are exposed by the component.
Syntax:
or
<method name="methodName"
internalName="subroutineName" dispid=dispatchID />
<method name="methodName"
internalName="subroutineName" dispid=dispatchID >
[<parameter name="param"/>]
</method>
The dispatchID is automatically generated unless you specify "0" as a
dispatchID, which will result in it being the default method of the component.
Parameter elements may belong to the method, if defined.
<parameter name="param"/>
The Event Element
Declare an event that can be fired from within the component.
Syntax:
<event name="name" dispid=dispatchID/>
DispatchID is a numeric value that is generated automatically unless you specify
it. From within your script, you use the fireEvent(eventname);
method to execute an event.
The Implements Element
The Implements element enables you to include more COM interface handlers within
your script.
Syntax:
<implements type="COMHandlerName"
[id="internalName"] [default=fAssumed] >
Information related to the COMHandler goes here
</implements>
COMHandlerName is the name of an interface handler that you wish to implement,
and internalName is the name to which you want to reference the COMHandler. This
is useful to avoid naming conflicts because the components exposed data is
available in the global namespace. The fAssumed flag is a boolean values used to
define if internalName is assumed in scripts, which is the default setting..
The Windows Script Components run-time (scrobj.dll) implement handlers for
Automation and ASP, and also handlers for accesing the DHTML object model in the
page.
<component>
<registration progid="SimpleASP.WSC"/>
<public>
<method name="TestWrite"/>
</public>
<implements type="ASP"/>
<script language="PerlScript">
<![CDATA[
sub TestWrite {
Response.Write("Hello
World, says ASP!")
}
]]>
</script>
</component>
The Script Element
The script element lets you define the scripting language to use, and then with
its closing-tag functions as delimiters for the script code.
Syntax:
For example.
<script language="languageName"> code </script>
<?XML version="1.0"?>
<component>
...
<script language="PerlScriptt">
<![CDATA[
sub ReturnValue {
#
# Perl code here
#
}
]]>
</script>
</component>
The Object Element
The object element enables you to create an instance of a COM object that you
want to use within your Windows Script Component
Syntax:
The objectID is the name by which you want to reference the object within your
script, and you can use either the progID or classID to locate the component.
For example, how to create an instance using the COM components progID:
<object id="objectID" [classid="classid:GUID" |
progid="progID"]/>
<?XML version="1.0"?>
<component>
<object id="conn" progid="ADODB.Connection.2.5">
<script language="PerlScript">
<![CDATA[
sub OpenConn {
my($status);
$conn->Open('ADOSamples');
if($conn->{State} ==
adStateOpen) {
$status
= "Connection was a success";
}
else {
$status
= "Connection failed because ";
$status
.= $conn->Errors(0)->{Description};
}
$conn->Close();
return $status;
}
]]>
</script>
</component>
The Resource Element
The resource element is a placeholder for strings or numeric data that should be
separate from the script commands yet may be used within the script.
Syntax:
You use the <resource id="resourceID"> text or number to represent
resource goes here </resource>
getResource(resourceID)
to retrieve the contents of the
resource specified in the resourceID parameter.
The Reference Element
You can import external type libraries into your component by using the
reference element. By importing a type library into your component, you will be
able to naturally access the constants that belongs to it, too.
Syntax:
For example, you can use an ActiveX Data Object within your component.
<reference [object="progID" | guid="typelibGUID"]
[version="versionNo"] />
<?XML version="1.0"?>
<component>
<reference object="ADODB.Connection.2.5"/>
<registration progid="SimpleADO.WSC"/>
<public>
<method name="OpenConn"/>
</public>
<script language="PerlScript">
<![CDATA[
sub OpenConn {
my($status);
$conn = new
Win32::OLE("ADODB.Connection");
$conn->Open("ADOSamples');
if($conn->{State} ==
adStateOpen) {
$status
= "Connection was a success";
}
else {
$status
= "Connection failed because ";
$status
.= $conn->Errors(0)->{Description};
}
$conn->Close();
return $status;
}
]]>
</script>
</component>
The Comment Element
The Comment element encloses descriptive strings that are used to describe the
on-goings in the code and the strings are ignored by the language parser when
preparing the code to execute.
<comment>
Author: John Doe
Description: This WSC component is used to output a
Binary Large Object (BLOB)
from an SQL Server database . . .
</comment>
AUTHOR AND COPYRIGHT
Copyright (c) 2000 Tobias Martinsson. All Rights Reserved.
When included as part of the Standard Version of Perl, or as part of its complete documentation whether printed or otherwise, this work may be distributed only under the terms of Perl's Artistic License. Any distribution of this file or derivatives thereof outside of that package require that special arrangements be made with copyright holder.
Irrespective of its distribution, all code examples in this file are hereby placed into the public domain. You are permitted and encouraged to use this code in your own programs for fun or for profit as you see fit. A simple comment in the code giving credit would be courteous but is not required.
Windows Script Components is copyright (c) 1991-2000 Microsoft Corporation. All rights reserved.
Windows Script Components |