Using Perl on Win32 for Fun and Profit

People often ask me if Perl is as versatile on Win32 as it is on UNIX. They wonder if it is really possible to use all the different 3-letter technologies like COM, DDE, OLE, GUI etc. from Perl. Is Perl on Win32 really the glue language of choice, just like on UNIX? I would say: Yes, it is!

I'm going to present some of the Can I... type questions I often get asked either privately or on the Mailing Lists. I'll try to show some small sample code to demonstrate that indeed it is possible to do this with Perl on Win32. I'll throw in some explanations as well as references to resources and alternate solutions.

The samples have been selected with the following criteria in mind:

The questions I'm trying to answer are:


Can I create standalone applications in Perl?

You want to create a single executable for your application? One that you can copy to another machine which might not even have Perl installed, and it will just work? Yes, you can do it! There are at least two choices available. However, they are both commercial:

I'll give you an example using PerlApp. Let's use the classical Hello.pl program:

    printf "Hello World!\n";

To compile this into an EXE file you simply say:

    PerlApp Hello.pl

This creates a Hello.exe file of 6,686 bytes. But this executable still relies on having a Perl installation on the machine it is running. If we want to be able to run it on any old Win32 machine, we have to create a freestanding executable. It will contain the complete Perl interpreter as well as any other modules and DLLs required to run.

    PerlApp -freestanding -script=Hello.pl -exe=Hello2.exe

Our new Hello2.exe is now 862,208 bytes large. That's the price we have to pay to get a portable self-reliant executable.

Note: PerlApp only works on Windows NT. The compiled program will be able to run on all Win32 systems, but the API calls needed to create the executable are not available on Win95. I believe that Perl2Exe doesn't have this limitation.


Can I call arbitrary DLL functions from Perl?

You want to call a function written in another language. You only have the API specification and a DLL. Can you call it from within Perl? Yes of course, you can!

You can always write an XS extension module to provide a Perl interface (start with h2xs). This is supported on Win32, but what if you don't have a C compiler? Or you don't want to go to all the trouble of producing a complete module because it's only for a quick and dirty hack. You can use the Win32::API module by Aldo Calpini to do it. I'll be reusing the example from Aldo's docs here. Don't think of it as plagiarism, think of it as reuse, which is supposed to be a virtue (did I already mention CPAN?).

The example is calling the Win32 GetTempPath() function. You can look up the function prototype in the Microsoft Platform SDK:

  DWORD GetTempPath(
      DWORD nBufferLength,  // size, in characters, of the buffer
      LPTSTR lpBuffer       // pointer to buffer for temp. path
  );

The function takes two parameters: a 4 byte integer containing the size of the buffer and a pointer to the buffer itself. It returns the length of the string, not counting the final \0 character. We can create a Win32::API object for this prototype:

  use strict;
  use Win32::API;

  my $GetTempPath = new Win32::API "kernel32", "GetTempPath", [qw(N P)], 'N';

This tells the Win32::API module that the required function is to be found in the kernel32.dll file. Its name is GetTempPath and the arguments consist of a number followed by a pointer. It will return a number as the result. Let's call the function:

  my $path = ' ' x 256;
  my $len = $GetTempPath->Call(length $path, $path);

We first have to make sure that the buffer argument is big enough to receive the string because Perl will not automatically resize the buffer for us; Perl itself knows nothing about the GetTempPath() function. We use the Call() method of the Win32::API object to actually call the function.

  if ($len == 0) {
      print "GetTempPath() failed: $^E\n";
  }
  elsif ($len > length $path) {
      print "Buffer too small; we need $len bytes\n";
  }
  else {
      $path = substr($path,0,$len);
      print "Temp path is $path\n";
  }

One of three things might have happened: An error occured, in which case GetTempPath() will return 0. Let's print the $^E variable to see the extended error information (same as Win32::GetLastError). If $len is larger than the actual size of our buffer then we didn't get the TempPath either: we should enlarge our buffer and call the function again. Finally, it is possible that the function call succeeded. In this case we still have to resize the buffer to the correct size; it is still 256 bytes long with possibly garbage after the first $len+1 bytes. I use the substr() function because I already know the correct size. What do you do when the API function doesn't return the used size but just puts a zero terminated string into the buffer? You just delete everything from the first \0 byte on:

  $path =~ s/\0.*//;

It is possible to call much more complicated APIs, which might e.g. take pointers to structures as arguments. You should become very familiar with pack() and unpack() before you try those at home. I must also warn you that any mistakes you make either in the prototype definition or in the actual packing of your parameters might lead to an access violation. This is not a bug in Perl. You are supposed to know what you are doing when you call arbitrary functions via Win32::API.


Can I access COM / OLE objects from Perl?

