Windows Shutdown calcs

Started by stanl, June 09, 2019, 05:10:32 AM

Previous topic - Next topic

stanl

The script below calculates the shutdown time from my registry as 2019:05:19:00:00 EST
Code (WINBATCH) Select


#Definefunction TimeYmdHmsGMT()
   Xbias = "System\CurrentControlSet\control\TimeZoneInformation[ActiveTimeBias]"
   offset = RegQueryDword(@REGMACHINE,Xbias)
   if offset<0 then val=timesubtract(timeymdhms(),strcat("0:0:0:0:",-offset,":0"))
   else val=timeadd(timeymdhms(),strcat("0:0:0:0:",offset,":0"))
   return val
#endfunction


#DefineFunction TimeAdjustCurrentZone(strUtcTime)
   nActiveBias =  RegQueryDword(@REGMACHINE,"SYSTEM\CurrentControlSet\Control\TimeZoneInformation[ActiveTimeBias]", 0)
   If nActiveBias >= 0
      Return TimeSubtract(strUtcTime,"0000:00:00:00:":nActiveBias:":00")
   Else
      Return TimeAdd(strUtcTime,"0000:00:00:00:":Abs(nActiveBias):":00")
#EndFunction
;Return TimeAdjustCurrentZone(ObjectType("DATE", fDate))




#DefineFunction HexByteToDec(_sHexByte)
_sHexByte = StrUpper(_sHexByte)
nHexOff = 55
nByte = 0
For i = 1 To 2
   cNybble = StrSub(_sHexByte,i,1)
   If !IsInt(cNybble) Then cNybble = Char2Num(cNybble) - nHexOff
   nByte = (nByte << 4) | cNybble
Next
Return nByte
#EndFunction


#DefineFunction Hex2Date(Hex)
bias = "0000:00:00:04:00:00"
aH = Arrayize(Hex," ")
If ArrInfo(aH,1)<8 Then Return ""
aD = ArrDimension(8)
For i = 0 To 7
   aD[i] = HexByteToDec(aH[i]) * 1.0
Next
Term = aD[7] * (2.0**56) + aD[6] * (2.0**48) + aD[5] * (2.0**40) + aD[4] * (2.0**32) + aD[3] * (2.0**24) + aD[2] * (2.0**16) + aD[1] * (2.0**8) + aD[0]
Days = Term / (1E7 * 86400)
;fDate = 0.0 + ObjectType("DATE",Days) + ObjectType("DATE","01-01-1601")
fDate = 0.0 + ObjectType("DATE",Days) + ObjectType("DATE","1601:01:01:00:00:00")
;Return(ObjectType("DATE",fDate))
;Return TimeSubtract(ObjectType("DATE",fDate),bias)
Return TimeAdjustCurrentZone(ObjectType("DATE", fDate))
#EndFunction


; Test
RegShutdownTime = RegQueryBin(@REGMACHINE,"System\CurrentControlSet\Control\Windows[ShutdownTime]")
ShutDownTime = Hex2Date(RegShutdownTime)
Message("",ShutDownTime:" EST")


Exit





A similar Powershell script comes up with Monday, May 20, 2019 7:25:59 AM


$regKey = Get-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Windows
$shutDown = $regKey.ShutdownTime
$Int64Value = [System.BitConverter]::ToInt64($shutDown, 0)
#$Int64Value
#Get-Date $Int64Value
$date = [DateTime]::FromFileTime($Int64Value)


$date



Outside of the formatting, the PS script has more precision. I will probably try converting the PS into a WB script with the CLR, but would be interested in making the above WB script more precise.


td

Slightly different approach but it does produce a result identical to your ps script without the use of .Burgerflipper.

Code (winbatch) Select
#DefineFunction TimeAdjustCurrentZone(strUtcTime)
   nActiveBias =  RegQueryDword(@REGMACHINE,"SYSTEM\CurrentControlSet\Control\TimeZoneInformation[ActiveTimeBias]", 0)
   If nActiveBias >= 0
      Return TimeSubtract(strUtcTime,"0000:00:00:00:":nActiveBias:":00")
   Else
      Return TimeAdd(strUtcTime,"0000:00:00:00:":Abs(nActiveBias):":00")
#EndFunction

