The PerlCtrl utility converts a Perl program into a standalone ActiveX control. PerlCtrl is used to develop controls that can be automated from applications and languages that support ActiveX.
With PerlCtrl, you can:
PerlCtrl combines a Perl component, all of the required Perl modules, and a modified Perl interpreter into one binary unit. When the resulting control is run, it searches for modules within itself before searching the filesystem.
PerlCtrl is not a compiler. The Perl source code in the script and the embedded modules must still be parsed and compiled on the fly when the control is instantiated.
The main benefit of PerlCtrl is that it makes your Perl component available to any language and application that supports ActiveX. It can even be used on remote machines using DCOM.
The control can also be deployed to systems that either do not have Perl or do not have the correct combination of modules installed. Additionally, PerlCtrl ensures that your code is always executed by a specific version of Perl, even if the user has a different Perl version already installed. As a side benefit, it can be used for some degree of source code hiding.
The only argument PerlCtrl requires is the name of the Perl program that you want to convert. In most cases, this produces a working control. Additional options are described in PerlCtrl command-line documentation.
The generated control must also be registered with the operating system. This is accomplished using the regsvr32.exe utility.
First, PerlCtrl determines which modules and external files the converted
script depends upon. The PerlCtrl program begins by scanning the source code
of the script. When it finds occurrences of use, do or require, it
attempts to locate the corresponding module and then parse the source
of that module. This continues as long as PerlCtrl finds new modules to
examine.
PerlCtrl does not attempt to run the script. It does not automatically
determine which modules a statement like 'require $module;' might load.
In such cases, you can manually specify the additional modules to traverse
with the --add option.
The PerlCtrl program has some built-in heuristics for major Perl modules
that determine additional modules at runtime, like DBI, LWP, Tk.
PerlCtrl predicts the additional required modules so that they will
be available in freestanding controls.
PerlCtrl then determines which modules to include in the generated control.
Usually, all the located modules are included. This also includes the
dynamic object files (.so/.dll) and AutoLoader files (.al) that go
with the located modules. If the --dependent option is used, only
modules located under the directories given by the --lib option are
included.
Finally, the control is built with all the modules compressed (unless the
--nocompress option is used) and included. When the control runs, it
attempts to load any use, do and require statements that are
bundled inside of the application itself.
When the control built with PerlCtrl runs, it extracts its dynamic object
files in the /tmp/pdk directory. If the control was built using the
--clean option, PerlCtrl also appends the process id to this directory
name to avoid race conditions during cleanup. The directory location can be
overridden with the TMPDIR environment variable. On Windows, the
TEMP environment variable is used to override the location. It is also
possible to hardcode the location with the --tmpdir command-line option.
Unless the --clean option is used, extracted files are left behind
when the control terminates and reused by later incarnations
of the same control (or by other PDK-created executables).
PerlCtrl uses the PERLCTRL_OPT environment variable to set default
command-line options. PerlCtrl treats these options as if they were
specified at the beginning of every PerlCtrl command-line. Note: Perl must
be in your PATH if you want to use PERLCTRL_OPT.
All directories specified in the PERL5LIB environment
variable are treated as if they had been specified with the
--lib command-line option. Therefore, modules located in PERL5LIB
directories are included even in dependent executables. If PERL5LIB
is not set, PerlCtrl uses the value of PERLLIB instead (just like regular
Perl).
PerlCtrl pipes the output of perlctrl --help through the program
specified in the PAGER environment variable if stdout is a terminal.
The following environment variables will not be visible to the
control built with PerlCtrl: PERL5LIB, PERLLIB, PERL5OPT,
PERL5DB and PERL5SHELL.
The temporary extraction directory is automatically added to
the PATH environment variable when a file is bound using the [extract]
option.
The syntax for generating a control is: perlctrl control-name.ctrl
The name of the control must begin with a letter and can contain only letters, digits, and underscores.
While PerlCtrl makes it easy to start developing your own ActiveX Components, you should be familiar with certain aspects of Perl.
Any kind of non-trivial programming task in Perl requires an understanding of Perl's reference mechanism. This feature makes it easy to develop nested data structures, such as scalar variables that contain references to arrays, arrays whose elements are references to hashes, etc. Any scalar value can be a reference to data or code. Perl's Reference feature is a cornerstone of Perl's object-oriented functionality and is also used to construct the 'type' library that is a key part of your Perl Control.
When you pass a reference to an array, scalar, or hash from a client script to a PerlCtrl, a copy of your data is passed, not a true reference to your data. Thus, when you manipulate the data from within the PerlCtrl, you are not manipulating the client's data. The ability to use references in a traditional manner from within PerlCtrl will be addressed in a future version of the Perl Dev Kit.
For complete information on Perl References, see the perlref document in the Perl documentation suite.
Perl controls rely on Perl's package mechanism to create the code that makes up your component. See the perlmod document in the Perl documentation suite for information about how packages are handled in Perl; see the perlfunc document in the Perl documentation suite for information regarding the package statement. See the perlsub document for additional information.
You can use Perl modules to create class definitions. This feature can be used with PerlCtrl to create Perl Controls that are object factories. Instead of exposing the functionality of your objects, object factories return instances of the objects. For example, you could create a Perl control that exposes a single method and returns instances of HTTP connection objects. You could then invoke methods on each connection object, rather than on the Perl control itself. Information about Perl's object-oriented features can be found in the perltoot, perlobj, and perlbot pages of the Perl documentation.
To use a Perl control with Visual J++, you can use Microsoft's com.ms.Active x package, which lets you build Java applications that host ActiveX components. To create and use an instance of a Perl Control, you must:
invoke() method
You can use a control over DCOM with J++ to launch instances of controls across the network, but it is more complicated than launching a local copy of the control. The example provided with the Perl Dev Kit relies on some utility classes that are included with the Microsoft SDK for Java 3.1. These utility classes are wrappers around JDirect calls that directly invoke Win32 API functions.
PerlCtrl creates freestanding controls by default. The DLL that is generated
contains everything needed to run the control on any Windows machine. This
DLL is much larger than a DLL generated with the --dependent switch
because it contains the compiled script, the Perl runtime components, and any
extensions or modules.
PerlCtrl includes any modules mentioned in a require or
use statement. For example, if you have file A.pm that requires
B.pm, which requires C.pm, PerlCtrl loads all of these packages.
However, PerlCtrl will not load a module mentioned in a variable. For
example, if a script has the following line:
require $module;
PerlCtrl does not include the module identified by $module. To explicitly
include this module, rebuild the PerlCtrl using the --add <list>
command-line switch, where <list> is a semicolon-delimited list of
the modules you wish to explicitly include. PerlCtrl detects which DLLs have
been loaded by the *.pm files. However, if a DLL loaded by a .pm file
depends upon a second DLL, the second DLL is not bound into the executable.
Otherwise, PerlCtrl includes numerous system DLLs.
To install a freestanding control on another computer, copy the DLL you
generated with PerlCtrl to the computer, and register it with regsvr32.
For example:
regsvr32 mycontrol.dll
When the control is registered, a message indicates that the control was installed successfully. The control can then be launched from programs running on that machine, or even by programs running on other machines that connect with it using DCOM.
Most examples in this document only show how to declare methods and properties with VT_BSTR (binary strings). PerlCtrl translates Perl values to these types. You need only supply the data types.
Type Name Type Symbol ------------------------------------ Unspecified VT_EMPTY Null VT_NULL 2-byte Signed Integer VT_I2 4-byte Signed Integer VT_I4 4-byte Real Value VT_R4 8-byte Real Value VT_R8 Currency VT_CY Date VT_DATE Binary String VT_BSTR IDispatch FAR* VT_DISPATCH Scodes VT_ERROR Boolean VT_BOOL Variant FAR* VT_VARIANT IUnknown FAR* VT_UNKNOWN Unsigned char VT_UI1
When you pass a reference to an array, scalar, or hash from a client script to a PerlCtrl, a copy of your data is passed, not a true reference to your data. So, when you manipulate the data from within the PerlCtrl, you are not manipulating the client's data. The ability to use references from within PerlCtrl will be addressed in a future version of the Perl Dev Kit.
Every top-level object created by PerlCtrl has its own interpreter. Each interpreter is capable of simultaneous and independent execution; that is, a thread in one interpreter does not interfere with a thread in another.
Given that each interpreter is isolated from other interpreters, PerlCtrl objects are "thread safe". This means that they can operate safely in both a single-threaded and a multi-threaded apartment. Synchronization of multiple threads in the same interpreter is handled by the PerlCtrl runtime on a per call basis.
Regardless of the threading model used, all method calls into an interpreter
are synchronized internally by the PerlCtrl runtime. In PerlCtrl, this would
mean only IDispatch::Invoke. Therefore it is only possible for one thread
to execute code in an interpreter at any given time. If two threads attempt
to invoke a PerlCtrl method at the same time, one thread is blocked. Once
the first thread completes its operation, the second thread is allowed to
run.
In addition to synchronizing per method calls, the PerlCtrl runtime also synchronizes per interpreter. This applies to two important scenarios:
When there are instantiated multiple PerlCtrl objects, each with its own interpreter, a method call to one PerlCtrl object would not block a method call to another. This means multiple PerlCtrl objects can coexist without conflicts.
While the PerlCtrl runtime synchronizes on a per call basis, it is still possible for an application to encounter problems. For example, if two threads attempt to invoke a PerlCtrl method at the same time, on the same object, or on objects that share an interpreter space. This would cause one thread to be blocked. When the first thread completes its operation, the second thread is allowed to run. Therefore, it is possible for the second thread to change in the object without the first thread recognizing this. Application developers should be aware of this situation and handle it accordingly.
Applications may incur a performance penalty based on the threading model used, but this performance hit would be due to the poor design of the host application and not PerlCtrl. For example, if a host application instantiates a PerlCtrl object in a single-threaded apartment and wants to share the interface pointer to another thread, the interface pointer must be marshaled across the apartment boundary. After this, all cross-apartment calls will have to go through the proxy/stub mechanism.
This is enforced via COM and not by PerlCtrl. In the above situation, it would be better if the object were instantiated in a multi-threaded apartment, allowing multiple threads to share the interface pointer directly.
PerlCtrl provides the ability to embed context-sensitive references to a compiled HTML Help (.chm) file within a control. When users press 'F1' from within the control, the specified help file and page are opened automatically.
Microsoft HTML Help is a technology used to create compiled help files from groups of HTML files. Compiled help files have the extension ".chm" and can only be viewed on the Windows platform. Within the compiled help, individual pages can be associated with numerical markers, which in turn can be associated with type libraries, and methods and properties contained within type libraries.
For more information about Microsoft's HTML Help, and to download HTML Help
components, see http://msdn.microsoft.com/library/default.asp
.
While Microsoft provides a graphical tool called the HTML Help Workshop for
creating and compiling help projects, help projects can also be compiled
from the command line. The HTML Help Workshop does not provide support for
adding [MAP] references to project files (as described in the next
section). Therefore, you must use an external editor and add these sections
manually to the project file.
HTML Help project files contain the definition for the help project and are used by the help compiler to generate the compiled output file. Project files have the extension ".hhp".
To associate a page in the compiled help file with a control, you must first
assign an ID number to the page. This ID number corresponds to the value
specified in the HelpContext setting in the type library definition. These
IDs must be configured under [MAP] sections in the help project file. For
example:
[FILES] MyControl.htm Greet.htm name.htm
[MAP] #define MyControl 1 #define Greet 10 #define name 20
The command-line compiler for generating HTML Help is called hhc.exe.
Note that in order to compile the project, the HTML files must be located in
the same directory as the .hhp project file. To compile a help file, enter:
hhc myhelp.hhp
This assumes that hhc.exe is in your system PATH; modify as necessary.
The compiled help file must be located in the same directory as the DLL generated by PerlCtrl.
There are three %TypeLib configuration items:
[MAP] section of the Help Project File.
For example:
%TypeLib = (
PackageName => 'MyControl',
DocString => 'My very own control',
HelpFileName => 'MyControl.chm',
HelpContext => 1,
This example shows how to associate a help page with a method:
'Greet' => {
DocString => "The Greet() method",
HelpContext => 10,
ActivePerl includes a OLE Type Library Browser that can be used to view the methods and properties within a control and their associated help files. To open the Type Library Browser, select OLE-Browser from the ActivePerl program group on the Windows Start menu.
In the middle pane, scroll down to the library generated with PerlCtrl. (The library is named according to the value of the DocString type library.) Click the library name to display the components; click a component name to view the methods and properties associated with the component. To view the help page associated with the method or property, select the method or property and click 'F1'.
Microsoft Office applications include a Visual Basic Editor that can be used to view the methods and properties within a control and their associated help files. To open the Visual Basic editor, first open an Office application (such as Word or Excel), then select Tools|Macro|Visual Basic Editor ('Alt'+'F11').
Within the Visual Basic Editor, use the Tools|References dialog to select the library generated with PerlCtrl. The reference is named according value of the DocString type library; select the check box beside the name to select it.
Use the Object Browser (View|Object Browser or 'F2') to view methods and properties within the library. Select the desired library from the drop-down list in the top left corner of the Object Browser, and then select the control name to display its members. Click on the desired method or property, then click 'F1' to view the associated page in the help file.
Configuring a component developed with PerlCtrl is a simple matter of configuring its DCOM security, since components developed with PerlCtrl are fully DCOM compatible. To configure a control' s DCOM security, use the dcomcnfg.exe utility:
dcomcnfg.exe , and click OK.
The Distributed COM Configuration Properties tool appears, and displays a
list of configurable applications.
ProgID value from the %TypeLib hash.
You must configure both Access and Launch properties for the control.

Make sure Use custom access permissions is selected, and click Edit. Be conservative - only provide access to users who need it. The following graphic shows a control configured on a server named OSCORB for local administrators, domain administrators, interactive users, and the system account.

Set the same permissions for Launch: select Use custom launch permissions, and then click Edit. Next, configure the permissions as you did for Access Permissions.
To give another user permission to configure this control, use the Configuration Permissions section of the Security tab to add the user. In most cases, you should not need to modify Configuration Permissions.
Select the Identity tab on the control's Properties dialog. By default, "The launching user" is selected.

"The launching user" is an acceptable setting, if you know that the users who launch the control remotely have sufficient permissions for the control's function. For example, if the control executes shell commands or reads the filesystem, the remote user must have permissions to perform these operations, or the control will not work correctly. Alternatively, you can create a new user account with the correct permissions, and then run the control under that user account (choose "This user" and select a user by clicking Browse or typing the username). You must supply the user account name as well as the password. Use this with caution, since you are allowing remote users to impersonate another user on your machine. If you choose "The interactive user" your control will run with the permissions of whoever is currently logged in to the server (it is possible that no one will be logged in). The "The interactive user" setting should only be used for testing in a secure environment.
Once you have configured your control for DCOM, using it is simple. If you
are programming with Perl on the remote machines, you can use Win32::OLE
to launch the control remotely.
You must have two networked machines. The machine with your PerlCtrl for DCOM is the server; the machine you launch the control from is the remote client. To use this example, either of two cases must be true:
OR
This is because the example launches the component using its ProgId (for
example, in the case of the sample control, Hello.World). The client machine
needs to resolve the ProgID into the CLSID before it can launch the
control. If the control is installed on the client machine, the
CLSID of the control is the same on each machine, so the client machine
can look up the CLSID in its own registry using its ProgID.
If the client machine does not have the component installed, Win32::OLE
will try to connect to the server's registry and look up the CLSID. If
your network configuration does not permit this, you can launch the control
using its CLSID in place of its ProgID. The following example launches
Hello.World on a remote machine using the ProgID and invokes its
Hello method. This example can be found in the DCOMHello subdirectory of
the Samples directory. Here is the source for dcomhello.pl:
#! perl -w
use Win32::OLE;
use strict;
#
# The user must supply the name of the server on which to launch the
# control.
unless (@ARGV >= 1) {
die "usage: $0 <SERVER>\n";
}
my $server = shift;
my $obj_hello = Win32::OLE->new( [$server, "Hello.World"] );
print $obj_hello->Hello();