Yes, you can! The Win32::OLE modules allows you to control any object supporting OLE Automation. I've already written millions of samples using Microsoft Excel or Word as the automation server; please look for them in the Mailing Lists archives. I'm going to play with something else this time: Voice output. I'll be using the Speech.VoiceText component from the Microsoft Speech SDK.

This sample creates a tied filehandle called SPEECH. Everything printed to this handle will not only appear on STDOUT but will also be read aloud by the speech synthesizer.

  use strict;
  use Win32::OLE;
  $| = 1;

  package Speech;
  sub TIEHANDLE {
      my $voice = Win32::OLE->new('Speech.VoiceText');
      $voice->Register('', 'Perl');
      bless \$voice => shift;
  }
  sub PRINT {
      my $voice = shift;
      my $text = join('', @_);
      print STDOUT $text, "\n";
      $$voice->Speak($text, 0);
  }
  sub DESTROY {
      my $voice = shift;
      sleep(1) while $$voice->IsSpeaking;
  }

Speech output is very much slower than video output. The VoiceText object runs in a seperate thread. If we would destroy our last reference to the VoiceText object as soon as the program is done then we might very well not receive any audio output at all. That's why the DESTROY method polls the VoiceText object and sleeps until it is no longer speaking.

  package main;
  tie *SPEECH, 'Speech'; END {untie *SPEECH}

Note that I create an END block to make sure that the SPEECH handle is untied before the program finally terminates. I'm doing this because the sequence of object destruction at Perl's global cleanup is quite indeterminate. The END block makes sure that the VoiceText object still exists when the SPEECH DESTROY method is called.

  print SPEECH "It is now ", scalar localtime;

The previous statement tells us the current time. Notice how the speech engine automatically expands the weekday and month names (for some reason it doesn't seem to know about 'Sat' and 'Sun' though). It also detects the time format and adds the words ``hours'', ``minutes'' and ``seconds'' automatically.


Can I talk DDE to other applications?

Your program doesn't support OLE Automation? You would like to try DDE instead? You can do that, using the Win32::DDE module! DDE is a deprecated protocol as far as Microsoft is concerned, but sometimes it is the only way to talk to another application (e.g. to Aldus PageMaker, which doesn't support OLE Automation).

On Windows 3.1 you could use DDE to control the Program Manager: You could add groups to the manager, and icons to the groups. This is better done through the Shell API on Win32 (e.g. using Win32::Shortcuts by Aldo Calpini). But the old PROGMAN interface is still supported to allow old 16 bit applications to register their shortcuts and folders. Here is a sample that uses DDE to query PROGMAN for all groups and then for all items in every group:

  use strict;
  use Win32::DDE::Client;

  my $Client = new Win32::DDE::Client 'PROGMAN', 'PROGMAN';
  die "Unable to initiate conversation" if $Client->Error;

  foreach my $group (split /\r\n/, $Client->Request('GROUPS')) {
      print "$group\n";
      print "    $_\n" foreach split /\r\n/, $Client->Request($group);
  }
  $Client->Disconnect;

The data is returned as multiple lines, separated by carriage return / line feed. The split() function easily converts them to a Perl list.

This script creates a lot of output. I'll show you only the entry for ActivePerl from my system (yes, the lines are rather long):

  ActivePerl
      "ActivePerl",H:\WINNT\Profiles\All Users\Startmenü\Programme\ActivePerl,1,1,1
      "Online Documentation","I:\Perl\html\INDEX~1.HTM",I:\Perl\html,H:\PROGRA~1\Plus!\MICROS~1\iexplore.exe,32,32,0,0,0

Note: The Win32::DDE module doesn't support warm or hot links, which provide automatic notification whenever the data has changed. Only cold links are implemented, which return the current value of the data.


Can I embed other scripting languages into Perl?

I fail to see why anyone might want to do this. But if you are sure you know what you are doing, well, you can do this of course. Use the Microsoft Script control. You can can use any ActiveX scripting engine with it:

  use strict;
  use Win32::OLE;

  my $script = Win32::OLE->new('ScriptControl');
  my $hash = Win32::OLE->new('Scripting.Dictionary');
  $hash->Add(Name => 'Perl');

When the Visual Basic people saw the Perl hash construct they found it so cool they wanted to have it too. So they created their Scripting.Dictionary object, which they document as: ``A Dictionary object is the equivalent of a PERL [sic] associative array''. I'm using it to show how we are sharing global variables with the script. Let's start with a VBScript sample:

  $script->{Language} = 'VBScript';
  $script->AddObject('Dict', $hash);
  $script->AddCode(<<VBS);
      Function Concat(Prefix)
          Concat = Prefix & " " & Dict.Item("Name")
      End Function
  VBS
  print $script->Run('Concat', 'Hello'), "\n";

