I've been trying to create a taskbar tray icon that displays the CPU usage (pulled from wbemtest if possible) when hovered over or clicked on using C#. I used the PercentProcessorTime Name from the ManagementClass Win32_PerfFormattedData_Counters_ProcessorInformation to pull the data. I haven't been able to find what data type the Name is even meant to return. Is there somewhere else I may be able to get the data from?
public void CPUactivitythread()
{
//Create a management object to open wbemtest
ManagementClass CPUdataclass = new ManagementClass("Win32_PerfFormattedData_Counters_ProcessorInformation");
try
{
//While Loop to pull consistent data from the CPU
while (true)
{
//Connect to the CPU Performance Instances in wbemtest
ManagementObjectCollection CPUobjectCollection = CPUdataclass.GetInstances();
foreach (ManagementObject obj in CPUobjectCollection) {
//Check that the "PercentProcessorTime" instance is there
if (obj["Name"].ToString() == "PercentProcessorTime")
{
if (Convert.ToUInt64(obj["PercentProcessorTime"]) > 0)
{
cPUUsageToolStripMenuItem.Text = (obj["PercentProcessorTime"]).ToString();
CPUoutputLabel.Text = (obj["PercentProcessorTime"]).ToString();
}
else
{
}
}
}
Thread.Sleep(1000);
}
}
The objects in the Collection correspond to the Task Manager CPU Information, one for each CPU, one for Total named "_Total". The "PercentProcessorTime" is a property of each performance object. Since you are getting the Formatted data, it has already been calculated ("cooked") according to its performance formula and can be used directly. LINQPad is a really useful tool for exploring objects if you don't like to read documentation :)
Try this:
ManagementClass CPUdataclass = new ManagementClass("Win32_PerfFormattedData_Counters_ProcessorInformation");
try {
//While Loop to pull consistent data from the CPU
while (true) {
//Connect to the CPU Performance Instances in wbemtest
ManagementObjectCollection CPUobjectCollection = CPUdataclass.GetInstances();
foreach (ManagementObject obj in CPUobjectCollection) {
//Check that the "PercentProcessorTime" instance is there
if (obj["Name"].ToString() == "_Total") {
var PPT = Convert.ToUInt64(obj.GetPropertyValue("PercentProcessorTime"));
if (PPT > 0) {
cPUUsageToolStripMenuItem.Text = PPT.ToString();
CPUoutputLabel.Text = PPT.ToString();
}
}
else {
}
}
}
Related
How can one get the corresponding process name of the installed programs in Windows (10)? For now, I'm using this
string uninstallKey = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
using (RegistryKey rk = Registry.LocalMachine.OpenSubKey(uninstallKey))
{
foreach (string skName in rk.GetSubKeyNames())
{
using (RegistryKey sk = rk.OpenSubKey(skName))
{
//returns installed programs
}
}
}
to return the installed software. Despite not every installed program being shown, how can I get the name of the process, like it would be shown in Task Manager, that the program would start if it was started?
I want to make an application blacklist. If an application gets started it compares its process with the blacklist. If the process matches with an entry in the list, the process gets killed.
Use static method GetProcesses of Process class to create component for each running process on the local computer.
You can get their names like this:
var processNames = Process.GetProcesses().Select(x => x.ProcessName).ToList();
More about Process class here:
https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process?view=net-6.0
You should consider to use the Windows integrated feature to block applications via the registry. You can create such entries programmatically.
However, you can implement your own, but you must know that you can't prevent applications from starting using your approach. You can only kill it after it was started and after it has allocated resources.
Create your blacklist first: collect all installed application paths and let the user pick the application to blacklist (see CreateInstalledApplicationIndex method).
Use WMI to observe any process starts by registering a corresponding event handler.
In the event handler retrieve the started Process and compare its filename to your blacklisted filenames to identify and handle a forbidden process.
private List<FileInfo> InstallationInfos { get; } = new List<FileInfo>();
private List<FileInfo> BlacklistedExecutables { get; } = new List<FileInfo>();
public void ApplyBlacklist()
{
CreateInstalledApplicationIndex();
WatchProcessStarts();
}
private void CreateInstalledApplicationIndex()
{
string uninstallKey = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
using RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(uninstallKey);
foreach (string subKeyName in registryKey.GetSubKeyNames())
{
using RegistryKey subKey = registryKey.OpenSubKey(subKeyName);
var installationPath = subKey.GetValue("InstallLocation") as string;
if (string.IsNullOrWhiteSpace(installationPath))
{
continue;
}
IEnumerable<FileInfo> fileInfos = Enumerable.Empty<FileInfo>();
try
{
var installationDirectoryInfo = new DirectoryInfo(installationPath);
fileInfos = installationDirectoryInfo.EnumerateFiles("*.exe", new EnumerationOptions());
}
catch (IOException)
{
continue;
}
foreach (FileInfo fileInfo in fileInfos)
{
this.InstallationInfos.Add(fileInfo);
// For demo, all executables are blacklisted.
// TODO::Let user fill Blacklisted collection.
this.BlacklistedExecutables.Add(fileInfo);
}
}
}
private void WatchProcessStarts()
{
WqlEventQuery query = new WqlEventQuery("SELECT * FROM Win32_ProcessStartTrace");
ManagementEventWatcher watcher = new ManagementEventWatcher(query);
watcher.EventArrived += OnProcessStarted;
// Start listening for process start events
watcher.Start();
// Stop listening for process start events
//watcher.Stop();
}
private void OnProcessStarted(object sender, EventArrivedEventArgs e)
{
uint startedProcessId = (uint)e.NewEvent["ProcessID"];
// Note: Convert.ToInt32 will throw an OverflowException
// in case uint does not fit into an int.
// You must decide whether to handle this particular exception or to let it crash your application.
// Since it is very very unlikely that a machine runs Int32.MaxValue processes,
// I recommend not to handle this exception.
Process startedProcess = Process.GetProcessById(Convert.ToInt32(startedProcessId));
bool isProcessBlacklisted = this.BlacklistedExecutables
.Select(fileInfo => fileInfo.FullName)
.Contains(startedProcess.MainModule.FileName);
// TODO::Handle blacklisted process e.g., by killing it
if (isProcessBlacklisted)
{
startedProcess.Kill(entireProcessTree: true);
}
}
It is possible that you have to run your application as administrator in order to observe process starts and to kill them. In this case ensure to prompt the user to elevate your application's rights by restarting it with administrator permissions.
I got a solution which looks like this:
First I get all installed programs based on this
public static void LoadInstalledPrograms()
{
var FOLDERID_AppsFolder = new Guid("{1e87508d-89c2-42f0-8a7e-645a0f50ca58}");
ShellObject appsFolder = (ShellObject)KnownFolderHelper.FromKnownFolderId(FOLDERID_AppsFolder);
foreach (var app in (IKnownFolder)appsFolder)
{
//regular installed programs
if (app.Properties.System.Link.TargetParsingPath.Value != null)
{
AddToInstalledProgramsList(app.Name, app.Properties.System.Link.TargetParsingPath.Value, "reg");
}
//Windows apps/Microsoft store apps
/*else
{
AddToInstalledProgramsList(app.Name, app.Properties.GetProperty("System.AppUserModel.PackageInstallPath").ValueAsObject.ToString(), "win");
}*/
}
}
and then write them to a dictionary which is observed by a BackgroundWorker who kills every process from the list
static Dictionary<String, String> programs = new Dictionary<String, String>();
public static void AddToInstalledProgramsList(string programName, string programPath, string programType)
{
string processName = "";
if (programType == "reg")
{
programPath = programPath.Replace("/", "\\");
processName = programPath.Split("\\").Last();
if (!programs.ContainsKey(programName))
{
programs.Add(programName, processName);
}
else
{
AddDuplicateEntry(programName, processName, 1);
}
}
else if (programType == "win")
{
//...
}
Debug.WriteLine(programName + ": " + processName);
}
If I stumble across problems with this approach I will update this thread.
Trying to mimic the command Get-CimInstance CIM_ManagedSystemElement in C#
string NamespacePath = "\\\\.\\Root\\CIMv2";
string ClassName = "CIM_ManagedSystemElement";
//Create ManagementClass
ManagementClass oClass = new ManagementClass(NamespacePath + ":" + ClassName);
//Get all instances of the class and enumerate them
foreach (ManagementObject oObject in oClass.GetInstances())
{
//access a property of the Management object
Console.WriteLine("Caption : {0}", oObject["Caption"]);
}
Sadly, that didnt work as expected, would like to get some help
Thanks
You do this like this (you have to add System.Management namespace)
Because CIM_ManagedSystemElement is at the default WMI namespace( which is Root\CIMV2) you don't have to specify it at ManagementObjectSearcher.
Also, be sure that you have the minimum supported client- Windows Vista
string query = #"SELECT * FROM CIM_ManagedSystemElement";
var moSearch = new ManagementObjectSearcher(query);
var moCollection = moSearch.Get();
foreach (ManagementObject mo in moCollection)
{
Console.WriteLine("Caption = " + mo["Caption"]);
}
Furthermore i suggest you use an ORM to remove boilerplate code like ORMi or Kexla
I also couldn't get your code to work, but in the meantime if you need a workaround you can use the PowerShell API from within C# using this simple program I wrote based on some online documentation. It will give you an output you're looking for. You should have access to all the properties in OutputCollection_DataAdded so if you need more than Caption you can grab it here. Also, at the end of the execution there is a foreach() loop that will contain the entire output collection if you need to do something with that. The execution is extremely slow so I had to make it async to work.
static void Main(string[] args)
{
using (PowerShell ps = PowerShell.Create())
{
ps.AddCommand("Get-CimInstance");
ps.AddParameter("-ClassName", "CIM_ManagedSystemElement");
var outputCollection = new PSDataCollection<PSObject>();
outputCollection.DataAdded += OutputCollection_DataAdded;
// invoke execution on the pipeline (collecting output)
var async = ps.BeginInvoke<PSObject, PSObject>(null, outputCollection);
// do something else until execution has completed.
// this could be sleep/wait, or perhaps some other work
while (async.IsCompleted == false)
{
Console.WriteLine("Waiting for pipeline to finish...");
Thread.Sleep(1000);
// might want to place a timeout here...
}
Console.WriteLine("Execution has stopped. The pipeline state: " + ps.InvocationStateInfo.State);
// loop through each output object item
foreach (PSObject outputItem in ps.EndInvoke(async))
{
// if null object was dumped to the pipeline during the script then a null
// object may be present here. check for null to prevent potential NRE.
if (outputItem != null)
{
//TODO: do something with the output item
// outputItem.BaseOBject
}
}
Console.Read();
}
}
private static void OutputCollection_DataAdded(object sender, DataAddedEventArgs e)
{
if (sender is PSDataCollection<PSObject>)
{
var output = (PSDataCollection<PSObject>)sender;
// Handle the output item here
var caption = output.Last().Properties["Caption"];
if (caption != null)
{
Console.WriteLine($"Caption: {caption.Value}");
}
}
}
I am trying to retrieve the total physical memory available value of my machine through Win32_OperatingSystem class in C#. Below is how I am retrieving this value.
ManagementClass mc = new ManagementClass("Win32_ComputerSystem");
ManagementObjectCollection moc = mc.GetInstances();
if (moc.Count != 0)
{
foreach (ManagementObject MO in mc.GetInstances())
{
try
{
computerSystemDetails["TotalPhysicalMemory"] = (MO["TotalPhysicalMemory"] == null) ? new string[] { } : new string[] { MO["TotalPhysicalMemory"].ToString() };
}
catch (Exception ex)
{
logger.Error("GetSystemDetails", "Error occurred when retrieving the computer system information", ex);
}
}
}
return computerSystemDetails;
The value I'm getting from the above is 16243 MB. But when I use DirectX Diagnostic tool I am getting the value 16384MB. What is the explanation for this difference? Please advice.
The documentation for that property explains why:
Be aware that, under some circumstances, this property may not return an accurate value for the physical memory. For example, it is not accurate if the BIOS is using some of the physical memory. For an accurate value, use the Capacity property in Win32_PhysicalMemory instead.
I am using NAudio for a screen recording software I am designing and I need to know if it's possible to not only control the specific application's volume but also display a VU Meter for the application's sound.
I've Googled all over the place and it seems I can only get a VU Meter for the devices currently on my computer and set the volume for those devices.
Even though I am using NAudio, I am open to other solutions.
I asked the question in more detail after this question. I have since found the answer so I will leave the answer here for those who stumble upon it. Trying to use NAudio & CSCore has gotten me quite familiar with so please ask if you need further assistance.
This block of code uses CSCore and is a modified and commented version of the answer found here:Getting individual windows application current volume output level as visualized in audio Mixer
class PeakClass
{
static int CurrentProcessID = 0000;
private static void Main(string[] args)
{
//Basically gets your default audio device and session attached to it
using (var sessionManager = GetDefaultAudioSessionManager2(DataFlow.Render))
{
using (var sessionEnumerator = sessionManager.GetSessionEnumerator())
{
//This will go through a list of all processes uses the device
//the code got two line above.
foreach (var session in sessionEnumerator)
{
//This block of code will get the peak value(value needed for VU Meter)
//For whatever process you need it for (I believe you can also check by name
//but I found that less reliable)
using (var session2 = session.QueryInterface<AudioSessionControl2>())
{
if(session2.ProcessID == CurrentProcessID)
{
using (var audioMeterInformation = session.QueryInterface<AudioMeterInformation>())
{
Console.WriteLine(audioMeterInformation.GetPeakValue());
}
}
}
//Uncomment this block of code if you need the peak values
//of all the processes
//
//using (var audioMeterInformation = session.QueryInterface<AudioMeterInformation>())
//{
// Console.WriteLine(audioMeterInformation.GetPeakValue());
//}
}
}
}
}
private static AudioSessionManager2 GetDefaultAudioSessionManager2(DataFlow dataFlow)
{
using (var enumerator = new MMDeviceEnumerator())
{
using (var device = enumerator.GetDefaultAudioEndpoint(dataFlow, Role.Multimedia))
{
Console.WriteLine("DefaultDevice: " + device.FriendlyName);
var sessionManager = AudioSessionManager2.FromMMDevice(device);
return sessionManager;
}
}
}
}
The following code block will allow you to change the volume of the device using NAudio
MMDevice VUDevice;
public void SetVolume(float vol)
{
if(vol > 0)
{
VUDevice.AudioEndpointVolume.Mute = false;
VUDevice.AudioEndpointVolume.MasterVolumeLevelScalar = vol;
}
else
{
VUDevice.AudioEndpointVolume.Mute = true;
}
Console.WriteLine(vol);
}
I have code from two different libraries only to answer the question I posted directly which was how to both set the volume and get VU Meter values (peak values). CSCore and NAudio are very similar so most of the code here is interchangeable.
I have an application where I need to print a ticket. Each ticket must be unique. The application is windows forms and written entirely in c#. For our application we're using Samsung ML- 2525 laser monochromatic printers.
The flow is basically the following, the operator picks a product/ticket (which is unique) and then it presses a button that does 2 things:
Connects to a database and updates the product as used
Prints the ticket (this is done using System.Drawing and GDI+)
For some reason, every once in a while, the image that needs to be printed is not sent to the printer. It's a rare case, but it happens.
I tried to connect to the printer using Win32_Printer ( http://msdn.microsoft.com/en-us/library/Aa394363 ) but I can't get to get the current printer's state (online, offline, low toner, paper jam, etc). I can only check if the printer exists and that the paper size is installed correctly. I tried code similar to the following but it didn't work
private string MonitorPrintJobWmi()
{
var jobMessage = String.Empty;
var scope = new ManagementScope(ManagementPath.DefaultPath);
scope.Connect();
var selectQuery = new SelectQuery { QueryString = #"select * from Win32_PrintJob" };
var objSearcher = new ManagementObjectSearcher(scope, selectQuery);
var objCollection = objSearcher.Get();
foreach (var job in objCollection)
{
if (job != null)
{
jobMessage += String.Format("{0} \r\n", job["Name"].ToString());
jobMessage += String.Format("{0} \r\n", job["JobId"].ToString());
_jobId = Convert.ToInt32(job["JobId"]);
jobMessage += String.Format("{0} \r\n", job["JobStatus"].ToString());
jobMessage += String.Format("{0} \r\n", job["Status"].ToString());
}
}
return jobMessage;
}
I tried to get an API for the printer but I couldn't get a hold of it. By the way, the printer's software do indicate different errors in the windows toolbar.
My question is if anyone can lead me in the right direction as to how to connect to a printer and check if printing was successful.
Also, it would be helpful if someone know of some other specific printer in which I may accomplish this ie, changing hardware.
Thanks,
To get a list of print queues on the local machine, try PrintServer's GetPrintQueues method.
Once you have an instance of the PrintQueue object associated with the relevant printer, you can use it to access the printer's status (IsOffline, IsPaperOut, etc.). Also, you can use it to get a list of the jobs in the given queue (GetPrintJobInfoCollection) which then will allow you to get job-specific status information (IsInError, IsCompleted, IsBlocked, etc.).
Hope this helps!
After try to print your PrintDocument (System.Drawing.Printing), try to check status of printjobs.
First step: Initialize your printDocument.
Second step: Get your printer Name From System.Drawing.Printing.PrinterSettings.InstalledPrinters.Cast<string>();
And copy it into your printerDocument.PrinterSettings.PrinterName
Third step: Try to print and dispose.
printerDocument.Print();
printerDocument.Dispose();
Last step: Run the check in a Task (do NOT block UI thread).
Task.Run(()=>{
if (!IsPrinterOk(printerDocument.PrinterSettings.PrinterName,checkTimeInMillisec))
{
// failed printing, do something...
}
});
Here is the implementation:
private bool IsPrinterOk(string name,int checkTimeInMillisec)
{
System.Collections.IList value = null;
do
{
//checkTimeInMillisec should be between 2000 and 5000
System.Threading.Thread.Sleep(checkTimeInMillisec);
// or use Timer with Threading.Monitor instead of thread sleep
using (System.Management.ManagementObjectSearcher searcher = new System.Management.ManagementObjectSearcher("SELECT * FROM Win32_PrintJob WHERE Name like '%" + name + "%'"))
{
value = null;
if (searcher.Get().Count == 0) // Number of pending document.
return true; // return because we haven't got any pending document.
else
{
foreach (System.Management.ManagementObject printer in searcher.Get())
{
value = printer.Properties.Cast<System.Management.PropertyData>().Where(p => p.Name.Equals("Status")).Select(p => p.Value).ToList();
break;
}
}
}
}
while (value.Contains("Printing") || value.Contains("UNKNOWN") || value.Contains("OK"));
return value.Contains("Error") ? false : true;
}
Good luck.