Chapter 10

Win32 Modules on Windows NT


CONTENTS


In the previous chapter I covered details on getting and installing Perl for Windows NT. In this chapter I cover the methods and functions of some of the Win32 modules that come with the NT Perl package.

Although this chapter covers details of how to make calls into the Windows 32 bit API, I do not go into the details of how the module itself works as it interacts with the API. Dealing with that topic would be well beyond the scope of this book. To get a good understanding of how to program an NT application, check out Jason Loveman's Moving to Windows NT Programming, Sams Publishing. This book may seem a bit dated at first, but it provides an excellent tutorial on how to get going from a Windows 3.x environment to NT. The other book that is a must-have is The Win32 API Desktop Reference by Jim McCord, also from Sams Publishing. This book is a comprehensive reference manual with a bit of instruction on how to program without the benefit of the Microsoft Foundation Classes.

Note
Remember from the previous chapter that to execute a Perl script, you must type the perl command on the prompt with the name of the Perl script:
c:\dos> perl myscript.pl

The Win32 Module

The Win32 module contains extensions for working specifically with the Windows NT system. Here are some miscellaneous functions that exist only under the Windows NT hierarchy-you'll find them quite useful when writing Perl scripts under NT:

Spawning Processes
The Win32::Spawn command is used to start a process. The syntax for this command is
Win32::Spawn( $command, $args, $pid);
A new process is started and runs the command in $command. Any command-line arguments passed into the process are specified in $args. The process ID is returned in $pid.
Starting System Shutdown
The Win32::InitiateSystemShutdown command is used to shut down a machine. The syntax for this command is
Win32::InitiateSystemShutdown (
    $machine, $message, $timeout, $forceClose, $reboot);
The machine name is specified in $machine, and a NULL value identifies the current machine. The $message value is sent to all the users logged into the machine at this time. The $timeout value is used to notify users of imminent shutdown. The $forceClose value is set to 1 if you want the system to close all open files without prompting the user. (This is useful during off times when the user might not be at his or her desk.) The machine will reboot if $reboot is set to true.
Aborting System Shutdown
The Win32::AbortSystemShutdown command attempts to stop the shutdown process on a given machine. The syntax for this command is
Win32::AbortSystemShutdown ($machine);
The abort request might not always work because the $timeout may happen before you get this command going. (I could not stop the shutdown/reboot process even with a very long timeout.) Stopping a shutdown process in the middle may leave your system in an indeterminate state. Once you get the ball rolling on a shutdown, let it go through all the way. However, if you must stop an accidental shutdown command, this command can be a valuable resource.
Several modules exist under the Win32:: extensions to enable you to work with processes, interprocess communications networks, and user resources. I cover these modules as well. One thing to keep in mind is that this code is in test mode at this time, and improvements will be made as the code develops. Also, these extensions are almost always NT-specific and might not work on Windows 95. This means that the code you write with these modules will not be portable to platforms other than Windows NT systems.
Listing 10.1 illustrates how to use some of the functions in the Win32 module.

Listing 10.1. Using the Win32 modules.
 1 use Win32;
 2
 3 $x = Win32::PerlVersion  ;
 4 print " Version number =  $x \n ";
 5
 6 $x = Win32::LoginName;
 7
 8 print " \n Login Name $x  ";
 9
10  $x = Win32::NodeName;
11 print " \n  Node Name = $x ";
12
13  $x = Win32::DomainName;
14 print " \n  Domain Name = $x ";
15
16  $x = Win32::FsType;
17 print " \n  File system type = $x ";
18
19  $x = Win32::GetCwd;
20 print " \n  Current Drive = $x ";
21
22  $x = Win32::GetOSVersion;
23 print " \n  Current OS version = $x ";
24
25 $x = Win32::GetTickCount ;
26 print " \n Tick count =  $x ";
27 print "\n This is an NT box "  if ( Win32::IsWinNT);
28 print "\n This is a 95 box "  if ( Win32::IsWin95);

The Process Module Extensions

The Win32::Process module extensions enable you to create different processes and start applications using Perl scripts. The Create function lets you create a Process object that will run an application. The syntax for this call is

Create( $pObject,
    $ApplicationName,
    $CommandLine,
    $InheritHandles,
    $CreateOptions,
    $CurrentDir)

$pObject is the returned reference to an object from this Create call. The Create function is the constructor for the Process class. Subsequent methods are called using this object as a reference.

$ApplicationName is the full pathname of the application you want to run in this Process object. The path can be specified using forward slashes or back slashes. For example, to fire up C:\dos\winzip.exe on a system, you can use either one of these two pathnames:

"c:/dos/winzip.exe"

"c:\\dos\\winzip.exe"

$CommandLine is the list of arguments passed at the command line to the application in $ApplicationName. The new application will inherit any open file handles if $InheritHandles is set to 1. If you do not want the called application to inherit file handles, set $InheritHandles to 0. The $CurrentDir variable is set to the working directory for executable programs.

