2024 π Daylatest newsbuy art
Drive, driven. Gave, given.YelloGive me a number of games.more quotes
very clickable
data + munging

The Perl Journal

Volumes 1–6 (1996–2002)

Code tarballs available for issues 1–21.

I reformatted the CD-ROM contents. Some things may still be a little wonky — oh, why hello there <FONT> tag. Syntax highlighting is iffy. Please report any glaring issues.

The Perl Journal
#7
Fall 1997
vol 2
num 3
Just the FAQs: Short Circuits
&& and || or and and or, and chomp() and LABELs.
Win32 Perl
Perl for Windows.
Infinite Lists
A new construct that can manipulate endless data streams.
Perl/Tk: Binding Basics
Associating actions with events.
Perl News
What's new in the Perl community.
Perfect Programming
A collection of tips for the paranoid programmer.
A Perl in the Oil Patch
Of salt and sysread().
WebPluck
Amassing a personalized newspaper from the web.
MakeMaker: Doing More While Doing Less
How to prepare your modules for maximum portability.
Obfuscated Perl Contest - The Winners
A frightening display of cryptic virtuosity.
The Perl Journal One-Liners
Dave Roth (1997) Win32 Perl. The Perl Journal, vol 2(3), issue #7, Fall 1997.

Win32 Perl

Perl for Windows.

Dave Roth


Regardless of how I feel about Microsoft Windows, they're here to stay and I have to administer a WAN of them. I've gotten used to them in spite of their limitations, but there's one burning issue that I can't get over: the lack of a decent scripting shell. The DOS-like command line isn't helpful for anything more than the most simple of automation. That's where Win32 Perl comes in.

The Win32 port of Perl works on both Windows NT and Windows 95, thanks to the folks from ActiveState (https://www.activestate.com). Dick Hardt and his team have done a remarkable job at developing the port and implementing useful features that make administering Win32 networks a breeze.

Win32 Perl is a Win32 native application that, for the most part, works just like its UNIX brethren and can handle almost anything that you can throw at it (with a few exceptions discussed later). If you subscribe to this magazine, you'll be right at home with it. Well, almost right at home, as you'll see.

Obtaining and Installing Win32 Perl

There are currently two major versions of Win32 Perl: the ActiveState version and the core distribution version. The O'Reilly Perl Conference initiated an ongoing effort to merge them.

The core distribution version. The Official Perl distribution available from the CPAN has rolled in Win32 support. Nick Ing-Simmons and Gurusamy Sarathy have done a great job with this port, which contains both source and binaries. Just make sure that you read the documentation, because you might need to jump through a few hoops if you compile the source code yourself. The Win32 specific extensions found in the ActiveState version are available, but come in a separate package called libwin32, which you can download from https://www.perl.com/CPAN/authors/id/GSAR/. So far all of my scripts originally written for ActiveState have worked perfectly with the core.

The ActiveState version. This is the version most users have because it's been around for a while. ActiveState distributes the source and binaries (for both x86 and Alpha chips). The binaries come with everything you need to get started. You just download it (check out their home page for the latest build), run the self-extracting archive, and everything snaps into place. Or, if you have a C compiler (Microsoft Visual C++ is preferred) then you can download the source code and compile the beast. The ActiveState port comes in three flavors; it's important that you know the differences between them: the regular Perl executable, PerlIS, and PerlScript.

The Perl executable. The ActiveState Perl executable (derived from Perl 5.003) is a Win32 console application. It runs as you would expect Perl to run, on a console-based command line.

PerlIS. PerlIS.DLL is an Internet Server API (ISAPI) extension designed to work with Microsoft's Internet Information Server (IIS). It works just like the Perl executable, but since it's a .DLL file ( A .DLL file is a Dynamic Linked Library, a library of functions loaded during run time and dynamically linked to by the calling application. It is similar in function to the ELF library format in the UNIX world. ) it is loaded only once during the life of the web server, remaining in memory until the server process has completed. You might want to use this for CGI scripts; the DLL links Perl into the web server, so there's no overhead for starting a new Perl process. This is faster than starting a script with perl.exe, but of course there's no speed gain for script execution.

PerlScript. Then there's PerlScript, similar in function to JavaScript and VBScript. It's an interface between Win32 Perl and Microsoft's Active Server Pages (ASP) that allow you to embed Perl code in HTML pages for processing by either web server or browser. [See PerlScript]

For the rest of this article, I'll concentrate on the ActiveState port of the Perl executable since it has the largest user base.

Gotchas & Dilemmas with the Win32 Port

I've used Win32 Perl for CGI scripting, database maintenance, and system administration, and in the process compiled a list of gotchas and dilemmas. Gotchas are little issues that I try to remember when coding; dilemmas are larger problems that I've had to write code to work around.

Gotchas. Most of these are annoyances that can be resolved easily. Novices spend most of their time grappling with these issues when experimenting with code from Perl books.

  • File Permissions. Using correct file permissions may be obvious to most but I've received so much email about them that I want to mention it here. If your Perl script performs flawlessly when you run it but fails when invoked as a CGI script, permissions are likely the problem. Perl users need permission to read the Perl executable, DLL file, packages, and extensions. Keep in mind that when you run a script it runs under your account with permissions granted to you. But when you run the same code under another account (such as the scheduler service, or as a CGI script from your web server) it runs under whatever account the service uses. Make sure your service account has the proper read permissions on the Perl tree.

  • Parse Exceptions. ActiveState has released many Win32 Perl builds, and occasionally a new build redefines the extension API, making it necessary to recompile your extensions. If you don't, you might receive a "parse exception" as your program runs. ActiveState has done a good job at making these changes invisible from the coder's perspective; the changes are reflected in the XS macros. Generally all you need to do is replace the Perl header files from the Perl source and recompile the extension, or download an updated build.

  • Paths. For sake of compatibility Win32 Perl recognizes UNIX-like paths that use slashes to delimit directories, e.g. /usr/lib. Win32 Perl translates the paths to the Microsoft dialect: \usr\lib. This even works when you want to open a UNC (A Universal Naming Convention (UNC) is Microsoft's way of specifying a path to a file or directory over a network. For example a file may be accessed as
    \\ServerName\SharedDir\perl\test\test.pl.)
    such as:
    //servername/sharename/directory/file.txt
    which is translated by Win32 Perl to:
    \\servername\sharename\directory\file.txt.
    It's inviting to use the slash instead of backslash in a path because a backslash needs to be escaped with another backslash. So the path:
    \\server\share\perl\bin\perl.exe
    when used as a string becomes:
    $path = "\\\\server\\share\\perl\\bin\\perl.exe"
    which is annoying. So the ability to use slashes is quite tempting. Yet this is another gotcha, because when you use system commands you have to revert to backslashes.
    @dir = 'dir //server/share/directory/*.*';
    This causes an error because Windows does not recognize the slash as a delimiter - even though Perl does.

  • Documentation. If you are fairly new to Perl and don't have a background in coding and/or a background in the Win32 API, you're in for quite a ride. The documentation is woefully incomplete and in some cases vague or even incorrect. I find that the best way to learn the syntax for a Win32 specific function is to look at the source code and reference the Win32 API manuals. You may, instead, want to consult some of the Win32 Perl FAQs and web pages available on the net.

In Search of the Win32 Guru

Dilemmas. Dilemmas are issues that aren't so easy to work around. These dilemmas pose a problem with porting scripts from other platforms such as UNIX. To ActiveState's credit, most of these dilemmas are either technical restrictions due to the Win32 platform, or exist simply because they haven't had time to address them yet.

What about DOS?

  • Win32's lack of fork(): One of the most obvious dilemmas with the Win32 port has to do with Win32 and not the port itself: fork() doesn't exist. This poses some serious problems for porting UNIX scripts to Windows NT or Windows 95. The closest thing that Microsoft has to a forked process is a thread - but Perl won't support multithreading until 5.005. If you can neither fork() nor thread, how can you port your scripts that need to spawn a child process? You can use the CreateProcess() method from the Win32::Process extension, but when you create a new process you are starting a process from scratch - there's no transfer of data from parent to child. This means that your new child process has no idea that it is a child. To top it off, the child is created without any variables set or pipes open. However, the new child process can inherit Win32 filehandles, so you could open a pipe in the parent process, spawn the child, and then transfer important data from the parent to the child, right? Almost. The new child process can indeed inherit the filehandles, but there's no way for Perl to access them since they're 32-bit Win32 filehandles and not the 16-bit ANSI-compliant C filehandles that Perl prefers.

When a new console processes is started, it begins with three open filehandles: STDIN, STDOUT, and STDERR. Each of these Win32 filehandles are mapped to C runtime filehandles with file descriptors of 0, 1, and 2 respectively. Let's say that your parent process opens an anonymous pipe:
pipe(READ, WRITE);
and then redirects STDIN to the READ filehandle:
open(STDIN, "< &READ");
When the child is created it can inherit this pipe as its STDIN. If the parent process then writes data to the pipe, the child can read it. It's also possible to use a socket instead of the pipe. This process works so long as you don't use the CREATE_NEW_CONSOLE flag for the new process, which creates a new console window and resets STDIN, STDOUT, and STDERR. This is obviously a poor replacement for fork() but with a bit of tweaking you can convince Win32 Perl to act as a server spawning children. The following two scripts show this process. The scripts are assumed to be in c:\perl\test, the Perl executable in c:\perl\bin.

Server Script

use Win32::Process; 
%Data = ( 	one => 1, 	two => 2, 	three => 3 ); 
pipe(READ, WRITE); 
select(WRITE); 
$| = 1; 
select(STDOUT); 
open(SAVEIN, "<&STDIN") || die "Can't save STDIN\n"; 
open(STDIN, "<&READ") || die "Can't redirect STDIN\n"; 
select(STDIN); 
$| = 1; 
select(STDOUT); 
Win32::Process::Create($Process, 
                      "	c:\\perl\\bin\\perl.exe", 
                      "c:\\perl\\bin\\perl.exe", 
                      "c:\\perl\\test\\client.pl", 
                      	1, NORMAL_PRIORITY_CLASS,
                      "c:\\perl\\test"); 
open(STDIN, "<&SAVEIN"); 
close(SAVEIN); 
close(READ); 
print "$0: Sending variables to child...\n"; 
foreach $Temp (keys(%Data)){ 
    print "$0:\t$Temp=$Data{$Temp}\n"; 
    	print WRITE "\$Data{$Temp}=$Data{$Temp};\n"; 
} 
print "$0: Finished sending variables.\n"; 
close(WRITE); 
print "$0: About to terminate. Waiting for <RETURN>...\n"; 
<STDIN>; 
print "$0: End.\n";

Client Script

print "$0: Starting.\n"; 
print "$0: Reading in variables...\n"; 
while(<STDIN>) { 
    eval($_); 
    print "$0: \t$_"; 
} 
print "$0: Finished reading variables.\n"; 
print "$0: Dumping variables...\n"; 
foreach $Temp (keys(%Data)){ 
    print "$0:\t$Temp=$Data{$Temp}\n"; 
} 
print "$0: End.\n";

  • Named Pipes. Win32 Perl does not yet support named pipes. This was the motivation for my Win32::Pipe extension, which makes it easy to create a named pipe. The server script creates a new Win32::Pipe object, waits for a connection from a client, and then reads from or writes to the pipe. The client opens a filehandle to the server's named pipe using either the standard open() function or using the Win32::Pipe functions (which provide additional features such as peeking ahead in the pipe before reading from it).

Server Script

use Win32::Pipe;
$Pipe = new Win32::Pipe("My_Pipe"); 
$Pipe->Connect(); 
$Pipe->Write("Hello client, from $0"); 
$Pipe->Close(); 

Client Script

open(CLIENT, "< //./pipe/My_Pipe") || die; 
$Data = <CLIENT>; 
print "$Data\n"; 
close(CLIENT);

Benefits of Win32 Perl

In addition to the rich features of Perl, Win32 Perl has some nifty extensions that make it a great tool for any administrator. These extensions give you the ability to remotely control, configure, and manage a machine from across the net. I routinely manage my customers' mail, web, and FTP servers in real time over the Internet using Win32 Perl and the Internet. I've written CGI scripts that allow users to change their NT domain passwords or modify other account information, all using the extensions bundled with Win32 or available on the Internet.

Extensions. Win32 Perl comes with a set of useful platform specific extensions that provide an interface to some of the Win32 API features. I've stumbled upon a few extensions that were missing segments of code or were miscoded. Some of these bugs have been corrected as new builds have been released. If you're not proficient at extension coding (or just have limited time), you should post to Usenet or send mail to the one of the Win32 Perl listservers; chances are someone else has experienced the same problem.

Installing extensions. If you look around your Perl directory tree you'll find that the extension packages (the .pm files) are in your library's Win32 subdirectory. Actual extensions, however, are in perl\lib\auto\win32 instead. In fact, all extensions must go into a subdirectory of perl\lib\auto. For example, when I installed the GD extension I copied the GD.PLL file to c:\perl\lib\auto\gd\tt>. (The extension itself uses a .PLL file extension even though it's a .DLL file.(When you create a Win32 Perl extension, the binary will be a DLL file. Win32 Perl, however, only recognizes it if it has an extension of .PLL, so renaming is necessary. )) Since Win32-based extensions fall into the Win32 namespace they can be found in subdirectories of perl\lib\auto\win32\; the Win32::ODBC extension can be found in c:\perl\lib\auto\win32\odbc\ odbc.pll on my machine.

Win32.pm. The most basic Win32-specific extension is the Win32 module. This isn't an extension so much as a package - there's no WIN32.PLL file. The methods found in this package are already built into Perl, but need to be accessed via this package. (The actual source code is found in ntxs.cpp.) This package gives you, among other things, access to the current user's name, domain, and computer name:

use Win32; 
$User   = Win32::LoginName(); 
$Domain = Win32::DomainName(); 
$Node   = Win32::NodeName(); 

There's also a handy function that returns an error message from the Win32 API for any API call regardless of what extension called it:

	$Error = Win32::FormatMessage(Win32::GetLastError()); 	

Perl and ActiveX

Win32::NetAdmin. The Win32::NetAdmin extension is an administrator's best friend, allowing you to manipulate user accounts and check for group membership. The following subroutine returns a 1 if the user in the specified domain is a member of the specified group:

sub CheckMembership { 
    	my($Domain, $User, $Group) = @_; 
    return Win32::NetAdmin::GroupIsMember(
                          $Domain, $Group, $User) 
        || Win32::NetAdmin::LocalGroupIsMember(
                          $Domain, $Group, $User); 
}	 

An administrator could use a tab-delimited text file or database to store information about users, so that if he ever had to reload the user database into the NT servers, it would be this simple:

open(FILE, "< users.txt") || die "Couldn't open ($!)"; 
@Users = <FILE>; 
close(FILE); 
foreach $Data (@Users){ 
    	chop $Data;
    	($Domain, $User, $Password, $HomeDir, $Password,
		$Comment) = split("\t", @Users);
		Win32::NetAdmin::UserCreate( 
      		  $Domain, 
      		  $User,			
      		  $Password, 
      		  $PasswordAge, 
      		  USER_PRIV_USER,	                   # Privileges 
      		  $HomeDir,	
      		  $Comment, 
      		  UF_NORMAL_ACCOUNT | UF_SCRIPT,	   # Flags 
      		  "c:\\scripts\\$User.bat") 	           # Logon script
		|| print "$User was not added.\n"; 
}

Just a few notes about the Win32::NetAdmin::UserCreate() function shown above: $Domain can be a domain name, a server or an empty string (the default domain); $PasswordAge is ignored and will not be used; the privilege must be USER_PRIV_USER (administrator privileges are assigned by group membership); and for the flags you must at specify at least these two flags ORed together: UF_NORMAL_ACCOUNT and UF_SCRIPT. Other flags that you can use are:

        UF_ACCOUNTDISABLE        Disable the account 
	UF_PASSWD_NOTREQD        No password is required 
	UF_PASSWD_CANT_CHANGE    User can't change the password
	UF_DONT_EXPIRE_PASSWD    The password never expires 

A relative to this extension is my Win32::AdminMisc extension, which provides some additional administrative methods. Among the additional functions are the ability to:

  • Log on as another user (impersonate users)
  • Create a process as another user
  • Check user's passwords
  • Change users passwords
  • Get and set a wider range of information about a user than the NetAdmin extension allows
  • Retrieve system information such as physical memory, service patch level, and physical drive information

The Registry. Win32 platforms have a configuration database called the registry, a central repository for information about system, user, and application configurations. Nearly all operating system values are stored in this database, from the type of your mouse to your IP address. The Win32::Registry extension allows you access to registries on both local and remote machines. Once you know the basic nature of the Registry, the extension is easy to use. For instance, this code has Windows automatically log on the next time the log on screen appears:

use Win32::Registry; 
if ($HKEY_LOCAL_MACHINE->Create( 
    "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\WinLogon",
    $Key)) { 
    	$Key->SetValueEx("DefaultUserName", 0, REG_SZ,
                     "picard"); 
    $Key->SetValueEx("DefaultDomainName", 0, REG_SZ, 
                     "enterprise"); 	
    $Key->SetValueEx("DefaultPassword", 0, REG_SZ,
                     "hair"); 
    $Key->SetValueEx("AutoAdminLogin", 0, REG_SZ, 1);
    $Key->Close(); 
}

ODBC. The Win32::ODBC extension has become quite popular for both administration and web scripting. It's an interface into the ODBC API that allows your script to talk to heterogeneous databases - provided you have ODBC drivers for them. The extension is easy to use: you connect to a database, submit a query, retrieve the results, and close the database. Here's a simple example that retrieves a user's name and phone number from a fictitious database called PhoneNumbers:

Installing the Activeware Port

 
use Win32::ODBC; 
$O = new Win32::ODBC("PhoneNumbers") || die; 
if (!($O->Sql("select Name, Phone from List"))) { 
    	while($O->FetchRow()) { 
        undef %Data; 
        		%Data = $O->DataHash(); 
        print "$Data{Name}\t$Data{Phone}\n"; 
    	} 
} else { 
    print "Error: ". $O->Error(); 
}
$O->Close(); 

You can, however, use other functions to make more sophisticated multi-cursor queries. The ODBC extension assumes that you have at least a rudimentary understanding of SQL. Information can be found on my web site: https://www.roth.net/odbc.

Other Extensions. Some other popular extensions:

  • ChangeNotification. Detects changes in files and directories.
  • EventLog. Gives read and write access to the event logs.
  • Semaphore. An interface into the Win32 IPC API: semaphores, mutexes, and the like.
  • NetResource. Manipulate shared network resources such as directories and printers.

The following six extensions are available from Aldo Capini's web site ( https://www.divinf.it/DADA/PERL ):

  • Clipboard. Access into the Windows clipboard.
  • Console. Control over a console window.
  • Internet. Access to the Internet API giving functions for FTP, HTTP, and other protocols.
  • Service. Interface into the Win32 service manager.
  • Shortcut. Access to creating and manipulating Windows 95 and Windows NT 4.0 shortcuts.
  • Sound. Lets you play .wav sound files.

These five are available at the Roth Consulting FTP site (ftp://ftp.roth.net/pub/ntperl/):

  • Dialog. An interface to writing Win32 dialog boxes with buttons, edit boxes, and other goodies.
  • GD. The Win32 port of the GIF graphics library.
  • Message. Lets you send network messages.
  • ODBC. Access to databases via ODBC.
  • Pipe. Access to named pipes.

On https://www.netaxs.com/~joc/perlwin32.html, Joe Casadonte maintains FUtils, various file utilities that aren't part of Perl. And finally, Monte Mitzelfelt (monte@conchas.nm.org) maintains FileSecurity, an interface to file permissions.

Conclusion

Win32 Perl is a great way to automate your Win32 platform. If flexibility, power, and price aren't enough to convince NT administrators to give Win32 Perl a try, the enjoyment of programming in Perl should be.


When Dave Roth isn't collapsing wave functions he is preaching Perl to his clients. He can be reached at rothd@roth.net.

listing 1

In Search Of The Win32 Guru
Dave Roth (1997) Win32 Perl. The Perl Journal, vol 2(3), issue #7, Fall 1997.
In Search Of The Win32 Guru

Getting help for Win32 Perl is easy. ActiveState's listserver has been a sounding board for questions, suggestions, and conversations related to the port. You can join any of six mailing lists: Perl-Win32-Users, Perl-Win32-Porters, Perl-Win32-Announce, Perl-Win32-Web, Perl-Win32-Admin, or Perl-Win32-Database. Just send mail to listmanager@activestate.com with a body containing subscribe name_of_mailing_list.

The comp.lang.perl.misc and comp.lang.perl.modules Usenet groups are also good sources of advice. These forums discuss all Perl platforms; don't assume that everyone reading your post will know that you're talking about the Win32 port. There are a few informative web pages for Win32 Perl. Here are five of my favorites:

  • The Perl-Win32-Archive by Aldo Calpini (https://www.divinf.it/perl-win32/index.sht), a search engine for the Win32-Perl-Users listserver.
  • Evangelo Prodromou's Perl for Win32 FAQ at: https://www.endcontsw.com/people/evangelo/Perl_for_Win32_FAQ.html.
  • The how-to guide to Win32 extensions by Philippe Le Berre, located at: https://www.inforoute.cgs.fr/leberre1/main.htm.
  • The great "Perl for Win32" resource by Joe Casadonte Jr. at https://www.netaxs.com/~joc/perlwin32.html.
  • Activeware's web site (https://www.activeware.com), where you can find binaries, source, samples of PerlScript, and assorted words of wisdom.
There's also a new book by O'Reilly, Learning Perl for Win32, that should hit shelves by the time this article goes to press.

listing 2

What about DOS?
Dave Roth (1997) Win32 Perl. The Perl Journal, vol 2(3), issue #7, Fall 1997.
What about DOS?

Anonymous benefactors have created a Perl 5 for DOS distribution available on the CPAN as ports/msdos/dosperl.zip. Based on Ilya Zakharevich's OS/2 port, it includes all the libraries and files needed to implement a semi-complete Perl 5 under DOS and Windows 3.1. A native DOS port will be available by the time you read this.

listing 3

Perl and ActiveX
Dave Roth (1997) Win32 Perl. The Perl Journal, vol 2(3), issue #7, Fall 1997.
Perl and ActiveX

Several people have independently created Win32 Perl ActiveX and VCL controls. These promise to bloom into full fruit when Perl multithreading is complete, but soon it will be possible to embed a fully functional Perl engine in your C++Builder, Delphi, Visual Basic or PowerBuilder programs. This will be announced in TPJ and comp.lang.perl.misc.

listing 4

Installing The Activeware Port
Dave Roth (1997) Win32 Perl. The Perl Journal, vol 2(3), issue #7, Fall 1997.
Installing The Activeware Port

You can always find the latest build of Win32 Perl at Activeware's web site, https://www.activeware.com/. Once you've downloaded the binary (it's a self-extracting archive), it's ready to be executed. The archive extracts files to wherever you specify and then installs itself by adding three keys to the registry:

HKEY_LOCAL_MACHINE\SOFTWARE\Activeware\Perl5\
BIN : REG_SZ : path to the Perl bin directory
HTML-DOCS : REG_SZ : path to the docs directory
PRIVLIB : REG_SZ : path to the Perl lib directory

It also maps an extension to the executable so if you type test.pl on a command line it will be the same as if you had typed perl test.pl. This extension mapping won't work in versions of NT lower than 4.0 unless you've enabled Command Extensions for your command interpreter (DOS, presumably). This is done by editing this registry key (you may need to create it): HKEY_CURRENT_USER\Software\Microsoft\CommandProcessor\EnableExtensions.
A REG_DWORD of 0x1 will enable the extensions; 0x0 will disable them.

Martin Krzywinski | contact | Canada's Michael Smith Genome Sciences CentreBC Cancer Research CenterBC CancerPHSA
Google whack “vicissitudinal corporealization”
{ 10.9.234.151 }