Enumerating special event logs in C# [duplicate] - c#

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.

Related

Get process names of installed programs

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.

Mimic this very simple powershell command in C#

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}");
}
}
}

Getting "File already in Use by other process" when using File.Move, why/how can I fix this?

I want to move several files that names are saved in an ObservableCollection<String> _collection with this method:
string firstFolderThatContainsEveryFile = "...\Folder\Files";
string secondFolderArchiv = "...\Folder\Files\Archiv";
foreach (var item in _collection)
{
string firstFolder = System.IO.Path.Combine(firstFolderThatContainsEveryFile, item);
string secondFolder = System.IO.Path.Combine(secondFolderArchiv, item);
File.Move(firstFolder, secondFolder);
}
This works at the first time, but if i load new files into firstFolderThatContainsEveryFile and try to use my move method i get an exception:
File already in Use by other process
This are the steps:
I open the programm -> use the move method -> success -> close the programm -> fill the folder with new files -> open the programm -> use the move method -> exception!
How can i get the processname or processID to close the process before i use my move method, or is there even a better way to get around this?
To figure out which process are using your file, with the solution proposed by this, you can use a tool Handle of Microsoft and this code C# to invoke the tool.
public void ViewProcess(string filePath)
{
Process tool = new Process();
tool.StartInfo.FileName = "handle.exe";
tool.StartInfo.Arguments = filePath + " /accepteula";
tool.StartInfo.UseShellExecute = false;
tool.StartInfo.RedirectStandardOutput = true;
tool.Start();
tool.WaitForExit();
string outputTool = tool.StandardOutput.ReadToEnd();
string matchPattern = #"(?<=\s+pid:\s+)\b(\d+)\b(?=\s+)";
foreach (Match match in Regex.Matches(outputTool, matchPattern))
{
try{
Console.WriteLine(match.Value); // this is the process ID using the file
}
catch(Exception ex)
{
}
}
}
If the file is using by the others program, you should figure why they use it, if by your program, so recheck your code to understand why.

Make application close when .exe is executed again

I'm currently working on a simple converter tool and was wondering if it's possible to make the application close if I run the .exe again. Some kind of "if two instances run -> close both".
I need this function because I run the application via a shortcut-button inside a third party program. So I would like if my converter app closes once I press this shortcut-button again.
I know it sounds counter intuitive running the exe again to close, but i have to have my app work the same way as the integrated tools in the third party program, and this involves opening and closing tools by pressing their respective toggle-buttons. I can't add a plug-in running inside the third party program, but i CAN add a shortcut button next to the integrated tools. It's a work around, but it will at least act like a toggle button.
You could do something like this:
Process currentProcess = Process.GetCurrentProcess();
bool suocide = false;
foreach (Process process in Process.GetProcessesByName(currentProcess.ProcessName))
{
if (process.MainModule.FileName == currentProcess.MainModule.FileName && process.Id != currentProcess.Id)
{
process.CloseMainWindow();
process.Close();
//process.Kill(); or you can do kill instead
suocide = true;
}
}
if (suocide)
currentProcess.Kill(); // you probably don't care about new process as it is just for closing purpose but if you do then do a proper application exit
You can put it inside your window constructor.
Step 1 Identify a 2nd instance:
I'd recommend the MUTEX answer in this question:
How can I prevent launching my app multiple times?
Step 2 Get that first instance closed
Although the MUTEX answer identifies a second instance, it gives no way to find it and tell it to close.
Solution: Listen with a named pipe in the app (first instance the ClosEE):
//using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
public static class SomeClass
{
public static void SomeMethod()
{
Threading.Thread t = new Threading.Thread(() =>
{
try {
while (true) {
dynamic server = new NamedPipeServerStream("Closer", PipeDirection.InOut, -1);
server.WaitForConnection();
if (!server.IsConnected)
return;
dynamic reader = new IO.StreamReader(server);
dynamic casetxt = reader.ReadToEnd();
server.Close();
RootForm.Invoke(() =>
{
if (casetxt == "End") {
System.Environment.Exit(0);
}
});
}
} catch (Exception ex) {
// try/catch required in all child threads as error silently ends app.
// log it...
}
});
t.IsBackground = true;
t.Name = "EnderListener";
t.Start();
}
}
//=======================================================
//Service provided by Telerik (www.telerik.com)
Then when you detect a second instance via the Mutex, send this message from the 2nd instance the "Closer":
dynamic serverloopcount = 1;
dynamic iteration = 1;
dynamic GotServerCount = false;
do {
NamedPipeClientStream client = new NamedPipeClientStream("Closer");
client.Connect();
if (!GotServerCount) {
GotServerCount = true;
serverloopcount = client.NumberOfServerInstances;
}
dynamic reader = new IO.StreamReader(client);
dynamic writer = new IO.StreamWriter(client);
writer.WriteLine("End");
writer.Flush();
writer.Close();
client.Close();
iteration += 1;
} while (iteration <= serverloopcount);
Good luck.