RegShutdownTime = RegQueryBin(@REGMACHINE,"System\CurrentControlSet\Control\Windows[ShutdownTime]")
RegShutdownTime = StrReplace(RegShutdownTime, ' ', '')
hFileTime = BinaryAlloc(8)
BinaryPokeHex(hFileTime, 0, RegShutdownTime)
hSysTime = DllStructAlloc('WORD:wYear;  WORD:wMonth;  WORD:wDayOfWeek;  WORD:wDay;  WORD:wHour;  WORD:wMinute;  WORD:wSecond;  WORD:wMilliseconds;')
bResult = DllCall('Kernel32.dll',long:'FileTimeToSystemTime', lpBinary:hFileTime, lpstruct:hSysTime)
BinaryFree(hFileTime)
strYMS = StrFixCharsL(DllStructPeek(hSysTime ,'wYear'),'0',4):':' 
strYMS := StrFixCharsL(DllStructPeek(hSysTime ,'wMonth'),'0',2):':'
strYMS := StrFixCharsL(DllStructPeek(hSysTime ,'wDay'),'0',2):':'
strYMS := StrFixCharsL(DllStructPeek(hSysTime ,'wHour'),'0',2):':'
strYMS := StrFixCharsL(DllStructPeek(hSysTime ,'wMinute'),'0',2):':'
strYMS := StrFixCharsL(DllStructPeek(hSysTime ,'wSecond'),'0',2)  ; Could round the seconds using wmilliseconds value.
DllStructFree(hSysTime)

strYMS = TimeAdjustCurrentZone(strYMS)
strTime = TimeFormat(strYms, "dddd', 'MMMM dd', 'yyyy hh':'mm':'ss t")

Pause('Shutdown Time', strTime)     
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

Neat....  Well on my path to trip over the CLR I tried this:
Code (WINBATCH) Select


;Winbatch 2019A - CLR to convert Windows ShutdownTime reg value to date
;
;
;=============================================================================================================
Gosub udfs
ObjectClrOption("useany","System")
RegShutdownTime = RegQueryBin(@REGMACHINE,"System\CurrentControlSet\Control\Windows[ShutdownTime]")
RegShutdownTime = StrReplace(RegShutdownTime, ' ', '')


value = Hex2Dec(RegShutdownTime) ;uses huge math extender
; CLR stuff
oBC = ObjectClrNew( 'System.Convert')
value= oBC.ToInt64(value)
oDT = ObjectClrNew( 'System.DateTime')
;this line gives error - Not a Valid Win32 filetime
dt = oDT.FromFileTime(value)


;final WB function for readable DateTime
dt = TimeFormat(dt, "dddd', 'MMMM dd', 'yyyy hh':'mm':'ss t")
Pause('Shutdown Time', dt)


Exit


:udfs
#DefineFunction Hex2Dec(hex)
   AddExtender("wwhug44i.dll") ; Huge Math Extender
   str="0123456789ABCDEF"
   hex=StrTrim(StrUpper(hex))
   hexlen=StrLen(hex)
   dec=0
   for x=1 to hexlen
        by16 = huge_Multiply(dec,16)
        ptr = StrIndex(str,strsub(hex,x,1),0,@fwdscan)-1
        dec = huge_Add( by16, ptr)
   next
   return(dec)
#EndFunction


Return



The error is commented in the code. Wondering is this should be run in 64-bit WB. 

kdmoyers

(Educational thread -- thanks!)
The mind is everything; What you think, you become.

td

I believe the problem is that the value stored in the registry is little-endian which is how Intel processors represent 64-bit integers in memory.  However, MSFT's FCL only understands big-endian representations of 64-bit integers.  So you need to do something like the following:

Code (winbatch) Select
ObjectClrOption("useany","System")
RegShutdownTime = RegQueryBin(@REGMACHINE,"System\CurrentControlSet\Control\Windows[ShutdownTime]")

;; Convert little-endian machine representation of int64 to
;; a big-endian representation.
RegTimeBigEnd = ''
for i=8 to 1 by -1
   RegTimeBigEnd := ItemExtract(i, RegShutdownTime, ' ')
next
   
