VBA code to monitor windows process displayed in task manager.
I am trying to get the RAM usage and CPU usage of an application and add those values in excel.
i tried using WMI class as below but howerver i am getting process ID.I am unable to retrieve RAM usage and CPU usage.
Can anyone help me in this?
Sub test2()
Set objWMIService = GetObject("winmgmts:\\.\root\CIMV2")
Set colItems = objWMIService.ExecQuery("SELECT * FROM Win32_Process where caption='excel.exe'", , 48)
For Each objItem In colItems
Sheet1.Range("d2").Value = objItem.ProcessId
Next
Set colItems = objWMIService.ExecQuery( _"SELECT * FROM Win32_PerfFormattedData_PerfProc_Process where IDProcess=" & Sheet1.Range("d2").Value, , 48)
For Each objItem In colItems
Sheet1.Range("A1").Value = "PercentProcessorTime: " & objItem.PercentProcessorTime
Next
End Sub
If you choose to go the WMI route, then you might be after the WorkingSetSize property of the Win32_Process class:
WorkingSetSize Data type: uint64 Access type: Read-only Qualifiers:
DisplayName ("Working Set Size"), Units ("bytes") Amount of memory in
bytes that a process needs to execute efficiently—for an operating
system that uses page-based memory management. If the system does not
have enough memory (less than the working set size), thrashing occurs.
If the size of the working set is not known, use NULL or 0 (zero). If
working set data is provided, you can monitor the information to
understand the changing memory requirements of a process.
And the PercentProcessorTime of the Win32_PerfFormattedData_PerfProc_Process class:
PercentProcessorTime Data type: uint64 Access type: Read-only
Qualifiers: CookingType ("PERF_100NSEC_TIMER") , Counter
("PercentProcessorTime") , PerfTimeStamp ("TimeStamp_Sys100NS") ,
PerfTimeFreq ("Frequency_Sys100NS") Percentage of time that the
processor is executing a non-idle thread. This property was designed
as a primary indicator of processor activity. It is calculated by
measuring the time that the processor spends executing the thread of
the idle process in each sample interval, and subtracting that value
from 100%. (Each processor has an idle thread which consumes cycles
when no other threads are ready to run.) It can be viewed as the
percentage of the sample interval spent doing useful work. This
property displays the average percentage of busy time observed during
the sample interval. It is calculated by monitoring the time the
service was inactive, and then subtracting that value from 100%.
But you might also want to look at the SWbemRefresher object (https://msdn.microsoft.com/en-us/library/aa393838(v=vs.85).aspx).
Skeleton VBA code for you to work with:
Dim srvEx As SWbemServicesEx
Dim xlProcSet As SWbemObjectSet
Dim xlPerfSet As SWbemObjectSet
Dim objEx As SWbemObjectEx
Set srvEx = GetObject("winmgmts:\\.\root\CIMV2")
Set xlProcSet = srvEx.ExecQuery("SELECT * FROM Win32_Process WHERE name = 'EXCEL.EXE'")
Set xlPerfSet = srvEx.ExecQuery("SELECT * FROM Win32_PerfFormattedData_PerfProc_Process WHERE name = 'EXCEL'")
For Each objEx In xlProcSet
Debug.Print objEx.Name & " RAM: " & objEx.WorkingSetSize / 1024 & "kb"
Next
For Each objEx In xlPerfSet
Debug.Print objEx.Name & " CPU: " & objEx.PercentProcessorTime & "%"
Next
These functions allow to monitor RAM and CPU usage as requested. Notice that the application name in one is case sensitive and includes the extension, in the other is case insensitive and does not include the extension.
Debug.Print ProcessCpuUsage("Excel")
Debug.Print ProcessMemoryUsage("EXCEL.EXE") / 1024
Function ProcessCpuUsage(ProcessName As String) As Double
Dim Wmi As Object, Processes As Variant, Process As Object
Set Wmi = GetObject("winmgmts:")
Set Processes = Wmi.ExecQuery("SELECT PercentProcessorTime FROM win32_PerfFormattedData_PerfProc_Process WHERE Name='" & ProcessName & "'")
For Each Process In Processes
Exit For
Next Process
ProcessCpuUsage = Process.PercentProcessorTime
End Function
Function ProcessMemoryUsage(ProcessName As String) As Double
Dim Wmi As Object, Processes As Variant, Process As Object
Set Wmi = GetObject("winmgmts:")
Set Processes = Wmi.ExecQuery("SELECT WorkingSetSize FROM Win32_Process WHERE Name='" & ProcessName & "'")
For Each Process In Processes
Exit For
Next Process
ProcessMemoryUsage = Process.WorkingSetSize
End Function
Related
My VB.NET winforms app runs a timer which creates a background worker to update the objects in an ObjectListView.
In the timer loop, a number of 'device' objects are added to an observable collection (in the backgroundworker_progresschanged event) and (in the backgroundworker_complete event), I use an OLV.SetObjects(allDevices, true) to populate them.
This all works flawlessly. However, the currently selected items in the OLV are lost during the OLV.setobjects so I need to restore them.
To do this, (in the backgroundworker_complete event), I want to access the selecteditems property of the OLV but I keep getting a "Cross-thread operation not valid: Control 'DeviceListView1' accessed from a thread other than the thread it was created on." All attempts at trying to read the selected listviewitems (either by OLV.selecteditems or a loop reading them from the OLV) fail with the cross-thread exception.
I may misunderstand but I thought I could access GUI elements on the backgroundworker_progresschanged and backgroundworker_complete events?
Here's the relevant code:
The PopulateDevices sub is called when the timer is started and will not run again until a specific time has passed. It runs the RunWorkerAsync of the Worker.
Public Sub PopulateDevices()
' Debug
_UpdateCount += 1
' Pause the Update Timer
UpdateTimer.Stop()
' Get the Starting Time of this Update
StartTime = DateTime.Now
' Stop updating the DeviceListView1 ObjectListView
ControlHelper.ControlInvoke(DeviceListView1, Sub() DeviceListView1.BeginUpdate())
' Clear Existing Devices from the List
AllDevices = New TrulyObservableCollection(Of DeviceItem)
' Get the selected devices
'_SelectedDevices = GetSetSelectedDevices(DeviceListView1)
' Prep the BackgroundWorker
PopulateDevicesWorker = New BackgroundWorker
PopulateDevicesWorker.WorkerReportsProgress = True
' Add the Event Handlers
AddHandler PopulateDevicesWorker.DoWork, AddressOf PopulateDevicesWorkerDoWork
AddHandler PopulateDevicesWorker.ProgressChanged, AddressOf PopulateDevicesWorkerProgressChanged
AddHandler PopulateDevicesWorker.RunWorkerCompleted, AddressOf PopulateDevicesWorkerCompleted
' Start the BackgroundWorker
If Not PopulateDevicesWorker.IsBusy Then
PopulateDevicesWorker.RunWorkerAsync()
End If
End Sub
The worker will read a list of devices from a SQLite DB and (in the progresschanged event) populate an observable collection (AllDevices):
Private Sub PopulateDevicesWorkerDoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs)
' We only continue if the \Clients\_Cache File exists and can be read
If Not File.Exists(CacheFilePath) Then
Exit Sub
End If
' Create a new SQLite Connection & Connect to database
Dim DBC As SQLiteDatabase = OpenDB(CacheFilePath)
If Not IsNothing(DBC) Then
' Count the Rows in the \Clients\_Cache file
Dim RowCount As Integer = CountTableRows(DBC, "_Cache")
' Set the SQL Query
SqlQuery = "SELECT * FROM _Cache WHERE Archived = #Archived"
' Create the SQLite Command
Using SQLitecmd As SQLiteCommand = New SQLiteCommand(SqlQuery, DBC.Connection)
SQLitecmd.Parameters.AddWithValue(String.Empty & "Archived", IIf(fMain.ButtonItem_VIEWARCHIVE.Checked, "True", "False"))
Using SQLiteReader = SQLitecmd.ExecuteReader()
Dim Counter As Integer = 0
' Read All Properties into the Array
While SQLiteReader.Read()
Using DeviceItem As New DeviceItem
With DeviceItem
' Get the Device Info here
End With
' Report progress at regular intervals
PopulateDevicesWorker.ReportProgress(CInt(100 * Counter / RowCount), DeviceItem)
' Increment the Counter (for Progress)
Counter += 1
End Using
End While
End Using
End Using
End If
CloseDB(DBC)
End Sub
Here is the WorkerProgressChanged event. It adds the current device (from the worker) into the observable collection (AlLDevices)
Private Sub PopulateDevicesWorkerProgressChanged(sender As Object, e As ProgressChangedEventArgs)
' Update Status
LabelItem_STATUS.Text = "Working.. (" & e.ProgressPercentage & "%)"
' Add the Device to Collection
AllDevices.Add(TryCast(e.UserState, DeviceItem))
End Sub
The WorkerCompleted event will set the objects in AllDevices to the OLV (DeviceListView1)
Private Sub PopulateDevicesWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) ' Handles PopulateDevicesWorker.RunWorkerCompleted
' This is producing Cross-Thread error
If Not IsNothing(_SelectedDevices) Then
For Each item As ListViewItem In _SelectedDevices
Debug.Print(item.Text)
Next
End If
' Populate the ObjectListView
ControlHelper.ControlInvoke(DeviceListView1, Sub() DeviceListView1.SetObjects(AllDevices, True))
' Re-enable Form Updates
ControlHelper.ControlInvoke(DeviceListView1, Sub() DeviceListView1.EndUpdate())
' If the refresh rate isn't already set, set it to the time taken to complete the Update PLUS the Seconds specified in the SETTINGS.INI File
Dim difference As TimeSpan = DateTime.Now.Subtract(StartTime)
If UpdateTimeInSeconds = -1 Then
UpdateTimer.Interval = (RefreshRate + difference.TotalSeconds) * 1000
End If
' Restart the Update Timer
UpdateTimer.Start()
End Sub
I was under the impression, that I can update the GUI (get the OLV selecteditems, etc.) from the WorkerProgressChanged and WorkerCompleted backgroundworker events but I get the darn cross-thread error.
I'm also having to INVOKE the BEGIN\END UPDATE as calling them directly produces error.
I have read that the olv.setobjects in ObjectListView 2.91 (the version I am using) should persist the selections but I haven't seen this at all.
Please! What am I missing? Its probably something daft or is there another way of doing this?
If you are not using a Forms.Timer (but Timers.Timer or Threading.Timer) for your UpdateTimer, anything called from the timer "tick" event will run on a different thread.
Thus, PopulateDevices would also be called from a non GUI thread and the BackgroundWorker will run on that thread as well.
First I want to check via VBA, before I do some transactions in SAP GUI, if a connection is already open. I am not able to login a second time, so I need to stay in the same connection.
Secondly I want to open another session. The second problem has been solved, if I assume SAP GUI is already open. But I don't know it for sure. So I need to find a way to access the current SAPGUI and Application and Connection, if they exist. If not, the standard code of If Not IsObject(SAPGUI) Then… is fine. But how do I define these variables correctly to check, if they are „filled“ Objects or not?
Thanks for help!
Based on a script by S. Schnell you can use the follwing function to find a free session
Function findGuiSession(ByVal sapSID As String, Optional tCode As String) As SAPFEWSELib.GuiSession
' this will find a free session using the systemnam resp. SID
' and optional one can also supply a transaction to
Dim CollCon As SAPFEWSELib.GuiComponentCollection
Dim CollSes As SAPFEWSELib.GuiComponentCollection
Dim guiCon As SAPFEWSELib.GuiConnection
Dim guiSes As SAPFEWSELib.GuiSession
Dim guiSesInfo As SAPFEWSELib.GuiSessionInfo
Dim i As Long, j As Long
Dim SID As String, transaction As String
'On Error GoTo EH
Dim guiApplication As SAPFEWSELib.guiApplication
Set guiApplication = getGuiApplication
If guiApplication Is Nothing Then
Exit Function
End If
Set CollCon = guiApplication.Connections
If Not IsObject(CollCon) Then
Exit Function
End If
' Loop through all existing connections
For i = 0 To CollCon.Count() - 1
Set guiCon = guiApplication.Children(CLng(i))
If Not IsObject(guiCon) Then
Exit Function
End If
Set CollSes = guiCon.Sessions
If Not IsObject(CollSes) Then
Exit Function
End If
' Now loop through all existing sessions
For j = 0 To CollSes.Count() - 1
Set guiSes = guiCon.Children(CLng(j))
If Not IsObject(guiSes) Then
Exit Function
End If
If guiSes.Busy = vbFalse Then
Set guiSesInfo = guiSes.Info
If guiSesInfo.user = "" Or guiSesInfo.user = "SAPSYS" Then
' Logon Screen - cannot be used
Else
If IsObject(guiSesInfo) Then
SID = guiSesInfo.SystemName()
transaction = guiSesInfo.transaction()
' Take the first one - In case one could also use the transactionaction addtionally
If Len(tCode) = 0 Then
If SID = sapSID Then
Set findGuiSession = guiSes
'FindSession = True
Exit Function
End If
Else
If SID = sapSID And transaction = tCode Then
Set findGuiSession = guiSes
'FindSession = True
Exit Function
End If
End If
End If
End If
End If
Next
Next
Exit Function
'EH:
End Function
Function getGuiApplication() As SAPFEWSELib.guiApplication
On Error GoTo EH
Set getGuiApplication = GetObject("SAPGUI").GetScriptingEngine
EH:
End Function
For this code to run you need to add a reference to the SAP library, described here
The following piece of code uses the above function to connect to a system with the name P11, starts the transaction MB52 and downloads the result in a Excel file
Option Explicit
Sub getMB52_data()
Dim guiSes As SAPFEWSELib.GuiSession
Set guiSes = getGuiSession("P11")
If Not guiSes Is Nothing Then
With guiSes
.StartTransaction "MB52"
.FindById("wnd[0]/usr/ctxtMATNR-LOW").Text = "<MATNR_LOW<" ' replace with a material nr
.FindById("wnd[0]/usr/ctxtMATNR-HIGH").Text = "<MATNR_HIGH<" ' replace with a material nr
.FindById("wnd[0]/usr/ctxtWERKS-LOW").Text = "<WERKS>" ' replace wiht a plant
.FindById("wnd[0]/tbar[1]/btn[8]").Press
.FindById("wnd[0]/tbar[0]/okcd").Text = "&XXL"
.FindById("wnd[0]/tbar[0]/btn[0]").Press
.FindById("wnd[1]/tbar[0]/btn[0]").Press
.FindById("wnd[1]/usr/ctxtDY_PATH").Text = "<xlPath>" ' Pathname
.FindById("wnd[1]/usr/ctxtDY_FILENAME").Text = "<xlFile>" ' filename
.FindById("wnd[1]/tbar[0]/btn[11]").Press
End With
Else
MsgBox "No free SAP Session", vbOKOnly + vbInformation, "SAP Verbindung"
End If
End Sub
Function getGuiSession(sapSID As String, Optional tCode As String) As SAPFEWSELib.GuiSession
Dim guiApp As SAPFEWSELib.guiApplication
Set guiApp = getGuiApplication
If Not guiApp Is Nothing Then
Set getGuiSession = findGuiSession(sapSID, tCode)
End If
End Function
Additonal remarks: (hopefully answering some questions in the comments)
Gui Connection: A GuiConnection represents the connection between SAP GUI and an application server. Connections can be opened from SAP Logon or from GuiApplication’s openConnection and openConnectionByConnectionString
methods
So, in other words a gui connection is a kind of login to an SAP system. And usually you have more than one SAP system in your organization. If you follow the guidelines you have a DEV, QUAL and PROD for a given system environment. Each of this system is identifid by a SID
What is SID?
SID is a unique identification code for every R/3 installation (SAP system) consisting of a database server & several application servers. SID stands for SAP System Identification. SAPSID — a three-character code such as C11, PRD, E56, etc.)
An SID is unique within the organization. Usually SAP licence only allows a user to login to a productive system only once, i.e. you cannot use the same user on different computers and you cannot even login to a SAP system with the same user on the same computer twice.
Having said that: One might be tempted to use guiApplication.Children(0) instead of looping through all connections as done in findGuiSession. And this will work as long as you can make sure that you are only logged on to one SAP system and it is the right one. But in my experience this is often not the case.
The parameter SID in findGuiSession tells the function which system to look for. As written above SID is unique and therefore identfies the system you want to use.
Using tCode in findGuiSession is optional and just forces the user to have a session in place with a given tCode already started. I use this very seldom.
OpenConnection: If you open a connection with the function OpenConnection("<SYSTEM>") you can, of course, use the returned object in order to work with it. But this only does a logon to the system if you have a kind of single sign on in your organization in place. Otherwise you have to provide logon credentials. I do not use this because I do not want to take care of this. And it also can happen that a password change is requested during the logon and I sure do not want to script this.
Example code
Rem Open a connection in synchronous mode
Set Connection = Application.OpenConnection( "U9C [PUBLIC]", True)
Set Session = Connection.Children(0)
Rem Do something: Either fill out the login screen
Rem or in case of Single-Sign-On start a transaction.
Session.SendCommand( "/nbibs")
Why GetHDD Result "_____WD-WX4645DRTEE" , Add 5 Space . Where is the code problem?
I added _____ instead of 5 Space.
Function GetHDD() As String
Dim Wmi As Object, Disks As Object, Disk As Object
Set Wmi = GetObject("winmgmts:{impersonationLevel" & "=impersonate}!root\cimv2")
Set Disks = Wmi.ExecQuery("Select * from Win32_DiskDrive")
For Each Disk In Disks
If Len(Disk.SerialNumber) > Len(GetSerialNumber) Then GetSerialNumber = Disk.SerialNumber
Next
Set Disk = Nothing
Set Disks = Nothing
Set Wmi = Nothing
GetHDD = GetSerialNumber
Debug.Print GetHDD
End Function
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
my code is
Public Function notcpuid()
Dim m As String
Const test = "BFEBF55555555"
m = GetCPUID
If m <> test Then
notcpuid = True
Else
notcpuid = False
End If
End Function
Function GetCPUID() As String
Dim cimv2, PInfo, PItem ' no idea what to declare these as
Dim PubStrComputer As String
PubStrComputer = "."
Set cimv2 = GetObject("winmgmts:\\" & PubStrComputer & "\root\cimv2")
Set PInfo = cimv2.ExecQuery("Select * From Win32_Processor")
For Each PItem In PInfo
Next PItem
GetCPUID = PItem.ProcessorID
End Function
Private Sub Workbook_Open()
If notcpuid Then
ThisWorkbook.Close
End If
End Sub
Why does it not work?
Why does it not work?
Why does it not work?
Why does it not work?
Why does it not work?
Why does it not work?
Why does it not work?
i want excel file protection by vba cpuid.
Declare the items as Object.
You also have your For Each loop out of order.
Function GetCPUID() As String
Dim cimv2 As Object
Dim PInfo As Object
Dim PItem As Object
Dim PubStrComputer As String
PubStrComputer = "."
Set cimv2 = GetObject("winmgmts:\\" & PubStrComputer & "\root\cimv2")
Set PInfo = cimv2.ExecQuery("Select * From Win32_Processor")
For Each PItem In PInfo
GetCPUID = PItem.ProcessorID
Next PItem
End Function
BTW, you do know that identical CPUs will return the same ID? Not the same as a serial number. For example:
Item Value
------ -----
Processor Name Intel(R) Core(TM) i7-7700 CPU # 3.60GHz
Code Name Kaby Lake
Info Intel64 Family 6 Model 158 Stepping 9
Maker GenuineIntel
ID BFEBFBFF000906E9
Max CPU Speed 3.6 GHz
Physical CPUs 1
Physical Cores 4
Logical Cores 8
Address Width 64
HyperThreading Enabled
VM Firmware Disabled
Socket U3E1
Update: Using Powershell I looked at the properties for the CPU_Object. It includes a Serial number but when checked on my PC I got this:
PS> $CPU_Object.serialnumber
To Be Filled By O.E.M.
HTH
Situation - I have a macro where I need to send keystrokes to two Firefox windows in order. Unfortunately both windows have the same title. To handle this I have activated the window, sent my keystrokes, then used F6 to load the URL of the second window and then send the keystrokes then use F6 to return it to the original page.
The issue is that loading the webpages is unreliable. Page load speeds vary so much that using a wait command is not consistent or reliable to ensure the keystroke makes it to the second window.
Question -
I've read a scattering of posts that mentioned that app activate will work with Process ID's. Since each window would have its own PID that would be an ideal way to handle 2 windows with the same title. I am unable to find information specifically how to determine the PID of each window with a given name.
You can use something like the following. You'll have to tinker about with the different info available in the Win32_Process class to figure out which window is which. It's also important to keep in mind that one window could mean many processes.
Public Sub getPID()
Dim objServices As Object, objProcessSet As Object, Process As Object
Set objServices = GetObject("winmgmts:\\.\root\CIMV2")
Set objProcessSet = objServices.ExecQuery("SELECT ProcessID, name FROM Win32_Process WHERE name = ""firefox.exe""", , 48)
'you may find more than one processid depending on your search/program
For Each Process In objProcessSet
Debug.Print Process.ProcessID, Process.Name
Next
Set objProcessSet = Nothing
End Sub
Since you'll probably want to explore the options with WMI a bit, you may want to add a Tools>>References to the Microsoft WMI library so you don't have to deal with Dim bla as Object. Then you can add breakpoints and see what's going on in the Locals pane.
After adding the reference:
Public Sub getDetailsByAppName()
Dim objProcessSet As WbemScripting.SWbemObjectSet
Dim objProcess As WbemScripting.SWbemObject
Dim objServices As WbemScripting.SWbemServices
Dim objLocator As WbemScripting.SWbemLocator
'set up wmi for local computer querying
Set objLocator = New WbemScripting.SWbemLocator
Set objServices = objLocator.ConnectServer(".") 'local
'Get all the gory details for a name of a running application
Set objProcessSet = objServices.ExecQuery("SELECT * FROM Win32_Process WHERE name = ""firefox.exe""", , 48)
RecordCount = 1
'Loop through each process returned
For Each objProcess In objProcessSet
'Loop through each property/field
For Each Field In objProcess.Properties_
Debug.Print RecordCount, Field.Name, Field.Value
Next
RecordCount = RecordCount + 1
Next
Set objProcessSet = Nothing
Set objServices = Nothing
Set objLocator = Nothing
End Sub
That will print out every property of every process found for the name 'firefox.exe'.