AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

COM help - AddEnum() Method in AHK? (Trying to find % CPU)

 
Reply to topic    AutoHotkey Community Forum Index -> Ask for Help
View previous topic :: View next topic  
Author Message
OceanMachine



Joined: 15 Oct 2007
Posts: 780
Location: England

PostPosted: Fri Mar 19, 2010 6:41 pm    Post subject: COM help - AddEnum() Method in AHK? (Trying to find % CPU) Reply with quote

Hi All,

I have used IE COM in the past to manupulate web pages, and I have found that great, once I got my head around it. I am trying to learn other things that can be done with COM, and am trying to convert some VBscript I have found to work with AHK.

The VBScript gets the % Processor time of all processes and displays them.

Below is the VBScript and what I have tried so far:

VBScript:
Code:
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")

Set objRefresher = CreateObject("WbemScripting.SWbemRefresher")
Set colItems = objRefresher.AddEnum _
    (objWMIService, "Win32_PerfFormattedData_PerfProc_Process").objectSet

For i = 1 to 5
    objRefresher.Refresh
    For Each objItem in colItems
        Wscript.Echo objItem.Name & " -- " & objItem.PercentProcessorTime
    Next
    Wscript.Echo
    Wscript.Sleep 5000
Next


And here is the "approximate AHK equivalent" that I am trying:
Code:
#NoEnv

COM_Init()

Namespace := "root\cimv2"
Class := "Win32_PerfFormattedData_PerfProc_Process"

