AndyS19

WMI SQL query results object can only be used once

6 posts in this topic

#1 ·  Posted

I have code that does a WMI SQL query to find all defined printers, and I want to parse the returned object in several places.  However, after parsing it the first time, all other times fail to find any printer objects.

Here is my test code:

test()

Func test()
    Local $oPrinters, $oPrinter, $err, $cnt, $oP, $query

    $query = "SELECT * FROM Win32_Printer"
    $oPrinters = doQuery($query)

    $err = @error
    LogMsg("+++: $err = " & $err & ", isObj($oPrinters) = " & IsObj($oPrinters))

    If ($err == 0) Then

        LogMsg("FIRST LOOP") ; <=== FIRST LOOP
        $cnt = 0
        $oP = $oPrinters
        LogMsg("+++: isObj($oP) = " & IsObj($oP))

        For $oPrinter In $oP
            $cnt += 1
            LogMsg("+++: isObj($oPrinter): " & IsObj($oPrinter) & ", $oPrinter.Name ==>" & $oPrinter.Name & "<==")
        Next
        LogMsg("+++: Found " & $cnt & " printers")

        LogMsg("SECOND LOOP") ; <== SECOND LOOP
        $cnt = 0
        $oP = $oPrinters
        LogMsg("+++: isObj($oP) = " & IsObj($oP))

        For $oPrinter In $oP
            $cnt += 1
            LogMsg("+++: isObj($oPrinter): " & IsObj($oPrinter) & ", $oPrinter.Name ==>" & $oPrinter.Name & "<==")
        Next
        LogMsg("+++: Found " & $cnt & " printers")
    EndIf

EndFunc   ;==>test

Func doQuery($sQuery, $lnum = @ScriptLineNumber)
    #forceref $lnum
    LogMsg("+++:" & $lnum & ": doQuery(" & '"' & $sQuery & '"' & ") entered")
    Local $oWMIService, $oResults, $errstr
    Local $wbemFlags = BitOR(0x20, 0x10) ; $wbemFlagReturnImmediately and wbemFlagForwardOnly

    $oWMIService = ObjGet("winmgmts:\\" & "localhost" & "\root\CIMV2")

    If (IsObj($oWMIService)) Then

        $oResults = $oWMIService.ExecQuery($sQuery, "WQL", $wbemFlags)

        If (IsObj($oResults)) Then
            LogMsg("+++: doQuery() returns @error = 0, Good: returning the object")
            Return (SetError(0, 0, $oResults)) ;;; Good: return the object
        Else
            $errstr = "" _
                     & "WMI Query failed." & @CRLF _
                     & "This is the query:" & @CRLF _
                     & "   " & $sQuery
            LogMsg("+++: ====>" & $errstr & "<===")
            LogMsg("+++: doQuery() returns @error = 1")
            Return (SetError(1, 0, $errstr)) ; Error: Query faled
        EndIf
    Else
        $errstr = "" _
                 & "WMI Output" & @CRLF _
                 & "No WMI Objects Found for class: " & @CRLF _
                 & "Win32_PrinterDriver" & @CRLF _
                 & "using this query:" & @CRLF _
                 & "   " & $sQuery
        LogMsg("+++: ====>" & $errstr & "<===")
        MsgBox(0, "ERROR", $errstr) ; Error: Cannot get $oWMIService object
        Exit (1)
    EndIf
EndFunc   ;==>doQuery

Func LogMsg($msg, $lnum = @ScriptLineNumber)
    ConsoleWrite("+++:" & $lnum & ": " & $msg & @CRLF)
EndFunc   ;==>LogMsg

Parsing the returned $oPrinters object shows 5 printers:

+++:15: FIRST LOOP
+++:18: +++: isObj($oP) = 1
+++:22: +++: isObj($oPrinter): 1, $oPrinter.Name ==>Microsoft XPS Document Writer<==
+++:22: +++: isObj($oPrinter): 1, $oPrinter.Name ==>Microsoft Office Document Image Writer<==
+++:22: +++: isObj($oPrinter): 1, $oPrinter.Name ==>Fax<==
+++:22: +++: isObj($oPrinter): 1, $oPrinter.Name ==>Canon MG7100 series Printer WS<==
+++:22: +++: isObj($oPrinter): 1, $oPrinter.Name ==>Canon MG6100 series Printer WS<==
+++:24: +++: Found 5 printers

Parsing it again, shows no printers:

+++:26: SECOND LOOP
+++:29: +++: isObj($oP) = 1
+++:35: +++: Found 0 printers

 

Share this post


Link to post
Share on other sites



#2 ·  Posted