; CLR stuff
oBC = ObjectClrNew( 'System.Convert')
value= oBC.ToInt64( RegTimeBigEnd, 16)
oDT = ObjectClrNew( 'System.DateTime')
dt = oDT.FromFileTime(System.Int64:value)


;final WB function for readable DateTime
dt = TimeFormat(dt, "dddd', 'MMMM dd', 'yyyy hh':'mm':'ss t")
Pause('Shutdown Time', dt)
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

td

Or if you want to use the FCL's registry functions:

Code (winbatch) Select
objReg = ObjectClrNew( 'Microsoft.Win32.Registry')
objMach = objReg.LocalMachine
objRegWin = objMach.OpenSubKey('System\CurrentControlSet\Control\Windows')
aShutdownTime = objRegWin.GetValue('ShutdownTime')
objRegWin.Close()
objMach.Close()
hShutdownTime = BinaryAlloc(8)

; Converts the byte array to a string.
for i=0 to 7
   BinaryPoke(hShutdownTime, i, aShutdownTime[i])
next
strShutdownTime = BinaryPeek8(hShutdownTime, 0)
BinaryFree(hShutdownTime)

; CLR stuff
oBC = ObjectClrNew( 'System.Convert')
value= oBC.ToInt64( strShutdownTime, 10)
oDT = ObjectClrNew( 'System.DateTime')
dt = oDT.FromFileTime(System.Int64:value)

;final WB function for readable DateTime
dt = TimeFormat(dt, "dddd', 'MMMM dd', 'yyyy hh':'mm':'ss t")
Pause('Shutdown Time', dt)
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

td

More fun.  Another slightly different take:

Code (winbatch) Select
objReg = ObjectClrNew( 'Microsoft.Win32.Registry')
objMach = objReg.LocalMachine
objRegWin = objMach.OpenSubKey('System\CurrentControlSet\Control\Windows')
aShutdownTime = objRegWin.GetValue('ShutdownTime')
objRegWin.Close()
objMach.Close()

; Converts the byte array to a string.
hShutdownTime = BinaryAlloc(8)
for i=0 to 7
   BinaryPoke(hShutdownTime, i, aShutdownTime[i])
next
strShutdownTime = BinaryPeek8(hShutdownTime, 0)
BinaryFree(hShutdownTime)
value = ObjectType('i8',strShutdownTime)

; CLR stuff
oDT = ObjectClrNew( 'System.DateTime')
dt = oDT.FromFileTime(value)

;final WB function for readable DateTime
dt = TimeFormat(dt, "dddd', 'MMMM dd', 'yyyy hh':'mm':'ss t")
Pause('Shutdown Time', dt)


"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

This is educational. After posting my CLR fubar, I started looking into byte-arrays and how they were used for 64bit values. Looking at your code and System.Int64:value [I had tried other combinations with no luck and using .Parse was even more frustrating. Probably need more explanation on when to use the colon, but otherwise my CLR is improving and I thank you sir.

td

The "System.Int64:value" business was completely unnecessary.  In this case, simply using "value" would have worked just fine.  I have a habit of including type information in parameters when I first use a method to avoid annoying "method not found" errors when initially writing code snippets.  I simply forgot to remove it before posting the example.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

In your script using the Windows Registry you processed the Shutdown value as an array. In general is that something you would have to know in advance.  I added [ t=aShutdownTime.GetType().Name ] which gave 'Array must have dimensions error'.


[EDIT]:  My bad - ObjectTypeGet(aShutdownTime) worked

td

If you use WinBatch Studio for script creation and development, variable type information is available in the Watch Window every time you run the script to a breakpoint, completion, or error.  Doing so makes developing a script with unfamiliar functions and methods much, much quicker.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

Quote from: td on June 11, 2019, 07:26:08 AM
If you use WinBatch Studio for script creation and development, variable type information is available in the Watch Window every time you run the script to a breakpoint, completion, or error.  Doing so makes developing a script with unfamiliar functions and methods much, much quicker.


Probably beating a dead horse, but prior to closing the registry object I tested Pause(aShutdownTime,objRegWin.GetValueKind('ShutdownTime')) which returned 3 [binary] so happy to note the WB return with ObjectTypeGet() was more informative.

td

"Binary" is a registry data type but not a programming language data type.  Obviously, the two types are not synonymous.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade