Bob Wells and Toby Everett present a Perl script that automates WINS changes

\[Editor's Note: Scripting Solutions is about using scripts to build solutions to specific business problems. This column doesn't teach you how to program with scripting languages, but how to use scripts to provide quick, easy-to-implement tools you can use right away.\]

"Automating NT Administration with Scripts" (November 1997) explained Windows NT scripting languages and how they offer a more flexible and powerful approach to solving NT administration problems. This article continues down that path by walking through a useful script to access and manipulate the Windows NT Registry. The script, WINSer.pl (shown in Listing 1, page 206), demonstrates how you can use Practical Extraction and Report Language (Perl) for Win32 to automate Windows Internet Naming Service (WINS) client IP address changes in an enterprise network. Although the script targets WINS addresses, you can easily modify it to accommodate almost any batch Registry change.

Perl for Win32
ActiveWare Internet (now ActiveState Tool) developed Perl for Win32 to fill the need for a strong scripting language on the NT and Windows 95 platforms. Perl for Win32 is a port of most of the functionality available in the version of Perl for UNIX environments. The WINSer.pl script demonstrates some of Perl's key concepts and capabilities. For a quick introduction to Perl fundamentals, see the sidebar, "Perl Basics," page 208. If you decide Perl can be of use to you, buy a good book about Perl and explore some online Perl resources (for information about Perl books and Web sites, see the online sidebar, "Perl Resources," at http://www.winntmag.com).

