Using Objects with ActivePerl
ActiveState's ActivePerl lets you run Perl scripts in the Windows Script Host environment. Perl's environment is already rich with file-management and network- communication tools, and if you're already a skilled Perl programmer you may wonder what WSH can add. In other words, why use cscript or wscript to run Perl, when you could just run perl.exe directly?
The answer is that in the WSH environment, it's a simple matter to access COM, OLE (Automation), and ActiveX objects. The helpful $WScript object is predefined in the WSH environment. COM objects are the key to accessing network configuration, Active Directory, and Windows Management Instrumentation (WMI). Although you probably won't want to bother with the Windows script objects for file and directory management, the system-management tools make WSH worthwhile.
Running Perl Scripts in WSH
The ActivePerl installer creates two file associations for Perl files: .pl (Perl) is associated with Perl.exe, and .pls (PerlScriptFile) is associated with Windows Script Host.
If you use the familiar .pl filename extension for Perl programs that you want to run in the Windows Script Host environment, you'd have to use the command
cscript /engine:Perlscript myscript.pl
to fire them up. Because you may want to start scripts with the command line or from Explorer, your life will be much easier if you use the extension .pls for programs meant to be used with WSH. This way, you can just double-click files in Explorer or you can use commands such as
start myscript.pls myscript cscript myscript.pls
to start script files, and WSH will know what to do. You can also use PerlScript inside the structured WSF files I'll discuss in Chapter 9.
Here are some important things to remember when writing Perl scripts for use with WSH:
You will not be able to use familiar Perl command-line switches such as -w. You will need to directly set option values in the script.
Any command-line arguments specified to cscript or wscript will not be placed in the ARGV array. Instead, you must fetch command-line arguments from the $WScript->Arguments collection.
The Perl Object Interface
ActivePerl can interact with COM, ActiveX, and OLE (Automation) objects. The extended syntax for accessing methods is
$objectname->Method[(arguments[, ...])];
Here's an example:
$myobject->SomeMethod $myobject->Anothermethod("argument", 47);
The syntax for accessing property values is
$objectname->{Propertyname}
Here's an example:
value = $myobject->{Length}; $myobject->{color} = "Red";
Because the syntax for accessing methods and properties is different, you must take care to check the COM object's documentation carefully to determine whether a value you want to use is a property or a method.
CAUTION
If you attempt to access an object property or method that does not exist or if you have misspelled the name, by default Perl will not generate an error. The result will simply be undefined (undef). This will make it very difficult for you to debug your script.
To avoid this pitfall, put
$^W = 1;
at the beginning of every script file. This will cause Perl to print an error message if you attempt to reference an undefined method or property. I have found that the error message may not tell you clearly that the problem is an unrecognized property or method, but at least you'll get some warning.
To set a read/write property, you can simply write the following:
$file->{name} = "newname";
The standard WScript object, which I'll discuss in more detail later in the chapter, is predefined by the Windows Script Host environment and is available to a Perl script. The current version of WSH can be printed with this script:
$WScript->Echo("The version is", $WScript->Version);
You could use conventional Perl I/O and write
print "The version is ", $WScript->Version;
instead. However, print writes to stdout, which is undefined when the script is run by WScript (the windowed version of WSH). A script using print will work under Cscript, but not WScript. It's up to you, of course, but if you want your script to work equally well within either environment, use the $WScript.Echo method for output.
NOTE
Perl is a case-sensitive language, and object variable names such as $WScript must be typed exactly as shown here. However, an object's method and property names are not case sensitive.
To create an instance of an Automation, OLE, or ActiveX object, you can use either of two methods. The simplest is to use the CreateObject method provided with the built-in $WScript object, as in
$myobj = $WScript->CreateObject("Scripting.FileSystemObject");
You can also use the ActivePerl Win32::OLE extensions provided with ActivePerl:
use Win32::OLE; $excel = Win32::OLE->new('Excel.Application') or die "OLE new failed";
For information on the OLE extensions, see the ActivePerl help file.
Working with Collections
Some of the COM objects you'll encounter in this book and elsewhere return collection objects, which are containers for a list of other objects. For example, the Drive property of Scripting.FileSystemObject returns a collection of Drive objects, and the WScript.Arguments property returns a collection of Argument objects. I discussed the methods and properties of the collection object earlier in the chapter.
Because the items of a collection object can't be obtained by an index value (at least, not directly), they must be "scanned" using an enumerator object. An enumerator gives you access to a collection object by maintaining the concept of a current location in the list, which you can then step through. To scan the list a second time, you must reset the current location to the beginning of the list and then step through the list again.
There are three ways to enumerate a collection. First, you can explicitly create an enumerator object, as in the following example:
$^W = 1; $fso = $WScript->CreateObject("Scripting.FileSystemObject"); $fls = $fso->GetFolder("C:\\")->Files; # get collection of files in c:\ $n = $fls->{Count}; # just for kicks say how many there are print $n, " files\n"; $enum = Win32::OLE::Enum->new($fls); # create enumerator object while (defined($file = $enum->Next)) { # assign $file to each item in turn print $file->{Name}, "\n"; # print the file names }
A second way uses the in function to hide the enumerator: the in operator returns Win32::OLE::Enum->All(obj), which in turn returns a Perl array given a collection object. Instead of creating $enum and using a while loop in the previous example, I could have written the following:
foreach $file (in $fls) { print $file->{Name}, "\n"; }
Seeing this, you may guess that the third way is to use in to create an array of the subobjects, which you can then access explicitly:
@file = in($fls); for ($i = 0; $i < $fls->{Count}; $i++) { print $file[i]->{Name}, "\n"; }
Of the three methods, the foreach method is the most straightforward, and it's the easiest on the eyes and fingers. Unless you really want to use an array, I recommend foreach as the way to go.
Now, you may want to skip ahead to the section titled "Using the WScript Object" for more information about WSH built-in objects.