The $CreateOptions variable can take a combination of the following values:

CREATE_DEFAULT_ERROR_MODE Gives the new process the default error mode.
CREATE_NEW_CONSOLE The new process has a new console. This cannot be used with DETAchED_PROCESS.
CREATE_NEW_PROCESS_GROUP The new process is the root of a new process group.
CREATE_SEPARATE_WOW_VDM The new process runs in its own 16-bit Virtual DOS Machine (VDM).
CREATE_SUSPENDED Starts the new process in a suspended state. The process can be forced to execute with the Resume method.
CREATE_UNICODE_ENVIRONMENT The new process environment contains support for Unicode characters.
DEBUG_PROCESS The calling process is being used as a debugger, and the new process is being debugged.
DEBUG_ONLY_THIS_PROCESS The new process is not debugged, even if the calling process is running under the debugger.
DETAchED_PROCESS The new process does not have access to the console of the calling process.

Once the Process object has been created, you can apply methods to it. The following methods exist for the Win32::Process object.

The Kill Method

This method kills the process associated with the Process object. The syntax for this method is

Kill( $ExitCode).

The $ExitCode is the value of the code that the process should return.

The Resume and Suspend Methods

The Suspend() method stops a process from executing. To restart the thread, call the Resume() method.

The Resume call can be used to resume a process that was created with the CREATE_SUSPENDED flag.

The GetExitCode() Function

The GetExitCode() function forces the pObject to terminate with its exit code set to the value of $ExitCode. The syntax for this function is

GetExitCode( $ExitCode );

You can use GetExitCode to find out how or if a process has exited.

The Wait($Timeout) Function

The calling process can wait for a process to terminate with the Wait function. The syntax for this call is

Wait($Timeout)

The $Timeout variable is set to the number of milliseconds to wait for the process to end. If the process wants to wait forever, use the keyword INFINITE for $Timeout. The returned value of Wait() is FALSE if it times out and the error code in $! is set to WAIT_FAILED. A returned value of TRUE indicates that the process terminated before the $Timeout value.

Class Priority

The GetPriorityClass and SetPriorityClass methods provide control over the priority of the process. The priority class of the process can be retrieved with a call to this method:

GetPriorityClass($Priority )

The returned value of $Priority is set to the priority class of the object this method is being called on. SetPriorityClass($Priority ) is the opposite of the GetPriorityClass() function.

The $Priority variable is set to the priority to set the Process to. The value can be set to one of the following:

IDLE_PRIORITY_CLASS Indicates a process whose threads run only when the system is idle.
NORMAL_PRIORITY_CLASS Standard process scheduling.
HIGH_PRIORITY_CLASS Runs at a higher priority level.
REALTIME_PRIORITY_CLASS Runs the process at the highest available priority. Be careful when using this value because you can lock up the machine with a runaway process.

Listing 10.2 shows you how to use the Win32::Process module.


Listing 10.2. Using the Win32::Process module.
 1 use Win32;
 2 use Win32::Process;
 3
 4 Win32::Process::Create($pobj,
 5         "c:/dos/winzip32.exe",
 6         "",   # no command line arguments
 7         0,
 8         DETAchED_PROCESS,  # run by yourself.
 9         ".") || die "oops";
10
11 $pobj->SetPriorityClass(NORMAL_PRIORITY_CLASS) || die $!;
12
13 $pobj->Wait(INFINITE) || die $!; # wait forever

The Win32::Semaphore Utilities

The Win32::Semaphore lets a Perl script access the semaphore facility in Windows NT. The Create call in a Semaphore module lets you create a semaphore object. The semaphore in an NT machine works in the same manner as in UNIX. For more information on how to use semaphores, see Chapter 13, "Messaging Facilities: The System V Ipc Functions." The syntax for the Create function is

Create ($semaphoreObj,$initialCount,$maxCount,$name);

The returned value from the Create call is placed to what the $semaphoreObj references. The $initialCount variable is used to set the initial count of the semaphore object. The maximum count of the semaphore is set in $maxCount. The semaphore can be identified in the NT system by the value in $name.

The DESTROY method removes a semaphore object specified in the argument to itself. The syntax is

DESTROY( $SemaphoreObj );

The DESTROY method is automatically called by Perl when the $SemaphoreObject scalar goes out of scope.

The Wait( $TimeOut ) function is used to wait for a timeout for the value in $TimeOut. The value in $Timeout is specified in milliseconds.

The Wait method makes the calling process wait on the semaphore. If the Semaphore is not released in $TimeOut milliseconds, the call returns, and the return value should be checked. For no timeout value, use the predefined constant INFINITE.

Once you are done with a semaphore, you should release it to let other processes access it. To release a semaphore, you have to make a call to

Release( $ReleaseCount, $lastCount);

The value in $ReleaseCount is incremented by the call to Release. The Release method releases a semaphore after it increments the count. The $LastCount variable contains the last value.

Listing 10.3 illustrates how to use the Semaphore module in NT. Note how the ErrorMsg subroutine displays the readable message from the GetLastError() call.


Listing 10.3. Using the Win32::Semaphore object.
 1 use Win32::Semaphore;
 2 use Win32;
 3
 4 my $sObject;
 5
 6 Win32::Semaphore::Create($sObject,1,1,"Bile")||die "Die: $!\n";
 7
 8 sub ErrorMsg {
 9 print Win32::FormatMessage( Win32::GetLastError());
10 }
11
12 &ErrorMsg;    # Tell me if there were any errors. :w
13
14 #
15 # wait for semaphore.
16 #
17 if( $sObject->Wait(INFINITE)){
18     #
19         #Access the shared resource here.
20     # Do something here with returned value.
21            # &Access( $Shared_Resource );
22
23     #
24     # Don't forget to release the semaphore
25     # as quickly as possible.
26     #
27         $sObject->Release( 1,$last );
28
29     #
30     # You can work with your copy of private data.
31     # with no need for locking.
32     #
33 }
34 else {
35     print" Whoa! I cannot access any semaphores!\n";
36 }

Using Mutex with Win32::Mutex

The Win32::Mutex module is used to create, destroy, and work with Mutexes in the NT system. A Mutex is used to provide mutually exclusive access to a resource. Processes vie to own a Mutex, and only one process can own a Mutex at one time. A process controlling a Mutex can then work exclusively on a shared resource such as a file or device. Mutexes differ from semaphores in that Mutexes are available only in kernel-mode processes and not for user-mode processes. Refer to Jason Loveman's book, Moving into Windows NT Programming, for some good examples on how to use Mutexes.

To create a Mutex, you have to call the Create function for the object. The first parameter into the Create call is assigned a reference to the Mutex object. The syntax for the Create call is

Win32::Mutex::Create($mutexObject, $owner, $mutexName )

The $mutexObject is assigned a reference to the new Mutex object. The value in the $owner variable can be 1 or 0. If it is 1, the calling process will be assigned the initial ownership at the time of creation.

If it is 0, the Mutex will be created as available. The name of the Mutex will be in $mutexName.

Create creates a Mutex object and returns the reference in $MutexObj. If the $InitialOwner flag is set (nonzero), the process calling the Create function has immediate ownership of the Mutex. Otherwise, the Mutex is available. $Name can be used by other processes in Win32::Mutex::Opencall to create an object to reference an already created Mutex.

To open a Mutex, use the Open call to get the Mutex object. The syntax for the Open call is

Win32::Mutex::Open($mObject,$mutexName);

The $mObject variable is assigned a reference to a Mutex object with the name value in $mutexName. The Mutex specified in $mutexName must already have been created or else a NULL value is returned in $mObject.

The Release() method on a Mutex object releases a Mutex object back to the system for use by other processes. The Mutex object is destroyed when it goes out of scope in the Perl program because the default DESTROY method of the Mutex object frees the system resources used by the Mutex.

The Wait() function for the Mutex object is used to wait for ownership of a Mutex. The syntax of this function is

Wait( $TimeOut );

The $TimeOut variable is specified in milliseconds. The value can be set to INFINITE for waiting indefinitely. The Wait() function returns a false value if the function timed out or returns true if the Mutex has become available.

Here's a fragment of code that shows how to use a Mutex:

#
# This is reserved for privileged users only
#
# Create a Mutex object
#

Win32::Mutex::Create( $Mut,0, "MyMutex")|| die $!;


#
# Use it
#
sub doPriviliged() {
local( $mObject ) = $_[0];  # The first arg in method is the object
$mObject->wait( 5000 )|| return ;    # Could not get it


    #
    # do your thing.
    #

$mObject->Release();
}

Using Win32::ChangeNotification

The Win32::ChangeNotification module is used for notifying a Perl application or process when a branch in the specific file system tree has been modified. The module relies on the FindFirst() call in the Win32 API, and this is the way to create the object. The syntax is

Win32::ChangeNotification::FindFirst(
        $cObject,
        $pathName, $watchSubtree,
        $filter);

The $cObject variable is set to the new ChangeNotification object created. $pathName is the path to the directory that you want to monitor. $watchSubTree function can be set to 0 or 1. If set to 0, any subdirectories of the path are not modified. If set to 1, the monitor process looks at the subtree, too.

The $filter variable specifies the conditions on which the program notifies. The value in $filter can be set to a value that is a combination of the following constants:

