PREVIOUS  TABLE OF CONTENTS  NEXT 

Win32: PerlCOM and PerlCtrl

Required packages
PerlCOM     O'Reilly Win32 Perl Resource Kit
PerlCtrl        O'Reilly Win32 Perl Resource Kit

Optional packages

Net::Ping      CPAN/modules/by-module/Net

Brian Jepson

When you're developing a non-trivial application, you'll often have to glue that application to something else. That something else could be a data server, a library of functions, or some sort of visual component. If there is a single, widely accepted way of doing things, the act of gluing doesn't have to be traumatic. However, it's not always this easy.

If you've worked in Perl for a while, you know that there's more than one way to do anything, and this is especially true when it comes to gluing Perl to other things. You might say that Perl has a glue habit. It can be tricky to pick the right way to glue things together, but if you look the problem directly in the eye, you can generally make some sense of it.

For example, let's take embedding. Perl offers a very simple embedding API; see the perlembed documentation for details. If you have the source code to a program, it's pretty easy to embed a Perl interpreter into it. But if you don't have the source code, it's difficult. And if the notion of embedding a language at compile time repulses you, you need an alternative.

On Win32, you can use PerlCOM and PerlCtrl to use Perl in surprising places. Both PerlCOM and PerlCtrl are based on COM (Microsoft's Component Object Model), which allows you to glue objects together at run-time in a language-independent way. It's language-independent because you can access a COM object written in C++ from Perl or Visual Basic, a COM object written in Perl from C++ or Visual Basic, and so on. If you can develop a COM component in one language, you can use that component from any other language that supports COM. I'll demonstrate both PerlCOM and PerlCtrl in this article. We'll use PerlCOM to use a Perl module, Net::Ping, from COM. Then we'll use PerlCtrl to turn a chunk of Perl code, Vector.ctrl, into an ActiveX control that can be embedded in a web page.

COM provides the low-level mechanisms to glue objects together. There are two ways to create controls with COM: OLE and ActiveX. OLE (see TPJ #10) is the bulkier of the two, providing services for desktop applications, such as facilities for creating compound documents. ActiveX is a slimmer version of OLE that was originally optimized for embedding controls in web documents. However, you can use ActiveX and OLE controls in applications written in a high-level language such as Visual C++, Visual Basic, or Perl.

Perl developers can use PerlCOM and PerlCtrl to leverage Perl in a COM environment. For example, if you've got a spreadsheet that needs to do some heavy regular expression work, you can use ActiveState's PerlCOM or PerlCtrl to call on Perl without having to embed Perl in the spreadsheet program itself. PerlCOM is an ActiveX component that exposes a Perl interpreter, and PerlCtrl is a tool that lets you develop ActiveX controls in Perl. ActiveState's PerlCOM and PerlCtrl are included with the Win32 Perl Resource Kit, which is published by O'Reilly and Associates. PerlCOM and PerlCtrl may also be available separately in the future. Keep an eye on http://perl.oreilly.com and http://www.ActiveState.com for details.

WHAT ARE PEOPLE DOING WITH COM?

People are developing all sorts of groovy things with COM - there's no shortage of ActiveX controls available to web developers. These include simple plugins such as the multimedia viewers to sophisticated tools like the WinFrame client, which allows you to launch remote Windows applications within your browser. ActiveX controls are available for application development as well, such as hierarchical data controls to data engines for performing mathematical manipulations.

Wouldn't it be neat to be able to tap in to Perl's richness from within Visual Basic or a desktop application like Word or Excel? With PerlCOM or PerlCtrl, it's no trouble to add powerful WWW access to your application with something like libwww-perl.

VBSCRIPT FOR THE SAKE OF DISCUSSION

I'm going to use VBScript to showcase the talents of PerlCOM and PerlCtrl. Although VBScript is, at best, a lightweight development tool, it is ideal for what we need to cover in this article. It is a generic flavor of Visual Basic - examples developed with VBScript can be easily adapted to other Visual Basic variants, or other programming languages such as Visual C++, or Perl - the Win32::OLE module lets you work with COM objects. Also, VBScript can be used in conjunction with the Windows Scripting Host to develop lightweight programs that run from the command line. Finally, you can get it for free from http://www.microsoft.com/scripting. Simply download and install the Windows Scripting Host, and you'll be able to try these examples yourself (provided you have PerlCOM and PerlCtrl installed).

SO WHAT'S THE DIFFERENCE?

If you're new to COM, you're probably wondering how PerlCOM and PerlCtrl differ. PerlCtrl is a utility that lets you build ActiveX controls in Perl, such as the Sample.Vector that I'll show later in this article. PerlCOM is an ActiveX component (PerlCOM.Script) that exposes a Perl interpreter. PerlCtrl is ideal for developing reusable controls, while PerlCOM is useful for adding Perl capabilities to programs written in other languages.

ELEMENTARY PERLCOM

Let's take a look at how we can bring a little Perl into Visual Basic. Before you do anything with PerlCOM, you need to create a PerlCOM.Script object. In VBScript, you simply use the Dim statement to allocate a variable for the object, and then create the object with the CreateObject function. You must use the Set statement to assign the object to the variable:

Dim objPerlCOM
Set objPerlCOM = CreateObject("PerlCOM.Script")

That's all there is to creating the object. If you want to do something simple, you can give the PerlCOM object (objPerlCOM) a little bit of Perl to evaluate using the EvalScript method. You simply pass a snippet of code to EvalScript, and it is instantly evaluated within the Perl interpreter that is embedded within PerlCOM.Script. This snippet of Perl code creates a subroutine called hello that returns the text Hello, World!:

objPerlCOM.EvalScript("sub hello { return 'Hello, World!'; }")

Now that you've defined a subroutine, you can invoke it through the PerlCOM object. If you'd like to display a message box with the return value of hello, it's as simple as this:

MsgBox objPerlCOM.hello

CLASSES AND OBJECTS WITH PERLCOM

You may not know that the only way to have serious (or at least extreme) fun with Perl is to use its object-oriented features. Mix with PerlCOM, and it's more fun than a barrel of drunken monkeys. If there is a class that you want to use with PerlCOM, you can use CreateObject() to use it and create a new object instance. The CreateObject method accepts the module name and the name of the constructor as its first two arguments. The CreateObject method returns a SubPerlCOM object that exposes the methods and properties of the underlying instance of the Perl object (object is used in two senses here - see the glossary for details).

PERLCOM EXAMPLE: USING NET::PING

Here is a simple example that uses the Net::Ping object to ping a remote host. Net::Ping is available separately - you can install it with the command ppm install Net::Ping. When you run this program, you must supply the name of the remote host, as in ping.vbs www.perl.com. After a couple of seconds (give or take a couple of seconds) you'll see a message box that states either www.perl.com is alive or No answer from www.perl.com. Here is the source code for ping.vbs:

' ping.vbs
'
' Simple VBScript program to demonstrate the use of the
' Net::Ping Perl module.
'
' Get the name of the host the user wants to ping.
'
If (WScript.Arguments.Count < 1) Then
MsgBox "Usage: ping.vbs hostname"
WScript.Quit
End If
host = WScript.Arguments(0)
' Create the PerlCOM object.
'
Dim objPerlCOM
Set objPerlCOM = CreateObject("PerlCOM.Script")
' Create an instance of Net::Ping and use the ICMP protocol.
'
Dim objPing
Set objPing = objPerlCOM.CreateObject("Net::Ping", "new", "icmp")
' Try to ping the remote host.
'
rc = objPing.ping(host, 3)
If (rc) Then
MsgBox host & " is alive."
Else
MsgBox "No answer from " & host & "."
End If

I've only touched on a couple of PerlCOM's features; there are others. The UsePackage method is handy when you need to use a module that isn't a class definition, such as LWP::Simple (a simple interface to libwww-perl). The PerlCOM::Hash feature of PerlCOM allows you to work with Perl hashes within a host language such as Visual Basic. PerlCOM::Hash wraps the hash in an ActiveX control that allows you to manipulate the hash and its contents.

PERLCTRL EXAMPLE: TURNING PERL CODE INTO AN ACTIVEX CONTROL

PerlCtrl is a utility that allows you to package a Perl module as an ActiveX control. The utility is an executable, PerlCtrl.exe, that performs two functions: with the -t switch, it generates a skeleton Perl control for you. You simply add your functionality to this template to define the control. When invoked without any switches, PerlCtrl.exe processes a Perl control and generates a DLL from it. You then run the Windows utility regsvr32 on the DLL, and the control is registered and ready to use. (The regsvr32 utility registers ActiveX components on your system. Once the component has been registered, other applications can use the ProgID (the human-readable name of the component) or the CLSID (the globally unique identifier for the component) to launch an instance of it.)

The control's source file includes both the class definition and a special hash that defines the COM attributes of the control. This includes certain GUIDs (Globally Unique Identifiers), the control name, and information about the methods and properties. Here is the first part of Vector.ctrl, which defines a very simple Vector class:

package Vector;
# Construct a new Vector.
#
sub new {
my $type = shift;
my $self = {};
bless($self);
$self->{array} = []; # the contents of the Vector
return $self;
}
# Add an element to the Vector.
#
sub addElement {
my $self = shift;
push @{ $self->{array} }, shift;
}
# Fetch an element from the Vector.
#
sub elementAt {
my $self = shift;
my $index = shift;
return $self->{array}->[ $index ];
}
# Retrieve the size of the Vector.
#
sub size {
my $self = shift;
return scalar @{ $self->{array} };
}
# Produce a comma-delimited representation of
# the Vector's elements.
#
sub toString {
my $self = shift;
return join(', ', @{ $self->{array} });
}

In order to use PerlCtrl with Perl's object-oriented features, your package must include a method that generates an instance of the object. Here's the second part of Vector.ctrl:

# Create a new instance of the Vector.
#
sub New {
return new Vector;
}

Note that we use New instead of new - that's because this is code meant to be used by external languages, not by Perl. Finally, you must include a %TypeLib hash that defines the methods you want to expose. The only method you need to expose through the control is the New method. This method actually returns an instance of a PerlCOM object that exposes all of the methods in the Vector class (addElement, elementAt, size, and toString). Here is the third and final part of Vector.ctrl:

=POD
=BEGIN PerlCtrl
%TypeLib = (
# Name of the package (in this file) that includes the
# method(s) you want to expose.
#
PackageName => 'Vector',
# Do not edit the next three lines. These are generated
# when you run PerlCtrl -t, and are globally unique
# identifiers.
#
TypeLibGUID => '{5A7AB274-1B74-11D2-B456-0800365DA902}',
ControlGUID => '{5A7AB275-1B74-11D2-B456-0800365DA902}',
DispInterfaceIID => '{5A7AB276-1B74-11D2-B456-0800365DA902}',
# The control name and other information.
#
ControlName => 'Sample.Vector',
ControlVer => 1, # increment if new object with same ProgID
# create new GUIDs as well
ProgID => 'Sample.Vector',
# Method information.
#
DefaultMethod => '',
Methods => {
'New' => {
RetType => VT_DISPATCH,
TotalParams => 0,
NumOptionalParams => 0,
ParamList => [ ]
},
}, # end of 'Methods'
# Property information.
#
Properties => {
# No properties
}, # end of 'Properties'
); # end of %TypeLib
=END PerlCtrl
=cut

Once you have a complete Perl control source file, you can generate its DLL by running it through PerlCtrl:

PerlCtrl Vector.ctrl
Vector.ctrl syntax OK
Creating PerlCtrl Vector.dll ...all done.

Next, you must run the DLL through the registry server with the command regsvr32 dllname, such as regsvr32 Vector.DLL. If it is successful, you will see a message box with the text DLLRegisterServer in Vector.dll succeeded.

The next example is a simple VBScript program that uses the Sample.Vector control to create two Vector objects. It then adds some elements to the Vectors and displays them in two different ways, as shown below.

Here is the source code for Vector.vbs:

' Vector.vbs
'
' A simple PerlCtrl example.
'
' Create an instance of the Sample.Vector object. This object
' is used to generate instances of the Vector class.
'
Dim objVectorFactory
Set objVectorFactory = CreateObject("Sample.Vector")
' Create two Vectors.
'
Dim objVectorOne
Set objVectorOne = objVectorFactory.New
Dim objVectorTwo
Set objVectorTwo = objVectorFactory.New
' Add some elements to our Vectors.
'
objVectorOne.addElement("A")
objVectorOne.addElement("B")
objVectorOne.addElement("C")
objVectorTwo.addElement("X")
objVectorTwo.addElement("Y")
objVectorTwo.addElement("Z")
' Display a message box with the string representation of
' objVectorOne's elements.
'
MsgBox objVectorOne.toString, vbOkOnly, "Vector One"
' Loop through objVectorTwo and produce a cr/lf
' delimited string with the index and values
'
message = ""
For i = 0 To objVectorTwo.size - 1
value = objVectorTwo.elementAt(i)
message = message & "Element " & i & " = " & value
message = message & chr(10) & chr(13)
Next
MsgBox message, vbOkOnly, "Vector Two"

OTHER PLACES

There is more to the Perl and COM picture than I've discussed here. PerlCOM and controls developed with PerlCtrl can both be used with DCOM (Distributed COM), which provides a low-hassle way to launch COM objects on remote machines. In fact, PerlCOM and controls you develop with PerlCtrl need no modification to work with DCOM. You just need to set their permissions with the DCOMCNFG.EXE utility to allow remote users to launch instances of the objects on your machine.

The future will bring exciting possibilities, as well. In the current incarnation of PerlCtrl, controls you develop require Perl on each machine you want to launch the control on. Future revisions of PerlCtrl may include the ability to develop standalone controls that can be bundled up and shipped out. World domination through Perl is a distinct possibility at this point!

__END__


Brian Jepson has written a fistful of books on Perl and other topics, such as Internet database development. He is also the Minister of Information for the SMT Computing Society, the elite technical strike force that manages the care and feeding of AS220's Technology Project (AS220 is a nonprofit alternative space in Providence, Rhode Island whose primary mission is the maintenance of an uncensored and unjuried forum for the arts).
PREVIOUS  TABLE OF CONTENTS  NEXT