how to find out the path of the program using c#

I found a snippet of code explaining how to use System.Diagnostics.Process.Start to run an external program in C#. The snippet shows running cmd.exe, which is in the path.
Let's assume that there is some external program (Beyond Compare for example). I don't know if it is installed on the PC. How can I check if this program is installed using C#? If the program is installed, I would like to find the path so that I can launch it.
I found this question, which directed me to this this article.
I've modified the source for readability, and to solve your problem specifically (note that I've guessed the description and executable name of Beyond Compare.)
You can call it like this, from your main:
string path = FindAppPath("Beyond Compare");
if (path == null)
{
Console.WriteLine("Failed to find program path.");
return;
}
path += "BeyondCompare.exe";
if (File.Exists(path))
{
Process beyondCompare = new Process()
{
StartInfo = new ProcessStartInfo()
{
FileName = path + "BeyondCompare.exe",
Arguments = string.Empty // You may need to specify args.
}
};
beyondCompare.Start();
}
The source for FindAppPath follows:
static string FindAppPath(string appName)
{
// If you don't use contracts, check this and throw ArgumentException
Contract.Requires(!string.IsNullOrEmpty(appName));
const string keyPath =
#"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(keyPath))
{
var installed =
(from skName in key.GetSubKeyNames()
let subkey = key.OpenSubKey(skName)
select new
{
name = subkey.GetValue("DisplayName") as string,
path = subkey.GetValue("InstallLocation") as string
}).ToList();
var desired = installed.FindAll(
program => program.name != null &&
program.name.Contains(appName) &&
!String.IsNullOrEmpty(program.path));
return (desired.Count > 0) ? desired[0].path : null;
}
}
Keep in mind that this method returns the first matching path, so don't feed it an appName argument that's too generic (eg. "Microsoft") or you probably won't get what you're looking for.
Well, if you're trying to see if a program exists where you're looking for it (like BeyondCompare.exe), you can just use a call to:
System.IO.File.Exists("path_to_program.exe");
If it returns true, then you know your program exists and you can run it with your process runner code. If it returns false, then you know it's not there and you shouldn't launch your process.
If I'm misunderstanding your question, please let me know and I'll update my answer accordingly.
Thanks. Hope this helps!
Simple logic to do for this.
string filepath = "c:\windows\test.exe";
bool bOk = false;
try
{
bOk = System.IO.File.Exists(filepath);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
if (!bOk)
{
Console.WriteLine("Error: Invalid Path");
}
else
{
Process p = new Process();
p.StartInfo.FileName = filepath;
p.StartInfo.Arguments = "/c dir *.cs";
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.Start();
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
Console.WriteLine("Output:");
Console.WriteLine(output);
}
Are you sure you don't need to make that check. Simply start the program without path (only the filename) and set ProcessStartInfo.UseShellExecute = true.
Windows will look for the app in its list of installed app. If it doesn't find it, Process.Start()will fail. The interesting thing is that you never had to care about where the app is stored.

Categories

Resources