Tsguy
(Getting the hang of it)
2011-06-30 09:48 PM
PST locate and log...

Hiya Kixers,

Been searching through the forums for some ideas on a script to do the following:

- Search the logged in user's outlook profile for PST's.
- Log where those PST's are - on local device or network.

Found this thread -

PST discover and move discussion

Which refers to a script which has a great deal more going on in it, than I need to deal with at this time.

I'm trying to plagurize only the bits and pieces I need, but I'm getting stuck on a few points.

For instance:

 Code:

$RootPath = 'HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles'

$DProfile = ReadValue($RootPath, 'DefaultProfile')
$WKey = $RootPath + '\' + $DProfile + '\'



I believe many of our staff won't have a "DefaultProfile" value, Outlook 2007 will create a profile value of "Outlook" if left to it's own devices, and several of our staff may have other values defined.

Here's the full code snip on searching a "profile" for attached PST's.

 Code:

$RootPath = 'HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles'

; get list of mapped drives 
$aMapped = WMIMappedDrives()

$DProfile = ReadValue($RootPath, 'DefaultProfile')
$WKey = $RootPath + '\' + $DProfile + '\'

$MServer = ReadValue($WKey + '13dbb0c8aa05101a9bb000aa002fc45a', '001e6602')
$DName = UtoA(ReadValue($WKey + '13dbb0c8aa05101a9bb000aa002fc45a', '001f3001'))

$eIndex = 0
$aIndex = -1
$EKey = EnumKey($WKey, $eIndex)
While Not @ERROR
$TVal = UtoA(ReadValue($WKey + $EKey, '001f6700'))
If $TVal
$aIndex = $aIndex + 1
ReDim Preserve $aPSTPaths[$aIndex]
ReDim Preserve $aPSTKeys[$aIndex]
$aPSTPaths[$aIndex] = $TVal
$aPSTKeys[$aIndex] = $WKey + $EKey
EndIf
$eIndex = $eIndex + 1
$EKey = EnumKey($WKey, $eIndex)
Loop

; Exit if nothing to do 
If $aIndex < 0
'No PST files are registered for user ' $DName ' - exiting!' ? ?
Exit 0
EndIf



Any guidance on adjusting this to be a bit more flexible with the user's Profile name would be appreciated.

Thanks,

TS


AllenAdministrator
(KiX Supporter)
2011-06-30 10:42 PM
Re: PST locate and log...

 Quote:

I believe many of our staff won't have a "DefaultProfile" value, Outlook 2007 will create a profile value of "Outlook" if left to it's own devices, and several of our staff may have other values defined.


The "defaultprofile" value is a registry setting. If you follow the path HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles in regedit, you will likely see the value is "Outlook". Hardcoded values are going to give you big time headache eventually. This method is much better.


Tsguy
(Getting the hang of it)
2011-07-06 12:00 AM
Re: PST locate and log...

Thanks Allen, having looked over the code more, and the registry I see that what I thought was a concern is not.

I have been digging more at this code however, and now have the following for a script:

 Code:
 
Break On

Dim $, $_ ; temp, throwaway var 
Dim $aPSTPaths[0] ; Array of PST paths 
Dim $aPSTKeys[0] ; Array of PST key paths 
Dim $RootPath ; registry root path 
Dim $WKey ; working key path 
Dim $EKey ; enumerated registry path 
Dim $TVal ; temp value for 
Dim $aIndex, $eIndex ; index pointers for array and enumeration 
Dim $MServer, $DName, $DProfile ; user data vars 
Dim $aMapped ; array of mapped network drives 
Dim $Drv, $NetFlag, $IsNet, $MoveFlag ; Drive letter enumerator, Network Only flag, IsNetwork flag, Move Flag 
Dim $PSTName, $PSTPath ; name & Path of current PST file 
Dim $NewPSTName, $NewPSTPath ; name of renamed PST file or new Path 
Dim $NewPSTRoot ; path of new PST path, when moving instead of renaming 
Dim $LogPath ; UNC path where logs are written 

$ = SetOption('Explicit', 'On')
$ = SetOption('WrapAtEOL', 'On')
$ = SetOption('NoVarsInStrings', 'On')
$ = SetOption('NoMacrosInStrings', 'On')

; # # # # # START OF CUSTOM PARAMETERS # # # # # 
;$NetFlag = 0 ; only process network PST files if 1 
$MoveFlag = 1 ; Move files if 1, otherwise disassociate and rename 
$LogPath = '\\servername\share\logs\' ; UNC path where renamed PST file locations are logged 
$NewPSTRoot = '\\servername\share\' ; UNC or mapped drive 
; # # # # # END OF CUSTOM PARAMETERS # # # # # 


; Identify PST

$RootPath = 'HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles'

; get list of mapped drives 
$aMapped = WMIMappedDrives()

$DProfile = ReadValue($RootPath, 'DefaultProfile')
$WKey = $RootPath + '\' + $DProfile + '\'

$MServer = ReadValue($WKey + '13dbb0c8aa05101a9bb000aa002fc45a', '001e6602')
$DName = UtoA(ReadValue($WKey + '13dbb0c8aa05101a9bb000aa002fc45a', '001f3001'))

; find the PST files associated with this profile - need to enumerate each subkey and find the 001f6700 value 
$eIndex = 0
$aIndex = -1
$EKey = EnumKey($WKey, $eIndex)
While Not @ERROR
$TVal = UtoA(ReadValue($WKey + $EKey, '001f6700'))
If $TVal
$aIndex = $aIndex + 1
ReDim Preserve $aPSTPaths[$aIndex]
ReDim Preserve $aPSTKeys[$aIndex]
$aPSTPaths[$aIndex] = $TVal
$aPSTKeys[$aIndex] = $WKey + $EKey
EndIf
$eIndex = $eIndex + 1
$EKey = EnumKey($WKey, $eIndex)
Loop

; Exit if nothing to do 
If $aIndex < 0
'No PST files are registered for user ' $DName ' - exiting!' ? ?
Exit 0
EndIf

;? ? 'Preparing for Mail Archiving on ' @WKSTA ' for user ' $DName ?

; now have all needed info 
; - terminate Outlook if it is running 
; - delete the PST registry keys 
; - rename the PST files 
; - copy the PST file to the network share

; terminate Outlook 
$ = Split(WMIProcessList('outlook.exe')[0], ',')[1]
If Not @ERROR
' Closing Outlook...' ?
$ = WMIProcessKill($) ; terminate Outlook 
Sleep 3 ; wait for Outlook to close open files 
EndIf


; Enumerate the PST files that are registered in Outlook 
For $ = 0 to UBound($aPSTPaths)

;$IsNet = -1 ; Default to Not Network, will be 0 or more if network PSTs are found 