Before you can use WINSer.pl, you need to install Perl for Win32. Simply download the distribution file from the ActiveState Tool Web site (http://www.activestate.com) and run it. We used Build 306 (i.e., pw32i306.exe) to build the script in this article. After extracting all the files, the Perl for Win32 installation process invokes an install script to update path and Registry information. When installation is complete, you're ready to start writing and using Perl scripts. One word of caution: If you've installed the version of Perl that comes with Microsoft Windows NT Server Resource Kit or Microsoft Windows NT Workstation Resource Kit, you'll encounter version incompatibility problems. The resource kit version of Perl is an old version that we do not recommend you use. You'll want to remove the resource kit version or at least make sure that c:\Perl\bin (or the like) appears before the resource kit directory entry in your path statement.

The Sample Script
WINSer.pl is a robust tool that lets you update WINS client IP addresses on local or remote servers and workstations. To run WINSer.pl, you simply enter a command sequence at the command prompt. The number of parameters you include in the command line depends on whether you want to update the local machine, a remote machine, or multiple machines. In every case, the first two parameters are the primary and secondary WINS server IP addresses. To update the local machine, enter

c:\> perl winser.pl  

To update a remote machine, enter

c:\> perl winser.pl   remote

To update multiple machines, enter

c:\> perl winser.pl file

where filename is the name of a text file that contains the names (one per line) of the machines you want to update.

WINSer.pl uses several functions in the Perl for Win32 Registry library, registry.pll, for accessing registries on remote computers. A friendlier interface for manipulating the Registry is available through the Win32::Registry module. However, this module lacks support for connecting to remote registries. (For descriptions of the functions in registry.pll, see the online table, "Registry.pll Functions," at http://www.winntmag.com.)

One of the more powerful, but less obvious, features of WINSer.pl has to do with the values we're changing—WINS addresses. Anyone who has worked with NT for a while knows that the location of the WINS entries in the Registry depends on the type of network adapter installed in the machine. WINSer.pl gets around this problem by connecting to the deepest common key, HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NetBT\Adapters, and enumerating its subkeys. For each subkey, WINSer.pl tests the subkey to make sure it is not a Remote Access Service (RAS) entry (i.e., the script checks whether the subkey contains the string NdisWan). If the subkey is not a RAS subkey, WINSer.pl updates its values. Of course, you must restart the machine for the change to take effect. You can add that piece of functionality to the script with a call to

Win32::InitiateSystemShutdown ($machine, $message, $timeout, $forceclose, $reboot)

but you risk annoying logged on users with this automatic reboot.

Walking Through the Script
The WINSer.pl script contains four key functional areas: a block that reads the command-line parameters, verifies them, and reads in the list of machine names if applicable (at callout A in Listing 1); an outer loop that traverses the computer name array and establishes the initial Registry connection (at C in Listing 1); an inner loop that steps through the adapters on a machine, updating the appropriate WINS addresses along the way (at D in Listing 1); and a subroutine for printing a Help message (at E in Listing 1).

The first two lines of WINSer.pl access the library files we need to work with the Registry. The next line tests the @ARGV command-line argument array for the number of arguments on the command line. Referencing the entire array in a scalar sense returns the number of elements the array contains. If the @ARGV array is empty (i.e., the user didn't specify any command-line arguments), WINSer.pl calls the &PrintHelp subroutine, which prints the Help information and then exits. Otherwise, WINSer.pl initializes $primary and $secondary with the first two arguments on the command line, the primary and secondary WINS server IP addresses.

After initializing $primary and $secondary, the script tests for the presence of a third command-line argument, $ARGV\[2\]. In the absence of $ARGV\[2\], control transfers to the last few lines of code at A in Listing 1, where the script assigns the local computer name to the @machines array of machine names. If $ARGV\[2\] is present, the program compares the lowercase version of $ARGV\[2\] to the string "remote" using Perl's eq operator. If the test is successful (i.e., the user is updating a remote machine), WINSer.pl assigns the fourth command-line argument, $ARGV\[3\] (the name of the remote machine), to the @machines array. If the "remote" string test fails, the script tests $ARGV\[2\] for the string "file". If the test is successful, the script attempts to open the file specified by $ARGV\[3\]. If the file cannot be opened, the script exits with an appropriate error message. If the file is successfully opened, the script retrieves the file's contents, assigns them to the @machines array, and closes the file.

A Peek at Perl's Power
Let's examine the line of code at B in Listing 1 to get a closer look at Perl's power. This single line of code uses two standard Perl library functions, map and grep, to convert an input file (even a badly formatted one) into a neatly ordered array. Figure 1, page 209, depicts this filtering process.

WINSer.pl first reads the file that the FILELIST file handle points to. Perl's file-handle read operator () assigns each line of the input file to an element of the @machines array, as shown in the second box in Figure 1. The map function applies a code block (\{s/\s//g; $_\}), to each element in the array. This particular code block strips all blank characters (e.g., spaces, tabs, new lines) from a line. The third box in Figure 1 shows the result of the map filter.

WINSer.pl then passes the filtered array to the grep function, which returns elements of the array that match a specified regular expression (/\s/). In this case, grep returns all the nonblank lines from the array. The result is an ordered (and clean) list of machine names, as shown in the fourth box in Figure 1.

Connecting to the Registry
The foreach loop at C in Listing 1 traverses all the computer names in the @machines array. In each iteration of the loop, WINSer.pl stores the computer name in $machine and then attempts to connect to the HKEY_LOCAL_MACHINE hive on the computer $machine specifies. If the connection fails, WINSer.pl prints an error message, drops out of the loop iteration, and advances to the next element in @machines. If the connection is successful, RegConnectRegistry returns a handle to the connection in $key_LM. Next, the script attempts to open HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NetBT\Adapters. If the open fails, WINSer.pl prints an appropriate error message, closes the connection with a call to RegCloseKey, and moves to the next element in @machines. If the open succeeds, WINSer.pl executes the loop at D in Listing 1.

WINSer.pl uses a while loop to walk through the subkeys of the \Adapters key. Before entering the loop, the code initializes $i (the loop counter and index for the subkeys) to 0.

For each iteration, the code checks to see whether the adapter name contains the string NdisWan. If the adapter name includes this string, WINSer.pl skips the subkey and moves to the next subkey. (This part of the script ensures that WINSer.pl doesn't try to update RAS-related WINS information.) If NdisWan does not appear in the subkey's name, WINSer.pl attempts to open the adapter subkey. If the attempt is not successful, WINSer.pl performs appropriate error recovery. Otherwise, WINSer.pl sets the primary WINS address (or performs error recovery if the attempt fails). WINSer.pl follows the same procedure for the secondary WINS address, and then prints a message saying it successfully updated the entries. The while loop ends by closing the appropriate keys and moving to the next \Adapter subkey. After WINSer.pl visits all the subkeys, it closes the connection to the remote Registry and continues with the next computer name in @machines.

Help for Users
The block of code at E in Listing 1 is the PrintHelp subroutine. This simple subroutine uses Perl's "here document" string syntax. The <

Beyond the Registry
Perl's ability to work with NT extends far beyond the Registry. Additional packages exist that provide programmatic access to the Security Account Manager (SAM) database, NT event logs, Open Database Connectivity (ODBC) databases, and the Internet. Future articles will explore some of these aspects of the language and present scripts built using other scripting languages.

Corrections to this Article:

  • The code for the Perl program, WINSer.pl, featured in Bob Wells' article, "Modify the Registry with Perl," contained errors.The corrected code is available from "Windows NT Magazine's" Web site at http://www.winntmag.com.