Setting the Language property of the ScriptControl tells it which scripting engine to use. The AddObject() method asks it to publish the $hash object to VBScript under the name Dict. AddCode() defines the enclosed VBScript function and Run() finally invokes it. The script will of course print ``Hello Perl''. Now that we are done with this, can we also invoke a little JScript function?

  $script->{Language} = 'JScript';
  $script->AddObject('Script', $script);
  $script->AddCode(<<JS);
      function Concat(Prefix)
      {
          return Prefix.concat(" from ").concat(Script.Language);
      }
  JS
  print $script->Run('Concat', 'Hello'), "\n";

Changing the Language property will reset the ScriptControl. Previously added objects or code sections are gone and we have to set up everything again. If you want to use VBScript and JScript simultaneously then you must create 2 script controls. You can still share global data between them by adding the same object to both controls.

In this case I've added the script control $script itself to the object. The JScript function accesses the Language property to return ``Hello from JScript''.

You can also instantiate J++ objects via COM monikers from Perl (note: J++ is not the same as JScript, it is Microsoft's version of Java):

  use Win32::OLE;
  my $Date = Win32::OLE->GetObject("java:java.util.Date");
  print $Date->toString;

It prints:

  Sat Jul 03 14:37:42 GMT+02:00 1999

Unfortunately this works only for constructors without arguments.


Can I embed a Perl interpreter into my application?

You have an application and want enhance it with an integrated programming language? Or you want to reuse code form CPAN in your C++ or Delphi program? That's not a problem; you can do it. There is a gotcha however: The ActivePerl executable is compiled with C++ to get PERL_OBJECT support (don't worry if you don't know what this is). Unfortunately this makes it rather hard to embed this binary directly; the normal embedding instructions won't work. This is expected to change for Perl 5.006. In the meantime you have several options:


Using PerlEz: Accessing Yahoo Quotes from C

First I'll show you an example using PerlEz. It is written in C and uses the YahooQuote module to retrieve stock prices over the internet.

  #include <stdio.h>
  #include <windows.h>
  #include <PerlEz.h>

  char *symbols[] = {"CPQ","IBM","MSFT",0};

  int main(void)
  {
      char cmd[80];
      char buffer[200];
      char **symbol = symbols;
      PERLEZHANDLE perl = PerlEzCreate(NULL, "-MFinance::YahooQuote");

      if (!perl)
          return 1;

      while (*symbol) {
          sprintf(cmd, "join ' ', (getonequote('%s'))[3,4,2]", *symbol);
          if (PerlEzEvalString(perl, cmd, buffer, sizeof(buffer)) == 0)
              printf("%-4s %s\n", *symbol, buffer);
          ++symbol;
      }

      PerlEzDelete(perl);
      return 0;
  }

The PerlEzCreate() function creates an instance of the Perl interpreter. The first parameter could be the filename of the script to load. I don't need it this time. The second parameter specifies the command line options. I use it to load the Finance::YahooQuote module.

Now PerlEzEvalString() is called in a loop once for each symbol. The string to be evaluated is constructed on the fly. It calls the getonequote() function from the module for every symbol. The return value is a list of fields. We are using only the elements 3, 4 and 2 in turn (using a list slice). These are the trade date, trade time and trade price respectively. A space separated list of these 3 values is returned to the C program.

I used the following little batch file to compile the program:

  set INCLUDE=I:\Perl\lib\CORE;%INCLUDE%
  set LIB=I:\Perl\lib\CORE;%LIB%
  cl -W3 embed.c -link -nodefaultlib PerlEz.lib PerlCRT.lib

I have installed Perl into I:\Perl. I had to tell the VC++ compiler where to look for the PerlEz include and library files. The output of embed.exe looks like:

  CPQ  7/2/1999 4:02PM 23.25
  IBM  7/2/1999 4:01PM 132.25
  MSFT 7/2/1999 4:01PM 92


Using PerlCOM: Babelfish for Microsoft Word

My other example shows you how I have used PerlCOM from the Perl Development Kit to embed Perl into Microsoft Word. It adds additional commands to Word to access the AltaVista Babelfish translator. The following code is actually the install/uninstall program for this feature. Let's walk through a lttle chunk at a time.

The first part loads all the required modules. It reads the Automation constants from the type libraries and starts an instance of the Word application:

  use strict;
  use Getopt::Long;
  use Win32::OLE qw(in with);
  use Win32::OLE::Const 'Microsoft Office';
  use Win32::OLE::Const 'Microsoft Visual Basic for Applications Extensibility';

  GetOptions(Uninstall => \my $Uninstall);
  my $Word = Win32::OLE->new('Word.Application', 'Quit');

Now the code will remove any previous installation of the Babelfish code. It'll remove the Babelfish menu entry from the Tools menu and it'll also remove the code module from the standard document template (NORMAL.DOT).

  # Uninstall menu entries and code module from NORMAL.DOT
  Win32::OLE->Option(Warn => 0);
  # Remove menu entry
  my $Control = $Word->CommandBars('Tools')->Controls('Babelfish');
  $Control->Delete if defined $Control;
  # Remove code module
  my $Components = $Word->NormalTemplate->VBProject->VBComponents;
  my $Component = $Components->Item('Babelfish');
  $Components->Remove($Component) if defined $Component;
  exit if $Uninstall;

If the user specified the -uninstall switch then we are already done. Otherwise we now have to add a new component to the template for our code module.

  Win32::OLE->Option(Warn => 1);
  my %Components = map { $_->{Name} => 1 } in $Components;
  # Create new code component; unfortunately Add() doesn't return the object
  $Components->Add(vbext_ct_StdModule);
  # Find new component in collection. This is probably the last element,
  # but the documentation doesn't guarantee it.
  my $CodeModule;
  foreach my $Component (in $Components) {
      unless ($Components{$Component->Name}) {
          $Component->{Name} = 'Babelfish';
          $CodeModule = $Component->CodeModule;
          last;
      }
  }
  die "Couldn't rename new module to 'Babelfish'" unless defined $CodeModule;

With the preliminaries out of the way we can finally insert the VBA function that contains the actual embedding:

  # Create main VisualBasic "Babelfish" function
  $CodeModule->AddFromString(<<'EndOfSub');
  Sub Babelfish(Source As String, Dest As String)
      If Selection.Start = Selection.End Then Exit Sub
      Set PerlCOM = CreateObject("PerlCOM.Script")
      Set Fish = PerlCOM.CreateObject("WWW::Babelfish", "new")
      NewText = Fish.translate("source", Source, "destination", Dest, _
                               "text", Selection.Text)
      Selection.Delete
      Selection.InsertAfter NewText
  End Sub
  EndOfSub

The Babelfish() function takes two parameters: the current language and the new language. It returns immediately if no text has been selected. Otherwise it creates an instance of the PerlCOM object. It then uses the CreateObject() method of PerlCOM to wrap the WWW::Babelfish module into another COM object. It used the new constructor to create the object. All Perl methods of the module are now also available as OLE methods on the Fish COM object. We can directly call the translate() method to do the actual work. The current text selection is deleted and replaced by the translated text.

We still have to create menu entries for all the available translations. We'll first create a submenu at the top of the Tools menu and call it BabelFish.

  # Create main "Babelfish" entry in the "Tools" menu
  my $Tools = $Word->CommandBars('Tools')->Controls;
  my $Menu = $Tools->Add({Type => msoControlPopup, Before => 1});
  $Menu->{Caption} = 'Babelfish';

Then we create submenues under Babelfish to invoke the specific translation, e.g. ``English to German''.

  # Create a submenu entry and a VB function for each possible translation
  foreach my $Direction (0..1) {
      foreach my $Language (qw(French German Italian Portuguese Spanish)) {
          my ($Source,$Dest) = $Direction ? ('English', $Language)
                                          : ($Language, 'English');
          # Create Visual Basic function for this translation
          $CodeModule->AddFromString(<<"EndOfSub");
  Sub ${Source}_$Dest()
      Babelfish "$Source", "$Dest"
  End Sub
  EndOfSub
          # Add submenu item for this language pair
          with($Menu->CommandBar->Controls->Add({Type => msoControlButton}),
               Caption  => "$Source to $Dest",
               OnAction => "Babelfish.${Source}_$Dest");
      }
  }

I had to create additional little VBA functions because the OnAction menu property can only be set to a function name; you cannot specify additional parameters.

So what happens when the user clicks on the ``English to German'' menu entry? Word will invoke the Babelfish.English_German function which in turn calls Babelfish.Babelfish("English", "German"). This function now creates a Perl interpreter and loads the WWW::Babelfish module, which in turn uses LWP::* to send the selected text to the AltaVista web page. This sounds rather complicated, but in practise it is very fast.


Can I create an ActiveX control in Perl?

Ok, so using all kinds of components from Perl or using Perl as a component is all cool and works. But how about creating ActiveX controls directly in Perl? No problem either! You can either use PerlCtrl from the Perl Development Kit or Scriptlets from the Microsoft Script components. The later however don't allow you to create standalone controls. You must have Perl installed on every computer you intend to deploy your Perl scriptlet.

In ``Can I embed other scripting languages into Perl?'' I told you that the Visual Basic people were so envious of the Perl hash that they created an object to clone it. Well, they have done it again: They decided that they must have Perl-like regular expressions too. They included a RegExp object in VBScript 5.0. I'll show you how that object could have been written as a Perl ActiveX control (via PerlCtrl). I didn't implemented the Execute() method to keep the sample short:

  package Perl::RegExp;

  # Properties
  $Global     = 1;
  $IgnoreCase = 1;
  $Pattern    = '';

  # Methods
  sub Test {
      my $Target = shift;
      my $Flags = $IgnoreCase ? '(?i)' : '';
      $Flags .= '(?g)' if $Global;
      return ($Target =~ /$Flags$Pattern/) ? 1 : 0;
  }

  sub Replace {
      my ($Target,$Replacement) = @_;
      my $Flags = $IgnoreCase ? '(?i)' : '';
      $Flags .= '(?g)' if $Global;
      $Target =~ s/$Flags$Pattern/$Replacement/;
      return $Target;
  }

The object has 3 properties: Global, IgnoreCase and Pattern. The first two correspond to the /g and /i regexp modifiers. The Test() method takes a Target argument and returns whether this matches against the Pattern property or not. The Replace() method takes an additional Replacement argument and returns the Target after substituting the Pattern property string by the Replacement. It's quite obvious actually.

The rest of the code is mostly boilerplate that I generated by running:

    PerlCtrl -t > regexp.pl

I edited it to correct the control names etc. and to provide definitions for my methods and properties.

  =POD
  =BEGIN PerlCtrl
      %TypeLib = (
          PackageName     => 'Perl::RegExp',
          TypeLibGUID     => '{05792760-31F5-11D3-9C88-000000000000}',
          ControlGUID     => '{05792761-31F5-11D3-9C88-000000000000}',
          DispInterfaceIID=> '{05792762-31F5-11D3-9C88-000000000000}',
          ControlName     => 'Perl.RegExp',
          ControlVer      => 1,
          ProgID          => 'Perl.RegExp',
          DefaultMethod   => '',
          Methods         => {
              'Test' => {
                      RetType             =>  VT_BOOL,
                      TotalParams         =>  1,
                      NumOptionalParams   =>  0,
                      ParamList           =>[ 'Target' => VT_BSTR]
                  },
              'Replace' => {
                      RetType             =>  VT_BSTR,
                      TotalParams         =>  2,
                      NumOptionalParams   =>  0,
                      ParamList           =>[ 'Target'      => VT_BSTR,
                                              'Replacement' => VT_BSTR ]
                  },
              },  # end of 'Methods'
          Properties      => {
              'Global' => {
                      Type                => VT_BOOL,
                      ReadOnly            => 0,
                  },
              'IgnoreCase' => {
                      Type                => VT_BOOL,
                      ReadOnly            => 0,
                  },
              'Pattern' => {
                      Type                => VT_BSTR,
                      ReadOnly            => 0,
                  },
              },  # end of 'Properties'
          );  # end of %TypeLib
  =END PerlCtrl
  =cut

The completed control can be compiled and installed with:

  PerlCtrl regexp.pl
  regsvr32 regexp.dll

Now it's time to test it. Here is a simple VBScript test program:

  Set RegExp = CreateObject("Perl.RegExp")
  RegExp.Pattern = "Fox"
  Wscript.Echo RegExp.Replace("The quick brown fox", "cat")
  RegExp.IgnoreCase = False
  Wscript.Echo RegExp.Replace("The quick brown fox", "cat")

I ran it using Windows Scripting Host:

  cscript //nologo regexp.vbs
  The quick brown cat
  The quick brown fox

It works as expected. The size of regexp.dll is 15,360 bytes. Creating a freestanding control inflates the size slightly to 1,095,168 bytes. But this includes Perl itself and all necessary DLLs and modules. So Perl is obviously not well suited for small applets to be downloaded over the web. But for standalone components implementing business logic it is a valid choice.


Can I create a GUI application in Perl?

You want to create an application with a graphical user interface? With menus, toolbars, dialog boxes and all that jazz? No worries, you'll do this just fine in Perl. On Win32 there are two popular packages for this: Tk and Win32::GUI.

Tk is the cross-platform choice. Your GUI code will run on UNIX too and even use the native look and feel on each system. Tk can be installed with the Perl Package Manager. Please look at the included widget demo program to see examples of the available interface controls.

The Win32::GUI module is still in beta, but some people do prefer it to Tk for Win32-only programs. One advantage is that Win32::GUI provides native look and feel even for complex widgets like the tree view. If you check out Win32::GUI don't forget to get the samples too to see how it is used.

I'm not including my own sample here because simple examples look kind of dull and fancy examples require lots of lines of code. Please look at the existing demo programs for these packages.


Can I create client side dynamic HTML in Perl?

If you've followed along all this time, then you probably know the answer already. Your can, if your browser supports ActiveX scripting engines. I'm including a fun sample. It is a translated version of the Microsoft Speech SDK demo from the Microsoft Speech SDK/Samples/ActiveX/WEB/Lady directory: It is a frame containing the JPG image of a woman as the background. This is overlaid with a Talking Mouth ActiveX control. I've only translated the JavaScript to PerlScript. Here is the Lady1.htm file:

  <HTML>
  <BODY BACKGROUND=SQGIRL12.JPG TOPMARGIN=0 LEFTMARGIN=0>
  <FORM ACTION="" NAME="Form">
  <TABLE CELLPADDING=0 CELLSPACING=0 BORDER=0>
  <TR><TD WIDTH="298" HEIGHT="363"></TD></TR>
  <TR><TD WIDTH="280" HEIGHT="100"></TD>
      <TD><OBJECT NAME="ActiveVoice" TYPE="application/x-oleobject"
                  CLASSID="clsid:EEE78591-FE22-11D0-8BEF-0060081841DE"
                  HEIGHT=120 WIDTH=135></OBJECT></TD></TR>
  </TABLE><BR>
  <TABLE><INPUT type="button" value="Say it" onCLick="SayIt()">
         <INPUT type="text" name="Text" size=30></TABLE>
  </FORM>
  </BODY>

I have wrapped the part above into a FORM so that it is easier to address the input elements from PerlScript. Note that the onClick action above is still in JavaScript, although you cannot tell from just looking at it.

Now follows the SayIt() function in PerlScript as well as the initialization code for the mouth object:

  <SCRIPT LANGUAGE="PerlScript">
    sub SayIt { $Form->ActiveVoice->Speak($Form->Text->value) }

    $Form->ActiveVoice->with(
        Initialized       => 1, 
        LipTension        => 0,
        TonguePosn        => 0,
        TeethLowerVisible => 0, 
        TeethUpperVisible => 0,
        JawOpen           => 0,
        MouthUpturn       => 220,
        MouthWidth        => 240,
        MouthHeight       => 0);

    $Form->ActiveVoice->Speak(<<EOT);
        Good morning.  I am an example of how to use the active x controls
        on a web page.  You can type text in the box and I will speak it.
  EOT
  </SCRIPT>
  </HTML>

To play with the sample you need to copy the lady.htm, none.htm and sqgirl12.jpg files from the SAPI directory. The .htm files are only needed to prevent the tiling of the background image.

Caution: Although using Perl for client side scripting is cool, I would recommend not to enable it for external pages. There is no sandbox limiting what a malicious webpage might do to your machine. Please read the How can I configute PerlScript security section in the Using PerlScript chapter of the ActivePerl documentation.


Can I control a GUI application from Perl?

You have a program with a graphical user interface and you want to interact with it programmatically. Unfortunately the application doesn't support DDE or OLE Automation. There is also no IP port or any other interface to sneak commands to it. Can you still put it on remote control or do you have to get the mouse clicking away? No, you can still interact with the program from Perl using the Win32::Setupsup program. This module has several ways of waiting for windows or dialogs to appear on your desktop. And it can also send virtual key events to any window on the system.

Let's make a little nonsensical but fun example. Assume you don't want to be able to load any file called AUTOEXEC.* into Notepad. The sample program will just wait patiently until it detects a window with the giveaway titlebar string. It will then send the Genie Microsoft Agent to that window, have it wave its magic wand and ``poof!'' close the file:

  use strict;
  use Win32::OLE;
  use Win32::OLE::Variant;
  use Win32::Setupsup qw(WaitForAnyWindow GetWindowProperties SendKeys);

  my $Agent = Win32::OLE->new('Agent.Control.1');
  $Agent->{Connected} = Variant(VT_BOOL, 1);
  $Agent->Characters->Load("Genie","Genie.acs");
  my $Genie = $Agent->Characters("Genie");

I'm loading the Genie character upfront so that we don't have any delay when it needs to be activated. It starts up invisible.

  while (1) {
      if (WaitForAnyWindow("AUTOEXEC.*- Editor", \my $window, 60_000, 500)) {
          GetWindowProperties($window, ['rect'], \my %hash);
          $Genie->Show;
          $Genie->MoveTo($hash{rect}->{left}, $hash{rect}->{top});
          $Genie->Play("DoMagic1");
          $Genie->Speak('I told you \emp\not to look!');
          $Genie->Play("DoMagic2");
          sleep(10);
          SendKeys($window, '\alt+\D\alt-\N', 1);
          sleep(5);
          $Genie->Hide;
      }
  }

The WaitForAnyWindow() functions looks every 500 milliseconds for the offending window. It uses a regular expression match to find it. After 60 seconds it will give up, but I've put it into an endless loop to start over. If the window is found then the window handle will be stored into $window and WaitForAnyWindow() will return immediately with a true value.

The GetWindowProperties() function is used to determine the exact location of the window, its screen rectangle. The Genie character magically appears and moves straight to the top left corner of the window. It waves its wand and tells you ``Tsk, Tsk''. The \emp\ characters are markup to emphasize the next word. The DoMagic2 animation will create the final ``poof!'' cloud and the gong sound. All these things happen asynchronously, so we have to wait a little bit for the animations to finish. I should have used OLE Events here, but somehow couldn't make it work. The MS Agent control is a bit peculiar when it is not instantiated from within a fully blown control container.

Coincidently (I hope) with the gong sound the file will be closed. SendKeys() sends the following keyboard events to the window with the handle $window: Alt key down, D key, Alt key up, N key (the string \alt+\ marks the keydown code and \alt-\ the corresponding keyup code). I'm using a German version of Notepad, so the Alt-D invokes the Datei menu (File) and the N key issues the Neu command (New), effectively closing the just opened file. Having done its work the Genie will fade into the background, but continues to watch you.

The Win32::Setupsup module is very useful for automating software installations. Some setup programs simply insist that you manually press a button. With this module you can stop that rude behavior.

Another interesting application for Win32::Setupsup is automatic regression testing of user interfaces.


Other questions

There are lots of other areas in which Perl is a very good choice on Win32. The solutions are often not Win32 specific, so I didn't include any samples for them. A few common application areas are:

Use standard internet protocols

The LWP::* and the Net::* modules work very well on Win32. You'll find the Net::* module in Graham Barr's libnet distribution on CPAN.

Use database

The canonical choice is of course Tim Bunce's database interface DBI with the various DBD::* drivers. You can also use Win32::ODBC or ActiveX Data Objects via Win32::OLE. Please subscribe to the Perl-Win32-Database Mailing Lists.


Resources


CPAN

The Comprehensive Perl Archive Network is the most valuable resource of every Perl programmer. You'll find a wealth of modules there, most of which will run on Win32 too. To install modules containing XS code you'll need a C compiler (Microsoft VC++ for ActivePerl). You can install precompiled distributions of popular modules with the Perl Package Manager. The central CPAN multiplexer resides here:

  http://www.perl.com/CPAN

You might prefer to use one of the following frontends for searching through CPAN:

  http://theoryx5.uwinnipeg.ca/cgi-bin/pause
  http://theory.uwinnipeg.ca/search/cpan-search.html

The ActivePerl binary for Win32 is not on CPAN. You can download it from:

  http://www.activestate.com/ActivePerl/


Mailing Lists

Other very important resources available to the Perl developer are the numerous mailing lists. You'll find a comprehensive summary of available lists at:

  http://www.perl.org/maillist.html

The ActiveState web site has more information about the lists hosted there. Please read the charters of the lists and follow the rules, e.g. don't send web questions to the database list! You can access these lists directly through the web interface:

  http://www.activestate.com/support/mailing_lists.htm

Before sending questions to the lists you should research carefully if the answer is not already in the FAQ or the archives of the lists. You might also want to check DejaNews first:

  http://www.deja.com/home_ps.shtml

When you have the impression that everyone is ignoring you in the news groups or the mailing lists, please check the following page by Mark-Jason Dominus:

  http://www.plover.com/~mjd/perl/Questions.html


Perl Package Manager

The Perl Package Manager let's you install binary distributions of Perl modules from an internet repository. You can find out more about the Perl Package Manager in your local ActivePerl docs. Please read the entry ActivePerl Components | Using PPM. You can also read it on the web at:

  http://www.activestate.com/ActivePerl/docs/Perl-Win32/perlwin32faq11.html

There is another webpage about PPM at:

  http://www.activestate.com/ppm/


Perl Development Kit

The Perl Development Kit is a commercial package by ActiveState. It includes PerlApp, PerlCOM, PerlCtrl and a visual Perl debugger. It is also available as a free upgrade to the O'Reilly Perl Resource Kit for Win32. You'll find out more at the product's webpage:

  http://www.activestate.com/pdk/


Perl2Exe

Perl2Exe is an alternative to PerlApp from the Perl Development Kit. In addition to Win32 it is also available for Linux and SunOS.

  http://www.perl2exe.com/perl2exe.htm


Win32::API

The Win32::API module is not included in the ActivePerl distribution. But you can easily install it using the Perl Package Manager:

  ppm install Win32-API

Win32::API has been written by Aldo Calpini (a.calpini@romagiubileo.it) The source code is available from Aldo's CPAN directory CPAN/Authors/Aldo_Calpini or from his homepage:

  http://www.divinf.it/dada/perl/

You'll find examples of Win32::API usage on Jan Krynicky's page:

  http://jenda.krynicky.cz/

Look especially at the Win32::FileOp module. Mike Blazer also has several modules built on top of Win32::API, e.g. the Win32::DriveInfo, Win32::File::Ver, or Win32::Clock modules:

  http://www.dux.ru/guest/fno/perl/


Win32::DDE

The Win32::DDE module can be installed using the Perl Package Manager:

  ppm install Win32-DDE

The module is currently being maintained by Doug Wegscheid (wegscd@whirlpool.com) You should be able to find the source code on CPAN.

You might also want to look at Jenda's Win32::Editor::PFE module which uses DDE to talk to the Programmer's File Editor.

  http://jenda.krynicky.cz/#Win32::Editor::PFE


Win32::GUI

The Win32::GUI module has been written by Aldo Calpini (see Aldo's contact information in the Win32::API entry). There is also a Win32::GUI mailing list. You can subscribe through the web interface (which also allows browsing of the archive) at:

  http://www.httptech.com/perl-win32-gui/

You can find a binary for ActivePerl on Jenda's page:

  http://jenda.krynicky.cz/#Win32::GUI

Don't forget to grab the examples too:

  http://jenda.krynicky.cz/perl/Win32-GUI-examples.zip

Jenda also describes how to get rid of the pesky DOS window for GUI programs:

  http://jenda.krynicky.cz/perl/GUIscripts.html


Win32::OLE

The Win32::OLE module comes already preinstalled in the ActivePerl distribution. I occasionally release a newer version in source code only to my CPAN directory CPAN/authors/id/J/JD/JDB. This will normally be included in the next ActivePerl build automatically.

I have written an article about Win32::OLE for The Perl Journal http://www.tpj.com/ which was published in issue #10 (Summer 1998). It contains a larger example using Microsoft Excel, ActiveX Data Objects and Lotus Notes.

Another article about the new experimental OLE Event support is published in issue #3 (July 1999) of the PerlMonth webzine http://www.perlmonth.com/


Win32::Setupsup

The Win32::Setupsup module has been written by Jens Helberg (jens.helberg@bosch.com) A binary distribution is available from Jenda's PPM repository:

  http://jenda.krynicky.cz/#PPM

You can also send an email message to Jens with a subject line saying "get:setupsup.zip" (without the quotes) and his email robot will send the module to you.

At the time of this writing there is a memory leak in the module. I hope that it'll be fixed by the time you read this. Make sure that you get a fairly recent version.


Babelfish

I'll include a little reference to Douglas Adams The Hitchhiker's Guide to The Galaxy just in case you really have no idea what a Babel fish is or looks like:

  http://www.cwd.co.uk/babel/adams.htm

The AltaVista search engine provides an automatic translation service for either plain text or complete web pages at:

  http://babelfish.altavista.com/

The WWW::Babelfish module by Daniel J. Urist (durist@world.std.com) connects to that website using LWP::*. It'll translate arbitrary text between any of the supported languages. This module is not Win32 specific. You can find it on CPAN at:

  http://www.perl.com/CPAN/modules/by-module/WWW/WWW-Babelfish-0.03.tar.gz


YahooQuote

The Yahoo! Finance service provides access to 15-20-minute delayed stock quotes:

  http://quote.yahoo.com/

The Finance::YahooQuote module by Dj Padzensky (djpadz@padz.net) interfaces to that site using LWP::*. It is not Win32 specific. You can download the module from its homepage:

  http://www.padz.net/~djpadz/YahooQuote/

The current version (as of this writing: 0.11) doesn't support access through a proxy. You can easily fix this by adding

  $ua->env_proxy;

to the getquote() function.


Microsoft Agent

Microsoft agents are ``interactive personalities'', ``an extension and enhancement of the existing interactive modalities of the Windows interface''. Read all about it at:

  http://msdn.microsoft.com/workshop/imedia/agent/default.asp

There is also a Microsoft Agent Web Ring. Do visit the Character Download Gallery. There are lots of funny Agent characters available for free downloading:

  http://www.msagentring.org/


Microsoft Script

The Microsoft Windows Script Technologies web page can be found at:

   http://msdn.microsoft.com/scripting/

It contains an ActiveX Script Control that allows you to host any ActiveX scripting language in your application. It lacks the special Perl support of PerlCOM from the Perl Development Kit. But then again it does support additional scripting languages like VBScript and JScript.

Another interesting technology to be found on that site are Scriptlets, which seem to be called Script Components this week. You'll find a discussion of Perl Scriptlets in The Perl Journal http://www.tpj.com/ issue #12 (Winter 1998). They are similar to PerlCtrl from the Perl Development Kit but cannot be used to create freestanding controls. On the other hand, they can (in princial) support additional interfaces beyond IDispatch.


Microsoft Speech

The Speech API SDK is available from Microsoft's Intelligent Interface Technologies website:

  http://www.microsoft.com/IIT/

I have to warn you that the SAPI SDK is fairly large: the x86 version has a download size of about 40Mb and requires about 90Mb harddisk space for installation. It includes not only text-to-speech support but also voice recognition. Take a look at Bruce Winters Mister House home automation system for an example of a Perl program receiving voice commands trough the telephone:

  http://members.home.net/winters/house/


Author

Jan Dubois (jan.dubois@ibm.net) - last change: 3. July 1999