$Drv = Left($aPSTPaths[$], 2) ; drive letter where PST is stored 
$_ = InStrRev($aPSTPaths[$], '\')
$PSTName = SubStr($aPSTPaths[$], $_ + 1) ; get name part 
$PSTPath = Left($aPSTPaths[$], $_) ; get path part 

; determine if the PST is on a network drive. 
For $_ = 0 to UBound($aMapped)
If $aMapped[$_][0] = $Drv
$IsNet = $_ ; is network! 
EndIf
Next

; Disassociate by deleting the registry key 
;'Disassociate'
$_ = DelTree($aPSTKeys[$])

; Rename the file 

$NewPSTName = @USERID + '_' + Join(Split(Join(Split($PSTName, ' '), '_'), '.pst'), '.pst')
;'; move'
Move $PSTPath + $PSTName $PSTPath + $NewPSTName
$NewPSTPath = $NewPSTRoot
Move $PSTpath + $NewPSTName $NewPSTPath + $NewPSTName


$_ = RedirectOutput($LogPath + '\' + @WKSTA + @userid +'.txt')


Next

Exit 0



; convert Unicode strings to ASCII 
Function UtoA($_String)

; return if string is empty 
If Not $_String Exit 0 EndIf

Dim $_S, $_I ; temp string, index pointer 

; get each character pair as hex and convert to ASCII character 
For $_I = 1 to Len($_String) Step 4
$_S = $_S + Chr(Val('&' + SubStr($_String, $_I, 2)))
Next

$UtoA = $_S
Exit 0

EndFunction

Function AtoU($_String)

Dim $_S, $_I ; temp string, index pointer 

; get each ASCII character and convert to hex words 
For $_I = 1 to Len($_String)
$_S = $_S + Right('00' + DecToHex(Asc(SubStr($_String, $_I, 1))), 2) + '00'
Next

$AtoU = LCase($_S) + '0000'
Exit 0 

EndFunction



;; 
;;====================================================================== 
;; 
;;FUNCTION TimeDiff() 
;; 
;;AUTHOR Glenn Barnas 
;; 
;;VERSION 2.2 / 2007/10/14 
;; Modified to increase accuracy, permit fracional second calculations 
;; 2.1 / 2007/03/17 
;; added "now" and "today" options for both start and end times 
;; 2.0 / 2006/11/20 
;; Changes for code efficiency; added defaults for midnight 
;; 
;;ACTION Calculates the time difference between two given date/time strings 
;; 
;;SYNTAX TimeDiff(Start [, End] [, Format] [, MSec]) 
;; 
;;PARAMETERS Start - REQUIRED, String value representing the start timestamp 
;; Format yyyy/mm/dd hh:mm:ss 
;; 
;; End - OPTIONAL, Defaults to "now" 
;; String value representing the ending time 
;; Format yyyy/mm/dd hh:mm:ss 
;; Can be the special value "now" for the current date/time, or "today" 
;; for midnight of the current day. 
;; 
;; When the time value is not specified, it defaults to 00:00:00.000 (midnight) 
;; 
;; Format - OPTIONAL, one of: 
;; "m" - return minutes 
;; "h" - return hours 
;; "d" - return days 
;; "y" - return years 
;; When a format value is specified, it returns the fractional part (ie 0.5 days for 12 hours). 
;; 
;; MSec - OPTIONAL, True if the fractional seconds should be returned. Default 
;; is false, returning whole seconds, to maintain compatibility with earlier versions. 
;; MSec only affects the return of fractional seconds, not fractional parts of other time formats. 
;; 
;;REMARKS Returns a value representing the difference in time between two date/time 
;; strings. Assumes that "Start" is in the past, but will properly return a 
;; negative value if it is in the future. 
;; 
;;RETURNS Double - difference between Start and End timestamps in seconds 
;; 
;;DEPENDENCIES None 
;; 
;;TESTED WITH Kix 4.2+, NT4, W2K, WXP, W2K3 
;; 
;;EXAMPLES If TimeDiff(GetFileTime('SomeFile.txt'), 'now', 'h') > 48 
;; "File is more than 2 days old!" ? 
;; EndIf 
; 
Function TimeDiff($_Start, OPTIONAL $_End, OPTIONAL $_Fmt, OPTIONAL $_MSec)

Dim $_, $_SDate, $a_Start, $_EDate, $a_End, $_Duration

; Check for special START parameters 
Select
Case $_Start = 'now'
$_Start = @DATE + ' ' + @TIME + '.' + @MSECS
Case $_START = 'today'
$_Start = @DATE + ' 00:00:00.000'
EndSelect

; Check for special END parameters 
Select
Case $_End = 'now' Or $_End = '' 
$_End = @DATE + ' ' + @TIME + '.' + @MSECS
Case $_End = 'today'
$_End = @DATE + ' 00:00:00.000'
EndSelect

; Validate parameters 
; Parameters passed are "yyyy/mm/dd hh:mm:ss[.sss]" - make sure the default time is added 
$a_Start = Split(Join(Split(Join(Split($_Start + ' 00:00:00.000', '/'), ' '), ':'), ' '), ' ', 6)
If UBound($a_Start) <> 5 Exit 87 EndIf ; bad start time parameter 
For $_ = 0 to 5
$a_Start[$_] = CDbl($a_Start[$_]) ; convert to numeric values 
Next

$a_End = Split(Join(Split(Join(Split($_End + ' 00:00:00.000', '/'), ' '), ':'), ' '), ' ', 6)
If UBound($a_End) <> 5 Exit 87 EndIf ; bad start time parameter 
For $_ = 0 to 5
$a_End[$_] = CDbl($a_End[$_]) ; convert to numeric values 
Next

; Convert dates to Days, then convert to seconds and add the time value 
If $a_Start[1] < 3
$a_Start[1] = $a_Start[1] + 12
$a_Start[0] = $a_Start[0] - 1
EndIf
$_SDate = $a_Start[2] + ( 153 * $a_Start[1] - 457 ) / 5 + 365 * $a_Start[0] + $a_Start[0] / 4 - $a_Start[0] / 100 + $a_Start[0] / 400 - 306
$_SDate = CDbl($_SDate) * 86400.0
$_SDate = $_SDate + $a_Start[3] * 3600 + $a_Start[4] * 60 + $a_Start[5]

If $a_End[1] < 3
$a_End[1] = $a_End[1] + 12
$a_End[0] = $a_End[0] - 1
EndIf
$_EDate = $a_End[2] + ( 153 * $a_End[1] - 457 ) / 5 + 365 * $a_End[0] + $a_End[0] / 4 - $a_End[0] / 100 + $a_End[0] / 400 - 306
$_EDate = CDbl($_EDate) * 86400.0
$_EDate = $_EDate + $a_End[3] * 3600 + $a_End[4] * 60 + $a_End[5]

; Get the duration between the timestamps 
$_Duration = CDbl($_EDate - $_SDate)

; Trim fractional seconds if the MSec flag wasn't set 
; Value returned is whole seconds 
If Not $_MSec
$_Duration = CInt($_Duration)
EndIf

; Return data as a Double - seconds (default), hours, minutes, days, or years 
Select
Case $_Fmt = 'm' ; minutes 
$TimeDiff = $_Duration / 60.0
Case $_Fmt = 'h' ; hours 
$TimeDiff = $_Duration / 3600.0
Case $_Fmt = 'd' ; days 
$TimeDiff = $_Duration / 86400.0
Case $_Fmt = 'y' ; years 
$TimeDiff = $_Duration / 31536000.0
Case 1
$TimeDiff = $_Duration
EndSelect

Exit 0

EndFunction




;; 
;;====================================================================== 
;; 
;;FUNCTION WMIMappedDrives() 
;; 
;;ACTION Return a list of mapped drives from a computer 
;; 
;;AUTHOR Glenn Barnas 
;; 
;;VERSION 1.0 / 2008/03/24 
;; 
;;SYNTAX WMIMappedDrives([, AuthPtr]) 
;; 
;;PARAMETERS AuthPtr - OPTIONAL - pre-authenticated WMI object pointer 
;; Use WMIAuthentication() udf to create the AuthPtr value 
;; AuthPtr is not needed if user has admin rights 
;; 
;;REMARKS By default, returns a detailed list of all processes from the local computer. An 
;; alternate computer can be specified, as can a specific process. When specifying 
;; processes, you can use a name (cmd.exe) or process ID. If you specify a name, all 
;; processeses matching that name will be returned, while specifying a PID will return 
;; exactly one element that matches that process (if it was found) 
;; If no match is found, the function exits with status 2 (file not found) 
;; 
;;RETURNS Array of drive letter / UNC Path arrays 
;; 
;;DEPENDENCIES WMI, 
;; 
;;TESTED WITH W2K, WXP, W2K3, Vista, x64 
;; 
;;EXAMPLES 
; 
Function WMIMappedDrives(OPTIONAL $_pAuth)

Dim $_objWMIService, $_colItems, $_objItem ; WMI object vars 
Dim $_Line ; line string 
Dim $_aTmp[0], $_I ; return array, index 
Dim $_ ; temp var 

$_I = -1

; If a pre-authenticated WMI object pointer was provided, use it, otherwise create a new object pointer 
If $_pAuth
$_objWMIService = $_pAuth
Else
$_objWMIService = GetObject('winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2')
If @ERROR Exit Val('&' + Right(DecToHex(@ERROR), 4)) EndIf
EndIf

; get the collection of process objects 
$_colItems = $_objWMIService.ExecQuery("Select * from Win32_MappedLogicalDisk",,48)
If @ERROR $_colItems = 0 Exit Val('&' + Right(DecToHex(@ERROR), 4)) EndIf

; Enumerate the collection of process data 
For Each $_objItem in $_colItems
$_I = $_I + 1
ReDim Preserve $_aTmp[$_I]
$_aTmp[$_I] = $_objItem.Name, $_objItem.ProviderName
Next

; return the array, close the collection, and gripe if no items were found 
$WMIMappedDrives = $_aTmp
$_colItems = 0
Exit 0

EndFunction



;; 
;;====================================================================== 
;; 
;;FUNCTION WMIProcessKill() 
;; 
;;ACTION Terminates a specific process by numeric PID 
;; 
;;AUTHOR Glenn Barnas 
;; 
;;VERSION 1.0 / 2007/10/20 
;; 
;;SYNTAX WMIProcessKill(Process [, Computer] [, AuthPtr]) 
;; 
;;PARAMETERS Process - PID of process to terminate 
;; 
;; Computer - OPTIONAL, Name of computer to target 
;; 
;; AuthPtr - OPTIONAL - pre-authenticated WMI object pointer 
;; Use WMIAuthentication() udf to create the AuthPtr value 
;; AuthPtr is not needed if user has admin rights 
;; 
;;REMARKS Terminates the process after verifying it exists 
;; 
;;RETURNS 1 (success) or 0 (failure) 
;; 
;;DEPENDENCIES WMI 
;; 
;;TESTED WITH W2K, WXP, W2K3, Vista, x64 
;; 
;;EXAMPLES 
; 
Function WMIProcessKill($_Process, Optional $_Computer, OPTIONAL $_pAuth)

Dim $_objWMIService, $_colItems, $_objItem ; WMI object vars 
Dim $_ ; temp var 
Dim $_Err ; error code 

; Must be a single numeric value to terminate 
If Val($_Process) <> $_Process
$WMIProcessKill = 0
Exit 87
EndIf

; insure a properly formatted computer name, default to local computer is not specified 
$_Computer = IIf(Not $_Computer, '.', Join(Split($_Computer,'\'),''))

; If a pre-authenticated WMI object pointer was provided, use it, otherwise create a new object pointer 
If $_pAuth
$_objWMIService = $_pAuth
Else
$_objWMIService = GetObject('winmgmts:{impersonationLevel=impersonate}!\\' + $_Computer + '\root\cimv2')
If @ERROR Exit Val('&' + Right(DecToHex(@ERROR), 4)) EndIf
EndIf

; get the collection of process objects 
$_colItems = $_objWMIService.ExecQuery("Select * from Win32_Process Where ProcessID=" + '"' + $_Process + '"',,48)
If @ERROR $_colItems = 0 Exit Val('&' + Right(DecToHex(@ERROR), 4)) EndIf

$_Err = 2 ; prepare for not found 

; Enumerate the collection of process data 
For Each $_objItem in $_colItems
$_ = $_objItem.Terminate
$_Err = @ERROR
Next
$_colItems = 0

; return appropriate values 
$WMIProcessKill = Not $_Err
Exit $_Err

EndFunction



;; 
;;====================================================================== 
;; 
;;FUNCTION WMIProcessList() 
;; 
;;ACTION Return a list of process info from a computer 
;; 
;;AUTHOR Glenn Barnas 
;; 
;;VERSION 1.0 / 2007/10/12 
;; Written as a replacement for PSList(), which uses SysInternals PSList.exe 
;; 
;;SYNTAX WMIProcessList([Process] [, Computer] [, AuthPtr]) 
;; 
;;PARAMETERS Process - return information about a specific process (group) by name 
;; or individual process ID (PID) 
;; 
;; Computer - OPTIONAL, Name of computer to query 
;; 
;; AuthPtr - OPTIONAL - pre-authenticated WMI object pointer 
;; Use WMIAuthentication() udf to create the AuthPtr value 
;; AuthPtr is not needed if user has admin rights 
;; 
;;REMARKS By default, returns a detailed list of all processes from the local computer. An 
;; alternate computer can be specified, as can a specific process. When specifying 
;; processes, you can use a name (cmd.exe) or process ID. If you specify a name, all 
;; processeses matching that name will be returned, while specifying a PID will return 
;; exactly one element that matches that process (if it was found) 
;; If no match is found, the function exits with status 2 (file not found) 
;; 
;;RETURNS Array of comma-delimited values, one element per process: 
;; Process Name 
;; Process ID (PID) 
;; Thread Count 
;; Handle Count 
;; Memory Usage (Bytes) 
;; User Time D:HH:MM:SS.sss format 
;; Kernel Time D:HH:MM:SS.sss format 
;; Elapsed Time D:HH:MM:SS.sss format 
;; 
;;DEPENDENCIES WMI, TimeDiff() external UDF 
;; 
;;TESTED WITH W2K, WXP, W2K3, Vista, x64 
;; 
;;EXAMPLES 
; 
Function WMIProcessList(OPTIONAL $_Process, Optional $_Computer, OPTIONAL $_pAuth)

Dim $_objWMIService, $_colItems, $_objItem ; WMI object vars 
Dim $_Line ; line string 
Dim $_aTmp[0], $_I ; return array, index 
Dim $_ ; temp var 
Dim $_BTime, $_CTime ; boot and current times from target system 

$_I = -1

; insure a properly formatted computer name, default to local computer is not specified 
$_Computer = IIf(Not $_Computer, '.', Join(Split($_Computer,'\'),''))

; If a pre-authenticated WMI object pointer was provided, use it, otherwise create a new object pointer 
If $_pAuth
$_objWMIService = $_pAuth
Else
$_objWMIService = GetObject('winmgmts:{impersonationLevel=impersonate}!\\' + $_Computer + '\root\cimv2')
If @ERROR Exit Val('&' + Right(DecToHex(@ERROR), 4)) EndIf
EndIf

; Get the current and boot times from the client system 
$_colItems = $_objWMIService.ExecQuery("Select * from Win32_OperatingSystem",,48)
For Each $_objItem in $_colItems
$_BTime = $_objItem.LastBootUpTime ; host-local boot time 
$_CTime = $_objItem.LocalDateTime ; host-local current time 
Next

$_colItems = 0
; convert to a normalized time string 
$_CTime = SubStr($_CTime, 1,4) + '/' + SubStr($_CTime, 5,2) + '/' +SubStr($_CTime, 7,2) + ' '
+ SubStr($_CTime, 9,2) + ':' + SubStr($_CTime, 11,2) + ':' + SubStr($_CTime, 13,6)

; get the collection of process objects 
$_colItems = $_objWMIService.ExecQuery("Select * from Win32_Process",,48)
If @ERROR $_colItems = 0 Exit Val('&' + Right(DecToHex(@ERROR), 4)) EndIf

; Enumerate the collection of process data 
For Each $_objItem in $_colItems
; add the data to the array if no process is specified, or if a specific process name or ID is specified 
If Not $_Process Or $_Process = $_objItem.Name Or $_Process = $_objItem.ProcessID 
$_Line = $_objItem.Name
$_Line = $_Line + ',' + $_objItem.ProcessID
$_Line = $_Line + ',' + $_objItem.Priority
$_Line = $_Line + ',' + $_objItem.ThreadCount
$_Line = $_Line + ',' + $_objItem.HandleCount
$_Line = $_Line + ',' + $_objItem.WorkingSetSize
; convert the following to d:hh:mm:ss.sss format 
$_Line = $_Line + ',' + _WMIPLTC(CDbl($_objItem.UserModeTime) * 0.0000001)
$_Line = $_Line + ',' + _WMIPLTC(CDbl($_objItem.KernelModeTime) * 0.0000001)
; Use the system boot time if creation date is not set 
$_ = IIf($_objItem.CreationDate , $_objItem.CreationDate, $_BTime)
; calculate elapsed time and convert to d:hh:mm:ss.sss format 
$_Line = $_Line + ',' + _WMIPLTC(_WMIPLET($_, $_CTime))
; Update the array 
$_I = $_I + 1
ReDim Preserve $_aTmp[$_I]
$_aTmp[$_I] = $_Line
EndIf
Next

; return the array, close the collection, and gripe if no items were found 
$WMIProcessList = $_aTmp
$_colItems = 0
If $_Process And $_I < 0 Exit 1911 EndIf
Exit 0

EndFunction


; support function to calculate elapsed time as Seconds 
; Dependent on TimeDiff UDF! 
Function _WMIPLET($_Time, $_CTime)

Dim $_CurrentTime

; Break into Date and Time parts, including 3 decimal points 
$_Time = SubStr($_Time, 1,4) + '/' + SubStr($_Time, 5,2) + '/' +SubStr($_Time, 7,2) + ' '
+ SubStr($_Time, 9,2) + ':' + SubStr($_Time, 11,2) + ':' + SubStr($_Time, 13,6)

; return the value with 3 decimal places 
$_WMIPLET = TimeDiff($_Time, $_CTime, '', 1); FormatNumber(, 3, -1, 0, 0) 
Exit 0

EndFunction


; support function to conver the time value (100ns units) to D:H:M:S.s format 
Function _WMIPLTC($_Units)

Dim $_D, $_H, $_M, $_S ; day, hour, minute, & second values 

; Find d/h/m and subtract result from units 
$_D = Int($_Units / 86400) $_Units = $_Units - $_D * 86400
$_H = Int($_Units/3600) $_Units = $_Units - $_H * 3600
$_M = Int($_Units/60)
$_S = FormatNumber($_Units - $_M * 60, 3, -1, 0, 0)

; return a time string 
$_WMIPLTC = '' + $_D + ':' + $_H + ':' + $_M + ':' + $_S
Exit 0

EndFunction 


I've largely trimmed it down to the set of actions that I need, however there's one piece that's stumping me.

The code, as is will step through all PST's which may be associated to an outlook profile, and I need it to NOT process a PST if it has the extention of *.mdc.

Anyone have some ideas on how I can add in an "exclusion" for PST's with a specific extention?

ty for your time and thoughts on this,

Lan


Glenn BarnasAdministrator
(KiX Supporter)
2011-07-06 01:05 PM
Re: PST locate and log...

Jeez, it's been a long time since I looked at this code!

If I understand your requirement correctly, put the following code in an If clause.., as shown here:
 Code:
If Right($PSTName, 3) <> '.mdc'
  ; determine if the PST is on a network drive. 
  For $_ = 0 to UBound($aMapped)
    If $aMapped[$_][0] = $Drv
      $IsNet = $_ ; is network! 
    EndIf
  Next

  ; Disassociate by deleting the registry key 
  ;'Disassociate'
  $_ = DelTree($aPSTKeys[$])

  ; Rename the file
  $NewPSTName = @USERID + '_' + Join(Split(Join(Split($PSTName, ' '), '_'), '.pst'), '.pst')
  ;'; move'
  Move $PSTPath + $PSTName $PSTPath + $NewPSTName
  $NewPSTPath = $NewPSTRoot
  Move $PSTpath + $NewPSTName $NewPSTPath + $NewPSTName
 
  $_ = RedirectOutput($LogPath + '\' + @WKSTA + @userid +'.txt')
EndIf
I noticed that you have commented the log messages, but have not disabled logging. When I used this script to eliminate PSTs and move their data into an archiving system, I found the logging helpful when things didn't go as planned.

The example IF clause allows anything EXCEPT .mdc files to process. If you want to restrict it to ONLY .pst files, change it to "If Right($PSTName, 3) = '.pst'" instead.

Glenn


Tsguy
(Getting the hang of it)
2011-07-06 06:39 PM
Re: PST locate and log...

Hiya Glenn \:\)

Was hoping that you might chime in on this one (since it's your code I'm mucking up \:\) )

Regarding the logging, I was a bit confused by some of it, so I rem'd it out while I focused on the rest of the script. Figured to loop back around to it eventually.

 Quote:
The example IF clause allows anything EXCEPT .mdc files to process. If you want to restrict it to ONLY .pst files, change it to "If Right($PSTName, 3) = '.pst'" instead.


And I want this script to process only PST files, not MDC files (which unfortunately show up under the PSTkeys). If I'm reading your response right, I'm going to be making the change: "If Right($PSTName, 3) = '.pst'"

I will be testing momentarily.

Thanks,

Lan


Tsguy
(Getting the hang of it)
2011-07-12 06:55 PM
Re: PST locate and log...

Hello again, not sure if I should make a new thread on this or not, but since I'm still working on the same script I figured I'd just tag on this thread where it was last left off.

All functions of this script are working perfectly for me . .except for the logging. About the only thing I'm able to get working properly with the logging is the creation of the TXT file that is to be the log.

Nothing actually gets logged in the TXT file, and in running through the script in Debug I recieve no errors / failures. Just nothing is written.

I specifically want to capture only 2 things in the logs:

- The original path and name of the PST file.
- The changed path and name of the PST file.

As we have cases where multiple PST's will be processed per user I am trying to make sure that if 3 PST's are processed by the script, that the before and after data of those three PST's is logged.

I toy'd around with writeprofilestring for a while, and was able to write data, but was having issues with the log data of the 1st PST processed being over-written when a 2nd PST was being processed.

So I've changed to a writeline format, which isn't doing anything :(.

I've pulled out just the logging components of the script to try and get them working . .here's that snipit. Please someone help me figure out what I'm missing . . I don't have much hair left that can be pulled out:

 Code:

$PSTPath = "\\server\share\"
$PSTName = "Bob.pst"
$OLDPST = $PSTpath + $PSTName
$NewPSTPath = "\\Newserver\share\"
$NewPSTName = "@USERID + '.pst'"
$NewPST = $NewPSTPath + $NewPSTName
$Logpath = "\\NewServer\share\logs"
$Log = $Logpath + '\' + @WKSTA + @userid + '.txt'
$Log2 = $OLDPST + @CRLF

open (1,$Log,1)
WriteLine (1,$Log2)	
Close (1)



I should also point out that RedirectOutput which was used for logging in the original script didn't seem to be working for me either, hence the change.

Thanks in advance for assistance on this.

Lan


ShaneEP
(MM club member)
2011-07-12 09:27 PM
Re: PST locate and log...

I made a couple of small changes, but it seems to be working for me as so..

 Code:
$PSTPath = "\\server\share\"
$PSTName = "Bob.pst"
$OLDPST = $PSTpath + $PSTName
$NewPSTPath = "\\Newserver\share\"
$NewPSTName = "@USERID + '.pst'"
$NewPST = $NewPSTPath + $NewPSTName
$Logpath = "\\NewServer\share\logs"
$Log = $Logpath + '\' + @WKSTA + @userid + '.txt'
$Log2 = $OLDPST + @CRLF

$logfh = FreeFileHandle()
$nul = open($logfh,$Log,5)
$nul = WriteLine($logfh,$Log2)	
$nul = Close($logfh)


I think the main issue was probably that you have to add 4 to the Open parameter in order to open the file with write access. Also changed it to use FreeFileHandle() which eliminates the possibility of using a file handle that may already be in use (just a practive I like to use).


Tsguy
(Getting the hang of it)
2011-07-12 10:09 PM
Re: PST locate and log...

Tyvm Shane!

I feel like a complete idiot for missing the open parameter for write mode...gah.

\:\)


Tsguy
(Getting the hang of it)
2011-07-12 11:15 PM
Re: PST locate and log...

Hmm . .working but not working so well.

Here's the code section from the script:

 Code:
; Rename and move the files

$NewPSTName = @USERID + '_' + Join(Split(Join(Split($PSTName, ' '), '_'), '.pst'), '.pst')

Move $PSTPath + $PSTName $PSTPath + $NewPSTName
Move $PSTPath + $NewPSTName $NewPSTPath + $NewPSTName

Open (1,$Log,4)
Writeline (1,$NewPSTName + @CRLF)
Close (1)

 


And what actually gets logged to the file =

 Quote:
2-302-302-302-30


There is a lot more going on within the full script obviously (Look at earlier posts), but the variable I'm referring to isn't writing out to the log correctly at all.

If I instead go the writeprofilestring route . .the variables will be written properly.

Any thoughts?


ShaneEP
(MM club member)
2011-07-12 11:52 PM
Re: PST locate and log...

Hmmm strange. I could be wrong, my suspicions lead me to think maybe you're doing a RedirectOutput somewhere, and these are actually return codes that are being logged??

What happens if you put $nul= in front of all your log lines? This will suppress the return codes by assigning to them to the $nul variable. Also helps eliminate the creation of a console window.

Couple other things...Make sure when you OPEN your log file to use a 5, this is the option to create a new file & the write access option added together. And this line looks a little redundant...I'm not sure anything is being accomplished by splitting a filename with '.pst', only to rejoin it with '.pst'.

$NewPSTName = @USERID+'_'+Join(Split(Join(Split($PSTName,' '),'_'),'.pst'),'.pst')

Are there any other problems notices when you run it? Are the files being successfully moved, just not logged?

$nul = Open (1,$Log,5)
$nul = Writeline (1,$NewPSTName + @CRLF)
$nul = Close (1)


Tsguy
(Getting the hang of it)
2011-07-12 11:54 PM
Re: PST locate and log...

Here's the code redone with writeprofilestring . .logs great, but doesn't work well for processing multiple PST files.

 Code:
 
$Log = $LogPath + '\' + @WKSTA + @userid +'.ini'

$NewPSTName = @USERID + '_' + Join(Split(Join(Split($PSTName, ' '), '_'), '.pst'), '.pst')

Move $PSTPath + $PSTName $PSTPath + $NewPSTName
Move $PSTPath + $NewPSTName $NewPSTPath + $NewPSTName

$OldPST = $PSTPath + $PSTName
$NewPST = $NewPSTPath + $NewPSTName

WriteProfileString($Log, @Userid, 'Old', $OldPST)
WriteProfileString($Log, @Userid, 'New', $NewPST)



Resulting log:

 Quote:

[UserID]
Old=H:\Archive1.pst
New=\\NewServer\share\UserID_Archive1.pst


I guess at this point I'm tempted to just use readprofilestring to check if the log file already has entries, and if it does, make new ones.

Will get to work on that...


Tsguy
(Getting the hang of it)
2011-07-12 11:58 PM
Re: PST locate and log...

I DID have a RedirectOutput in there Shane! Good catch . .I actually had to remove it for getting writeprofilestring to work lol.

Makes perfect sense now that the number strings would be return codes . . not the values I want.

Ok, with that in mind going to redo it again and run some more tests. Thanks much for the gentle shove in the right direction \:\)

Lan


Tsguy
(Getting the hang of it)
2011-07-13 12:47 AM
Re: PST locate and log...

It works!

Woot!

Full script being posted in all it's glory below. This is based off of code originally written by Glenn B . .much thanks to him, and much thanks again to Shane for helping me to clear up the final stumbling blocks I made for myself ;).

Script does the following:

-Enumerates all PST's attached to the default outlook profile.
-Closes Outlook (if open)
-Detaches PST's from the default outlook profile.
-Moves all discovered PST's to a Network share.
-Logs - Original PST location and name, New PST location and name.

 Code:
 
Break On

Dim $, $_ ; temp, throwaway var 
Dim $aPSTPaths[0] ; Array of PST paths 
Dim $aPSTKeys[0] ; Array of PST key paths 
Dim $RootPath ; registry root path 
Dim $WKey ; working key path 
Dim $EKey ; enumerated registry path 
Dim $TVal ; temp value for 
Dim $aIndex, $eIndex ; index pointers for array and enumeration 
Dim $MServer, $DName, $DProfile ; user data vars 
Dim $aMapped ; array of mapped network drives 
Dim $Drv, $NetFlag, $IsNet, $MoveFlag ; Drive letter enumerator, Network Only flag, IsNetwork flag, Move Flag 
Dim $PSTName, $PSTPath ; name & Path of current PST file 
Dim $NewPSTName, $NewPSTPath ; name of renamed PST file or new Path 
Dim $NewPSTRoot ; path of new PST path, when moving instead of renaming 
Dim $LogPath ; UNC path where logs are written 
Dim $Log ; Log file for this session
Dim $OLDPST ; old PST info
Dim $NewPST ; new PST info

$ = SetOption('Explicit', 'On')
$ = SetOption('WrapAtEOL', 'On')
$ = SetOption('NoVarsInStrings', 'On')
$ = SetOption('NoMacrosInStrings', 'On')

; # # # # # START OF CUSTOM PARAMETERS # # # # # 
$MoveFlag = 1 ; Move files if 1, otherwise disassociate and rename 
$LogPath = '\\Server\sharename\logs\' ; UNC path where renamed PST file locations are logged 
$NewPSTPath = '\\Server\sharename\' ; UNC or mapped drive 
; # # # # # END OF CUSTOM PARAMETERS # # # # # 

; Identify PST

$RootPath = 'HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles'

; get list of mapped drives 
$aMapped = WMIMappedDrives()

$DProfile = ReadValue($RootPath, 'DefaultProfile')
$WKey = $RootPath + '\' + $DProfile + '\'

$MServer = ReadValue($WKey + '13dbb0c8aa05101a9bb000aa002fc45a', '001e6602')
$DName = UtoA(ReadValue($WKey + '13dbb0c8aa05101a9bb000aa002fc45a', '001f3001'))

; find the PST files associated with this profile - need to enumerate each subkey and find the 001f6700 value 
$eIndex = 0
$aIndex = -1
$EKey = EnumKey($WKey, $eIndex)
While Not @ERROR
$TVal = UtoA(ReadValue($WKey + $EKey, '001f6700'))
If $TVal
$aIndex = $aIndex + 1
ReDim Preserve $aPSTPaths[$aIndex]
ReDim Preserve $aPSTKeys[$aIndex]
$aPSTPaths[$aIndex] = $TVal
$aPSTKeys[$aIndex] = $WKey + $EKey
EndIf
$eIndex = $eIndex + 1
$EKey = EnumKey($WKey, $eIndex)
Loop

; Exit if nothing to do 
If $aIndex < 0
'No PST files are registered for user ' $DName ' - exiting!' ? ?
Exit 0
EndIf

; now have all needed info 
; - terminate Outlook if it is running 
; - delete the PST registry keys 
; - rename the PST files 
; - copy the PST file to the network share

; terminate Outlook 
$ = Split(WMIProcessList('outlook.exe')[0], ',')[1]
If Not @ERROR
' Closing Outlook...' ?
$ = WMIProcessKill($) ; terminate Outlook 
Sleep 3 ; wait for Outlook to close open files 
EndIf

; Enumerate the PST files that are registered in Outlook 
For $ = 0 to UBound($aPSTPaths)

$Drv = Left($aPSTPaths[$], 2) ; drive letter where PST is stored 
$_ = InStrRev($aPSTPaths[$], '\')
$PSTName = SubStr($aPSTPaths[$], $_ + 1) ; get name part 
$PSTPath = Left($aPSTPaths[$], $_) ; get path part 

; Disassociate by deleting the registry key 

$_ = DelTree($aPSTKeys[$])

; Rename and move the files

$NewPSTName = @USERID + '_' + Join(Split(Join(Split($PSTName, ' '), '_'), '.pst'), '.pst')

Move $PSTPath + $PSTName $PSTPath + $NewPSTName
Move $PSTPath + $NewPSTName $NewPSTPath + $NewPSTName

$OldPST = $PSTPath + $PSTName
$NewPST = $NewPSTPath + $NewPSTName

$Log = $LogPath + '\' + @WKSTA + @userid +'.txt'

Open (1,$Log,5)
Writeline (1,$OldPST + @CRLF)
Writeline (1,$NewPST + @CRLF)
Writeline (1,@CRLF)
Close (1)

Next

Exit 0



; convert Unicode strings to ASCII 
Function UtoA($_String)

; return if string is empty 
If Not $_String Exit 0 EndIf

Dim $_S, $_I ; temp string, index pointer 

; get each character pair as hex and convert to ASCII character 
For $_I = 1 to Len($_String) Step 4
$_S = $_S + Chr(Val('&' + SubStr($_String, $_I, 2)))
Next

$UtoA = $_S
Exit 0

EndFunction

Function AtoU($_String)

Dim $_S, $_I ; temp string, index pointer 

; get each ASCII character and convert to hex words 
For $_I = 1 to Len($_String)
$_S = $_S + Right('00' + DecToHex(Asc(SubStr($_String, $_I, 1))), 2) + '00'
Next

$AtoU = LCase($_S) + '0000'
Exit 0 

EndFunction



;; 
;;====================================================================== 
;; 
;;FUNCTION TimeDiff() 
;; 
;;AUTHOR Glenn Barnas 
;; 
;;VERSION 2.2 / 2007/10/14 
;; Modified to increase accuracy, permit fracional second calculations 
;; 2.1 / 2007/03/17 
;; added "now" and "today" options for both start and end times 
;; 2.0 / 2006/11/20 
;; Changes for code efficiency; added defaults for midnight 
;; 
;;ACTION Calculates the time difference between two given date/time strings 
;; 
;;SYNTAX TimeDiff(Start [, End] [, Format] [, MSec]) 
;; 
;;PARAMETERS Start - REQUIRED, String value representing the start timestamp 
;; Format yyyy/mm/dd hh:mm:ss 
;; 
;; End - OPTIONAL, Defaults to "now" 
;; String value representing the ending time 
;; Format yyyy/mm/dd hh:mm:ss 
;; Can be the special value "now" for the current date/time, or "today" 
;; for midnight of the current day. 
;; 
;; When the time value is not specified, it defaults to 00:00:00.000 (midnight) 
;; 
;; Format - OPTIONAL, one of: 
;; "m" - return minutes 
;; "h" - return hours 
;; "d" - return days 
;; "y" - return years 
;; When a format value is specified, it returns the fractional part (ie 0.5 days for 12 hours). 
;; 
;; MSec - OPTIONAL, True if the fractional seconds should be returned. Default 
;; is false, returning whole seconds, to maintain compatibility with earlier versions. 
;; MSec only affects the return of fractional seconds, not fractional parts of other time formats. 
;; 
;;REMARKS Returns a value representing the difference in time between two date/time 
;; strings. Assumes that "Start" is in the past, but will properly return a 
;; negative value if it is in the future. 
;; 
;;RETURNS Double - difference between Start and End timestamps in seconds 
;; 
;;DEPENDENCIES None 
;; 
;;TESTED WITH Kix 4.2+, NT4, W2K, WXP, W2K3 
;; 
;;EXAMPLES If TimeDiff(GetFileTime('SomeFile.txt'), 'now', 'h') > 48 
;; "File is more than 2 days old!" ? 
;; EndIf 
; 
Function TimeDiff($_Start, OPTIONAL $_End, OPTIONAL $_Fmt, OPTIONAL $_MSec)

Dim $_, $_SDate, $a_Start, $_EDate, $a_End, $_Duration

; Check for special START parameters 
Select
Case $_Start = 'now'
$_Start = @DATE + ' ' + @TIME + '.' + @MSECS
Case $_START = 'today'
$_Start = @DATE + ' 00:00:00.000'
EndSelect

; Check for special END parameters 
Select
Case $_End = 'now' Or $_End = '' 
$_End = @DATE + ' ' + @TIME + '.' + @MSECS
Case $_End = 'today'
$_End = @DATE + ' 00:00:00.000'
EndSelect

; Validate parameters 
; Parameters passed are "yyyy/mm/dd hh:mm:ss[.sss]" - make sure the default time is added 
$a_Start = Split(Join(Split(Join(Split($_Start + ' 00:00:00.000', '/'), ' '), ':'), ' '), ' ', 6)
If UBound($a_Start) <> 5 Exit 87 EndIf ; bad start time parameter 
For $_ = 0 to 5
$a_Start[$_] = CDbl($a_Start[$_]) ; convert to numeric values 
Next

$a_End = Split(Join(Split(Join(Split($_End + ' 00:00:00.000', '/'), ' '), ':'), ' '), ' ', 6)
If UBound($a_End) <> 5 Exit 87 EndIf ; bad start time parameter 
For $_ = 0 to 5
$a_End[$_] = CDbl($a_End[$_]) ; convert to numeric values 
Next

; Convert dates to Days, then convert to seconds and add the time value 
If $a_Start[1] < 3
$a_Start[1] = $a_Start[1] + 12
$a_Start[0] = $a_Start[0] - 1
EndIf
$_SDate = $a_Start[2] + ( 153 * $a_Start[1] - 457 ) / 5 + 365 * $a_Start[0] + $a_Start[0] / 4 - $a_Start[0] / 100 + $a_Start[0] / 400 - 306
$_SDate = CDbl($_SDate) * 86400.0
$_SDate = $_SDate + $a_Start[3] * 3600 + $a_Start[4] * 60 + $a_Start[5]

If $a_End[1] < 3
$a_End[1] = $a_End[1] + 12
$a_End[0] = $a_End[0] - 1
EndIf
$_EDate = $a_End[2] + ( 153 * $a_End[1] - 457 ) / 5 + 365 * $a_End[0] + $a_End[0] / 4 - $a_End[0] / 100 + $a_End[0] / 400 - 306
$_EDate = CDbl($_EDate) * 86400.0
$_EDate = $_EDate + $a_End[3] * 3600 + $a_End[4] * 60 + $a_End[5]

; Get the duration between the timestamps 
$_Duration = CDbl($_EDate - $_SDate)

; Trim fractional seconds if the MSec flag wasn't set 
; Value returned is whole seconds 
If Not $_MSec
$_Duration = CInt($_Duration)
EndIf

; Return data as a Double - seconds (default), hours, minutes, days, or years 
Select
Case $_Fmt = 'm' ; minutes 
$TimeDiff = $_Duration / 60.0
Case $_Fmt = 'h' ; hours 
$TimeDiff = $_Duration / 3600.0
Case $_Fmt = 'd' ; days 
$TimeDiff = $_Duration / 86400.0
Case $_Fmt = 'y' ; years 
$TimeDiff = $_Duration / 31536000.0
Case 1
$TimeDiff = $_Duration
EndSelect

Exit 0

EndFunction




;; 
;;====================================================================== 
;; 
;;FUNCTION WMIMappedDrives() 
;; 
;;ACTION Return a list of mapped drives from a computer 
;; 
;;AUTHOR Glenn Barnas 
;; 
;;VERSION 1.0 / 2008/03/24 
;; 
;;SYNTAX WMIMappedDrives([, AuthPtr]) 
;; 
;;PARAMETERS AuthPtr - OPTIONAL - pre-authenticated WMI object pointer 
;; Use WMIAuthentication() udf to create the AuthPtr value 
;; AuthPtr is not needed if user has admin rights 
;; 
;;REMARKS By default, returns a detailed list of all processes from the local computer. An 
;; alternate computer can be specified, as can a specific process. When specifying 
;; processes, you can use a name (cmd.exe) or process ID. If you specify a name, all 
;; processeses matching that name will be returned, while specifying a PID will return 
;; exactly one element that matches that process (if it was found) 
;; If no match is found, the function exits with status 2 (file not found) 
;; 
;;RETURNS Array of drive letter / UNC Path arrays 
;; 
;;DEPENDENCIES WMI, 
;; 
;;TESTED WITH W2K, WXP, W2K3, Vista, x64 
;; 
;;EXAMPLES 
; 
Function WMIMappedDrives(OPTIONAL $_pAuth)

Dim $_objWMIService, $_colItems, $_objItem ; WMI object vars 
Dim $_Line ; line string 
Dim $_aTmp[0], $_I ; return array, index 
Dim $_ ; temp var 

$_I = -1

; If a pre-authenticated WMI object pointer was provided, use it, otherwise create a new object pointer 
If $_pAuth
$_objWMIService = $_pAuth
Else
$_objWMIService = GetObject('winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2')
If @ERROR Exit Val('&' + Right(DecToHex(@ERROR), 4)) EndIf
EndIf

; get the collection of process objects 
$_colItems = $_objWMIService.ExecQuery("Select * from Win32_MappedLogicalDisk",,48)
If @ERROR $_colItems = 0 Exit Val('&' + Right(DecToHex(@ERROR), 4)) EndIf

; Enumerate the collection of process data 
For Each $_objItem in $_colItems
$_I = $_I + 1
ReDim Preserve $_aTmp[$_I]
$_aTmp[$_I] = $_objItem.Name, $_objItem.ProviderName
Next

; return the array, close the collection, and gripe if no items were found 
$WMIMappedDrives = $_aTmp
$_colItems = 0
Exit 0

EndFunction



;; 
;;====================================================================== 
;; 
;;FUNCTION WMIProcessKill() 
;; 
;;ACTION Terminates a specific process by numeric PID 
;; 
;;AUTHOR Glenn Barnas 
;; 
;;VERSION 1.0 / 2007/10/20 
;; 
;;SYNTAX WMIProcessKill(Process [, Computer] [, AuthPtr]) 
;; 
;;PARAMETERS Process - PID of process to terminate 
;; 
;; Computer - OPTIONAL, Name of computer to target 
;; 
;; AuthPtr - OPTIONAL - pre-authenticated WMI object pointer 
;; Use WMIAuthentication() udf to create the AuthPtr value 
;; AuthPtr is not needed if user has admin rights 
;; 
;;REMARKS Terminates the process after verifying it exists 
;; 
;;RETURNS 1 (success) or 0 (failure) 
;; 
;;DEPENDENCIES WMI 
;; 
;;TESTED WITH W2K, WXP, W2K3, Vista, x64 
;; 
;;EXAMPLES 
; 
Function WMIProcessKill($_Process, Optional $_Computer, OPTIONAL $_pAuth)

Dim $_objWMIService, $_colItems, $_objItem ; WMI object vars 
Dim $_ ; temp var 
Dim $_Err ; error code 

; Must be a single numeric value to terminate 
If Val($_Process) <> $_Process
$WMIProcessKill = 0
Exit 87
EndIf

; insure a properly formatted computer name, default to local computer is not specified 
$_Computer = IIf(Not $_Computer, '.', Join(Split($_Computer,'\'),''))

; If a pre-authenticated WMI object pointer was provided, use it, otherwise create a new object pointer 
If $_pAuth
$_objWMIService = $_pAuth
Else
$_objWMIService = GetObject('winmgmts:{impersonationLevel=impersonate}!\\' + $_Computer + '\root\cimv2')
If @ERROR Exit Val('&' + Right(DecToHex(@ERROR), 4)) EndIf
EndIf

; get the collection of process objects 
$_colItems = $_objWMIService.ExecQuery("Select * from Win32_Process Where ProcessID=" + '"' + $_Process + '"',,48)
If @ERROR $_colItems = 0 Exit Val('&' + Right(DecToHex(@ERROR), 4)) EndIf

$_Err = 2 ; prepare for not found 

; Enumerate the collection of process data 
For Each $_objItem in $_colItems
$_ = $_objItem.Terminate
$_Err = @ERROR
Next
$_colItems = 0

; return appropriate values 
$WMIProcessKill = Not $_Err
Exit $_Err

EndFunction



;; 
;;====================================================================== 
;; 
;;FUNCTION WMIProcessList() 
;; 
;;ACTION Return a list of process info from a computer 
;; 
;;AUTHOR Glenn Barnas 
;; 
;;VERSION 1.0 / 2007/10/12 
;; Written as a replacement for PSList(), which uses SysInternals PSList.exe 
;; 
;;SYNTAX WMIProcessList([Process] [, Computer] [, AuthPtr]) 
;; 
;;PARAMETERS Process - return information about a specific process (group) by name 
;; or individual process ID (PID) 
;; 
;; Computer - OPTIONAL, Name of computer to query 
;; 
;; AuthPtr - OPTIONAL - pre-authenticated WMI object pointer 
;; Use WMIAuthentication() udf to create the AuthPtr value 
;; AuthPtr is not needed if user has admin rights 
;; 
;;REMARKS By default, returns a detailed list of all processes from the local computer. An 
;; alternate computer can be specified, as can a specific process. When specifying 
;; processes, you can use a name (cmd.exe) or process ID. If you specify a name, all 
;; processeses matching that name will be returned, while specifying a PID will return 
;; exactly one element that matches that process (if it was found) 
;; If no match is found, the function exits with status 2 (file not found) 
;; 
;;RETURNS Array of comma-delimited values, one element per process: 
;; Process Name 
;; Process ID (PID) 
;; Thread Count 
;; Handle Count 
;; Memory Usage (Bytes) 
;; User Time D:HH:MM:SS.sss format 
;; Kernel Time D:HH:MM:SS.sss format 
;; Elapsed Time D:HH:MM:SS.sss format 
;; 
;;DEPENDENCIES WMI, TimeDiff() external UDF 
;; 
;;TESTED WITH W2K, WXP, W2K3, Vista, x64 
;; 
;;EXAMPLES 
; 
Function WMIProcessList(OPTIONAL $_Process, Optional $_Computer, OPTIONAL $_pAuth)

Dim $_objWMIService, $_colItems, $_objItem ; WMI object vars 
Dim $_Line ; line string 
Dim $_aTmp[0], $_I ; return array, index 
Dim $_ ; temp var 
Dim $_BTime, $_CTime ; boot and current times from target system 

$_I = -1

; insure a properly formatted computer name, default to local computer is not specified 
$_Computer = IIf(Not $_Computer, '.', Join(Split($_Computer,'\'),''))

; If a pre-authenticated WMI object pointer was provided, use it, otherwise create a new object pointer 
If $_pAuth
$_objWMIService = $_pAuth
Else
$_objWMIService = GetObject('winmgmts:{impersonationLevel=impersonate}!\\' + $_Computer + '\root\cimv2')
If @ERROR Exit Val('&' + Right(DecToHex(@ERROR), 4)) EndIf
EndIf

; Get the current and boot times from the client system 
$_colItems = $_objWMIService.ExecQuery("Select * from Win32_OperatingSystem",,48)
For Each $_objItem in $_colItems
$_BTime = $_objItem.LastBootUpTime ; host-local boot time 
$_CTime = $_objItem.LocalDateTime ; host-local current time 
Next

$_colItems = 0
; convert to a normalized time string 
$_CTime = SubStr($_CTime, 1,4) + '/' + SubStr($_CTime, 5,2) + '/' +SubStr($_CTime, 7,2) + ' '
+ SubStr($_CTime, 9,2) + ':' + SubStr($_CTime, 11,2) + ':' + SubStr($_CTime, 13,6)

; get the collection of process objects 
$_colItems = $_objWMIService.ExecQuery("Select * from Win32_Process",,48)
If @ERROR $_colItems = 0 Exit Val('&' + Right(DecToHex(@ERROR), 4)) EndIf

; Enumerate the collection of process data 
For Each $_objItem in $_colItems
; add the data to the array if no process is specified, or if a specific process name or ID is specified 
If Not $_Process Or $_Process = $_objItem.Name Or $_Process = $_objItem.ProcessID 
$_Line = $_objItem.Name
$_Line = $_Line + ',' + $_objItem.ProcessID
$_Line = $_Line + ',' + $_objItem.Priority
$_Line = $_Line + ',' + $_objItem.ThreadCount
$_Line = $_Line + ',' + $_objItem.HandleCount
$_Line = $_Line + ',' + $_objItem.WorkingSetSize
; convert the following to d:hh:mm:ss.sss format 
$_Line = $_Line + ',' + _WMIPLTC(CDbl($_objItem.UserModeTime) * 0.0000001)
$_Line = $_Line + ',' + _WMIPLTC(CDbl($_objItem.KernelModeTime) * 0.0000001)
; Use the system boot time if creation date is not set 
$_ = IIf($_objItem.CreationDate , $_objItem.CreationDate, $_BTime)
; calculate elapsed time and convert to d:hh:mm:ss.sss format 
$_Line = $_Line + ',' + _WMIPLTC(_WMIPLET($_, $_CTime))
; Update the array 
$_I = $_I + 1
ReDim Preserve $_aTmp[$_I]
$_aTmp[$_I] = $_Line
EndIf
Next

; return the array, close the collection, and gripe if no items were found 
$WMIProcessList = $_aTmp
$_colItems = 0
If $_Process And $_I < 0 Exit 1911 EndIf
Exit 0

EndFunction


; support function to calculate elapsed time as Seconds 
; Dependent on TimeDiff UDF! 
Function _WMIPLET($_Time, $_CTime)

Dim $_CurrentTime

; Break into Date and Time parts, including 3 decimal points 
$_Time = SubStr($_Time, 1,4) + '/' + SubStr($_Time, 5,2) + '/' +SubStr($_Time, 7,2) + ' '
+ SubStr($_Time, 9,2) + ':' + SubStr($_Time, 11,2) + ':' + SubStr($_Time, 13,6)

; return the value with 3 decimal places 
$_WMIPLET = TimeDiff($_Time, $_CTime, '', 1); FormatNumber(, 3, -1, 0, 0) 
Exit 0

EndFunction


; support function to conver the time value (100ns units) to D:H:M:S.s format 
Function _WMIPLTC($_Units)

Dim $_D, $_H, $_M, $_S ; day, hour, minute, & second values 

; Find d/h/m and subtract result from units 
$_D = Int($_Units / 86400) $_Units = $_Units - $_D * 86400
$_H = Int($_Units/3600) $_Units = $_Units - $_H * 3600
$_M = Int($_Units/60)
$_S = FormatNumber($_Units - $_M * 60, 3, -1, 0, 0)

; return a time string 
$_WMIPLTC = '' + $_D + ':' + $_H + ':' + $_M + ':' + $_S
Exit 0

EndFunction 


We'll be using this script to move all PST's scattered around the network to a server share for import into Symantec's Evault. Evault's PST location process won't work with our share structure unfortunatly, so we needed a scripted solution.

In testing the move of a 2gb PST file with this script, the file was moved in just over 5 minutes.

I believe there's some additional cleanup that could be done on this script, however since it's working well now I'm not sure I want to mess with it any further ;).

thanks again for the huge assist Kixers.

Lan


ShaneEP
(MM club member)
2011-07-13 06:09 AM
Re: PST locate and log...

Awesome...Glad you got it working.

NTDOCAdministrator
(KiX Master)
2011-07-13 11:23 AM
Re: PST locate and log...

Only thing I might add is that I'd be a bit leery of using KiX for the copy/move process when dealing with such large files.

I myself would probably look at maybe using RoboCopy for doing the actual file copy/move.


Tesdall
(Getting the hang of it)
2011-07-13 03:10 PM
Re: PST locate and log...

Can someone help, im trying to use the script above. I get into some kind of loop when i hit
 Code:
If Not $_Process Or $_Process = $_objItem.Name Or $_Process = $_objItem.ProcessID 


Tsguy
(Getting the hang of it)
2011-07-13 06:10 PM
Re: PST locate and log...

Hi there.

Looks like that line is from the WMIProcessList function that's used to close outlook if it's open.

That is an older function written by Glenn Barnas, he may have an updated one. Are you testing on Windows 7?

Another option is to REM out the whole section on terminating outlook, and just make sure that outlook is closed manually, or with a "taskkill /F /IM Outlook.exe" in the script used to launch the kix script.

HTH

Lan

PS - NTdoc, is there file size that's too much for kix to handle that you feel robocopy would work better? I was quite pleased that a 2gb file was moved between network shares as fast as it was by kix . . when copied via windows drag and drop it took close to an hour to move :(. I know I have users with 10gb+ PST's...is there a breaking point for kix's move funtion?


Richard H.Administrator
(KiX Supporter)
2011-07-14 10:13 AM
Re: PST locate and log...

 Originally Posted By: Tsguy
PS - NTdoc, is there file size that's too much for kix to handle that you feel robocopy would work better? I was quite pleased that a 2gb file was moved between network shares as fast as it was by kix . . when copied via windows drag and drop it took close to an hour to move :(. I know I have users with 10gb+ PST's...is there a breaking point for kix's move funtion?


There is no KiXtart limit (KiXtart will use the underlying OS APIs). The reason for using ROBOCOPY or similar tools on very large files is because of the restart options.

More of a problem on slower WAN connections, it provides the option when your 10 GB copy fails at 9.5 GB to resume and copy the last 0.5 GB instead of starting from scratch. It can also be configured to automatically retry interrupted / failed copies so that for example if network connection dropped for a couple of minutes the copy would continue when it came back.


Glenn BarnasAdministrator
(KiX Supporter)
2011-07-14 01:44 PM
Re: PST locate and log...

 Originally Posted By: Tsguy
That is an older function written by Glenn Barnas, he may have an updated one. Are you testing on Windows 7?
You can always find the latest revisions of my UDFs in the Kix UDF Library on my web site. Our entire Kix UDF library is PostPrepped and published at PM (EST) nightly.

Glenn


NTDOCAdministrator
(KiX Master)
2011-07-15 12:45 PM
Re: PST locate and log...

Yes Glenn has some excellent scripts.

The Kixtart UDF Library from Glenn should be here


Tsguy
(Getting the hang of it)
2011-07-21 02:04 AM
Re: PST locate and log...

I'm back \:\)

Darn project managers and their changing wants / needs.

I've got some requests for amendments to this script as it was last posted. I've gotten all the requests sorted out except for one.

It's been decided that we'd like to use this script to centralize PST's on a new share location, and re-attach the moved PST's to the default outlook profile once they are moved.

I have the following code from the original script:

 Code:
 
; re-associate the PST registry key with the new location 
;'; reassociate' 
$_ = AtoU($NewPST)
$_ = WriteValue($aPSTKeys[$], '001f6700-0', $_, 'REG_BINARY')


Seems like it should go through, but it does not. Is it possible that it's not working since I'm still 'deleting' the $aPSTKeys[$] earlier within the script?

This is a lengthy script and the other adjustments are simply messagebox popups which shouldn't be affecting this problem. If I need to post the full meal deal again to help sort this out I will.

Much thanks for your thoughts and advice,

Lan


ShaneEP
(MM club member)
2011-07-21 02:57 AM
Re: PST locate and log...

Nothing seems wrong with those two lines, as long as all the variables are indeed populated correctly. What error(s) is it generating?

Tsguy
(Getting the hang of it)
2011-07-21 05:55 PM
Re: PST locate and log...

heh . .good question. I hadn't gone thru a debug on it yet. Will step thru that in a few minutes.

Will also post the full code in a few.

Lan


Glenn BarnasAdministrator
(KiX Supporter)
2011-07-21 07:17 PM
Re: PST locate and log...

How many PSTs are we talking about here? You do know that Microsoft does not recommend or support the use of PST files across a network? I've seen a file server brought to its knees with as few as 40-50 open PST files.

Glenn


Tsguy
(Getting the hang of it)
2011-07-21 08:26 PM
Re: PST locate and log...

Current full code:

 Code:
Break On

Dim $, $_ ; temp, throwaway var 
Dim $aPSTPaths[0] ; Array of PST paths 
Dim $aPSTKeys[0] ; Array of PST key paths 
Dim $RootPath ; registry root path 
Dim $WKey ; working key path 
Dim $EKey ; enumerated registry path 
Dim $TVal ; temp value for 
Dim $aIndex, $eIndex ; index pointers for array and enumeration 
Dim $MServer, $DName, $DProfile ; user data vars 
Dim $aMapped ; array of mapped network drives 
Dim $Drv, $NetFlag, $IsNet, $MoveFlag ; Drive letter enumerator, Network Only flag, IsNetwork flag, Move Flag 
Dim $PSTName, $PSTPath ; name & Path of current PST file 
Dim $NewPSTName, $NewPSTPath ; name of renamed PST file or new Path 
Dim $NewPSTRoot ; path of new PST path, when moving instead of renaming 
Dim $LogPath ; UNC path where logs are written 
Dim $Log ; Log file for this session
Dim $OLDPST ; old PST info
Dim $NewPST ; new PST info
Dim $Message ; message box message
Dim $UserDir ; user specific directory

$ = SetOption('Explicit', 'On')
$ = SetOption('WrapAtEOL', 'On')
$ = SetOption('NoVarsInStrings', 'On')
$ = SetOption('NoMacrosInStrings', 'On')

; # # # # # START OF CUSTOM PARAMETERS # # # # # 
$MoveFlag = 1 ; Move files if 1, otherwise disassociate and rename 
$LogPath = 'z:\pst\logs\' ; UNC path where renamed PST file locations are logged 
$NewPSTPath = 'c:\temp\' + @userid + '\'; UNC or mapped drive 
; # # # # # END OF CUSTOM PARAMETERS # # # # # 

; Identify PST

$RootPath = 'HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles'

; get list of mapped drives 
$aMapped = WMIMappedDrives()

$DProfile = ReadValue($RootPath, 'DefaultProfile')
$WKey = $RootPath + '\' + $DProfile + '\'

$MServer = ReadValue($WKey + '13dbb0c8aa05101a9bb000aa002fc45a', '001e6602')
$DName = UtoA(ReadValue($WKey + '13dbb0c8aa05101a9bb000aa002fc45a', '001f3001'))

; find the PST files associated with this profile - need to enumerate each subkey and find the 001f6700 value 
$eIndex = 0
$aIndex = -1
$EKey = EnumKey($WKey, $eIndex)
While Not @ERROR
$TVal = UtoA(ReadValue($WKey + $EKey, '001f6700'))
If $TVal
$aIndex = $aIndex + 1
ReDim Preserve $aPSTPaths[$aIndex]
ReDim Preserve $aPSTKeys[$aIndex]
$aPSTPaths[$aIndex] = $TVal
$aPSTKeys[$aIndex] = $WKey + $EKey
EndIf
$eIndex = $eIndex + 1
$EKey = EnumKey($WKey, $eIndex)
Loop

; Exit if nothing to do 
If $aIndex < 0
'No PST files are registered for user ' $DName ' - exiting!' ? ?
Exit 0
EndIf

; now have all needed info 
; - terminate Outlook if it is running 
; - delete the PST registry keys 
; - rename the PST files 
; - copy the PST file to the network share

If Not Exist ($NewPSTPath)
	MD $NewPSTPath
EndIf

; terminate Outlook 
$ = Split(WMIProcessList('outlook.exe')[0], ',')[1]
If Not @ERROR
' Closing Outlook...' ?
$ = WMIProcessKill($) ; terminate Outlook 
Sleep 3 ; wait for Outlook to close open files 
EndIf

; Enumerate the PST files that are registered in Outlook 
For $ = 0 to UBound($aPSTPaths)

$Drv = Left($aPSTPaths[$], 2) ; drive letter where PST is stored 
$_ = InStrRev($aPSTPaths[$], '\')
$PSTName = SubStr($aPSTPaths[$], $_ + 1) ; get name part 
$PSTPath = Left($aPSTPaths[$], $_) ; get path part 

; Disassociate by deleting the registry key 

$_ = DelTree($aPSTKeys[$])

; Rename and move the files

$NewPSTName = @USERID + '_' + Join(Split(Join(Split($PSTName, ' '), '_'), '.pst'), '.pst')

Move $PSTPath + $PSTName $PSTPath + $NewPSTName
Move $PSTPath + $NewPSTName $NewPSTPath + $NewPSTName

$OldPST = $PSTPath + $PSTName
$NewPST = $NewPSTPath + $NewPSTName

$Log = $LogPath + '\' + @WKSTA + @userid +'.txt'

Open (1,$Log,5)
Writeline (1,$OldPST + @CRLF)
Writeline (1,$NewPST + @CRLF)
Writeline (1,@CRLF)
Close (1)

; re-associate the PST registry key with the new location 
;'; reassociate' 
$_ = AtoU($NewPST)
$_ = WriteValue($aPSTKeys[$], '001f6700-0', $_, 'REG_BINARY')


Next

$Message = '             PST Move completed, you may now open outlook again.              '
MessageBox ($Message,"                ***PST Consolidation completed***                   ",0,10)


Exit 0



; convert Unicode strings to ASCII 
Function UtoA($_String)

; return if string is empty 
If Not $_String Exit 0 EndIf

Dim $_S, $_I ; temp string, index pointer 

; get each character pair as hex and convert to ASCII character 
For $_I = 1 to Len($_String) Step 4
$_S = $_S + Chr(Val('&' + SubStr($_String, $_I, 2)))
Next

$UtoA = $_S
Exit 0

EndFunction

Function AtoU($_String)

Dim $_S, $_I ; temp string, index pointer 

; get each ASCII character and convert to hex words 
For $_I = 1 to Len($_String)
$_S = $_S + Right('00' + DecToHex(Asc(SubStr($_String, $_I, 1))), 2) + '00'
Next

$AtoU = LCase($_S) + '0000'
Exit 0 

EndFunction



;; 
;;====================================================================== 
;; 
;;FUNCTION TimeDiff() 
;; 
;;AUTHOR Glenn Barnas 
;; 
;;VERSION 2.2 / 2007/10/14 
;; Modified to increase accuracy, permit fracional second calculations 
;; 2.1 / 2007/03/17 
;; added "now" and "today" options for both start and end times 
;; 2.0 / 2006/11/20 
;; Changes for code efficiency; added defaults for midnight 
;; 
;;ACTION Calculates the time difference between two given date/time strings 
;; 
;;SYNTAX TimeDiff(Start [, End] [, Format] [, MSec]) 
;; 
;;PARAMETERS Start - REQUIRED, String value representing the start timestamp 
;; Format yyyy/mm/dd hh:mm:ss 
;; 
;; End - OPTIONAL, Defaults to "now" 
;; String value representing the ending time 
;; Format yyyy/mm/dd hh:mm:ss 
;; Can be the special value "now" for the current date/time, or "today" 
;; for midnight of the current day. 
;; 
;; When the time value is not specified, it defaults to 00:00:00.000 (midnight) 
;; 
;; Format - OPTIONAL, one of: 
;; "m" - return minutes 
;; "h" - return hours 
;; "d" - return days 
;; "y" - return years 
;; When a format value is specified, it returns the fractional part (ie 0.5 days for 12 hours). 
;; 
;; MSec - OPTIONAL, True if the fractional seconds should be returned. Default 
;; is false, returning whole seconds, to maintain compatibility with earlier versions. 
;; MSec only affects the return of fractional seconds, not fractional parts of other time formats. 
;; 
;;REMARKS Returns a value representing the difference in time between two date/time 
;; strings. Assumes that "Start" is in the past, but will properly return a 
;; negative value if it is in the future. 
;; 
;;RETURNS Double - difference between Start and End timestamps in seconds 
;; 
;;DEPENDENCIES None 
;; 
;;TESTED WITH Kix 4.2+, NT4, W2K, WXP, W2K3 
;; 
;;EXAMPLES If TimeDiff(GetFileTime('SomeFile.txt'), 'now', 'h') > 48 
;; "File is more than 2 days old!" ? 
;; EndIf 
; 
Function TimeDiff($_Start, OPTIONAL $_End, OPTIONAL $_Fmt, OPTIONAL $_MSec)

Dim $_, $_SDate, $a_Start, $_EDate, $a_End, $_Duration

; Check for special START parameters 
Select
Case $_Start = 'now'
$_Start = @DATE + ' ' + @TIME + '.' + @MSECS
Case $_START = 'today'
$_Start = @DATE + ' 00:00:00.000'
EndSelect

; Check for special END parameters 
Select
Case $_End = 'now' Or $_End = '' 
$_End = @DATE + ' ' + @TIME + '.' + @MSECS
Case $_End = 'today'
$_End = @DATE + ' 00:00:00.000'
EndSelect

; Validate parameters 
; Parameters passed are "yyyy/mm/dd hh:mm:ss[.sss]" - make sure the default time is added 
$a_Start = Split(Join(Split(Join(Split($_Start + ' 00:00:00.000', '/'), ' '), ':'), ' '), ' ', 6)
If UBound($a_Start) <> 5 Exit 87 EndIf ; bad start time parameter 
For $_ = 0 to 5
$a_Start[$_] = CDbl($a_Start[$_]) ; convert to numeric values 
Next

$a_End = Split(Join(Split(Join(Split($_End + ' 00:00:00.000', '/'), ' '), ':'), ' '), ' ', 6)
If UBound($a_End) <> 5 Exit 87 EndIf ; bad start time parameter 
For $_ = 0 to 5
$a_End[$_] = CDbl($a_End[$_]) ; convert to numeric values 
Next

; Convert dates to Days, then convert to seconds and add the time value 
If $a_Start[1] < 3
$a_Start[1] = $a_Start[1] + 12
$a_Start[0] = $a_Start[0] - 1
EndIf
$_SDate = $a_Start[2] + ( 153 * $a_Start[1] - 457 ) / 5 + 365 * $a_Start[0] + $a_Start[0] / 4 - $a_Start[0] / 100 + $a_Start[0] / 400 - 306
$_SDate = CDbl($_SDate) * 86400.0
$_SDate = $_SDate + $a_Start[3] * 3600 + $a_Start[4] * 60 + $a_Start[5]

If $a_End[1] < 3
$a_End[1] = $a_End[1] + 12
$a_End[0] = $a_End[0] - 1
EndIf
$_EDate = $a_End[2] + ( 153 * $a_End[1] - 457 ) / 5 + 365 * $a_End[0] + $a_End[0] / 4 - $a_End[0] / 100 + $a_End[0] / 400 - 306
$_EDate = CDbl($_EDate) * 86400.0
$_EDate = $_EDate + $a_End[3] * 3600 + $a_End[4] * 60 + $a_End[5]

; Get the duration between the timestamps 
$_Duration = CDbl($_EDate - $_SDate)

; Trim fractional seconds if the MSec flag wasn't set 
; Value returned is whole seconds 
If Not $_MSec
$_Duration = CInt($_Duration)
EndIf

; Return data as a Double - seconds (default), hours, minutes, days, or years 
Select
Case $_Fmt = 'm' ; minutes 
$TimeDiff = $_Duration / 60.0
Case $_Fmt = 'h' ; hours 
$TimeDiff = $_Duration / 3600.0
Case $_Fmt = 'd' ; days 
$TimeDiff = $_Duration / 86400.0
Case $_Fmt = 'y' ; years 
$TimeDiff = $_Duration / 31536000.0
Case 1
$TimeDiff = $_Duration
EndSelect

Exit 0

EndFunction




;; 
;;====================================================================== 
;; 
;;FUNCTION WMIMappedDrives() 
;; 
;;ACTION Return a list of mapped drives from a computer 
;; 
;;AUTHOR Glenn Barnas 
;; 
;;VERSION 1.0 / 2008/03/24 
;; 
;;SYNTAX WMIMappedDrives([, AuthPtr]) 
;; 
;;PARAMETERS AuthPtr - OPTIONAL - pre-authenticated WMI object pointer 
;; Use WMIAuthentication() udf to create the AuthPtr value 
;; AuthPtr is not needed if user has admin rights 
;; 
;;REMARKS By default, returns a detailed list of all processes from the local computer. An 
;; alternate computer can be specified, as can a specific process. When specifying 
;; processes, you can use a name (cmd.exe) or process ID. If you specify a name, all 
;; processeses matching that name will be returned, while specifying a PID will return 
;; exactly one element that matches that process (if it was found) 
;; If no match is found, the function exits with status 2 (file not found) 
;; 
;;RETURNS Array of drive letter / UNC Path arrays 
;; 
;;DEPENDENCIES WMI, 
;; 
;;TESTED WITH W2K, WXP, W2K3, Vista, x64 
;; 
;;EXAMPLES 
; 
Function WMIMappedDrives(OPTIONAL $_pAuth)

Dim $_objWMIService, $_colItems, $_objItem ; WMI object vars 
Dim $_Line ; line string 
Dim $_aTmp[0], $_I ; return array, index 
Dim $_ ; temp var 

$_I = -1

; If a pre-authenticated WMI object pointer was provided, use it, otherwise create a new object pointer 
If $_pAuth
$_objWMIService = $_pAuth
Else
$_objWMIService = GetObject('winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2')
If @ERROR Exit Val('&' + Right(DecToHex(@ERROR), 4)) EndIf
EndIf

; get the collection of process objects 
$_colItems = $_objWMIService.ExecQuery("Select * from Win32_MappedLogicalDisk",,48)
If @ERROR $_colItems = 0 Exit Val('&' + Right(DecToHex(@ERROR), 4)) EndIf

; Enumerate the collection of process data 
For Each $_objItem in $_colItems
$_I = $_I + 1
ReDim Preserve $_aTmp[$_I]
$_aTmp[$_I] = $_objItem.Name, $_objItem.ProviderName
Next

; return the array, close the collection, and gripe if no items were found 
$WMIMappedDrives = $_aTmp
$_colItems = 0
Exit 0

EndFunction



;; 
;;====================================================================== 
;; 
;;FUNCTION WMIProcessKill() 
;; 
;;ACTION Terminates a specific process by numeric PID 
;; 
;;AUTHOR Glenn Barnas 
;; 
;;VERSION 1.0 / 2007/10/20 
;; 
;;SYNTAX WMIProcessKill(Process [, Computer] [, AuthPtr]) 
;; 
;;PARAMETERS Process - PID of process to terminate 
;; 
;; Computer - OPTIONAL, Name of computer to target 
;; 
;; AuthPtr - OPTIONAL - pre-authenticated WMI object pointer 
;; Use WMIAuthentication() udf to create the AuthPtr value 
;; AuthPtr is not needed if user has admin rights 
;; 
;;REMARKS Terminates the process after verifying it exists 
;; 
;;RETURNS 1 (success) or 0 (failure) 
;; 
;;DEPENDENCIES WMI 
;; 
;;TESTED WITH W2K, WXP, W2K3, Vista, x64 
;; 
;;EXAMPLES 
; 
Function WMIProcessKill($_Process, Optional $_Computer, OPTIONAL $_pAuth)

Dim $_objWMIService, $_colItems, $_objItem ; WMI object vars 
Dim $_ ; temp var 
Dim $_Err ; error code 

; Must be a single numeric value to terminate 
If Val($_Process) <> $_Process
$WMIProcessKill = 0
Exit 87
EndIf

; insure a properly formatted computer name, default to local computer is not specified 
$_Computer = IIf(Not $_Computer, '.', Join(Split($_Computer,'\'),''))

; If a pre-authenticated WMI object pointer was provided, use it, otherwise create a new object pointer 
If $_pAuth
$_objWMIService = $_pAuth
Else
$_objWMIService = GetObject('winmgmts:{impersonationLevel=impersonate}!\\' + $_Computer + '\root\cimv2')
If @ERROR Exit Val('&' + Right(DecToHex(@ERROR), 4)) EndIf
EndIf

; get the collection of process objects 
$_colItems = $_objWMIService.ExecQuery("Select * from Win32_Process Where ProcessID=" + '"' + $_Process + '"',,48)
If @ERROR $_colItems = 0 Exit Val('&' + Right(DecToHex(@ERROR), 4)) EndIf

$_Err = 2 ; prepare for not found 

; Enumerate the collection of process data 
For Each $_objItem in $_colItems
$_ = $_objItem.Terminate
$_Err = @ERROR
Next
$_colItems = 0

; return appropriate values 
$WMIProcessKill = Not $_Err
Exit $_Err

EndFunction



;; 
;;====================================================================== 
;; 
;;FUNCTION WMIProcessList() 
;; 
;;ACTION Return a list of process info from a computer 
;; 
;;AUTHOR Glenn Barnas 
;; 
;;VERSION 1.0 / 2007/10/12 
;; Written as a replacement for PSList(), which uses SysInternals PSList.exe 
;; 
;;SYNTAX WMIProcessList([Process] [, Computer] [, AuthPtr]) 
;; 
;;PARAMETERS Process - return information about a specific process (group) by name 
;; or individual process ID (PID) 
;; 
;; Computer - OPTIONAL, Name of computer to query 
;; 
;; AuthPtr - OPTIONAL - pre-authenticated WMI object pointer 
;; Use WMIAuthentication() udf to create the AuthPtr value 
;; AuthPtr is not needed if user has admin rights 
;; 
;;REMARKS By default, returns a detailed list of all processes from the local computer. An 
;; alternate computer can be specified, as can a specific process. When specifying 
;; processes, you can use a name (cmd.exe) or process ID. If you specify a name, all 
;; processeses matching that name will be returned, while specifying a PID will return 
;; exactly one element that matches that process (if it was found) 
;; If no match is found, the function exits with status 2 (file not found) 
;; 
;;RETURNS Array of comma-delimited values, one element per process: 
;; Process Name 
;; Process ID (PID) 
;; Thread Count 
;; Handle Count 
;; Memory Usage (Bytes) 
;; User Time D:HH:MM:SS.sss format 
;; Kernel Time D:HH:MM:SS.sss format 
;; Elapsed Time D:HH:MM:SS.sss format 
;; 
;;DEPENDENCIES WMI, TimeDiff() external UDF 
;; 
;;TESTED WITH W2K, WXP, W2K3, Vista, x64 
;; 
;;EXAMPLES 
; 
Function WMIProcessList(OPTIONAL $_Process, Optional $_Computer, OPTIONAL $_pAuth)

Dim $_objWMIService, $_colItems, $_objItem ; WMI object vars 
Dim $_Line ; line string 
Dim $_aTmp[0], $_I ; return array, index 
Dim $_ ; temp var 
Dim $_BTime, $_CTime ; boot and current times from target system 

$_I = -1

; insure a properly formatted computer name, default to local computer is not specified 
$_Computer = IIf(Not $_Computer, '.', Join(Split($_Computer,'\'),''))

; If a pre-authenticated WMI object pointer was provided, use it, otherwise create a new object pointer 
If $_pAuth
$_objWMIService = $_pAuth
Else
$_objWMIService = GetObject('winmgmts:{impersonationLevel=impersonate}!\\' + $_Computer + '\root\cimv2')
If @ERROR Exit Val('&' + Right(DecToHex(@ERROR), 4)) EndIf
EndIf

; Get the current and boot times from the client system 
$_colItems = $_objWMIService.ExecQuery("Select * from Win32_OperatingSystem",,48)
For Each $_objItem in $_colItems
$_BTime = $_objItem.LastBootUpTime ; host-local boot time 
$_CTime = $_objItem.LocalDateTime ; host-local current time 
Next

$_colItems = 0
; convert to a normalized time string 
$_CTime = SubStr($_CTime, 1,4) + '/' + SubStr($_CTime, 5,2) + '/' +SubStr($_CTime, 7,2) + ' '
+ SubStr($_CTime, 9,2) + ':' + SubStr($_CTime, 11,2) + ':' + SubStr($_CTime, 13,6)

; get the collection of process objects 
$_colItems = $_objWMIService.ExecQuery("Select * from Win32_Process",,48)
If @ERROR $_colItems = 0 Exit Val('&' + Right(DecToHex(@ERROR), 4)) EndIf

; Enumerate the collection of process data 
For Each $_objItem in $_colItems
; add the data to the array if no process is specified, or if a specific process name or ID is specified 
If Not $_Process Or $_Process = $_objItem.Name Or $_Process = $_objItem.ProcessID 
$_Line = $_objItem.Name
$_Line = $_Line + ',' + $_objItem.ProcessID
$_Line = $_Line + ',' + $_objItem.Priority
$_Line = $_Line + ',' + $_objItem.ThreadCount
$_Line = $_Line + ',' + $_objItem.HandleCount
$_Line = $_Line + ',' + $_objItem.WorkingSetSize
; convert the following to d:hh:mm:ss.sss format 
$_Line = $_Line + ',' + _WMIPLTC(CDbl($_objItem.UserModeTime) * 0.0000001)
$_Line = $_Line + ',' + _WMIPLTC(CDbl($_objItem.KernelModeTime) * 0.0000001)
; Use the system boot time if creation date is not set 
$_ = IIf($_objItem.CreationDate , $_objItem.CreationDate, $_BTime)
; calculate elapsed time and convert to d:hh:mm:ss.sss format 
$_Line = $_Line + ',' + _WMIPLTC(_WMIPLET($_, $_CTime))
; Update the array 
$_I = $_I + 1
ReDim Preserve $_aTmp[$_I]
$_aTmp[$_I] = $_Line
EndIf
Next

; return the array, close the collection, and gripe if no items were found 
$WMIProcessList = $_aTmp
$_colItems = 0
If $_Process And $_I < 0 Exit 1911 EndIf
Exit 0

EndFunction


; support function to calculate elapsed time as Seconds 
; Dependent on TimeDiff UDF! 
Function _WMIPLET($_Time, $_CTime)

Dim $_CurrentTime

; Break into Date and Time parts, including 3 decimal points 
$_Time = SubStr($_Time, 1,4) + '/' + SubStr($_Time, 5,2) + '/' +SubStr($_Time, 7,2) + ' '
+ SubStr($_Time, 9,2) + ':' + SubStr($_Time, 11,2) + ':' + SubStr($_Time, 13,6)

; return the value with 3 decimal places 
$_WMIPLET = TimeDiff($_Time, $_CTime, '', 1); FormatNumber(, 3, -1, 0, 0) 
Exit 0

EndFunction


; support function to conver the time value (100ns units) to D:H:M:S.s format 
Function _WMIPLTC($_Units)

Dim $_D, $_H, $_M, $_S ; day, hour, minute, & second values 

; Find d/h/m and subtract result from units 
$_D = Int($_Units / 86400) $_Units = $_Units - $_D * 86400
$_H = Int($_Units/3600) $_Units = $_Units - $_H * 3600
$_M = Int($_Units/60)
$_S = FormatNumber($_Units - $_M * 60, 3, -1, 0, 0)

; return a time string 
$_WMIPLTC = '' + $_D + ':' + $_H + ':' + $_M + ':' + $_S
Exit 0

EndFunction 



Glenn, I understand the concern, but at this point we already have users with PST's on network shares that Evault can't suck their PST's in from.
The intent of running this script and moving the PST's is to put them at a location where we can 'vault' them from.

As you can see from the code, I'm actually moving to the C: drive on the test system, in my testing I'm pulling the PST's off a mapped drive. Everything below is working -

-pst indentification
-forced closing of outlook
-pst disconnection from outlook
-pst rename and move
-pst old and new location logging
-messagebox popup indicating completion.

A user with 4 pst's will have them all moved and renamed.

What's not working at this point is the re-attaching of the PST's to the profile. Stepping through the code in debug I don't see any errors returned.

Lan


Glenn BarnasAdministrator
(KiX Supporter)
2011-07-21 09:42 PM
Re: PST locate and log...

I gotcha..

When I wrote this original process, we were moving PSTs from local machines to a file share after disconnecting them from the user's Outlook config. We renamed them from .PST to .TSP so users would not find and attach them from the share. In our process to add messages to the vault, we processed just a few departments per day - about 50 users per day. This was about as much as the back-end process to import them into the archive system could handle. For 2500 users, it took about 60-70 days to complete the project to move everything to the vault and eliminate PSTs.

Using this method, most users were without their PST messages for at most a day, so there was no need to re-attach them to their PSTs on the file server.

Glenn


Tsguy
(Getting the hang of it)
2011-07-21 10:26 PM
Re: PST locate and log...

Glenn,

When I first started working on this script, my intent was also to move the PST's and prevent user access to them until they could be vaulted.

Unfortunately things change.

We are also looking to do department moves into evault, and our timeline is looking to be quite a few days as well.

It is most likely at this point that we'll be able to run this script on workstations in advance of the user's being enabled for evault.

It's not a huge factor that the PST's are re-attaching via the script as my helpdesk monkeys can probably handle that manually ;), but it would't hurt to have it work. \:\)

I was thinking that perhaps the variable for $NewPST isn't being converted properly.

I'm going to run thru debug again here in a few minutes and see if anything is actually written to the registry.

Lan


Tsguy
(Getting the hang of it)
2011-07-22 01:14 AM
Re: PST locate and log...

Hmm . .alright results from further testing with registry exports along the way.

The only thing that jumps out is that the original value for the PST location key = 001f6700

And the writevalue from the copied code = 001f6700-0

Changing the writtenvalue to - 001f6700 does not resolve the problem.

Just in case tried a log off / log on for the user and there was no change.

I'm going to try instead to remove the "delete" of the PST keys, and just leave the write in.

Lan