I need to monitor some system events like shutdown, logoff, lock etc.
Now I have 2 questions:
How can I do something before the system get shutdowned, logged off or something like that?
When the process get killed via taskmanager do I have any chance todo something before it get closed, without a second process? Probably not or? (not so important)
What I have so far:
public MainWindow()
{
InitializeComponent();
SystemEvents.SessionSwitch += SystemEvents_SessionSwitch;
SystemEvents.SessionEnded += SystemEvents_SessionEnded;
SystemEvents.EventsThreadShutdown += SystemEvents_ThreadShutdown;
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
}
private void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
{
if(e.Mode == PowerModes.Suspend)
{
Thread.Sleep(5000);
Log("PowerMode Suspend: " + DateTime.Now.ToString("HH:mm:ss") + "\r\n");
}
else if(e.Mode == PowerModes.Resume)
{
Thread.Sleep(5000);
Log("PowerMode Resume: " + DateTime.Now.ToString("HH:mm:ss") + "\r\n");
}
}
private void SystemEvents_ThreadShutdown(object sender, EventArgs e)
{
Thread.Sleep(5000);
Log("EventThread Shutdown: " + DateTime.Now.ToString("HH:mm:ss") + "\r\n");
}
private void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e)
{
if (e.Reason == SessionSwitchReason.SessionLock)
{
Thread.Sleep(5000);
Log("Locked the machine: " + DateTime.Now.ToString("HH:mm:ss") + "\r\n");
}
else if (e.Reason == SessionSwitchReason.SessionUnlock)
{
Thread.Sleep(5000);
Log("Unlocked the machine: " + DateTime.Now.ToString("HH:mm:ss") + "\r\n");
}
else if (e.Reason == SessionSwitchReason.SessionLogoff)
{
Thread.Sleep(5000);
Log("Logged of the machine: " + DateTime.Now.ToString("HH:mm:ss") + "\r\n");
}
}
private void SystemEvents_SessionEnded(object sender, SessionEndedEventArgs e)
{
if (e.Reason == SessionEndReasons.SystemShutdown)
{
Thread.Sleep(5000);
Log("Shutdown of the machine: " + DateTime.Now.ToString("HH:mm:ss") + "\r\n");
}
else if (e.Reason == SessionEndReasons.Logoff)
{
Thread.Sleep(5000);
Log("Logoff of the machine: " + DateTime.Now.ToString("HH:mm:ss") + "\r\n");
}
}
The sleep just should emulate some actions todo before this events.
Only the Windows Lock & Window Closed does work, the other ones not, probably because the program is already closed.
Any idea how I could fix that?
Related
I tried to display the string into the richtextbox, but always got the same or duplicate string after the second time it was run like in the example of displaying a second string.
Example of displaying the first string:
[21:55:07] - [#] Starting scanning, please wait. . .
[21:55:09] - [√] Finished.
Example of displaying a second string:
[21:55:07] - [#] Starting scanning, please wait. . .
[21:55:09] - [√] Finished.
[21:55:07] - [#] Starting scanning, please wait. . .
[21:55:09] - [√] Finished.
private void Display(string text, bool newLine = true)
{
DisplayTextBox.AppendText(text);
if (newLine)
{
DisplayTextBox.AppendText(Environment.NewLine);
DisplayTextBox.ScrollToCaret();
}
}
private void startScan()
{
Display("...........................................................................................", true);
Display("[ " + DateTime.Now.ToLongTimeString() + " ] - [ # ] Starting scanning, please wait . . . . . .", true);
Display("...........................................................................................", true);
Display(Environment.NewLine, true);
Thread.Sleep(100);
this.DirButton.Enabled = false;
this.ScanButton.Enabled = false;
scanned = 0;
skipped = 0;
worker.WorkerReportsProgress = true;
worker.DoWork += Scan;
worker.ProgressChanged += this.ProgressChanged;
worker.RunWorkerCompleted += this.RunWorkerCompleted;
worker.RunWorkerAsync();
}
private static void Scan(object sender, DoWorkEventArgs e)
{
startScan(e.Argument.ToString());
}
private void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
Display(e.UserState.ToString(), true);
}
private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
this.Display("[ " + DateTime.Now.ToLongTimeString() + " ] - [ ! ] Exception: " + e.Error.ToString(), true);
}
if (!e.Cancelled)
{
string text = "[ " + DateTime.Now.ToLongTimeString() + " ] - Scanned [ " + scanned + " ] files ";
string text2 = "[ " + DateTime.Now.ToLongTimeString() + " ] - Skipped [ " + skipped + " ] files ";
Display(Environment.NewLine,true);
Display("--------------------------------------------------------------------------------------", true);
Display("[ " + DateTime.Now.ToLongTimeString() + " ] - [ √ ] Finished.", true);
Display("--------------------------------------------------------------------------------------", true);
Thread.Sleep(100);
if (Form1.skipped > 0)
{
Display(text2,true);
}
else if (Form1.skipped == 0)
{
Display(text2,true);
}
this.Display(text,true);
Display("--------------------------------------------------------------------------------------", true);
Thread.Sleep(100);
DirButton.Enabled = true;
ScanButton.Enabled = true;
return;
}
Display("[ " + DateTime.Now.ToLongTimeString() + " ] - [ x ] Scanning cancelled", true);
DirButton.Enabled = true;
ScanButton.Enabled = true;
return;
}
private static int scanned = 0;
private static int skipped = 0;
private static BackgroundWorker worker = new BackgroundWorker();
how do I prevent it from displaying duplicate strings like in the example second string?
I ~think~ you simply want the RichTextBox to start fresh when the scan is started again?
If so, use the Clear() method at the beginning of startScan():
private void startScan()
{
DisplayTextBox.Clear();
// ... rest of your existing code ...
}
---------- EDIT ----------
You are wiring up the events of the backgroundworker, each time the scan is performed. This should only be done ONCE. A good place would be the Load() event of the form.
These are the lines that need to be moved, and only called once:
worker.DoWork += Scan;
worker.ProgressChanged += this.ProgressChanged;
worker.RunWorkerCompleted += this.RunWorkerCompleted;
I started coding my first WPF app and I'm having trouble with a textbox that displays some system info (cpu, memory, disk usage, mac address, etc.).
My apps has navigation between two pages and the said textbox is on one of the pages. The textbox's content is retrieved via WMI queries.
The issue I have noticed is that while navigating to tha page with the textbox it freezes the UI for about two seconds before going to and display the page.
I'm a newbie and my best guess is that eighter the WMI queries(could be badly coded too) are doing that or I'm loading the content in the textbox wrongfully.
An example of how my queries are constructed
public string getCPU()
{
ManagementObjectSearcher searcher = new
ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_Processor");
StringBuilder sb = new StringBuilder();
foreach (ManagementObject wmi in searcher.Get())
{
try
{
sb.Append("Processor: " + wmi.GetPropertyValue("Name").ToString() + Environment.NewLine);
}
catch
{
return sb.ToString();
}
}
return sb.ToString();
}
public string getRAMsize()
{
ManagementClass mc = new ManagementClass("Win32_ComputerSystem");
ManagementObjectCollection moc = mc.GetInstances();
foreach (ManagementObject item in moc)
{
return Convert.ToString(Math.Round(Convert.ToDouble(item.Properties["TotalPhysicalMemory"].Value) / (1024 * 1024 * 1024), 0)) + " GB";
}
return "RAMsize";
}
And this is what I use to retrieve the data in the textbox:
private void TextBox1_Loaded(object sender, RoutedEventArgs e)
{
TextBox1.Text = getCPU();
TextBox1.Text += "Memory: " + getRAMsize() + Environment.NewLine;
TextBox1.Text += "Free Space: " + GetTotalFreeSpace(sysdrive) + " GB" + Environment.NewLine;
if (Is64BitSystem)
{
TextBox1.Text += getOS() + " 64bit" + Environment.NewLine;
}
else
{
TextBox1.Text += getOS() + " 32 Bit" + Environment.NewLine;
}
TextBox1.Text += "MAC Address : " + System.Text.RegularExpressions.Regex.Replace(GetMacAddress().ToString(), ".{2}", "$0 ") + Environment.NewLine;
TextBox1.Text += av();
}
My question is what am I doing wrong and how can I get around with it. In my mind , if the queries are constructed correctly, it would be because they are done again and again everytime the textbox is loaded (on navigation or at startup) and maybe If I could get it to load only once and remember those values(since most of the data should stay the same).
But as I said I'm a noob and any help will be greatly appreciated.
Thanks in advance
You are aquiring your data in the UI Thread.
You should load the data in the background thread and then, since you use wpf, binding the textbox text to a variable.
Example (This code is Copy'n'Paste ready):
// INotifyPropertyChanged is an Interface which enables binding of Properties in your window
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string _systemInformation;
// Stub (works actually)
private bool is64BitSystem = (IntPtr.Size == 8);
// Stub
private string sysdrive = "YOLO\\:";
public MainWindow()
{
InitializeComponent();
// Set Datacontext normally set to a viewmodel but ther is none in this code
this.DataContext = this;
// The Backgroundworker does things in the background (nonblocking)
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += DoItButDontInterruptMeDuh;
bw.RunWorkerAsync();
}
public event PropertyChangedEventHandler PropertyChanged;
public string SystemInformation { get => _systemInformation; set => _systemInformation = value; }
//Stub
public string getCPU()
{
return "Fancy CPU";
}
//Stub
public string getRAMsize()
{
return "1 PB";
}
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
//Stub
private string av()
{
return "Whatever av means.";
}
private void DoItButDontInterruptMeDuh(object sender, DoWorkEventArgs e)
{
// Simulate loading time
Thread.Sleep(5000);
SystemInformation = getCPU();
SystemInformation += "Memory: " + getRAMsize() + Environment.NewLine;
SystemInformation += "Free Space: " + GetTotalFreeSpace(sysdrive) + " GB" + Environment.NewLine;
if (is64BitSystem)
{
SystemInformation += getOS() + " 64bit" + Environment.NewLine;
}
else
{
SystemInformation += getOS() + " 32 Bit" + Environment.NewLine;
}
SystemInformation += "MAC Address : " + System.Text.RegularExpressions.Regex.Replace(GetMacAddress().ToString(), ".{2}", "$0 ") + Environment.NewLine;
SystemInformation += av();
OnPropertyChanged("SystemInformation");
}
//Stub
private object GetMacAddress()
{
return "Macintoshstreet 1234";
}
//Stub
private string getOS()
{
return "Cool OS";
}
//Stub
private string GetTotalFreeSpace(object sysdrive)
{
return "0";
}
}
and in the .xaml:
<TextBox Text={Binding Path=SystemInformation}/>
After checking each and every query I found out that the getCPU one is causing a delay in the load.
I replaced it with a Registry.GetValue, wich is very fast.
Thanks to Sebastian L, because altough his code didn't work for me it put me on the right path and I was able to adapt own code with a backgroundworker to avoid UI freeze of any kind
my working code:
private void TextBox1_Loaded(object sender, RoutedEventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += new DoWorkEventHandler(delegate (object o, DoWorkEventArgs args)
{
});
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(delegate (object o, RunWorkerCompletedEventArgs args)
{
TextBox1.Text = (string)Registry.GetValue(#"HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\CentralProcessor\0", "ProcessorNameString", null) + Environment.NewLine;
TextBox1.Text += "Memory: " + getRAMsize() + Environment.NewLine;
TextBox1.Text += "Free Space: " + GetTotalFreeSpace(sysdrive) + " GB" + Environment.NewLine;
if (Is64BitSystem)
{
TextBox1.Text += getOS() + " 64bit" + Environment.NewLine;
}
else
{
TextBox1.Text += getOS() + " 32 Bit" + Environment.NewLine;
}
TextBox1.Text += "MAC Address : " + System.Text.RegularExpressions.Regex.Replace(GetMacAddress().ToString(), ".{2}", "$0 ") + Environment.NewLine;
TextBox1.Text += av();
});
bw.RunWorkerAsync();
}
Imagine there are two buttons that call an asynchronous function
int packProcesses=0; //the number of processes we are dealing with
bool busy = false; //are we busy?
int v=10;
private async void button5_Click(object sender, EventArgs e)
{
packProcesses++;
busy = true;
Trace.WriteLine("PROCESSES " + packProcesses + " busy? " + busy);
//Do something
var result = await DelayAndReturnAsync(v);
//finished?
packProcesses--;
if (packProcesses <= 0) busy = false;
Trace.WriteLine("Processes " + packProcesses + " busy? " + busy);
}
private async void button6_Click(object sender, EventArgs e)
{
packProcesses++;
busy = true;
Trace.WriteLine("PROCESSES " + packProcesses + " busy? " + busy);
//Do something
var result = await DelayAndReturnAsync(v);
//finished?
packProcesses--;
if (packProcesses <= 0) busy = false;
Trace.WriteLine("Processes " + packProcesses + " busy? " + busy);
}
Where the asynchronous function is
async Task<int>DelayAndReturnAsync(int val)
{
await Task.Delay(TimeSpan.FromSeconds(val)).ConfigureAwait(false);
Trace.WriteLine("Time" + DateTime.Now);
return val;
}
and I want to have another button that calls both of the buttons.
If I just put both click functions one after another I will have both processes started at once.
Since I want one processes to start after the other I do
private async void button8_Click(object sender, EventArgs e)
{
button5_Click(sender, e);
do
{
await Task.Delay(1000);
} while (busy);
button6_Click(sender, e);
}
I took the idea from this answer
Is this a good idea? I don't want to clog the CPU in order to do this.
Is there a better way to wait for one process to complete to start the other?
You can move your logic from inside the handler to another method:
private async void button1_Click(object sender, EventArgs e)
{
await Process1();
}
private async Task Process1()
{
packProcesses++;
busy = true;
Trace.WriteLine("PROCESSES " + packProcesses + " busy? " + busy);
//Do something
var result = await DelayAndReturnAsync(v);
//finished?
packProcesses--;
if (packProcesses <= 0) busy = false;
Trace.WriteLine("Processes " + packProcesses + " busy? " + busy);
}
private async void button2_Click(object sender, EventArgs e)
{
await Process2();
}
private async Task Process2()
{
packProcesses++;
busy = true;
Trace.WriteLine("PROCESSES " + packProcesses + " busy? " + busy);
//Do something
var result = await DelayAndReturnAsync(v);
//finished?
packProcesses--;
if (packProcesses <= 0) busy = false;
Trace.WriteLine("Processes " + packProcesses + " busy? " + busy);
}
Then you can await both them:
private async void button3_Click(object sender, EventArgs e)
{
await Process1();
await Process2();
}
if you can at all, I would try and avoid having your button6_Click and button5_Click methods returning void. if instead you have them return a Task you can await them.
private async Task button5_Click(object sender, EventArgs e)
{ ... }
private async Task button8_Click(object sender, EventArgs e)
{
await button5_Click(sender, e);
await button6_Click(sender, e);
}
edit:
private async Task HandleButton5_Click()
{
...
}
private async void button5_Click(object sender, EventArgs e)
{
await HandleButton5_Click();
}
private async void button8_Click(object sender, EventArgs e)
{
button5_Click(sender, e);
button6_Click(sender, e);
}
I have a DotNetZip code for zipping the folder. It doesnt zip the file for the first time after cleaning the solution. After that it works fine. Can anybody know the issue why its happening??
Button Click Event Code
private void button3_Click(object sender, EventArgs e)
{
try
{
copy_stuff(textBox1.Text, textBox2.Text, textBox3.Text);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Method that gets called from Button Click
private void copy_stuff(string srcFolder, string destFolder, string Backup)
{
using (ZipFile zip = new ZipFile())
{
zip.AddProgress += AddProgressHandler;
zip.CompressionLevel = Ionic.Zlib.CompressionLevel.Default;
zip.SaveProgress += SaveProgress;
zip.StatusMessageTextWriter = System.Console.Out;
zip.AddDirectory(destFolder);
zip.Save(Backup + "\\VibrantBackup" + DateTime.Now.ToString("yyyyMMdd hh.mm.ss") + ".zip");
label1.Text = "Compression completed.";
}
}
Add & Save Handlers for Progress
int _numEntriesToAdd = 0;
int _numEntriesAdded = 0;
void AddProgressHandler(object sender, AddProgressEventArgs e)
{
switch (e.EventType)
{
case ZipProgressEventType.Adding_Started:
_numEntriesToAdd = 0;
_numEntriesAdded = 0;
label1.Text = "Adding files to the zip...";
label1.Update();
Application.DoEvents();
break;
case ZipProgressEventType.Adding_AfterAddEntry:
_numEntriesAdded++;
label1.Text = String.Format("Adding file: {0} :: {2}",
_numEntriesAdded, _numEntriesToAdd, e.CurrentEntry.FileName);
label1.Update();
Application.DoEvents();
break;
case ZipProgressEventType.Adding_Completed:
label1.Text = "Added all files";
label1.Update();
Application.DoEvents();
break;
}
}
public void SaveProgress(object sender, SaveProgressEventArgs e)
{
if (e.EventType == ZipProgressEventType.Saving_Started)
{
label1.Text = "Begin Saving: " + e.ArchiveName;
label1.Update();
Application.DoEvents();
}
else if (e.EventType == ZipProgressEventType.Saving_BeforeWriteEntry)
{
label1.Text = "Processing : " + e.CurrentEntry.FileName;
label1.Update();
label3.Text = "Files Processed: (" + (e.EntriesSaved + 1) + "/" + e.EntriesTotal + ")";
label3.Update();
Application.DoEvents();
progressBar3.Maximum = e.EntriesTotal;
progressBar3.Value = e.EntriesSaved + 1;
}
else if (e.EventType == ZipProgressEventType.Saving_EntryBytesRead)
{
//progressBar2.Value = (int)((e.BytesTransferred * 100) / e.TotalBytesToTransfer);
//label3.Text = "Writing: " + e.CurrentEntry.FileName + " (" + (e.EntriesSaved + 1) + "/" + e.EntriesTotal + ")";
label1.Update();
Application.DoEvents();
}
}
I'm trying to access the Script class that is in the code block below in the event triggered when a file download is completed. How would I be able to do that?
public void DownloadScript(Script script, string DownloadLocation)
{
AddLog(GenerateLog("Downloading Script", "Started", "Downloading " + script.Name + " from " + script.DownloadURL + "."));
WebClient Client = new WebClient();
Client.DownloadFileCompleted += new System.ComponentModel.AsyncCompletedEventHandler(Client_DownloadFileCompleted);
Client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(Client_DownloadProgressChanged);
Client.DownloadFileAsync(new Uri(script.DownloadURL), DownloadLocation + "test1.zip");
}
Here is the event that is triggered.
public void Client_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
if (e.Error.Message != string.Empty)
{
AddLog(GenerateLog("Downloading Script", "Error", "There was an error downloading " + script.Name + " from " + script.DownloadURL + ". " + e.Error.Message));
Console.WriteLine("Error");
}
else
{
AddLog(GenerateLog("Downloading Script", "Done", "Finished downloading " + script.Name + " from " + script.DownloadURL + "."));
Console.WriteLine("Done");
}
}
You can use a lambda expression to capture the Script object and pass it along to the handler as an extra parameter.
public void DownloadScript(Script script, string DownloadLocation) {
...
WebClient Client = new WebClient();
Client.DownloadFileCompleted += (sender, e) => Client_DownloadFileCompleted(
sender,
e,
script);
}
public void Client_DownloadFileCompleted(
object sender,
AsyncCompletedEventArgs e,
Script script) {
...
}