; objWMIService := COM_GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\" . Namespace)
objWMIService := COM_GetObject("winmgmts:\\.\" . Namespace)
objRefresher := COM_CreateObject("WbemScripting.SWbemRefresher")

colItems := COM_Invoke(COM_Invoke(objRefresher,"AddEnum",objWMIService,Class),"objectSet")

Loop, 5
{
   COM_Invoke(objRefresher,"Refresh")

   Loop, % COM_Invoke(colItems,"Length")
   {
      pItm := COM_Invoke(colItems,"Item",A_Index-1)
      MsgBox, % COM_Invoke(pItm,"Name") . " -- " . COM_Invoke(pItm,"PercentProcecssorTime")
      COM_Release(pItm), pItm := 0
   }

   Sleep, 5000
}

COM_Release(objWMIService), objWMIService := 0
COM_Release(objRefresher), objRefresher := 0
COM_Release(colItems), colItems := 0

COM_Term()
ExitApp


Now I know I am probably doing something completely wrong (or not realising I have to use another COM function to achieve this), but I am getting a COM error as follows:

Quote:
---------------------------
COM Error Notification
---------------------------
Function Name: "AddEnum"
ERROR: Type mismatch.
(0x80020005)
ERROR2: Member not found.
(0x80020003)
Will Continue?
---------------------------
Yes No
---------------------------


Can anyone help with where I am going wrong please?

TIA
Back to top
View user's profile Send private message
OceanMachine



Joined: 15 Oct 2007
Posts: 780
Location: England

PostPosted: Sat Mar 20, 2010 4:57 pm    Post subject: Reply with quote

I managed to get something to work using the below code (based on some code I found by Sean), but the values always come back as 0 for this class (as explained here) unless you use a SWbemRefresher object... which is what I am trying to do above without success
Embarassed

I would like to get this working with a refresher, if anyone has any pointers?

Thanks in advance

Code:
#NoEnv
COM_Init()

NameSpace := "root\cimv2"
Class := "Win32_PerfFormattedData_PerfProc_Process"
Query := "SELECT PercentProcessorTime FROM " . Class

objWMIService :=   COM_GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\" . NameSpace)

pset :=   COM_Invoke(objWMIService, "ExecQuery", Query)
penm :=   COM_Invoke(pset, "_NewEnum")

Loop
{
   If   COM_Enumerate(penm, pobj) = 0
   {
      MsgBox,   % "Name: " COM_Invoke(pobj, "Name") " % Time: " COM_Invoke(pobj, "PercentProcessorTime")
      COM_Release(pobj), pobj := 0
   }
   Else
      Break
}

COM_Release(penm)
COM_Release(pset)
COM_Release(objWMIService)

COM_Term()
ExitApp
Back to top
View user's profile Send private message
Sean



Joined: 12 Feb 2007
Posts: 2462

PostPosted: Sun Mar 21, 2010 5:52 am    Post subject: Reply with quote

OceanMachine wrote:
---------------------------
COM Error Notification
---------------------------
Function Name: "AddEnum"
ERROR: Type mismatch.
(0x80020005)
ERROR2: Member not found.
(0x80020003)
Will Continue?
---------------------------
Yes No
---------------------------

With COM.ahk, you should write it as:
Code:
COM_Invoke(objRefresher, "AddEnum", "+" objWMIService, Class)
Back to top
View user's profile Send private message
OceanMachine



Joined: 15 Oct 2007
Posts: 780
Location: England

PostPosted: Sun Mar 21, 2010 12:16 pm    Post subject: Reply with quote

Excellent, thanks Sean, that got rid of the error with AddEnum Smile
What does the "+" actually do? Sorry for my ignorance, never seen that in the COM syntax before (I am a newb, as you can see).

Now I am having an issue accessing items within the objSet.

I can get a count of the number of processes (that matches how many processes I have running on my PC) with COM_Invoke(colItems,"Count") but I can't seem to access the items with COM_Invoke(colItems, "Item", A_Index-1 ). I tried several variations of this like COM_Invoke(colItems, "Item[" A_Index-1 "]") but obviously this is not the way to access the items... Embarassed

The seems to be "Member Not Found" for member "Item" in "SWbemObjectSet", but looking on msdn here, the Item method is supposed to exist... - what am I doing wrong?

I get the following error:
Quote:
---------------------------
COM Error Notification
---------------------------
Function Name: "Item"
ERROR: (0x80041001)
PROG: SWbemObjectSet
DESC: Generic failure
HELP: ,0
ERROR2: Member not found.
(0x80020003)
Will Continue?
---------------------------
Yes No
---------------------------


Here is the code I am using now:
Code:
#NoEnv

COM_Init()

Namespace := "root\cimv2"
Class := "Win32_PerfFormattedData_PerfProc_Process"

; objWMIService := COM_GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\" . Namespace)
objWMIService := COM_GetObject("winmgmts:\\.\" . Namespace)
objRefresher := COM_CreateObject("WbemScripting.SWbemRefresher")

colItems := COM_Invoke(COM_Invoke(objRefresher, "AddEnum", "+" objWMIService, Class), "objectSet")

Loop, 5
{
   COM_Invoke(objRefresher, "Refresh")

   MsgBox, % "Count: " COM_Invoke(colItems, "Count") ; <-- this works

   Loop, % COM_Invoke(colItems, "Count")
   {
      pItm := COM_Invoke(colItems, "Item", A_Index-1 ) ; <-- this isn't working
      sMsgTxt .= COM_Invoke(pItm, "Name") . " -- " . COM_Invoke(pItm, "PercentProcecssorTime") . "`n"
      COM_Release(pItm), pItm := 0
   }

   MsgBox, %sMsgTxt%
}

COM_Release(objWMIService), objWMIService := 0
COM_Release(objRefresher), objRefresher := 0
COM_Release(colItems), colItems := 0

COM_Term()
ExitApp
Back to top
View user's profile Send private message
Sean



Joined: 12 Feb 2007
Posts: 2462

PostPosted: Sun Mar 21, 2010 3:48 pm    Post subject: Reply with quote

OceanMachine wrote:
What does the "+" actually do? Sorry for my ignorance, never seen that in the COM syntax before (I am a newb, as you can see).
It's explained in the post.
http://www.autohotkey.com/forum/viewtopic.php?t=22923

BTW, you're confused between Item of SWbemRefresher and Item of SWbemObjectSet which should be passed a string. Anyway, I don't recommend using Item which will be slow. Use instead:
Code:
   oEnum := COM_Invoke(colItems, "_NewEnum")
   While COM_Enumerate(oEnum, pItm) = 0
   ...
Back to top
View user's profile Send private message
OceanMachine



Joined: 15 Oct 2007
Posts: 780
Location: England

PostPosted: Sun Mar 21, 2010 6:41 pm    Post subject: Reply with quote

Sean wrote:
It's explained in the post.
So it is, sorry for that, I didn't think to use that.

Sean wrote:
BTW, you're confused between Item of SWbemRefresher and Item of SWbemObjectSet which should be passed a string.
Ah yes, sorry I meant to link here instead, which says there should be an Item method, no?

Anyway, if you advise it will be slower, I will use the "_NewEnum" way you suggested instead.

I used this in the below code (which is working BTW), but it seems that I have to refresh twice to get the values (probably for the same reason as I linked in my second post?) and also without a sleep of about 500ms-1 second between the refreshes, strange values come back...

Anyway if anyone is interested, here is the code I ended up with. Thanks Sean for all your help (once again) Smile I will probably get round to making a more useful version of this and posting it, but this will be OK for now.

Please bear in mind that the second MsgBox showing both 'used' and 'idle' percentages is just a demo... you will probably find that they don't add up to 100% due to them being from different refreshes. They may not even add up to 100% when the parameter "all" is passed (maybe due to rounding issues) but the Idle and Total values should be OK, meaning you can use them to get values that add up to 100%.
Code:
#NoEnv
OnExit, ExitSub
Hotkey, Esc, ExitSub

COM_Init()

MsgBox, % "##### ALL PROCESSES #####:`n`n" . GetCPUTime("all")
MsgBox, % "Called Separately (these values may not add up to 100%!):`n`nCPU Usage = " . GetCPUTime("used") . "%`nIdle: " . GetCPUTime("idle") . "%"

SetTimer, ShowCPUTip, 1000 ; will show a tip with the correct values for Used, Idle and _Total (which will add up to 100% as they are from the same refresh)
GoSub, ShowCPUTip
Return

ShowCPUTip:
   CPUVals := GetCPUTime("all")
   Loop, Parse, CPUVals , `n, `r
   {
      Loop, Parse, A_LoopField, `:
      {
         If ( A_Index = 1 )
            ProcName := A_LoopField
         Else If ( A_Index = 2 )
            ProcVal := A_LoopField
      }

      If ( ProcName = "_Total" or ProcName = "Idle" )
         %ProcName% := ProcVal
   }

   Used := _Total - Idle ; should be what is used
   ToolTip, % "CPU Used: " Used "%`nIdle: " Idle "%`n_Total: " _Total "%"
Return

Esc::
ExitSub:
   COM_Term()
   ExitApp
Return

;####################################################################
;####################################################################

GetCPUTime(ReqType="used")
{
; Returns various values about CPU usage.
; default is to return the percent used = ( _Total - Idle )
; possible values are "used", "idle" or "all"
; the first 2 will return a single integer value
; and "all" will return a list of all
; enumerated processed found in the form:
; Name1:Value1`nName2:Value2

   Namespace := "root\cimv2"
   Class := "Win32_PerfFormattedData_PerfProc_Process"
   
   objWMIService := COM_GetObject("winmgmts:\\.\" . Namespace)
   objRefresher := COM_CreateObject("WbemScripting.SWbemRefresher")
   
   colItems := COM_Invoke(COM_Invoke(objRefresher, "AddEnum", "+" objWMIService, Class), "objectSet")

   ; the need to refresh twice is explained here:
   COM_Invoke(objRefresher, "Refresh") ; have to refresh first, only get values on the second refresh.
   Sleep, 500 ; for some reason, values come back strangely if there is no sleep here, I found 500-1000ms seems to be best...
   
   sMsgTxt := "", IdleTime := "", TotalTime := ""

   COM_Invoke(objRefresher, "Refresh")
   oEnum := COM_Invoke(colItems, "_NewEnum")

   While( COM_Enumerate(oEnum, pItm) = 0 )
   {
      ItemName := COM_Invoke(pItm, "Name")
      ItemVal := COM_Invoke(pItm, "PercentProcessorTime")

      If ( ItemName = "Idle" )
         IdleTime := ItemVal
      Else If ( ItemName = "_Total" )
         TotalTime := ItemVal

      AllTime .= ItemName . ":" . ItemVal . "`n"
   }
   COM_Release(oEnum), oEnum := 0

   If ( ReqType = "used" )
      ReturnVal := TotalTime - IdleTime ; used cpu time
   Else If ( ReqType = "idle" )
      ReturnVal := IdleTime
   Else If ( ReqType = "all" )
      ReturnVal := AllTime

   COM_Release(objWMIService), objWMIService := 0
   COM_Release(objRefresher), objRefresher := 0
   COM_Release(colItems), colItems := 0

   Return IdleTime != "" ? ReturnVal : -1 ; -1 means there was a problem
}
Back to top
View user's profile Send private message
Display posts from previous:   
Reply to topic    AutoHotkey Community Forum Index -> Ask for Help All times are GMT
Page 1 of 1

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group