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}");
}
}
}
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.
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 {
}
}
}
I am trying to write a web interface that will show the current VMs on a remote Hyper-V host.
So far, I have this
protected void getVMS(object sender, GridViewCommandEventArgs e)
{
//command to run
string cmdToRun = "get-vm -computername fsyovs02";
var shell = PowerShell.Create();
shell.Commands.AddScript(cmdToRun);
var results = shell.Invoke();
if (results.Count > 0)
{
var builder = new StringBuilder();
foreach (PSObject vm in results)
{
builder.Append(vm.BaseObject.ToString() + "\r\n");
}
ResultBox.Text = Server.HtmlEncode(builder.ToString());
}
}
This is returning something, but not what I want. For each VM, it is returning the line
Microsoft.HyperV.PowerShell.VirtualMachine
What I want is it to display exactly how it does via powershell.
Can anyone help me please - as I am going out of my mind!
Many Thanks
Mark
Sorry had to edit my answer as you cannot access those types from C#. You should then use the Members collection on PSObject to access particular properties of the VirtualMachine. Please check this approach in your case:
foreach (PSObject vm in results)
{
builder.Append(vm.Members["Name"].Value + "\r\n");
}
This question already has answers here:
How do I create a hierarchy of lognames in the Windows event system?
(2 answers)
Closed 9 years ago.
I am trying to get a list of the "special" event logs in C#, like the "Microsoft\Windows\Audio\CaptureMonitor" log and all the others like it. They don't seem to be returned when I use System.Diagnostics.EventLog.GetEventLogs(). Is there a special way to get a list of all the special event logs?
I'll be honest and admit I don't know how these views tie into EventLogs and EventSources but take a look at the registry key:
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WINEVT\Channels
And see if that starts you down the right path. Also checkout:
How do I create a hierarchy of lognames in the Windows event system?
You may use the WevtUtil.exe tool:
To access event log information from the command line, use the
WevtUtil.exe tool. This tool is located in the %SystemRoot%\System32
directory. For WevtUtil.exe tool help, use the wevtutil /? command.
I guess you might use a System.Diagnostigs.Process, launch the tool, then capture and parse the console output.
using System;
using System.Diagnostics;
using System.Linq;
class Program
{
static void Main(string[] args)
{
var output = "";
var p = new Process();
var psi = new ProcessStartInfo("wevtutil.exe", "el");
psi.CreateNoWindow = true;
psi.RedirectStandardOutput = true;
psi.UseShellExecute = false;
p.StartInfo = psi;
p.Start();
using (var processOutput = p.StandardOutput)
{
output = processOutput.ReadToEnd();
}
p.WaitForExit();
var eventLogs = output
.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries)
.ToList();
foreach (var item in eventLogs)
{
Console.WriteLine(item);
}
}
}
For reading the event log, you could use the same approach (for example, call wevtutil qe Microsoft-Windows-Audio/CaptureMonitor /f:text) or the System.Diagnostics.Eventing.Reader Namespace.Try the following:
using System;
using System.Diagnostics.Eventing.Reader;
class Program
{
static void Main(string[] args)
{
EventLogQuery subscriptionQuery =
new EventLogQuery("Microsoft-Windows-Audio/CaptureMonitor",
PathType.LogName, "*");
using (EventLogReader logReader =
new EventLogReader(subscriptionQuery))
{
DisplayEventAndLogInformation(logReader);
}
}
private static void DisplayEventAndLogInformation(EventLogReader logReader)
{
for (EventRecord eventInstance = logReader.ReadEvent();
null != eventInstance; eventInstance = logReader.ReadEvent())
{
Console.WriteLine("--------------------------------------");
Console.WriteLine("Event ID: {0}", eventInstance.Id);
Console.WriteLine("Publisher: {0}", eventInstance.ProviderName);
try
{
Console.WriteLine("Description: {0}",
eventInstance.FormatDescription());
}
catch (EventLogException)
{
// The event description contains parameters,
// and no parameters were passed to the
// FormatDescription method, so an exception is thrown.
}
// Cast the EventRecord object as an EventLogRecord
// object to access the EventLogRecord class properties
EventLogRecord logRecord = (EventLogRecord)eventInstance;
Console.WriteLine("Container Event Log: {0}",
logRecord.ContainerLog);
}
}
}
You may have to tweak a little bit the EventLogQuery constructor's query parameter (*) according to your needs. The topic How to: Query for Events shows an implementation example.
I've looked and couldn't find what should be a simple question:
How can a Windows Service determine the ServiceName for which it was started?
I know the installation can hack at the registry and add a command line argument, but logically that seems like it should be unnecessary, hence this question.
I'm hoping to run multiple copies of a single binary more cleanly than the registry hack.
Edit:
This is written in C#. My apps Main() entry point does different things, depending on
command line arguments:
Install or Uninstall the service. The command line can provide a non-default
ServiceName and can change the number of worker threads.
Run as a command-line executable (for debugging),
Run as a "Windows Service". Here, it creates an instance of my ServiceBase-derived
class, then calls System.ServiceProcess.ServiceBase.Run(instance);
Currently, the installation step appends the service name and thread count to the ImagePath in the registry so the app can determine it's ServiceName.
From: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=387024
Here is a WMI solution. Overriding the ServiceBase.ServiceMainCallback() might also work, but this seems to work for me...
protected String GetServiceName()
{
// Calling System.ServiceProcess.ServiceBase::ServiceNamea allways returns
// an empty string,
// see https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=387024
// So we have to do some more work to find out our service name, this only works if
// the process contains a single service, if there are more than one services hosted
// in the process you will have to do something else
int processId = System.Diagnostics.Process.GetCurrentProcess().Id;
String query = "SELECT * FROM Win32_Service where ProcessId = " + processId;
System.Management.ManagementObjectSearcher searcher =
new System.Management.ManagementObjectSearcher(query);
foreach (System.Management.ManagementObject queryObj in searcher.Get()) {
return queryObj["Name"].ToString();
}
throw new Exception("Can not get the ServiceName");
}
ServiceBase.ServiceName property gives the compile-time name of service. If you specify a different name when installing the service, then ServiceName attribute will not give correct name. So, I had to use below code to obtain the service name of my service.
It's an alternative (without using LINQ) to NVRAM's method:
/**
* Returns the service name of currently running windows service.
*/
static String getServiceName()
{
ServiceController[] scServices;
scServices = ServiceController.GetServices();
// Display the list of services currently running on this computer.
int my_pid = System.Diagnostics.Process.GetCurrentProcess().Id;
foreach (ServiceController scTemp in scServices)
{
// Write the service name and the display name
// for each running service.
// Query WMI for additional information about this service.
// Display the start name (LocalSytem, etc) and the service
// description.
ManagementObject wmiService;
wmiService = new ManagementObject("Win32_Service.Name='" + scTemp.ServiceName + "'");
wmiService.Get();
int id = Convert.ToInt32(wmiService["ProcessId"]);
if (id == my_pid)
{
return scTemp.ServiceName;
#if IS_CONSOLE
Console.WriteLine();
Console.WriteLine(" Service : {0}", scTemp.ServiceName);
Console.WriteLine(" Display name: {0}", scTemp.DisplayName);
Console.WriteLine(" Start name: {0}", wmiService["StartName"]);
Console.WriteLine(" Description: {0}", wmiService["Description"]);
Console.WriteLine(" Found.......");
#endif
}
}
return "NotFound";
}
I was incorrectly trying to obtain the name of windows service as first line in main() without first calling ServiceBase.Run(). We must register our executable as service using ServiceBase.Run() before obtaining its name.
Ref.: http://msdn.microsoft.com/en-us/library/hde9d63a.aspx#Y320
Short version with Linq
int processId = System.Diagnostics.Process.GetCurrentProcess().Id;
ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_Service where ProcessId = " + processId);
ManagementObjectCollection collection = searcher.Get();
var serviceName = (string)collection.Cast<ManagementBaseObject>().First()["Name"];
By searching for a better solution i tried this:
string serviceName = "myDynamicServiceName";
string serviceBin = "path\\to\\Service.exe";
string configFile = "path\\to\\myConfig.xml";
string credentials = "obj= .\\mytestuser password= test";
string scCommand = string.Format( "sc create {0} start= auto binPath= \"\\\"{1}\\\" -ini={2} -sn={3}\" type= own{4}", serviceName, serviceBin, configFile , serviceName ,credentials );
I passed the servicename and an configuration file to the binpath.
The service was installed by using the SC.exe (i don't use the installutil!)
On the service you can get the Commandline-Arguments
protected override void OnStart(string[] args){
string binpath = new System.IO.FileInfo(System.Reflection.Assembly.GetAssembly(this.GetType()).Location).DirectoryName + "\\";
System.IO.StreamWriter sw = new System.IO.StreamWriter( binpath + "test.log");
sw.WriteLine( binpath );
string[] cmdArgs = System.Environment.GetCommandLineArgs();
foreach (string item in cmdArgs) {
sw.WriteLine(item);
}
sw.Flush();
sw.Dispose();
sw = null;
}
I had a chicken-and-egg problem where I needed to know the service location before completing Service.Run() (Service could be part of a client or server installation, installer named them appropriately, and I needed to detect which it was on startup)
I relied on the registry to get me the name.
public String IdentifySelfFromRegistry()
{
String executionPath = Assembly.GetEntryAssembly().Location;
Microsoft.Win32.RegistryKey services = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(
#"SYSTEM\CurrentControlSet\services");
if (services != null)
{
foreach(String subkey in services.GetSubKeyNames())
{
if (executionPath.Equals(ServicePathFromServiceKey(services.OpenSubKey(subkey))))
return subkey;
}
}
return String.Empty;
}
protected static String ServicePathFromServiceKey(Microsoft.Win32.RegistryKey serviceKey)
{
if (serviceKey != null)
{
String exec = serviceKey.GetValue(ServicePathEntry) as String;
if (exec != null)
return exec.Trim('\"');
}
return String.Empty;
}
The ServiceMain() entry point that every service executable must implement receives the ServiceName as its first input argument.
If you are writing your service using .NET, the ServiceMain() entry point is implemented by .NET for you. The ServiceName is assigned when the service is installed using the ServiceProcess.ServiceBase.ServiceName property. If you are trying to customize a .NET service to support dynamic ServiceName values, I have no clue how to access the actual ServiceName at runtime.
public static bool IsServiceInstalled(string serviceName)
{
// get list of Windows services
ServiceController[] services = ServiceController.GetServices();
// try to find service name
foreach (ServiceController service in services)
{
if (service.ServiceName == serviceName)
return true;
}
return false;
}
What's wrong with this.ServiceName, if you're inside the service.cs?
i.e.:
protected override void OnStart(string[] args)
{
Logger.Info($"{this.ServiceName} started on {Environment.MachineName}...");
}