Maybe because the "cursor" is at the end of the resulting record set returned by the query.
You could try to loop throug $oP this way:

For $i = 1 to $oP.count
   ; Process $oP($i)
Next

 


My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2017-04-18 - Version 1.4.8.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX (NEW 2017-02-27 - Version 1.3.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2015-04-01 - Version 0.4.0.0) - Download - General Help & Support - Example Scripts
Excel - Example Scripts - Wiki
Word - Wiki
PowerPoint (2015-06-06 - Version 0.0.5.0) - Download - General Help & Support

Tutorials:
ADO - Wiki

 

Share this post


Link to post
Share on other sites

#3 ·  Posted

When I try the following, 
$cnt = $oP.count

I get this error:
==> The requested action with this object has failed.:

Share this post


Link to post
Share on other sites

#4 ·  Posted


My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2017-04-18 - Version 1.4.8.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX (NEW 2017-02-27 - Version 1.3.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2015-04-01 - Version 0.4.0.0) - Download - General Help & Support - Example Scripts
Excel - Example Scripts - Wiki
Word - Wiki
PowerPoint (2015-06-06 - Version 0.0.5.0) - Download - General Help & Support

Tutorials:
ADO - Wiki

 

Share this post


Link to post
Share on other sites

#5 ·  Posted

Right.  When I set the wbem flags to 0 (wait for complete) and went back to my original code, I was able to make multiple passes of the $oPrinters object.  However, no matter what I tried, I could not access one of the printer objects in $oPrinters using an index, like $oPrinters[2].

So it's fixed, thank you,

Share this post


Link to post
Share on other sites

#6 ·  Posted

:)


My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2017-04-18 - Version 1.4.8.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX (NEW 2017-02-27 - Version 1.3.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2015-04-01 - Version 0.4.0.0) - Download - General Help & Support - Example Scripts
Excel - Example Scripts - Wiki
Word - Wiki
PowerPoint (2015-06-06 - Version 0.0.5.0) - Download - General Help & Support

