Jump to content
Sign in to follow this  
Colyn1337

Automatic Firmware Updates - SQLite & WMI

Recommended Posts

Colyn1337

#cs -----------------------------------------------------------------------------------------------------------------
Author:
Colyn Via
Script Function:
Query local device WMI info and perform a SQL lookup to check for firmware updates. The SQL database contains
device models, current firmware version, and the path to the update package. This script looks for the firmware
version based on the device model, and then copies the installer to the host drive. The script then performs a
silent install of the firmware update all the while preventing an accidental shutdown. This script has a verbose
log which gets copied to a network location if there's a problem. If it's successful the log writes a single
line to a master log in the same network location reporting it's success. Once the update is complete, the
installer file is deleted, as well as the local log file.
Version Notes:
1.0.03 - Adjusted final write to local log so that the $FirmwareDBResult is written in the log.
1.0.02 - Added processwait() to script to prevent premature script completion
1.0.01 - Added values to BIOSString dump in addition to names
1.0.00 - Initial complete version
#ce -----------------------------------------------------------------------------------------------------------------
#NoTrayIcon
#include 
#include 
; Unless directed otherwise, autoit will do an internet lookup for the driver to use. Later in the code
; the _SQLite_Startup UDF directs the program to get the driver locally. This is why it's important to
; include the driver in the package.
FileInstall("SQLite3.dll", @ScriptDir & "", 1)
Global $FWversion, $Product, $hDB, $hQuery, $aRow, $UpgradeDBResult, $FWversionDBResult, $CheckCurrent
Global $CheckDB, $UpgradeEXE, $SQLQuery1, $SQLQuery2, $PID
Global Const $Timestamp = @MON & "/" & @MDAY & "/" & @YEAR & " " & @HOUR & ":" & @MIN & ":" & @SEC & " "
Global Const $Log = "C:AMTFirmwareLog_" & @ComputerName & ".txt"
Global Const $GlobalLog = 'redactedvProFirmware ResponsesSuccessSucceeded.txt'
Global Const $FirmwareDB = 'redactedvProFirmware.db'
Global Const $GlobalResponse = 'redactedvProFirmware Responses'
; This script was designed to be verbose and log most events. This code creates the log and adds the first line.
FileWriteLine($Log, $Timestamp & "Hello! My name is " & @ComputerName & ". " & @OSVersion & " " & @OSArch & " is how I roll.")
FileWriteLine($Log, $Timestamp & "I'm running script 1.0.03.")
FWversion()
; This function runs a WMI query to get the current AMT firmware version on the device.
Func FWversion()
$objIntelME = ObjGet("winmgmts:" & "." & "rootIntel_ME")
; An error catch must be present here, as AutoIt does not mute COM errors naturally. Without this catch,
; an ugly sounding error will appear to the user. IsObj() is not enough, as it cannot handle a return.
If @error Then
FileWriteLine($Log, $Timestamp & "Intel_ME is not a valid NAMESPACE.")
FileCopy($Log, $GlobalResponse)
FileDelete($Log)
Exit
EndIf
$colIntelME = $objIntelME.ExecQuery("SELECT * FROM ME_System")
; The "Else" of this statement reports back which devices couldn't determine their firmware version.
If IsObj($colIntelME) Then
For $objMEsystem In $colIntelME
$FWversion = $objMEsystem.fwversion
Next
Else
FileWriteLine($Log, $Timestamp & "ME_System query failed.")
FileCopy($Log, $GlobalResponse)
FileDelete($Log)
Exit
EndIf
FileWriteLine($Log, $Timestamp & "Local host vPro firmware lookup result = " & $FWversion)
ConsoleWrite("Local host vPro firmware lookup result = " & $FWversion & @LF)
EndFunc ;==>FWversion
Product()
; This function runs a WMI query to get the current model information for the device.
Func Product()
; Since these variables are used often in general coding practice, they are Local'd instead of
; declared global. When the code finishes this function, it will drop these variables.
Local $x, $y, $prodArray, $aRefine
$objWMI = ObjGet("winmgmts:" & @ComputerName & "rootHPInstrumentedBIOS")
If @error Then
FileWriteLine($Log, $Timestamp & "rootHPInstrumentedBIOS is not a valid NAMESPACE.")
FileCopy($Log, $GlobalResponse)
FileDelete($Log)
Exit
EndIf
$colItems = $objWMI.ExecQuery("SELECT * FROM HPBIOS_BIOSString")
; The "Else" of this block accounts for the possibility that this code might run on a non HP device.
If IsObj($colItems) Then
FileWriteLine($Log, $Timestamp & "Attempting to grab hardware info.")
For $objItems In $colItems
If $objItems.Name = "Notebook Model" Then
$Product = $objItems.Value
FileWriteLine($Log, $Timestamp & "Raw value for " & $objItems.Name & " is " & $Product)
$prodArray = StringSplit($Product, " ")
; BIOS revisions change slightly how the information is presented by WMI. Sometimes it's
; simply the Model number, other times it can be a string of words. For simplicity and
; uniformity, this code strips out just the Model number from the string. This code is
; looking for a word that starts with a number. If HP begins using Model Numbers that
; start with letters, this code will need to be reworked.
For $x = 1 To $prodArray[0]
If StringIsAlpha($prodArray[$x]) Then
Else
; As it turns out, HP in its infinite wisdom decided to, in some cases, put both
; product identifiers in the SAME CIM component value. This confuses the scrip and
; it ends up recording the wrong value. To correct for this, and as the model
; number always starts with a number (currently), I've directed the script to check
; the first character in the string it's wanting to use to see if it's a number
; IsInt means "is it an integer", which in this case, the value is not. AutoIt
; sees it as a string, so Number() is used instead.
FileWriteLine($Log, $Timestamp & "I'm considering using " & $prodArray[$x] & ".")
$aRefine = StringSplit($prodArray[$x], "")
If Number($aRefine[1]) Then
FileWriteLine($Log, $Timestamp & "It turns out that " & $prodArray[$x] & " starts with " & $aRefine[1] & _
" and must therefore be what I'm looking for. Right?")
$Product = ($prodArray[$x])
EndIf
EndIf
Next
ExitLoop
ElseIf $objItems.Name = "Product Name" Then
$Product = $objItems.Value
FileWriteLine($Log, $Timestamp & "Raw value for " & $objItems.Name & " is " & $Product)
$prodArray = StringSplit($Product, " ")
For $x = 1 To $prodArray[0]
If StringIsAlpha($prodArray[$x]) Then
Else
FileWriteLine($Log, $Timestamp & "I'm considering using " & $prodArray[$x] & ".")
$aRefine = StringSplit($prodArray[$x], "")
If Number($aRefine[1]) Then
FileWriteLine($Log, $Timestamp & "It turns out that " & $prodArray[$x] & " starts with " & $aRefine[1] & _
" and must therefore be what I'm looking for. Right?")
$Product = ($prodArray[$x])
EndIf
EndIf
Next
ExitLoop
EndIf
Next
FileWriteLine($Log, $Timestamp & "Finished attempts to grab hardware info.")
; In the event that "Notebook Model" is not the value to read, it helps to see what should be added.
; The following code runs a dump of the contents of HPBIOS_BIOSstring and copies the log to the log directory.
; Later the results of the log can be used to determine what other $objItems.Name might exist.
If $Product = "" Then
FileWriteLine($Log, $Timestamp & "Query (SELECT * FROM HPBIOS_BIOSString) SUCCEEDED! However unable to " & _
"resolve object.name of known variables on " & @ComputerName & ".")
FileWriteLine($Log, "*************** Beginning Dump of HPBIOS_BIOSstring ***************")
For $objItems In $colItems
FileWriteLine($Log, "*** " & $objItems.name & " = " & $objItems.Value)
Next
FileWriteLine($Log, "*************** Done Exporting HPBIOS_BIOSstring ***************")
Sleep(5)
FileCopy($Log, $GlobalResponse)
FileDelete($Log)
Exit
EndIf
Else
FileWriteLine($Log, $Timestamp & "rootHPInstrumentedBIOS is not a valid NAMESPACE on " & @ComputerName & ".")
EndIf
ConsoleWrite("WMI query for Model produced " & $Product & @LF)
EndFunc ;==>Product
If $FWversion = "" Then Exit
If $Product = "" Then Exit
SQL()
; This function names the SQL queries and initializes the SQL engine.
Func SQL()
; SQL Queries
$SQLQuery1 = "SELECT " & '"' & "Update" & '"' & " FROM Lookup WHERE Product=" & '"' & $Product & '"' & ';'
$SQLQuery2 = "SELECT " & '"' & "FWVersion" & '"' & " FROM Lookup WHERE Product=" & '"' & $Product & '"' & ';'
; Check the DB and open it if the script can find it
FileWriteLine($Log, $Timestamp & "Checking to see if " & '"' & $FirmwareDB & '"' & " exists...")
If FileExists($FirmwareDB) Then
FileWriteLine($Log, $Timestamp & "Found it!")
Else
FileWriteLine($Log, $Timestamp & "I can't see it!")
FileCopy($Log, $GlobalResponse)
FileDelete($Log)
Exit
EndIf
_SQLite_Startup("", "", 0)
_SQLite_Open($FirmwareDB)
EndFunc ;==>SQL
Eligibility()
; This function determines the devices eligibility to upgrade, and figures out where to get the update from.
Func Eligibility()
; Since these variables are used often in general coding practice, they are Local'd instead of
; declared global. When the code finishes this function, it will drop these variables.
Local $x, $y, $z, $Proceed, $diff, $exe
_SQLite_Query(-1, $SQLQuery1, $hQuery)
If _SQLite_FetchData($hQuery, $aRow) = $SQLITE_OK Then
Else
ConsoleWrite("Query Failed")
FileWriteLine($Log, $Timestamp & $SQLQuery1 & " FAILED.")
FileCopy($Log, $GlobalResponse)
FileDelete($Log)
_SQLite_QueryFinalize($hQuery)
_SQLite_Close()
_SQLite_Shutdown()
Exit
EndIf
$UpgradeDBResult = $aRow[0]
FileWriteLine($Log, $Timestamp & $SQLQuery1 & " SUCCEEDED.")
_SQLite_QueryFinalize($hQuery)
ConsoleWrite($UpgradeDBResult & @LF)
; Need to get just the name of the update executable for use later.
$exe = StringSplit($UpgradeDBResult, "")
$UpgradeEXE = $exe[($exe[0])]
FileWriteLine($Log, $Timestamp & "The executable needed later is " & $UpgradeEXE & ".")
; Testing purposes - Making my host appear to be a 6550b
;~ $SQLQuery2 = "SELECT " & '"' & "FWVersion" & '"' & " FROM Lookup WHERE Product=" & '"' & "6550b" & '"' & ';'
_SQLite_Query(-1, $SQLQuery2, $hQuery)
If _SQLite_FetchData($hQuery, $aRow) = $SQLITE_OK Then
Else
ConsoleWrite("Query Failed")
FileWriteLine($Log, $Timestamp & $SQLQuery2 & " FAILED.")
FileCopy($Log, $GlobalResponse)
FileDelete($Log)
_SQLite_QueryFinalize($hQuery)
_SQLite_Close()
_SQLite_Shutdown()
Exit
EndIf
$FWversionDBResult = $aRow[0]
FileWriteLine($Log, $Timestamp & $SQLQuery2 & " SUCCEEDED.")
_SQLite_QueryFinalize($hQuery)
ConsoleWrite($FWversionDBResult & @LF)
FileWriteLine($Log, $Timestamp & "Query result was " & $FWversionDBResult)
; Be sure to close down SQL when it's not needed.
_SQLite_Close()
_SQLite_Shutdown()
; Testing purposes - Make my firmware appear older
;~ $FWversion = "6.2.0.1022"
FileWriteLine($Log, $Timestamp & "Comparing firmware versions.")
; So as not to run the risk of destroying the data, the data is copied to new
; variables before be begin to break it apart into arrays.
$CheckCurrent = StringSplit($FWversion, ".")
$CheckDB = StringSplit($FWversionDBResult, ".")
FileWriteLine($Log, $Timestamp & "Splitting firmware strings.")
; This code checks to make sure the two arrays are equal in terms of size.
; If they are not, the code will generate an error. This code also manually
; adjusts either array so that it always matches the larger of the two.
If $CheckDB[0] > $CheckCurrent[0] Then
$diff = ($CheckDB[0] - $CheckCurrent[0])
For $z = 1 To $diff
_ArrayAdd($CheckCurrent, 0)
Next
EndIf
If $CheckDB[0] < $CheckCurrent[0] Then
$diff = ($CheckCurrent[0] - $CheckDB[0])
For $z = 1 To $diff
_ArrayAdd($CheckDB, 0)
Next
EndIf
FileWriteLine($Log, $Timestamp & "Finished comparing stringsplit array sizes, continuing to determine if upgrade is needed.")
; This part is checking to see if the firmware update indicated by the SQL query
; is newer than the current version on the device. If the firmware is current or
; better, the program deletes the log file and closes.
Do
For $x = 1 To $CheckDB[0]
If $CheckDB[$x] > $CheckCurrent[$x] Then
$Proceed = "y"
ExitLoop
ElseIf $CheckDB[$x] < $CheckCurrent[$x] Then
$Proceed = "n"
ExitLoop
ElseIf $CheckDB[$x] = $CheckCurrent[$x] And $x = $CheckDB[0] Then
$Proceed = "n"
ExitLoop
EndIf
Next
Until $Proceed <> ""
If $Proceed = "n" Then
FileDelete($Log)
Exit
EndIf
FileWriteLine($Log, $Timestamp & "Finished comparing firmware versions. Proceeding to UpdateFirmware()")
EndFunc ;==>Eligibility
; $UpgradeDBResult is the path to the firmware update as retrieved by the Eligibility() function
UpdateFirmware()
; This function pulls down the update and executes it. It also writes to the $GlobalLog.
Func UpdateFirmware()
FileWriteLine($Log, $Timestamp & "Attempting to copy " & '"' & $UpgradeDBResult & '"' & " to local drive.")
If FileExists($UpgradeDBResult) Then
FileCopy($UpgradeDBResult, @HomeDrive & "")
Else
FileWriteLine($Log, $Timestamp & "FileExists() check failed. I quit!")
FileCopy($Log, $GlobalResponse)
FileDelete($Log)
Exit
EndIf
FileWriteLine($Log, $Timestamp & "Run command issued for " & @HomeDrive & "" & $UpgradeEXE)
; The -s switch is for silent install
Run(@HomeDrive & "" & $UpgradeEXE & " -s", "", @SW_HIDE)
; Prevent shutdown during firmware update process
ProcessWait($UpgradeEXE, 60)
Do
Run("shutdown /a", @SystemDir, @SW_HIDE)
Sleep(10)
$PID = ProcessExists($UpgradeEXE)
Until $PID = 0
FileWriteLine($GlobalLog, $Timestamp & @ComputerName & " launched " & $UpgradeEXE & " to achieve " & $FWversionDBResult & ".")
EndFunc ;==>UpdateFirmware
FileDelete(@HomeDrive & "" & $UpgradeEXE)
FileDelete($Log)
Exit

Edited by Colyn1337

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

  • Similar Content

    • FrancescoDiMuro
      By FrancescoDiMuro
      Good evening everyone
      I'm working on a little project of mines, and I was trying to use WMI Object.
      The question which I don't find an answer is: 
      Once I do the query with WMI Object, something like "SELECT * FROM Win32_LogonSession", instead of specify the field of the collection returned, ( i.e. $colItems.Caption ), can I loop though each property and each value of the property, writing so one row of code only?
      Hope my question was clear enough.
      Thanks in advance.

      Best Regards.
    • dangr82
      By dangr82
       
      I have created this function for a database, but I can not make it work. I always have two error messages: "not an error" ... and the file created, in the script directory, does not contain anything.
      Global $sDBName = "Hen.db" Func DatabaseTable() Local $sConnDB _SQLite_Startup() If @error Then MsgBox($MB_SYSTEMMODAL, "SQLite Error", "SQLite.dll Can't be Loaded!") Exit -1 EndIf $sConnDB = _SQLite_Open($sDBName) If @error Then MsgBox($MB_SYSTEMMODAL, "SQLite Error", "Can't Load Database!") Exit -1 EndIf If Not _SQLite_Exec($sDBName, 'CREATE TABLE Animal ("Name", "Age");') = $SQLITE_OK Then MsgBox($MB_SYSTEMMODAL, "SQLite Error", _SQLite_ErrMsg()) If Not _SQLite_Exec($sDBName, 'INSERT INTO Animale VALUES ("Charlie","5");') = $SQLITE_OK Then MsgBox($MB_SYSTEMMODAL, "SQLite Error", _SQLite_ErrMsg()) _SQLite_Close($sConnDB) _SQLite_Shutdown() EndFunc  
    • Eminence
      By Eminence
      Hello,
      I've been searching on how to select the first row in my database which includes a time column based on the latest start_time date for today's date.
      This is my current code:
      Local $iCurrentDate = _Now() Local $sqlHandle = _dbOpen($sDbPath) Local $aResult, $iRows, $iCols _SQLite_GetTable2d($sqlHandle, "SELECT start_time FROM " & $srawDb & " ORDER BY datetime(start_time) DESC LIMIT 1", $aResult, $iRows, $iCols) _ArrayDisplay($aResult) _dbClose($sDbpath) What it does is it gets the first row entry in the database however it does not select the latest start_time entry in the database, it always select the first row. I've tried changing DESC to ASC to see if that will do it but the issue still remains. Any thoughts on this? Thanks in advance.
       
      *EDIT
      The date format in the database is by MM/DD/YYYY HH:MM:SS.
    • AnonymousX
      By AnonymousX
      Hello,
      So this may be more of a challenge of effective programming then specific to AutoIT but I want to solve this problem with AutoIT  so i'm putting it here. (If someone has a better language to solve with I'm all ears)
       
      So the task I'm trying to achieve is that I have multiple .CSV files that have: year, month, day, hour, value. I need to be able to sum up all the values that have the same date/time, then find which date and time has the maximum value.
       
      The problem is that each file may or may not have same amount of days/hours as the rest. So I need to devise a way to handle this. 
       
      Example:
      File A   File B   File C 2018 1 1  1:00 10   2018 1 1 2:00 10   2018 1 1  1:00 10 2018 1 1  2:00 12   2018 1 1 3:00 12   2018 1 2 1:00 12 2018 1 1  3:00 14   2018 1 1 4:00 14   2018 2 1  1:00 16 2018 2 1  1:00 16   2018 2 1  1:00 16              
       Answer I want to be spit out is Feb 1st 2018 at 2:00 with value of 48
       
      So far I've got code to store all .CSV files to an array, then a loop to go through each csv, but not sure how to effectively manipulate the data. Keep in mind each file has over 7000 time entry points.
       
      If anyone can solve this that would be pretty awesome! 
      #include <Array.au3> #include <File.au3> #include <MsgBoxConstants.au3> RefineData() Func RefineData() Local $i, $filenum, $file, $csvArray, $FilePath = @ScriptDir $fileList = _FileListToArrayRec($FilePath, "*.csv", 1) ;Create and array of all .csv files within folder Local $chkArray[UBound($fileList)][2] ;=====Loop through the .csv files within the folder====== For $filenum = 1 To UBound($fileList) - 1 Step 1 $file = $fileList[$filenum] $sFilePath = $FilePath & "\" & $file ;=====Create array based on csv file===== _FileReadToArray($sFilePath, $csvArray, $FRTA_NOCOUNT, ",") ;#### Operations here ###### next msgbox(0,"", "Date: " & $date_of_max & "Value: " & $maxVal );display solution endfunc  
    • corz
      By corz
      Associative Array Functions
      I've seen a couple of UDFs for this on the forum. One of them I quite like. But it's still nearly not as good as this method, IMHO.
      I don't recall if I discovered the "Scripting.Dictionary" COM object myself or if I got the original base code from somewhere online. I have recently searched the web (and here) hard for any AutoIt references to this, other than my own over the years I've been using this (in ffe, etc..), and I can find nothing, so I dunno. If anyone does, I'd love to give credit where it's due; this is some cute stuff! It could actually be all my own work! lol
      At any rate, it's too useful to not have posted somewhere at autoitscript.com, so I've put together a wee demo.
      For those who haven't heard of the COM "Scripting.Dictionary".. 
      If you've ever coded in Perl or PHP (and many other languages), you know how useful associative arrays are. Basically, rather than having to iterate through an array to discover it's values, with an associative array you simply pluck values out by their key "names".
      I've added a few functions over the years, tweaked and tuned, and this now represent pretty much everything you need to easily work with associative arrays in AutoIt. En-joy!
      The main selling point of this approach is its simplicity and weight. I mean, look at how much code it takes to work with associative arrays! The demo is bigger than all the functions put together! The other selling point is that we are using Windows' built-in COM object functions which are at least theoretically, fast and robust.
      I've used it many times without issues, anyhow, here goes..
      ; Associative arrays in AutoIt? Hells yeah! ; Initialize your array ... global $oMyError = ObjEvent("AutoIt.Error", "AAError") ; Initialize a COM error handler ; first example, simple. global $simple AAInit($simple) AAAdd($simple, "John", "Baptist") AAAdd($simple, "Mary", "Lady Of The Night") AAAdd($simple, "Trump", "Silly Man-Child") AAList($simple) debug("It is said that Trump is a " & AAGetItem($simple, "Trump") & ".", @ScriptLineNumber);debug debug("") ; slightly more interesting.. $ini_path = "AA_Test.ini" ; Put this prefs section in your ini file.. ; [test] ; foo=foo value ; foo2=foo2 value ; bar=bar value ; bar2=bar2 value global $associative_array AAInit($associative_array) ; We are going to convert this 2D array into a cute associative array where we ; can access the values by simply using their respective key names.. $test_array = IniReadSection($ini_path, "test") for $z = 1 to 2 ; do it twice, to show that the items are *really* there! for $i = 1 to $test_array[0][0] $key_name = $test_array[$i][0] debug("Adding '" & $key_name & "'..");debug ; key already exists in "$associative_array", use the pre-determined value.. if AAExists($associative_array, $key_name) then $this_value = AAGetItem($associative_array, $key_name) debug("key_name ALREADY EXISTS! : =>" & $key_name & "<=" , @ScriptLineNumber);debug else $this_value = $test_array[$i][1] ; store left=right value pair in AA if $this_value then AAAdd($associative_array, $key_name, $this_value) endif endif next next debug(@CRLF & "Array Count: =>" & AACount($associative_array) & "<=" , @ScriptLineNumber);debug AAList($associative_array) debug(@CRLF & "Removing 'foo'..");debug AARemove($associative_array, "foo") debug(@CRLF & "Array Count: =>" & AACount($associative_array) & "<=" , @ScriptLineNumber);debug AAList($associative_array) debug(@CRLF & "Removing 'bar'..");debug AARemove($associative_array, "bar") debug(@CRLF & "Array Count: =>" & AACount($associative_array) & "<=" , @ScriptLineNumber);debug AAList($associative_array) quit() func quit() AAWipe($associative_array) AAWipe($simple) endfunc ;; Begin AA Functions func AAInit(ByRef $dict_obj) $dict_obj = ObjCreate("Scripting.Dictionary") endfunc ; Adds a key and item pair to a Dictionary object.. func AAAdd(ByRef $dict_obj, $key, $val) $dict_obj.Add($key, $val) If @error Then return SetError(1, 1, -1) endfunc ; Removes a key and item pair from a Dictionary object.. func AARemove(ByRef $dict_obj, $key) $dict_obj.Remove($key) If @error Then return SetError(1, 1, -1) endfunc ; Returns true if a specified key exists in the associative array, false if not.. func AAExists(ByRef $dict_obj, $key) return $dict_obj.Exists($key) endfunc ; Returns a value for a specified key name in the associative array.. func AAGetItem(ByRef $dict_obj, $key) return $dict_obj.Item($key) endfunc ; Returns the total number of keys in the array.. func AACount(ByRef $dict_obj) return $dict_obj.Count endfunc ; List all the "Key" > "Item" pairs in the array.. func AAList(ByRef $dict_obj) debug("AAList: =>", @ScriptLineNumber);debug local $k = $dict_obj.Keys ; Get the keys ; local $a = $dict_obj.Items ; Get the items for $i = 0 to AACount($dict_obj) -1 ; Iterate the array debug($k[$i] & " ==> " & AAGetItem($dict_obj, $k[$i])) next endfunc ; Wipe the array, obviously. func AAWipe(ByRef $dict_obj) $dict_obj.RemoveAll() endfunc ; Oh oh! func AAError() Local $err = $oMyError.number If $err = 0 Then $err = -1 SetError($err) ; to check for after this function returns endfunc ;; End AA Functions. ; debug() (trimmed-down version) ; ; provides quick debug report in your console.. func debug($d_string, $ln=false) local $pre ; For Jump-to-Line in Notepad++ if $ln then $pre = "(" & $ln & ") " & @Tab ConsoleWrite($pre & $d_string & @CRLF) endfunc  
      ;o) Cor
×

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.