Windows2003-3790/tools/wmife.pm

860 lines
28 KiB
Perl

#-----------------------------------------------------------------//
# Script: WMIFE.pm
#
# (c) 2001,2002 Microsoft Corporation. All rights reserved.
#
# addresses the generic OLE->WMI interface to test various stuff
# defined on the given remote box
#
# Version: <2.01> 06/11/2002 : Serguei Kouzmine
#
#-----------------------------------------------------------------//
package WMIFE;
use lib $ENV{RAZZLETOOLPATH} . "\\PostBuildScripts";
use lib $ENV{RAZZLETOOLPATH};
use PbuildEnv;
use Logmsg;
require Exporter;
use vars qw( @ISA @EXPORT_OK );
@ISA = ( 'Exporter' );
@EXPORT_OK = qw( $verbose );
my $hPrivileges = {"SeBackupPrivilege" => "Backup",
"SeSecurityPrivilege" => "Security",
"SeRestorePrivilege" => "Restore",
"SeShutdownPrivilege" => "ShutDown",
"SeRemoteShutdownPrivilege" => "RemoteShutdown"
};
use strict;
no strict 'refs';
use Win32::OLE qw(in with);
use Win32::OLE::Variant;
# use 'in' loop for retrieve WQL cursors
use vars qw( $verbose
$NameSpace
$impersonationLevel
$authLevel
$Privileges
$ComputerName
$MasterData
$Provider
$Query
$DisplayFields
$User
$Password
$DEBUG
$TIMEOUT
$sRootKey
$sBootTestDataSubKey
$sLogonUserDataSubKey
);
my $VERSION = "1.1";
$TIMEOUT = 60;
$DEBUG = 0;
$verbose = ["NO","YES"];
my $SEPARATOR = "\n". join("", map{chr(61)} 0..79). "\n";
$NameSpace = "root\\cimv2";
$sRootKey = "HKEY_LOCAL_MACHINE";
$sBootTestDataSubKey = "SOFTWARE\\MICROSOFT\\BOOTTEST";
$sLogonUserDataSubKey = "SOFTWARE\\MICROSOFT\\WINDOWS NT\\CURRENTVERSION\\WINLOGON";
$impersonationLevel = "impersonate";
$authLevel = "pktPrivacy";
$Privileges = join(",", values(%$hPrivileges));
$Provider = q(WbemScripting.SWbemLocator);
$MasterData=
q(winmgmts:{impersonationLevel=$impersonationLevel,authenticationLevel=$authLevel, ($Privileges)}!
\\\\$ComputerName\\$NameSpace);
sub ObjectExecMethod{
my ($WbemObjClassName, $MethodName, $ComputerName, $ParameterName, $ParameterValue, $User, $Password) = @_;
# play with the command line expansion
# here.
$DEBUG and print STDERR
"ObjectExecMethod: using PROVIDER: ", $Provider, " " , $User, "\n";
my $WbemLocator = Win32::OLE->new($Provider);
OLEerrmsg("Could not instatiate $Provider") unless defined $WbemLocator;
my $SWbemServices = ($User =~ /\*/) ?
$WbemLocator->ConnectServer( $ComputerName, $NameSpace) :
$WbemLocator->ConnectServer( $ComputerName, $NameSpace, $User, $Password );
defined $SWbemServices or
OLEerrmsg("Cannot connect: $ComputerName, $NameSpace");
# Make sure we are using the appropriate security level
OLEerrmsg("Error setting security level")
if ( !($SWbemServices->{Security_}->{ImpersonationLevel} = 3) );
map {
$SWbemServices->{Security_}->{Privileges}->AddAsString($_)
or OLEerrmsg( "Error setting $hPrivileges->$_ privileges");
} keys %$hPrivileges;
# Get the process object on the remote machine
my $WbemObject = $SWbemServices->Get($WbemObjClassName);
my $Result = $WbemObject->SetProperty($ParameterName, $ParameterValue) unless !defined($ParameterName);
my $ProcessId = \0;
#~ $REALDEBUG and
#~ print $SEPARATOR , $WbemObject->Invoke("GetObjectText_"), $SEPARATOR;
#~ too much info here
my $ResultArrayRef = [(in $WbemObject->Invoke("Methods_"))];
$DEBUG and print STDERR
"$WbemObjClassName: METHODS_:\n";
$DEBUG and
map {&FormatDebugMsg($_->{"Name"}, $_->{"value"})} @$ResultArrayRef;
OLEerrmsg("undefined method: $MethodName") unless grep {/$MethodName/i}
map {$_->{"Name"}} @$ResultArrayRef;
defined $WbemObject or
OLEerrmsg("Could not create remote $WbemObjClassName");
# Now invoke the method
$DEBUG and
print STDERR $WbemObject->{CommandLine}, "\n";
OLEerrmsg("At $ComputerName, $ParameterName($ParameterValue) => $Result" )
if ($Result = $WbemObject->Invoke($MethodName,
$WbemObject->{CommandLine},
undef,
undef,
$ProcessId)); # ):- $ProcessId not filled up :-(
undef $WbemLocator;
undef $SWbemServices;
undef $WbemObject;
Win32::OLE->Uninitialize();
$DEBUG and print STDERR "OK\n";
0;
}
sub ExecQuery{
local ($ComputerName, $Query, $DisplayFields, $User, $Password) = @_;
# the "local" keyword is necessary to get search/replace able to do /e switch
my @MasterData = split (/\n/, $MasterData);
my %Caption = map {$_=> $_} @$DisplayFields;
# The values of %Caption must not be identical to the retrieved field names
my $Caption = \%Caption;
grep {s/\$\{?(\w+)\}?/${$1}/ge} @MasterData, $Query;
# here we replace all the vars with their values
$MasterData = join("", @MasterData);
$Query =~ s/([^\\])\\([^\\])/$1\\\\$2/g;
my $ResultSet = undef;
$DEBUG and print STDERR
"ExecQuery: using PROVIDER: ", $Provider, " " , $User, "\n";
my $WbemLocator = Win32::OLE->new($Provider);
OLEerrmsg("Could not instatiate $Provider") unless defined $WbemLocator;
my $SWbemServices = ($User =~ /\*/) ?
$WbemLocator->ConnectServer( $ComputerName, $NameSpace) :
$WbemLocator->ConnectServer( $ComputerName, $NameSpace, $User, $Password );
defined $SWbemServices or
OLEerrmsg("Cannot connect: $ComputerName, $NameSpace");
# Make sure we are using the appropriate security level
OLEerrmsg("Error setting security level")
if ( !($SWbemServices->{Security_}->{ImpersonationLevel} = 3) );
map {
$SWbemServices->{Security_}->{Privileges}->AddAsString($_)
or OLEerrmsg( "Error setting $hPrivileges->$_ privileges");
} keys %$hPrivileges;
$DEBUG and print STDERR
"\$Query: ", qq($Query), "\n";
###
### eval { $ResultSet = Win32::OLE->GetObject($MasterData)->ExecQuery($Query); };
### $DEBUG and $Query = ""
eval { $ResultSet = $SWbemServices->ExecQuery($Query); };
#
#
my $ResultArrayRef = [(in $ResultSet)] unless ($@);
$ResultArrayRef or
print STDERR "Win32::OLE::LastError ", Win32::OLE::LastError, "\n";
my @ResultCursor;
# pack ResultSet -> ResultCursor;
map {map {push @ResultCursor, {$Caption->{$_} => $ResultArrayRef->[0]->{$_}}}
@$DisplayFields and shift @$ResultArrayRef}
(0..$#$ResultArrayRef);
Win32::OLE->Uninitialize();
\@ResultCursor;
}
# &IsRunning($ProcMask, $runAtHost);
# lists processes on the host $runAtHost to
# find pattern $ProcMask.
# for all process found, collect the
# $1 of the search
# e.g. you get language of the running postbuiild with
# the pattern:
# "\\\\POSTBUILDSCRIPTS\\\\PBUILD\\\.CMD\\\"\\s\+\\\-l[: ]\(\\w\{3\}\)";
#
# plus some properties of the $ProcessSet
# return value: array pointer. The arguments are filtered out by default:
# (/S:\\BLD_WNXF1\RELEASE\GER\3562.X86FRE.MAIN.011002-1901\PRO /TEMPDRIVE:D /#T:7)
# PID= 00220
# NAME= winnt32.exe
# CMD= \\bld_wnxf1\release\ger\3562.x86fre.main.011002-1901\pro\i386\winnt32.exe /s:\\bld_wnxf1\release\ger\3562.x86fre.main.011002-1901\pro /tempdrive:d /#t:7
sub IsRunning{
my (
$WMIclass, $ProcMask, $Field, $Fields, $runAtHost) = @_;
# filter out arguments
$ProcMask = $ProcMask."\\\"\? \(\.\+\)\$" unless $ProcMask=~/\(/;
$DEBUG and print STDERR $ProcMask, "\n";
my $ProcessSet;
my @result;
my $Moniker = "winmgmts:{impersonationLevel=impersonate}!\\\\HOST\\root\\cimv2";
$Moniker =~ s/HOST/$runAtHost/;
$DEBUG and print STDERR
"using MONIKER: ", $Moniker, "\n";
eval {
$ProcessSet = Win32::OLE->GetObject($Moniker)->InstancesOf ($WMIclass); };
unless($@){
foreach my $ProcessInst (in $ProcessSet){
push @result, join("\n",
uc($1), map {"$_=".$ProcessInst->{$_}} @$Fields),
if $ProcessInst->{$Field} =~ m/$ProcMask/gi;
}
}else{
print STDERR Win32::OLE->LastError, "\n";
}
$DEBUG and print STDERR join ("\n", @result) , "\n";
\@result;
}
# supplyRegData
# usage:
# $defaultData = {
# "DEBUG" => $DEBUG,
# "Lang" => $Lang ,
# ...
# }
# $volatileData = {}
#
# &supplyRegData($sTargetcomputer, $REGISTRY_KEY,
# $defaultData, $volatileData , $FovWrite);
#
#
sub supplyRegData{
my ($sTargetcomputer, $sBootTestDataSubKey, $defaultData, $volatileData, $FovWrite) = @_;
use strict;
use Win32API::Registry 0.21 qw(HKEY_LOCAL_MACHINE
REG_OPTION_VOLATILE
REG_OPTION_NON_VOLATILE
KEY_ALL_ACCESS
KEY_READ
REG_SZ
regLastError
RegConnectRegistry
RegOpenKeyEx
RegEnumValue
RegQueryInfoKey
RegQueryValueEx
RegCreateKeyEx
RegSetValueEx
RegDeleteValue
RegCloseKey
);
# This provides fairly low-level access to the Win32 System API calls
# dealing with the Registry [mostly from WINREG.H]. This is mostly
my $hRootKey = $sRootKey;
{ no strict 'refs';
$hRootKey =~ s/(\w+)/$1/eeg;
use strict 'refs';
}
my $sStatusSubKey = "STATUS";
my $ohKey = undef;
my $ohSubKey = undef;
my $ohStatusSubKey = undef;
my $ocValues;
my $olValName;
my $olValData;
my $olSecDesc;
my $opValData;
my $osValName;
my $sValueNames = [];
my $sValueName;
my $nStatus = undef;
$DEBUG and print STDERR
"COMPUTER: ".uc($sTargetcomputer)."\n";
&RegConnectRegistry( $sTargetcomputer,
$hRootKey,
$ohKey )
or die "Can't open $hRootKey: ", regLastError(),"\n";
$DEBUG and print STDERR
"Registry Key: ".$sRootKey."\\$sBootTestDataSubKey\n";
$nStatus = &RegOpenKeyEx( $ohKey,
$sBootTestDataSubKey ."\\". $sStatusSubKey,
0,
KEY_READ,
$ohStatusSubKey );
if (scalar(keys(%$volatileData))){
$sValueName = (keys(%$volatileData))[0];
$opValData = undef;
if ($nStatus){
&RegQueryValueEx( $ohStatusSubKey,
$sValueName ,
[],
[],
$opValData,
[] );
# error is ignored.
$opValData = "(undef)" unless $opValData;
&RegCloseKey($ohStatusSubKey);
}
if ($nStatus ){
$DEBUG and print STDERR
"\\\\$sTargetcomputer\\$sRootKey\\$sBootTestDataSubKey\\$sStatusSubKey\\$sValueName: ",$opValData, "\n";
$FovWrite or return 1;
}
}
RegCreateKeyEx($ohKey,
$sBootTestDataSubKey,
0,
"",
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
[],
$ohSubKey,
[]);
RegCloseKey($ohSubKey);
&RegOpenKeyEx( $ohKey,
$sBootTestDataSubKey ,
0,
KEY_ALL_ACCESS,
$ohSubKey )
or die "Can't open $sBootTestDataSubKey: \n", regLastError(),"\n";
&RegQueryInfoKey($ohSubKey,
[],
[],
[],
[],
[],
[],
$ocValues,
$olValName,
$olValData,
[],
[]
)
or die "Can't query $sBootTestDataSubKey: ", regLastError(),"\n";
$DEBUG and print STDERR
$ocValues, " values found\n";
foreach my $Data (keys(%$defaultData)){
if ($FovWrite){
# tsanders: existing values do not get overwritten.
&RegDeleteValue($ohSubKey,
$Data);
$DEBUG and print STDERR
"\t\tDeleting Default Data: \"$Data\"\n\t\t$^E\n";
#ignore the "$^E" here.
}
&RegSetValueEx($ohSubKey,
$Data,
0,
REG_SZ,
$defaultData->{$Data},
0
)
or die "Can't RegSetValueEx($Data,...$defaultData->{$Data}: ", regLastError(),"\n";
}
foreach my $nCnt (0..$ocValues-1){
&RegEnumValue($ohSubKey,
$nCnt,
$osValName,
0,
[],
[],
$opValData,
0 );
push @$sValueNames, $osValName;
}
foreach $sValueName (@$sValueNames){
&RegQueryValueEx( $ohSubKey,
$sValueName ,
[],
[],
$opValData,
[] )
or die "Can't read $sValueName: ", regLastError(),"\n";
$DEBUG and print STDERR
"$sValueName=$opValData\n";
}
RegCreateKeyEx($ohSubKey,
$sStatusSubKey,
0,
"",
REG_OPTION_VOLATILE,
KEY_ALL_ACCESS,
[],
$ohStatusSubKey,
[]);
foreach my $Data (keys(%$volatileData)){
&RegSetValueEx($ohStatusSubKey,
$Data,
0,
REG_SZ,
$volatileData->{$Data},
0
)
or die "Can't RegSetValueEx($Data,...$volatileData->{$Data}: ",
regLastError(),"\n";
}
map {defined($_) and RegCloseKey($_)} ($ohSubKey, $ohKey, $ohStatusSubKey);
0;
}
sub OLEerrmsg($)
{
my $text = shift;
errmsg( "$text (". Win32::OLE->LastError(). ")" );
exit 1;
}
sub ScheduleTime{
use Time::localtime;
my $shiftTime = shift;
my $tNow = time();
$DEBUG and print STDERR
"Current time: " , &localtime->hour(), ":", &localtime->min(), "\n";
$tNow += $shiftTime;
my @result = map {sprintf("%02d", $_)} (&localtime($tNow)-> hour(),
&localtime($tNow)-> min(),
&localtime($tNow)-> sec(),
&localtime($tNow)-> mon() + 1,
&localtime($tNow)-> mday(),
&localtime($tNow)-> year() + 1900
);
$DEBUG and print STDERR
"Schedule time: " , $result[0], ":",
$result[1], ":",
$result[2], " ",
$result[3], "/",
$result[4], "/",
$result[5], "\n";
\@result;
}
sub FormatDebugMsg{
format STDERR =
# field name field value
@<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$_[0] $_[1]
.
write STDERR;
}
# BackTick
# sample usage: &BackTick("dir");
# &BackTick(<<BLOCKBOUNDARY);
# ...cmd code...
# BLOCKBOUNDARY
# passes block of code to the cmd interpreter.
# lines are concatenated
# comments thrown away
# useful stuff when the code is already
# developed and just needs to be inserted in place
# has limitations.
sub BackTick{
my ($cmd) = @_;
my $res = undef;
$DEBUG and
print STDERR join(" &", grep (!/^:|REM/i, ($cmd =~ /^(.+)/gm)));
$cmd = join(" &", grep (!/^:|REM/i, ($cmd =~ /^(.+)/gm)));
$res=qx ($cmd);
$res;
}
sub pPpPinfo{
my @infa = @_;
my $opBrws = Win32::OLE->new("WScript.Shell");
$opBrws->Popup(join("\n", @infa), $TIMEOUT ,
ref($opBrws) . "<- $0,". $VERSION,
0 + 64);
undef($opBrws);
Win32::OLE->FreeUnusedLibraries();
Win32::OLE->Uninitialize();
1;
}
1;
__END__
#-------------------------------------------------------------//
=head1 NAME
B<WMIFE> - Interface the query engine exposed by WMI
=head1 SYNOPSIS
use WMIFE;
&WMIFE::ExecQuery($ComputerName, $Query, [@Fields]);
&WMIFE::FormatDebugMsg($Comment, $Field);
=head1 DESCRIPTION
Generates the WQL (A.K.A. WMI SQL) query on the remote machine against
its namespace.
Sample objects that reside on the remote box $ComputerName are:
* local files [/.//ROOT/CIMV2/CIM_DATAFILE],
* running processes [/.//ROOT/CIMV2/WIN32_PROCESSES],
* registry entries [/.//ROOT/DEFAULT/STDREGPROV].
The WMI is functional even in the absence of logon sessions on $ComputerName.
=head1 METHODS
=head2 FormatDebugMsg($fieldName, $FieldValue)
Nice (?) formatting of the RDB like table field output (A.K.A. browse view):
Name perl.exe
Description perl.exe
ProcessID 2920
ParentProce 2692
ExecutableP d:\ntt\tools\perl\bin\perl.exe
...
=head2 ExecQuery($ComputerName, $Query, $DisplayFields)
Execute the WMI WQL query against the namespace of the remote machine
where
$ComputerName is the name of the machine which namespaces are about to be queried.
$Query the string representing the query, with the dollarsigned names placeholders for parameters
$DisplayFields reference to the field list to retusn the result
The return value is a reference to the like the RDB cursor.
(array of hashes where keys are described in $DisplayFields array).
If no data is returned from the query the returned value is a reference to undefined value.
=head2 ObjectExecMethod($MethodName, $ComputerName, $ParameterName, $ParameterValue, $User, $Password
Access the namespace on the remote box $ComputerName, and execute the method $MethodName of the class
$WbemObjClassName (currently only for Win32_Process). Supply the parameter(s) via name/value.
PErfurm the call under $User account, proving the password.
=head2 Sample calls:
=head2 To list files in the root directory of the logical disk labeled $Drive of the machine $Machine
$Results = &WMIFE::ExecQuery($Machine,
qq(SELECT * FROM cim_Datafile WHERE Drive="$Drive" AND Path="\\\\" "),
[qw(Drive Path Name)]);
=head2 To list logical disks of the machine $Machine
$Results = &WMIFE::ExecQuery($Machine,
qq(SELECT * FROM Win32_LogicalDisk WHERE DriveType=3),
[qw(Name Description)]);
=head2 To list the running processes
$Results = &WMIFE::ExecQuery($ComputerName,
qq(SELECT * FROM Win32_Process),
[qw(Name Description ProcessID ParentProcessId ExecutablePath )]);
=head2 To view installed software
$Results = &WMIFE::ExecQuery($ComputerName,
qq(SELECT * FROM Win32_Product),
[qw(Name Vendor Version)]);
=head2 To verify administrators group
$Results = &WMIFE::ExecQuery($ComputerName,
qq(SELECT * FROM Win32_GroupUser WHERE GroupComponent="Win32_Group.Domain=\\"$ComputerName\\",Name=\\"Administrators\\""),
[qw(GroupComponent)]);
=head2 To browse the process dependency hierarchy
$Results = &WMIFE::ExecQuery($ComputerName,
qq(SELECT * FROM CIM_ProcessExecutable),
[qw(Antecedent Dependent)]);
=head2 Query who is currently logged to somewhere.
$Results =
&WMIFE::ExecQuery($ENV{"COMPUTERNAME"}, # interactive session
qq(SELECT * FROM Win32_LogonSession WHERE LogonType=2),
[qw(LogonId LogonType)]);
foreach my $Result (@$Results){
$DEBUG and
map {&WMIFE::FormatDebugMsg($_, $Result->{$_})} keys (%$Result);
push @Ids, $Result->{"LogonId"};
}
$Results =
&WMIFE::ExecQuery($ENV{"COMPUTERNAME"}, # Difficult to build the WHERE condition.
qq(SELECT * FROM Win32_LoggedOnUser),
[qw(Antecedent Dependent)]);
my @Users = ();
my $User = undef;
my $LoggedUser = undef;
my @LoggedUsers = ();
# a little bit ugly.
foreach my $Id (grep {/\d+/} @Ids){
foreach my $Result (@$Results){
$User = $Result->{"Antecedent"};
if ($User =~ /\S/){
push @Users, $User;
}
if ((grep {/\b$Id\b/} $Result->{"Dependent"})){
$LoggedUser = pop @Users;
push @LoggedUsers, $LoggedUser;
$DEBUG and &WMIFE::FormatDebugMsg("win32_user", $LoggedUser);
$DEBUG and &WMIFE::FormatDebugMsg("win32_logonSession", $Id);
}
}
}
map {($user, $domain) = (/Win32_Account.Domain=\"(\w+)\",Name=\"(\w+)\"/);
$DEBUG and
print $user, "\t" , $domain,"\n"; } @LoggedUsers;
=head2 To list the $Result contents, the following framework may be used:
use WMIFE;
&WMIFE::ExecQuery($ENV{"COMPUTERNAME"},
qq(SELECT * FROM Win32_BootConfiguration),
[qw(Caption BootDirectory Description)]);
foreach my $Result (@$Results){
map {&WMIFE::FormatDebugMsg($_, $Result->{$_})} keys (%$Result);
}
=head2 To design the new query
please use the %WINDIR%\SYSTEM32\WBEM\wbemtest.exe vendor shell for accessing and
querying the WMI classes and then use the query string designed in that shell as an argument to the Perl qq()
[double backslashes]
Some queries may take long time to execute, before being sufficiently refined.
=head1 SEE ALSO
Whole bunch of tools interfacing WMI is in progress. Note that the Perl interface to WMI is not as powerful as
VBSCRIPT/JSCRIPT since there is no working examples for P/P (A.K.A. object sinking) and in general any
interface(s) other than SQL. the design of such sample code might prove to be complicated indeed.
=head2 supplyRegData($sTargetcomputer, $Lang, $BldNum, $Sku, $Drive, $BuildName, $Unattend, $FovWrite)
Write the data to the REGISTRY of the Boot test machine to start the execution
of test build installation.
The REGISTRY path where the data is stored is:
HKEY_LOCAL_MACHINE\SOFTWARE\MICROSOFT\BOOTTEST
The data includes the following fields:
-------------+-----------------------------------------------------------
$Lang | fields are used to find the test bits, in the form:
$BuildName | $Drive\$Lang\$BuildName
$BldNum | $BldNum, $Sku and $Lang are used to verify the
$Sku | REGISTRY data is actual for the build being installed.
$Drive | target drive for winnt32.exe
$Unattend | location of the answer file
The data is written by the postbuild on the build machine (NTDEV\NTBUILD),
and is later consumed from boot machine side by (NTDEV\<AUTOGENERATED> user).
The registry access across the network hence the use of HKEY_LOCAL_MACHINE.
The default REGISTRY key names are:
HKLM\SOFTWARE\MICROSOFT\BOOTTEST\_BldNum => $BldNum
HKLM\SOFTWARE\MICROSOFT\BOOTTEST\_BuildArch => $_BuildArch
HKLM\SOFTWARE\MICROSOFT\BOOTTEST\LANG => $Lang
HKLM\SOFTWARE\MICROSOFT\BOOTTEST\_BldSku => $Sku
HKLM\SOFTWARE\MICROSOFT\BOOTTEST\_BuildType => $_BuildType
HKLM\SOFTWARE\MICROSOFT\BOOTTEST\_BldDrive => $Drive
HKLM\SOFTWARE\MICROSOFT\BOOTTEST\Unattend => $Unattend
HKLM\SOFTWARE\MICROSOFT\BOOTTEST\_BldName => $BuildName
Running regedit.exe will show the following data:
+-----------------------------------------------
|Windows Registry Editor Version 5.00
|
|[HKEY_LOCAL_MACHINE\SOFTWARE\MICROSOFT\BOOTTEST]
|"_BuildArch"="x86"
|"_BuildType"="fre"
|"_BldSku"="SRV"
|"_BldName"="3615.x86fre.main.020306-1639"
|"_BldDrive"="D"
|"Unattend"="unattend.txt"
|"LANG"="GER"
|"_BldNum"="3615"
|
|[HKEY_LOCAL_MACHINE\SOFTWARE\MICROSOFT\BOOTTEST\Status]
|"COMPUTERNAME"="BLD_WNXF1"
+-----------------------------------------------
The Status subkey is to prevent concurrent execution of the more than one
test build install on the single test machine. The function will return
the data observed e.g.
\\sergueik4\HKEY_LOCAL_MACHINE\SOFTWARE\MICROSOFT\BOOTTEST\STATUS\COMPUTERNAME: SERGUEIK2
To ignore the presence of the Status set $FovWrite = 1.
Booting test machine (but not logging off of the user) will cause Status
key to disappear.
=head2 &ScheduleTime($Delay)
Calculate the time/date for execution of the scheduled tasks in
$Delay secs from NOW.
Example usage:
use WMIFE;
$WMIFE::DEBUG = 1;
my $DELAY1 = 1800; # delay in 1800 secs = 30 min
my $string = "/st %HH%:%MM%:%SS% /sd %mm%/%dd%/%yyyy%" ;
my $marker = ["HH" , "MM", "SS", "mm", "dd", "yyyy"];
# this is the way to set the date/time of scheduled task
my $ScheduleTime = &WMIFE::ScheduleTime($DELAY1);
print STDERR "\"$string\"\t";
map {$string =~ s/\%$marker->[$_]\%/$ScheduleTime->[$_]/g;}
(0..$#$marker);
print STDERR "\"$string\"\n";
Current time: 15:51
Schedule time: 16:21:32 03/25/2002
"/st %HH%:%MM%:%SS% /sd %mm%/%dd%/%yyyy%"
"/st 16:21:32 /sd 03/25/2002"
=head1 AUTHOR
Serguei Kouzmine sergueik@microsoft.com
=cut
1;