FILE_NOTIFY_chANGE_FILE_NAME Notifies when a file in the monitored directory is renamed, created, or deleted.
FILE_NOTIFY_chANGE_DIR_NAME Notifies whether the name of the directory is changed.
FILE_NOTIFY_chANGE_ATTRIBUTES Notifies whether the attributes of the directory are changed.
FILE_NOTIFY_chANGE_SIZE Notifies whether the size of the underlying directory is actually written to disk. This notification might not happen immediately due to the way NT flushes cached writes of data to the disk.
FILE_NOTIFY_chANGE_LAST_WRITE Notifies whether the last write time of the underlying file is modified and actually written to disk. This notification might not happen immediately due to the way NT flushes cached writes of data to the disk.
FILE_NOTIFY_chANGE_SECURITY Notifies of any security descriptor changes in the directory being monitored.

Other methods for this module include the following:

FindNext(); The FindNext method requests that the operating system signal the change notification object the next time it detects an appropriate change.
Close(); Stops the notification object monitoring.
Wait( $TimeOut); $TimeOut is the time out in milliseconds. The Wait method causes the calling process to block until notification of the change.

When the ChangeNotification object goes out of scope, the object's DESTROY method is called by Perl. The DESTROY method closes any outstanding notification.

Listing 10.4 shows how to use the ChangeNotification object.


Listing 10.4. Using the ChangeNotification object.
 1 use Win32;
 2 use Win32::ChangeNotification;
 3
 4 my $watch = TRUE ;
 5 my $flags = "FILE_NOTIFY_chANGE_ATTRIBUTES | FILE_NOTIFY_chANGE_SIZE" ;
 6 my $Path =  "D:/perl5/";
 7
 8 Win32::ChangeNotification::FindFirst( $pobj,
 9     $Path,
10     ,1,
11     $flags) || die $!;
12
13 sub ErrorMsg {
14 print Win32::FormatMessage( Win32::GetLastError());
15 }
16
17 &ErrorMsg;    # Tell me if there were any errors. :w
18
19 $pobj->FindNext();
20 &ErrorMsg;    # Tell me if there were any errors. :w
21 $pobj->Wait(8000);
22 print "Hey! Someone's messing with drive D: \n" ;
23 $pobj->Close;

The Win32::Eventlog Module

The Windows NT system tracks events in the system using an event logging facility. Perl scripts can read, modify, and extract information from log records. This feature is a very powerful tool to use when generating status reports about a system.

Before you use Win32::Eventlog, you have to open an event log and associate a Win32::Eventlog object with it. Here's the call to do this:

Open Win32::EventLog($EventObj, $sourceName, [$serverName]);

On returning from this function, the $EventObj variable will be set to a reference to an EventLog object. The name of the source for the events that will be set is $sourceName. The $ServerName variable is optional and, if omitted or explicitly set to NULL, will be set to the local machine.

You can even create your own backups by using the Backup method on the $EventObj object. The file is created and the events in the current event log in the $EventObj object are written to.

Any previously written file will be overwritten if a file with the same name already exists. The syntax for this command is

Backup( $filename );

where $filename is the string of the file to which to write the event log.

Reading Events

To read events in a backup or event log, you have to use the Read() method on the $EventObject. The syntax for the Read() method is

Read($readFlags,$recordOffset,%EventInfo);

The $readFlags variable is set to specify how to read the events. The $recordNumber variable is set to the index (starting from 1, not 0). The hash %EventInfo is set to the returned event's value. $readFlag can be set to a combination of these values:

EVENTLOG_FORWARDS_READ Reads in forward chronological order. Cannot be used with EVENTLOG_BACKWARDS_READ.
EVENTLOG_BACKWARDS_READ Reads in reverse chronological order. Cannot be used with EVENTLOG_FORWARDS_READ.
EVENTLOG_SEEK_READ Reads from record number specified in $RecordOffset. You must specify the direction in which you are to read the file by setting either EVENTLOG_FORWARDS_READ or EVENTLOG_BACKWARDS_READ.
EVENTLOG_SEQUENTIAL_READ Reads from the next record after the previous read operation.

Reporting the Contents of an Event

To get a report of what you have just read, you can use the Report() method. The syntax for this call is

Report($eventType, %EventInfo );

$eventType is the type of event and %EventInfo is the returned value of an %EVENTINFO hash, which is usually the value returned from a Read() call. The options for $eventType are

EVENTLOG_ERROR_TYPEError event
EVENTLOG_WARNING_TYPEWarning event
EVENTLOG_INFORMATION_TYPEInformation event
EVENTLOG_AUDIT_SUccESS_TYPESuccess audit event
EVENTLOG_AUDIT_FAILURE_TYPEFailure audit event

%EventInfo hash can be parsed with the following keys:

Category An application-specific integer value for the type of event
EventID A source-specific ID value of the event
EventRawData Any application-specific raw binary data
Strings Any application text strings
user User name to which this event applies

