Using Objects with ActivePerl
ActiveState's ActivePerl lets you run Perl scripts in the WSH environment. Perl's environment is already rich with file-management and network-communication tools, and if you're already a skilled Perl programmer, you might 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 don'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 WSH.
If you use the familiar .pl filename extension for Perl programs that you want to run in the WSH environment, you have to use the command
cscript /engine:Perlscript myscript.pl
to fire them up. Because you might want to start scripts with the command line or from Explorer, your life is much easier if you use the extension .pls for programs meant to be used with WSH. This way, you can double-click the files in Explorer or use commands such as
start myscript.pls myscript cscript myscript.pls
to start script files; WSH knows what to do. You can also use PerlScript inside the structured .WSF files I discuss in Chapter 9, "Deploying Scripts for Computer and Network Management."
Here are some important things to remember when writing Perl scripts for use with WSH:
- You cannot use familiar Perl command-line switches such as -w. You need to directly set option values in the script.
- Any command-line arguments specified to cscript or wscript are not 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 method.
To set a read/write property, you can simply assign a value to it, as in the following:
$file->{name} = "newname";
The standard WScript object, which I discuss in more detail later in the chapter, is predefined by the WSH environment and is available to a Perl script. For example, 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 works 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.
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 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 might 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.
Now, you might want to skip ahead to the section titled "Using the WScript Object" for more information about WSH built-in objects.