Tutorials:
ADO - Wiki

 

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

  • Similar Content

    • nikink
      By nikink
      Hi all,
      I have a bit of code that works on my old Win10 PC, that fails on my new Win10 PC, and I think the only significant difference is the version of Autoit - old PC has 3.3.12, new has 3.3.14.
      I couldn't find anything mentioned in the change logs though, so perhaps I'm wrong.
      Anyway, the code to replicate my issue is:
      Test('username', 'DOMAIN') ; THIS ERRORS: ;Test('localun', 'DOMAIN') ; THIS ERRORS: ;Test(' ', ' ') ; THIS ERRORS: ;Test('', '') ; THIS ERRORS: ;Test('localun', '') ; THIS ERRORS: ;Test('', 'DOMAIN') Func Test($un, $dom) $compName = 'PCNAME' $FullName = '.' $Description = '.' ; get the WIM object $objWMIService = ObjGet("winmgmts:\\" & $compName & "\root\cimv2") ; get default user full name and description $objAccount = $objWMIService.Get("Win32_UserAccount.Name='" & $un & "',Domain='" & $dom & "'") If IsObj($objAccount) Then $FullName = $objAccount.FullName $Description = $objAccount.Description EndIf ConsoleWrite($FullName & @CRLF) ConsoleWrite($Description & @CRLF) Return EndFunc  
      On my old PC this code will output just . and . for each of those line currently commented out. Which is fine.
      On my new PC any of those commented out lines of code cause an error, and the script won't even compile.
      $objAccount = $objWMIService.Get("Win32_UserAccount.Name='" & $un & "',Domain='" & $dom & "'") $objAccount = $objWMIService^ ERROR I'm very much a newb with the WMI stuff and objects, but it looks like the .Get property is failing when either $un or $dom aren't valid in v3.3.14, whereas in 3.3.12 the .Get would fail to return an object, which is then caught by the If statement.
      Am I on track with this? Is there some new/better way to code the example so that 3.3.14 will compile it?
    • ModemJunki
      By ModemJunki
      Edit: I found out from looking into the post from coffeeturtle that not all motherboards support WMI temperature reading. You have to explore the WMI namespaces for your system!
      I (stupidly) fried a computer. It was an old Zotac small form factor device that was junked at work, so I turned it into a home theater PC.
      But I had put it in my "audio rack", which has glass doors and not very good ventilation. Normally I would open the doors while it was running and I use a media remote which would hibernate the system. At some point, it woke up by itself (probably Windows Update) and did not go to sleep again. Unfortunately this happened during the summer months while I was away on a small holiday and of course the glass doors were closed.
      When I finally noticed the system was on, it was super hot and the fan was at full blast. Dead. No POST, no green light on the mainboard even with a new power supply, old mechanical laptop hard drive gives errors when connected to another device. My next home project will be to make some appropriate ventilation in the audio cabinet.
      For the replacement I bought a used HP ultra small form factor machine and decided to start working on an app to monitor the ambient temperature sensor on the motherboard and shutdown the system if needed. I used some code for smooth label updates from here (needed updating to work with latest AutoIT) and temperature conversion from here (not really needed, only if you want temperatures in something other than C).
      Maybe I will work on this some more and make it configurable with an .INI file or even storing the settings in registry, but since I probably won't I thought I'd put it up as an example of what a non-expert can do with an afternoon of coding and ideas from the community here.
      This uses an ambient temperature sensor populated on HP machines in HP-specific WMI extensions but the WMI query can easily be changed to any available sensor including CPU or GPU.
      The watchdog monitors temperature, warns with S.O.S beeping if the set point is exceeded, and either shuts down if a timeout is reached while the temperatures are high or goes back to monitoring if the temperature goes lower then the set point, and logs events to the Windows application event logs.
      The GUI it puts up is very small at the top center of the screen (very small on a 4k display anyway).
      #requireadmin because of the WMI query.
      A timer for the WMI query because of prior experience using similar functions in WinPE to get model infos from HP WMI bios extensions.
      If you play around with this, please post your fixes/improvements/changes back to this thread.
      Have fun!
      Edit: Updated to show "Unable to query temperature" if the WMI query returns a null
      #AutoIt3Wrapper_Icon=temperature-2-multi-size.ico #RequireAdmin #NoTrayIcon #include <Temperature.au3> #include <FFLabels.au3> #include <GUIConstantsEx.au3> #include <StaticConstants.au3> #include <WindowsConstants.au3> Global $winLogger = "C:\WINDOWS\system32\eventcreate.exe" ; application to use for event logging Global $MessageSource = "HP TempMon" Global $width = 185 Global $height = 15 Global $FontSize = 8 Global $FontFamily = 'Microsoft Sans Serif' Global $normClr = 0xFF000000 Global $warnClr = 0xFF0000 Global $WMInameSpace = "HP_BIOSNumericSensor" ;~ Global $WMIReadSensor = "Chassis Thermal Index" ; HP 8300 Global $WMIReadSensor = "System Ambient Temperature" ; HP Z400 Global $warnTmp = 46.1111111111 ; degrees C ;~ Global $warnTmp = 24 ; degrees C for testing Global $ovrtmpTime = 5 ; in minutes Global $hGUI = GUICreate("Board Temperature", $width, $height, -1, 0, BitOR($WS_SYSMENU, $WS_POPUP), BitOR($WS_EX_TOPMOST, $WS_EX_WINDOWEDGE, $WS_EX_TOOLWINDOW)) Global $lb1 = _GUICtrlFFLabel_Create($hGUI, "", -1, -1, $width, $height, 8, -1, 0, 1, $normClr) GUISetState(@SW_SHOW) _TempSenseLoop() Func _TempSenseLoop() While 1 Sleep(250) $nMsg = GUIGetMsg() Switch $nMsg Case $GUI_EVENT_CLOSE GUIDelete($hGUI) Return Case Else $s_ambTempC = _doQueryHPNumSens(5, $WMInameSpace, $WMIReadSensor, "100") ; + 100 Select Case $s_ambTempC = "" _GUICtrlFFLabel_SetTextColor($lb1, $normClr) _GUICtrlFFLabel_SetData($lb1, "Unable to query temperature") Case $s_ambTempC < $warnTmp _GUICtrlFFLabel_SetTextColor($lb1, $normClr) _GUICtrlFFLabel_SetData($lb1, "Board temperature: " & $s_ambTempC & "C" & "/" & Round(_CelsiusToFahrenheit($s_ambTempC), 0) & "F") Case Else _GUICtrlFFLabel_SetTextColor($lb1, $warnClr) _GUICtrlFFLabel_SetData($lb1, "Board temperature: " & $s_ambTempC & "C" & "/" & Round(_CelsiusToFahrenheit($s_ambTempC), 0) & "F") _doLog("System ambient temperature has exceeded " & $warnTmp & "C.", "WARN", $MessageSource) _AlarmMonitor($ovrtmpTime, $warnTmp) EndSelect EndSwitch WEnd EndFunc ;==>_TempSenseLoop Func _AlarmMonitor($min, $tmp) Local $Shutdown = 1 Local $s_ambTemp Local $freq = 3500 Local $shrt = 200 Local $long = 500 Local $timer = TimerInit() Local $wait = 1000 * ($min * 60) While TimerDiff($timer) < $wait $s_ambTemp = _doQueryHPNumSens(5, $WMInameSpace, $WMIReadSensor, "100") ; + 100 If $s_ambTemp >= $tmp Then _GUICtrlFFLabel_SetData($lb1, "WARNING! OVERTEMP!") Beep($freq, $shrt) Beep($freq, $shrt) Beep($freq, $shrt) Sleep(250) Beep($freq, $long) Beep($freq, $long) Beep($freq, $long) Sleep(250) Beep($freq, $shrt) Beep($freq, $shrt) Beep($freq, $shrt) _GUICtrlFFLabel_SetData($lb1, "Board temperature: " & $s_ambTemp & "C" & "/" & _CelsiusToFahrenheit($s_ambTemp) & "F") Sleep(2000) ElseIf $s_ambTemp < $tmp Then $Shutdown = 0 ExitLoop EndIf WEnd If $Shutdown = 1 Then _doLog("The system is shutting down due to overtemperature conditions. The temperature at the time of shutdown was " & $s_ambTemp & "C" & "/" & _CelsiusToFahrenheit($s_ambTemp) & "F", "ERRR", $MessageSource) Shutdown(24) ElseIf $Shutdown = 0 Then _doLog("System ambient temperature has been reduced below " & $warnTmp & "C.", "INFO", $MessageSource) EndIf EndFunc ;==>_AlarmMonitor Func _doQueryHPNumSens($min, $class, $name, $sleep) ; $sleep = milliseconds Local $s_tempReading = "" Local $s_HPBiosWMIService = "winmgmts:\\" & @ComputerName & "\Root\HP\InstrumentedBIOS" Local $objHPBiosWMIService = ObjGet($s_HPBiosWMIService) Local $s_biosQuery = $objHPBiosWMIService.ExecQuery("select * from " & $class & " where Name='" & $name & "'") $timer = TimerInit() ; we may need to wait for the HP WMI extensions to enumerate in WMI, in WinPE this takes some minutes. $wait = 1000 * ($min * 60) If Not IsObj($s_biosQuery) Then ; we do need to wait, put up a splash screen $s_WMISplash = 1 SplashTextOn("WMI", "Probing WMI (up to 10 minutes)...", 300, 50, -1, -1, 1) Sleep(1000) While TimerDiff($timer) < $wait Sleep(500) $objHPBiosWMIService = ObjGet($s_HPBiosWMIService) If IsObj($objHPBiosWMIService) Then Sleep(500) $colProdName = $s_biosQuery If IsObj($colProdName) Then For $oItem In $colProdName $s_tempReading = $oItem.CurrentReading Next EndIf EndIf WEnd ElseIf IsObj($s_biosQuery) Then ConsoleWrite($s_biosQuery & @CRLF) For $oItem In $s_biosQuery $s_tempReading = $oItem.CurrentReading Next Else $s_tempReading = 0 EndIf Sleep($sleep) Return $s_tempReading EndFunc ;==>_doQueryHPNumSens Func _doLog($message, $type, $msgsrc) Select Case $type = "INFO" $logTyp = "INFORMATION" Case $type = "WARN" $logTyp = "WARNING" Case $type = "ERRR" $logTyp = "ERROR" EndSelect If FileExists($winLogger) Then RunWait(@ComSpec & " /c " & $winLogger & " /L Application /T " & $logTyp & " /SO " & Chr(34) & $msgsrc & Chr(34) & " /ID 1000 /D " & Chr(34) & $message & Chr(34), "", @SW_HIDE) EndIf EndFunc ;==>_doLog  
       
       
      Temperature.au3
      FFLabels.au3
      HP_Temp.au3
    • sc4ry
      By sc4ry
      Dear all,
      I already have a formula which will select doublicates, but sometimes there might be entries with additional information starting after "-", which I now want to trim away.
      Current formula is:
      Select DISTINCT a.title, a.oid, a.size, a.disk from DB a, DB b where (a.title like b.title and a.oid <> b.oid)" & $ActiveDisks_Filter & " AND (a.comment <> 'Delete' AND b.comment <> 'Delete') Order by a.title ASC, a.size DESC; testwise I want to add a simple solution like left, but afterwards I get an error but I do not know why.
      Select DISTINCT a.title, a.oid, a.size, a.disk from DB a, DB b where (left(a.title,15) like b.title and a.oid <> b.oid) Order by a.title ASC, a.size DESC; Error: near "(": syntax error: Finally I would like to insert intstr to search for "-" but currently I am not able to extend my current code =/
      Thanks a lot for your help.
      EDIT: hey, I managed it, left was not working also it was somehow shown that the tool knowed the comment. I did it now with substr+instr
      (substr(a.title, 1, instr(a.title, ' - '))  
    • ModemJunki
      By ModemJunki
      Hello,
      In Windows 10 PowerShell, one can do this to change the metric for a NIC in Windows 10:
      Get-NetAdapter | Where-Object -FilterScript {$_.InterfaceAlias -Eq "Ethernet 2"} | Set-NetIPInterface -InterfaceMetric 2 I know I can script the above PowerShell line (and it works!), but I wanted to try something I hadn't done before after looking into jguinch's most excellent Network configuration UDF. I wanted to make use of the SetIPConnectionMetric method in the WMI classes. There is an example VBscript here but this is not for Windows 10. Using AutoIT would also give better control over capturing error return codes than with PowerShell.
      But I cannot get my script to work! The return from SetIPConnectionMetric() is 0, which would indicate success. Yet the change does not happen. I also tried WMI methods using .put_ but this fails.
      Anyone more experienced than I have ideas to make this work?
      #RequireAdmin _SetNicInterfaceMetric2("Ethernet 2", "2") Func _SetNicInterfaceMetric2($NIC_NAME, $METRIC) Local $s_setIndx = 0 $objWMIService = ObjGet("winmgmts:{impersonationLevel = impersonate}!\\" & "." & "\root\cimv2") $colNICItems = $objWMIService.ExecQuery("SELECT * FROM Win32_NetworkAdapter WHERE NetConnectionID = '" & $NIC_NAME & "'", "WQL") If IsObj($colNICItems) Then For $objItem In $colNICItems $s_nicIndex = $objItem.Index Next ConsoleWrite("SELECT * FROM Win32_NetworkAdapterConfiguration WHERE Index = '" & $s_nicIndex & "'" & @CRLF) $colNAC = $objWMIService.ExecQuery("SELECT * FROM Win32_NetworkAdapterConfiguration WHERE Index = '" & $s_nicIndex & "'", "WQL") If IsObj($colNAC) Then For $objNetCard In $colNAC If $METRIC <> $objNetCard.IPConnectionMetric Then ConsoleWrite("Metric was set to " & $objNetCard.IPConnectionMetric & ". Setting to " & $METRIC & "." & @CRLF) $s_isSet = $objNetCard.SetIPConnectionMetric($METRIC) ConsoleWrite("SetIPConnectionMetric Result = " & $s_isSet & @CRLF) Else ConsoleWrite("Metric is already set to " & $METRIC & @CRLF) EndIf Next EndIf EndIf EndFunc ;==>_SetNicInterfaceMetric2  
    • GhostLine
      By GhostLine
      Hello guys !
      I'm stuck in something stupid (I guess), but since I've no clue on how to solve it, here I come
      I'm trying to collect the result of the query "select @@hostname" on MSSQL Server, but it doesn't work ... and I'm sure I'm connected to the database (I've successfully seen the database treeview). 

      #Region ;**** Directives created by AutoIt3Wrapper_GUI **** #AutoIt3Wrapper_UseX64=n #EndRegion ;**** Directives created by AutoIt3Wrapper_GUI **** #include<_sql.au3> #include<array.au3> Local $aData,$iRows,$iColumns ;Server ID and credentials for tCards Global $ServerAddressT = "10.200.88.1" Global $ServerUserNameT = "user" Global $ServerPasswordT = "password" Global $DatabaseNameT = "DataBASE" ;Connect to DB _SQL_RegisterErrorHandler();register the error handler to prevent hard crash on COM error $OADODB = _SQL_Startup() If $OADODB = $SQL_ERROR Then MsgBox(0 + 16 + 262144, "Error", _SQL_GetErrMsg()) If _sql_Connect(-1, $ServerAddressT, $DatabaseNameT, $ServerUserNameT, $ServerPasswordT) = $SQL_ERROR Then     MsgBox(0 + 16 + 262144, "Error 1", _SQL_GetErrMsg())     _SQL_Close()     Exit Else     If _SQL_Execute(-1,"select @@SERVERNAME") = $SQL_ERROR then         Msgbox(0 + 16 +262144,"Error",_SQL_GetErrMsg())     Else         _ArrayDisplay(_SQL_GetTable(-1,"select @@SERVERNAME",$aData,$iRows,$iColumns)) ;~         $toto = _SQL_Execute(-1,"select @@SERVERNAME") ;~         _ArrayDisplay($toto)     EndIf EndIf