Three other functions also exist to get more information about the position of events in a file. The returned values of these functions can be used to specify what event record you are going to process data from. Keep in mind that records are indexed by starting at 1 and not 0.

The Win32::Registry Module

The Win32::Registry module lets you work with the Windows NT Registry model. The way to start working with the model is to first create a Win32::Registry object with the open() call for a key. The syntax for the open call is

Win32::Registry::Open($RegistryObj,$key )

$RegistryObj returns a reference to a reference object for the predefined key specified in $key. If the specified key does not exist, it is not created. $key can only hold any already opened key.

When the Registry module is loaded, four Registry objects are created in the main:: namespace. These predefined Registry objects can be referred to by the generic names within Windows NT:

To create a new key, you have to use the Create() call. Here's the syntax for the Create() call:

Win32::Registry::Create($RegistryObj,$key )

where $RegistryObj returns a reference to a Registry object. The $key variable contains the name of a key. If the key exists, then the create function will open it; otherwise, it will create a new key and return a reference to it. The user must have the security privileges to create a key.

Once you have a key in an object, you can get its value with the QueryValue method. The syntax for the QueryValue method is

$regObject->QueryValue($subKey,$valueRef);

Given the name of the subkey of the regObject in $subKey, the QueryValue function sets the value of the variable at $valueRef. To get more information about a key, you can use the QueryKey method with this calling syntax:

$regObject->QueryKey ($keyClass, $numSubkeys, $numValues);

All three arguments to this function are set to a value when the function returns. On returning, $keyClass is set to a string specifying the class of the key. The $numSubKeys contains the number of subkeys, and the $numValues variable contains the number of values for the current key.

Keys can have more than one value associated with them. To get these values from a key, call the GetValues() method. The syntax for this call is

$regObject->GetValues(\%Values);

The %Values hash is set to the values in the $regObject key. The hash %Values will be keyed by deriving a value from the name and type of the object to get a key of the form {$name,$type,$data}.

The GetKeys(\@Subkeys) function returns a list of names of subkeys for a given key in the array @Subkeys. To get a list of subkeys for an object, the call will be of this form:

$regObject->GetValues(\@subkeys);

You can save the current key status (called hive) with a call to the Save() method for a Registry object. The syntax for this method is

$regObject->Save( $filename);

where $filename is the name of the file to save to.

To load the information about a subkey from a file on disk, you can use the Load method. The syntax for this call is

$regObject->Load( $subkey, $filename);

The $subkey is the subkey to load the file into from the filename specified in $filename.

You can delete keys or their values. The function to delete a key is DeleteKey($keyname). To delete a value, use DeleteValue($keyname);. Here are some examples of these functions:

$regObject->DeleteKey($keyname);
$CURRENT_USER->DeleteValue($keyname);

The Win32::NetAdmin Module

The Win32::NetAdmin module is useful for working with users and user groups on an NT machine. Normally you would perform this operation at the main console using the windowing interface in NT. However, addition and deletion of users on multiple machines would tax the system administrator's time too much. A Perl script can list the attributes of a user or group and perhaps be able to do this automatically and remotely on a network. This section covers some of the functions available from within Perl.

Note
The Win32::NetAdmin module will not load on a Windows 95 machine.

Adding a User

To add a user using the Win32::NetAdmin module, you can call the UserCreate() function. The syntax for this function is

UserCreate($server,
        $userName,
        $password,
        $passwordAge,
        $privilege,$
        homeDir,
        $comment,
        $flags,
        $scriptPath);

The $server variable is the name of the server. It may be NULL to specify the local server. The $userName variable is the login name of the new user. $groupName is the name of the group. The $comment variable is a string that will have the attributes field of that group when the function returns. The user's password and the time before it expires is in the variables $password and $PasswordAge, respectively. The home directory of the user is set in the $homeDir argument. The comment variable contains appropriate information about the user. The $scriptPath variable is the Uniform Naming Convention (Unc) pathname of the login script for that user.

The $privilege variable controls the privileges of the new user. This variable can be set to one of these values:

USER_PRIV_MASK to set for all users
USER_PRIV_GUEST to set for all guest users
USER_PRIV_USER to set for all normal users
USER_PRIV_ADMIN to set for the administrative accounts

Here are the defined values the $flag option can take:

UF_SCRIPT The script to execute when the user logs on.
UF_AccOUNTDISABLE Disables the account.
UF_PASSWRD_NOTREQD No password is required to create this account.
UF_PASSWRD_CANT_chANGE The user cannot change his or her password.
UF_LOCKOUT Locks the current account out.
UF_DONT_EXPIRE_PASSWORD The password never expires on the account.
UF_NORMAL_AccOUNT This is a default account type for use with all typical users.
UF_TEMP_DUPLICATE_AccOUNT Use this account for users whose primary account is in another domain. This user has access to the current domain but is not a trusted account and is referred to as a local account.
UF_WORKSTATION_TRUST_AccOUNT Used for a computer account for a Windows NT Workstation server which is also a member of the current domain.
UF_SERVER_TRUST_AccOUNT Used for a computer account for a Windows NT backup domain controller which is also a member of the current domain.
UF_INTERDOMAIN_TRUST_AccOUNT Gives permission to trust this account.

Please refer to the Windows NT reference manual for more information about these flags. Most of the flags are descriptive enough to indicate the functions they provide.

To delete a user from the system, call the UserDelete($server, $user) function. $server is the name of the server and can be NULL for the local machine. $user is the name of the user to delete. It's probably a good idea to know the attributes of a user before you delete the account. To get the attributes of a user, you have to use this call:

UserGetAttributes(
        $server, # The name of the server.
        $userName, # The name of the user.
        $password, # Their password.
        $passwordAge, # The time before the password expires.
        $privilege,  #Their  privilege.
        $homeDir, # Their home directory.
        $comment, # Any comments about them.
        $flags, # for the account
        $scriptPath );  # Pathname of login script.

You can reset these values to different ones and modify an account's attribute with the UserSetAttributes call. The parameters passed into the function are the same as the UserGetAttributes() function.

Listing 10.5 obtains listing information about a user.


Listing 10.5. Using the NetAdmin module.
use Win32::NetAdmin;
$userName = "khusain";

   Win32::NetAdmin::UserGetAttributes('',$userName,
                              $Getpassword,
                              $GetpasswordAge,
                              $Getprivilege,
                              $GethomeDir,
                              $Getcomment,
                              $Getflags,
                              $Getscriptpath ) || warn();

printf "\n Attributes=%s\n%x\n%s\n%s\n%s\n%s\n%s\n%s\n",
                              $Getpassword,
                              $GetpasswordAge,
Listing 10.5. continued
                              $Getprivilege,
                              $GethomeDir,
                              $Getcomment,
                              $Getflags,
                              $Getscriptpath ;

To delete a user, you can use the UserDelete function. Here's the syntax:

use Win32::NetAdmin;
Win32::NetAdmin::UserDelete($server,$username);

Creating or Deleting a Group

To create a group, you can make one of these calls:

LocalGroupCreate($server, $group, $comment);
GroupCreate($server, $group, $comment);

$server is the name of the server. It may be NULL to specify the local server. $groupName is the name of the group. The $comment field is a string placed in the attributes field of that group.

To delete a group, you have to specify the name of the group in one of the following functions:

LocalGroupDelete($server, $group);
GroupDelete($server, $group);

$server is the name of the server. It may be NULL to specify the local server. The $groupName is the name of the group you are about to delete.

Comments About a Group

Given a group name, you can get its attributes with a call to one of these functions:

LocalGroupGetAttributes($server, $groupName, $comment);
GroupGetAttributes($server, $groupName,$comment);

$server is the name of the server. It may be NULL to specify the local server. $groupName is the name of the group. The $comment field is a string that will have the attributes field of that group when the function returns.

You can set the values of these comments using the following functions:

LocalGroupSetAttributes($server, $groupName, $comment);
GroupSetAttributes($server, $groupName, $comment);

Adding Users to a Group

To add one or more users to a group, you can use one of these two functions:

LocalGroupAddUsers($server, $groupName, $users);
GroupAddUsers($server, $groupName, @users);

$server is the name of the server. It may be NULL to specify the local server. $groupName is the name of the group. The @user array is a list of strings of user names to add. Each string does not have to be case-sensitive in NT. The function returns true if all the users were added and false if there was an error. To add just one user name, make a list with one item in it.

Removing Users from a Group

To remove one or more users from a group, you can use one of two functions:

LocalGroupDelUsers($server, $groupName, @users);
GroupDelUsers($server, $groupName, @users);

$server is the name of the server. It may be NULL to specify the local server. $groupName is the name of the group. @user is a list of strings of user names to remove. Each string does not have to be case sensitive in NT. The function returns true if all the users were removed and false if there was an error. To delete just one user, make a list with one item in it.

Checking Group Membership

To check if a user belongs to a group, you can use one of two functions:

LocalGroupIsMember($server, $groupName,$user);
GroupIsMember($server, $groupName, $user);

$server is the name of the server. It may be NULL to specify the local server. $groupName is the name of the group. $user is the string of the user name to check. The string does not have to be case-sensitive in NT. Each function returns true if the user is part of the group and false if the user is not part of the group.

Listing Group Members

There are two functions to list the members in a given group:

LocalGroupGetMembers($server, $groupName, \@userArray)
GroupGetMembers($server, $groupName,\@userArray)

$server is the name of the server. It may be NULL to specify the local server. $groupName is the name of the group. The local members of this group are those that are local to this host. The userArray is a reference to an array of user names. The function will fill the $userArray with the members of $groupName.

