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