The GetDomainController() function returns the name of the server's domain controller. The syntax for this call is

GetDomainController($server,$domain,$returnedName);

$server is the name of the server. It may be NULL to specify the local server. $domain is the name of the domain you are querying. $returnedName is the name of the controller as a string.

Using the Win32::Service Module

The Win32::Service module is used to start and stop services on an NT system. You should be a privileged user (such as Administrator) for the functions in this module to work.

The Win32::Service::StartService() function starts a named service on a host. The syntax for this function is

StartService($hostname, $servicename );

The name of the host at which to start the service is specified in $hostname. The value can be set to NULL for the local host. Be careful how you specify the host name, because any name resolution will generate a call to the DNS name server. If the name server relies on the service you just happen to be starting, whoops, your program may hang for a long time. $servicename is a string specifying the name of the NT service to start. This service you are attempting to start must be registered with the NT Service Control Manager.

To stop a service, you can call the StopService() command. This command uses a syntax similar to StartService:

StopService($hostname, $servicename )

The name of the host and service to stop are specified in the same manner as the StartService function.

You can get the status of a service on a host by calling the GetStatus() function. The syntax for this call is

GetStatus($hostname, $servicename, $status );

The name of the host at which to start the service is specified in $hostname. The value can be set to NULL for the local host. The name of the service is specified in $serviceName. The returned value of the status is a hash set in $status when the function returns. The keys of this hash are

'ServiceType'which can be one of these values: SERVICE_WIN32_OWN_PROCESS, SERVICE_WIN32_SHARE_PROCESS, SERVICE_KERNEL_DRIVER, or SERVICE_FILE_SYSTEM_DRIVER.
'CurrentState'which can be one of these values: SERVICE_STOPPED, SERVICE_START_PENDING, SERVICE_STOP_PENDING, SERVICE_RUNNING, SERVICE_CONTINUE_RUNNING, SERVICE_PAUSE_PENDING, or SERVICE_PAUSED.
'ControlsAccepted'The types of control codes the service can accept.
'Win32ExitCode'The error code the service will return when it stops or starts.
'ServiceSpecificExitCode'Any special codes returned if Win32ExitCode has a value of ERROR_SERVICE_SPECIFIC_ERROR.
'CheckPoint'A value that is incremented at regular intervals as a service runs. This may always return a value of 0.
'WaitHint'An estimate in milliseconds of time left until completion of the current state.

For more information on the values of these items, check out the members of the SERVICE_STATUS struct in the Windows NT reference manual.

A service can be paused and restarted using these functions:

PauseService($hostname ,$servicename );
ResumeService($hostname ,$servicename);

The $hostname and $servicename values are set the same as with other related functions.

To get a list of all the services available on a host, you can use the EnumServices() function. The syntax for this function is

EnumServices($hostname,\@list);

The $hostname is specified the same as it is for StartService. The $list variable should be a reference to a list of service names:

# Print out all services on this host

   use Win32::Service;
#
# Get a list of available services.
#
   Win32::Service::EnumServices(NULL,\@list );

print "\n The services on this system are:";
$i =0;
foreach $Service (@list){
    print "$i++ :  $service \n";
   }

Using the Win32::NetResource Module

The Win32::NetResource module is used to work with network resource objects in the NT system. Using these functions, you can get and set network resources while logged in as the administrator.

Use the GetSharedResources function to get a list of shared resources. The syntax of this function is

GetSharedResources(\@Resources,$dwType);

The $dwType word specifies the type of resource to get. It can take one of these values:

RESOURCETYPE_ANYAll resources
RESOURCETYPE_DISKResources for use with disk
RESOURCETYPE_PRINTResources for use with printing

@Resources is a list of references to hashes. Each hash in the list is of the type %NETRESOURCE. Each %NETRESOURCE item has the following keys:

'Scope' The scope of the item
'Type' The type of resource
'DisplayType' The display type of the resource
'Usage' Specifies how the resource is used
'LocalName' The name of the local device for this resource
'RemoteName' The network name of the resource (if any)
'Comment' Free form text
'Provider' The name of the vendor-providing resource

The first item in the %NETSOURCE hash is the scope of what the resource affects. The scope of a resource can be one of the following values:

RESOURCE_CONNECTED Connected resources only
RESOURCE_REMEMBERED Resources reconnected at each login
RESOURCE_GLOBALNET Resources available to entire network

The type of resource is the same as specified in $dwType. The display type item in %NETSOURCE is the third item in the hash. It contains more information about how the object is displayed in NT. Here are the possible values:

RESOURCEDISPLAYTYPE_DOMAINDisplay as domain
RESOURCEDISPLAYTYPE_SERVERDisplay as server
RESOURCEDISPLAYTYPE_SHAREDisplay as sharepoint
RESOURCEUSAGE_CONNECTABLEConnected to a local device
RESOURCEUSAGE_CONTAINERResource contains more resources

To make a new connection, you can use the AddConnection function. The syntax for this function is

AddConnection($resource,$Password,$UserName,$Flags)

$resource is a reference to a %NETRESOURCE hash with the connection to make. The user name and password are provided by the user making the connection to the resources. $Flag is set to 1 if the connection has to be recorded on disk for all future logins.

To break off the connection, use the CancelConnection function call. The syntax for this function call is

CancelConnection($Name,$Connection,$Force);

$Name is set to the name of the local device the resource is connected to. $Connection is set to the type of connection: 1 for persistent connections and 0 for nonpersistent connections. If $Force is set to 1, the connection is broken regardless of any errors that may require a delay.

To see if there were any errors after a command, you can call the WNetGetLastError() function. The syntax for this function is

WNetGetLastError($ErrorCode,$Description,$Name);

All three arguments into this function call return a piece of information about the last error. $ErrorCode is set to the error code number, $Description contains a description of the error, and $Name is set to the name of the error. This function gets the Extended Network Error. This is only applicable if Win32::GetLastError() returns ERROR_EXTENDED_ERROR.

The GetError method returns the last error for a Win32::NetResource call. The syntax for this function is

GetError( $ErrorCode );

The Unc name for a local path can be derived from a local path with the GetUncName() function. The syntax for this function is

GetUncName( $UncName, $LocalPath );

$UncName is set to the Unc name of the network connection. $LocalPath is the local path.

To add a new share object, call the NetShareAdd function. The syntax for this function is

NetShareAdd(\%SHARE,$parm_err,$servername )

%SHARE_INFO is a hash describing the share. The ShareInfo hash is used to pass information to the NetResource functions about the share objects. The keys for this hash are

'netname' The name of the share
'type' The type of share
'remark' A string comment
'permissions' Permissions values
'maxusers' The maximum number of users
'current-users' The current number of users
'path' The path of the share
'passwd' A password if one is required

The $parm_err variable is set upon returning from the function to an error value, if any. $servername is the name of the server. To offer a disk resource for sharing on the network, call the NetShareCheck() function. The syntax for this function is

NetShareCheck($device,$type,$servername)

$device is the name of the device to be checked for shared access. The type of share is returned in $type. The value in $type is only valid if the function returns a nonzero value. $servername is the name of the server.

Use the NetShareDel function to remove a share from a machine's list of shares. The syntax for this function is

NetShareDel( $netname, $servername );

The name of the share to delete is $netname, and the machine offering the share is listed in $servername.

To get the %SHARE_INFO information structure, you can use the NetShareGetInfo() function. The syntax of this function is

NetShareGetInfo( $netname, \%SHARE,$servername);

The name of the share to get information for is named in $netname, and the machine offering the share is listed in $servername. The \%SHARE is a reference to the SHARE_INFO hash describing the share.

Once you have the information, you can set its values with the NetShareSetInfo() function. The syntax is

NetShareSetInfo( $netname,\%SHARE,$parm_err,$servername);

The name of the share to get information for is named in $netname, and the machine offering the share is listed in $servername. The \%SHARE is a reference to the SHARE_INFO hash describing the share.

Handling Input from Multiple Sources

The Win32::Ipc module is useful when waiting for input from different types of input. In Perl on a UNIX system, you can wait on multiple sources using the select(2) call, but the select() operation excludes semaphores and messages. On an NT system you can use the Win32::Ipc module to wait on sources such as a Mutex, ChangeNotification, Semaphore, or even another process. The syntax for this call is

WaitForMultipleObjects(@list , $WaitAll ,$TimeOut );

@list is a list of objects to wait for. The $WaitAll variable is set to 1 if the function has to return when all the objects have input. If $WaitAll is set to 0, the function returns when there is input for the first available object. The timeout value is given as milliseconds in $TimeOut.

Here's an example of how to wait for more than one input. There are two semaphores and one Mutex to wait for in Listing 10.6.


Listing 10.6. Handling input from more than one source.
 1 use Win32::Mutex;
 2 use Win32::Semaphore;
 3 #  Create all the objects here:
 4 Win32::Semaphore::Create($sem, 1,1,"MySemaphore")||die $!;
 5 Win32::Semaphore::Create($com, 1,1,"ComPort1")||die $!;
 6 Win32::Mutex::Create( $mut,0,"MyMutex") ||die $!;
 7 #
 8 # note: the WaitForMultipleObjects call is inherited, and
 9 #
10
11 Win32::Semaphore::WaitForMultipleObjects(($sem,$mut,$com),
12     1, INFINITE )
13     || die $!;

Summary

This chapter has provided enough information on how to use Win32:: and its extension modules to access Windows NT system facilities. Some of these functions may change over time and as development progresses. However, you have enough information now to write system administration applications with the Win32 version of Perl for NT.