Alright so basicly I have this simple application running in system tray that has one timer. Every tick it performs a check to see if a given directory and file exists, and based on the result it changes its icon.
The problem is every single timer tick the memory for the application raises ~100kb. I currently have it running for about 5 mins and it already uses 40MB of memory, which is unacceptable for such "micro" application.
Here's my code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
namespace Tray
{
public partial class Main : Form
{
string drive = "C:\\";
string file = "test.txt";
System.Drawing.Image imgRed = Image.FromFile("res\\icon-red.png");
System.Drawing.Image imgOrange = Image.FromFile("res\\icon-orange.png");
System.Drawing.Image imgGreen = Image.FromFile("res\\icon-green.png");
System.Drawing.Icon icoRed = System.Drawing.Icon.ExtractAssociatedIcon("res\\icon-red.ico");
System.Drawing.Icon icoOrange = System.Drawing.Icon.ExtractAssociatedIcon("res\\icon-orange.ico");
System.Drawing.Icon icoGreen = System.Drawing.Icon.ExtractAssociatedIcon("res\\icon-green.ico");
public Main()
{
InitializeComponent();
}
public static string ShowPrompt(string text, string caption)
{
Form prompt = new Form();
prompt.Width = 500;
prompt.Height = 150;
prompt.Text = caption;
Label textLabel = new Label() { Left = 50, Top = 20, Text = text };
TextBox textBox = new TextBox() { Left = 50, Top = 50, Width = 400 };
Button confirmation = new Button() { Text = "Ok", Left = 350, Width = 100, Top = 70 };
confirmation.Click += (sender, e) => { prompt.Close(); };
prompt.Controls.Add(confirmation);
prompt.Controls.Add(textLabel);
prompt.Controls.Add(textBox);
prompt.ShowDialog();
return textBox.Text;
}
public void updateInfo(){
this.statusDrive.Text = "Drive [" + drive + "]";
this.statusFile.Text = "File [" + drive + file + "]";
}
public void exec(){
int status = 0;
this.trayIcon.Text = "[Drive - ";
if (Directory.Exists(drive)){
this.statusDrive.Text += " - OK";
this.statusDrive.Image = imgGreen;
status++;
this.trayIcon.Text += "OK] ";
} else{
this.statusDrive.Text += " - FAIL";
this.statusDrive.Image = imgRed;
this.trayIcon.Text += "FAIL] ";
}
this.trayIcon.Text += "[File - ";
if (File.Exists(drive + file))
{
this.statusFile.Text += " - OK";
this.statusFile.Image = imgGreen;
status++;
this.trayIcon.Text += "OK] ";
}
else
{
this.statusFile.Text += " - FAIL";
this.statusFile.Image = imgRed;
this.trayIcon.Text += "FAIL] ";
}
switch (status)
{
case 2:
this.Icon = icoGreen;
this.trayIcon.Icon = icoGreen;
this.status.Image = imgGreen;
break;
case 1:
this.Icon = icoOrange;
this.trayIcon.Icon = icoOrange;
this.status.Image = imgOrange;
break;
case 0:
default:
this.Icon = icoRed;
this.trayIcon.Icon = icoRed;
this.status.Image = imgRed;
break;
}
}
private void Form1_Load(object sender, EventArgs e)
{
this.Hide();
}
private void timer1_Tick(object sender, EventArgs e)
{
updateInfo();
exec();
}
private void chDrive_Click(object sender, EventArgs e)
{
this.drive = ShowPrompt("Enter drive path", "Change drive");
}
private void chFile_Click(object sender, EventArgs e)
{
this.file = ShowPrompt("Enter new file path:", "Change file");
}
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
Application.Exit();
}
}
}
I already tried to optimize the app by preloading the icons and images into variables and assigning those to the appropriate properties, however this didn't solve my problem.
Also, note that I managed to hide my main window by doing this (in Program.cs):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace Tray
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Main mForm = new Main();
Application.Run();
}
}
}
UPDATE
I just noticed that the memory usage climbs up to 50MB and drops to 20MB afterwards (and goes up again). Is this something I can possibly address or is it a windows "issue"?
I'm going to take a stab at it being the string concatenations happening once a second. Consider using a StringBuilder. 40MB is nothing though really.
RE: Your update. The Garbage Collector is reclaiming the memory as it sees fit.
You never appear to be disposing your form correctly in ShowPrompt, so I'd imagine this is your problem.
Because a form displayed as a dialog box is not closed, you must call the Dispose method of the form when the form is no longer needed by your application.
ShowDialog
Some points that could cut down on memory usage:
Try to prebuild all those strings you're building in exec(). It looks like they're all runtime constants, but you build them every tick instead of building them once when the application starts. If this isn't possible, use StringBuilder instead of +=.
Only change properties on controls (icon, trayText, etc) if there has been a change. I.E. if tray text is already "[Drive C:\ - OK]", don't set its value again to "[Drive C:\ - OK]" next tick.
Garbage Collector does all the work of memory management for you. Temporary rise in memory doesn't always mean that there is a memory leak. It may come down when the GC collects memory. In case you suspect that there are memory leaks you need to do memory profiling which is no easy job. You need to read into this article for steps that you can take to find out the problem in your code. Alternatively, there are multiple tools avaiable in the market to do this job for you. You can use Ants Profiler of Red Gate, memprofiler amongst others.
One thing you might consider is rather than using a timer why not use the FileSystemWatcher and attach to events:
var watcher = new FileSystemWatcher("somepath");
watcher.Deleted += (sender, eventArgs) => { };
watcher.Changed += (sender, eventArgs) => { };
watcher.Error += (sender, eventArgs) => { };
watcher.Renamed += (sender, eventArgs) => { };
I also agree that you should be disposing of the forms once you're done with them.
Related
I am trying to create an application that shows the system statistics but it also executes a timer each second to update the current CPU clock speed. I have tried numerous solutions but none seem to work for me. I will attach my code below. I am using the System.Management DLL to call the stats I am wanting. Thank you!
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Management;
namespace ezCPU
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
this.Text = this.Text + " - v" + Application.ProductVersion;
getCPUInfo();
}
public string convertClockSpeed(string s)
{
double clockSpeed = Convert.ToDouble(s);
double newClockSpeed = (clockSpeed / 1000);
return Math.Round(newClockSpeed, 2).ToString() + " GHz";
}
public void getCPUInfo()
{
ManagementObjectSearcher myProcessorObject = new ManagementObjectSearcher("select * from Win32_Processor");
foreach (ManagementObject obj in myProcessorObject.Get())
{
string cpuName = obj["Name"].ToString();
txtCPUName.Text = cpuName;
string cpuManufacturer = obj["Manufacturer"].ToString();
if (cpuManufacturer == "GenuineIntel")
{
txtCPUManufacturer.Text = "Genuine Intel";
}
else
{
txtCPUManufacturer.Text = cpuManufacturer;
}
string cpuCores = obj["NumberOfCores"].ToString();
txtCores.Text = cpuCores;
string cpuThreads = obj["ThreadCount"].ToString();
txtThreads.Text = cpuThreads;
string cpuMaxSpeed = obj["MaxClockSpeed"].ToString();
txtClockSpeed.Text = convertClockSpeed(cpuMaxSpeed);
}
}
private void cpuTimer_Tick(object sender, EventArgs e)
{
ManagementObjectSearcher myProcessorObject = new ManagementObjectSearcher("select * from Win32_Processor");
foreach(ManagementObject obj in myProcessorObject.Get())
{
string currentClockSpeed = obj["CurrentClockSpeed"].ToString();
txtCPUSpeed.Text = convertClockSpeed(currentClockSpeed);
}
}
}
}
This is the result of the code. However, I am unable to move the form around while it is still updating.
Have you tried the windows forms timer? It has the elapsed event that even spins up a background thread to do the work, keeping your ui responsive:
https://learn.microsoft.com/en-us/dotnet/api/system.timers.timer.elapsed?redirectedfrom=MSDN&view=netframework-4.8
public class Form1: Form
{
public System.Windows.Forms.Timer aTimer = new System.Windows.Forms.Timer();
public Form1()
{
InitializeComponent();
// Create a timer and set a two second interval.
aTimer.Interval = 2000;
// Hook up the tick event for the timer.
aTimer.Tick += OnTimedEvent;
// Have the timer fire repeated events (true is the default)
aTimer.AutoReset = true;
// Start the timer
aTimer.Enabled = true;
Console.WriteLine("Press the Enter key to exit the program at any time... ");
Console.ReadLine();
}
private void OnTimedEvent(object sender, EventArgs e)
{
// update your statistics here
}
}
If you want to update the ui there, then the tick event is the way to go: https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.timer.tick?view=netframework-4.8
I have created two buttons in c#. Is there any way to deactivate a button if anything goes wrong in the other button's function. I am giving a sample code fragment. There are two buttons. one is for connect and other is for browse files. I want to disable the browse button if the connectivity fails in the connect button. How to do this?
Sample code fragment:
using System;
using System.Drawing;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Collections.Generic;
class Client:Form
{
public Client()
{
Size = new Size(400, 380);
Button connect = new Button();
connect.Parent = this;
connect.Text = "Connect";
connect.Location = new Point(295, 20);
connect.Size = new Size(6 * Font.Height, 2 * Font.Height);
connect.Click += new EventHandler(ButtonConnectOnClick);
Button browse = new Button();
browse.Parent = this;
browse.Text = "Browse";
browse.Location = new Point(220, 20);
browse.Size = new Size(6 * Font.Height, 2 * Font.Height);
browse.Click += new EventHandler(ButtonbrowseOnClick);
}
void ButtonConnectOnClick(object obj, EventArgs ea)
{
tcpClient = new TcpClient("127.0.0.1", 1234);
}
[STAThread]
public static void Main()
{
Application.Run(new Client());
}
}
Another Question:
Is there any way to reset the button (say, the browse button) activity at a certain time and wait for clicking again?
You can just use a global flag:
bool connectButtonBroken = false;
private void ButtonbrowseOnClick(object sender, EventArgs e)
{
if(!connectButtonBroken)
{
//do code
}
}
Just wrap your connect call in a Try/Catch block and disable the other button in the Catch:
catch (Exception)
{
ButtonConnect.Enabled = false;
}
You should probably re-enable it just before the end of the Try with this:
ButtonConnect.Enabled = true;
You will need to store a reference to your dynamically created Connect button in a variable called ButtonConnect in a form level variable instead of a local one in the constructor for this to work.
The problem is below. Here's my code...
// Contents of Form1.cs
// Usual includes
namespace ProcessMonitor
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public Boolean getStatus()
{
// Returns true if the system is active
if (label1.Text.Equals("Active"))
return true;
return false;
}
private void button1_Click(object sender, EventArgs e)
{
if(getStatus())
{
label1.Text = "Not Active";
button1.Text = "Activate";
}
else
{
label1.Text = "Active";
button1.Text = "Deactivate";
}
}
private void Form1_Load(object sender, EventArgs e)
{
Monitor mon = new Monitor(this);
mon.Run();
}
}
}
// Contents of Monitor.cs
// Usual includes
using System.Management;
using System.Diagnostics;
using System.Threading;
namespace ProcessMonitor
{
class Monitor
{
Form1 parent;
private void ShowAlert(Alert al)
{
al.Show();
}
public Monitor(Form1 parent)
{
this.parent = parent;
}
public void InvokeMethod()
{
//This function will be on main thread if called by Control.Invoke/Control.BeginInvoke
Alert frm = new Alert(this.parent);
frm.Show();
}
// This method that will be called when the thread is started
public void Run()
{
var query = new WqlEventQuery("__InstanceCreationEvent", new TimeSpan(0, 0, 0, 0, 1),
"TargetInstance isa \"Win32_Process\");
while (true)
{
using (var watcher = new ManagementEventWatcher(query))
{
ManagementBaseObject mo = watcher.WaitForNextEvent();a
//MessageBox.Show("Created process: " + ((ManagementBaseObject)mo["TargetInstance"])["Name"] + ",Path: " + ((ManagementBaseObject)mo["TargetInstance"])["ExecutablePath"]);
ManagementBaseObject o = (ManagementBaseObject)mo["TargetInstance"];
String str = "";
foreach (PropertyData s in o.Properties)
{
str += s.Name + ":" + s.Value + "\n";
}
this.parent.Invoke(new MethodInvoker(InvokeMethod), null);
}
}
}
}
}
Alert.cs is just a blank form with a label that says “new process has started”. I intend to display the name of the process and location, pid, etc. by passing it to this alert form via the Thread (i.e. class Monitor). I have deliberately made the thread load in form_load so that I can resolve this error first. Adding it as a thread properly after the main form loads fully is a later task. I need to fix this first..
The delegate creates the Alert form but I can’t click on it, its just stuck. Need help to solve this.
Your while loop in Run is blocking the UI thread.
by passing it to this alert form via the Thread
You never actually create a new thread or task here - you just run code which executes in the UI thread, and causes an infinite loop. This will prevent the main form, as well as your Alert form, from ever displaying messages.
You need to push this into a background thread in order for it to work, ie:
private void Form1_Load(object sender, EventArgs e)
{
ThreadPool.QueueUserWorkItem(_ =>
{
Monitor mon = new Monitor(this);
mon.Run();
});
}
I'm running a windows form with a background worker to update a textbox based on the output of a python script. Its all working pretty well, except the redirected output is not in real time; its delayed pretty significantly.
Any ideas how I can increase the redirected outputs response time?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;
using System.IO;
namespace JiraHeartBeat
{
public partial class Form1 : Form
{
delegate void AppendTextDelegate(string text);
BackgroundWorker Worker = new BackgroundWorker();
public Form1()
{
InitializeComponent();
Worker.DoWork += new DoWorkEventHandler(Worker_DoWork);
Worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Worker_RunWorkerCompleted);
}
void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
StartButton.PerformClick();
}
private void StartButton_Click(object sender, EventArgs e)
{
if (!Worker.IsBusy)
{
Worker.RunWorkerAsync();
}
}
public void Worker_DoWork(object sender, DoWorkEventArgs e)
{
Process pro = new Process();
pro.StartInfo.RedirectStandardOutput = true;
pro.StartInfo.RedirectStandardError = true;
pro.StartInfo.UseShellExecute = false;
pro.StartInfo.CreateNoWindow = true;
pro.EnableRaisingEvents = true;
pro.OutputDataReceived += new DataReceivedEventHandler(OnDataReceived);
pro.ErrorDataReceived += new DataReceivedEventHandler(OnDataReceived);
pro.StartInfo.FileName = "C:\\Python27\\python.exe";
pro.StartInfo.Arguments = "\"C:\\Python27\\myscript.py\"";
try
{
pro.Start();
pro.BeginOutputReadLine();
pro.BeginErrorReadLine();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
Thread.Sleep(5000 * 60);
}
public void OnDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
string temp = (e.Data) + Environment.NewLine;
appendText(temp);
}
}
public void appendText(string text)
{
if (ResultTextBox.InvokeRequired)
{
ResultTextBox.Invoke(new AppendTextDelegate(appendText), new object[] { text });
}
else
{
ResultTextBox.AppendText(text);
}
}
}
}
Actually, the issue is that Python does not redirect output until the script is complete. I believe IronPython will redirect while the script is running (have not tested this though), but unfortunately, regular Python must wait for the script to end before redirecting output.
Try removing the below line from the Worker_DoWork, I suspect it is delaying the execution of the RunWorkerCompleted event.
Thread.Sleep(5000 * 60);
EDIT
Since the above approach was attempted and did not solve the problem entirely I investigated a bit further and confirmed that when capturing the output from a python script the response is delayed. However, by adding a call to sys.stdout.flush() I was able to get the desired behavior. Here is the python script I used which worked successfully in my test.
import time
import sys
for x in xrange(0,11):
print x
time.sleep(1)
sys.stdout.flush()
I am totally new to C# coding - I am trying to take screen shots of webpages whose URLs are initially picked up form a notepad uploaded on the same form.
As I read through the documentation on the web_browser control in MSDN.. I did arrive at the following code - yet when I run, I only get white screens
I tried uploading a .txt with (google/yahoo URLs as test data in 2 lines)
Can someone please say what more should I take care apart from handling which should get what I want as I read in MSDN.
P.S: pls forgive if I'm terribly wrong in coding style .. as aforesaid I'm just starting my C# coding :)
Code that I tried...
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace MSDN_wbc_tut1
{
public partial class Form1 : Form
{
public int temp = 0;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void FileUploadButton1_Click(object sender, EventArgs e)
{
//once a file is uploaded i want Pgm to read contents & browse them as URLS
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.CheckFileExists = true;
openFileDialog.AddExtension = true;
openFileDialog.Multiselect = true;
//Filtering for Text files alone
openFileDialog.Filter = "text files (*.txt)|*.txt";
//if file is selected we must then do our coding part to proceed hecneforth
if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
//my code to say pgm wat to do once upload done...
//Getting my Chosen file's name
string Fileuploaded = openFileDialog.FileName;
//Read all line opens files - reads till EOF & closes ,prevents a while condition
string[] FileuploadedContent = System.IO.File.ReadAllLines(Fileuploaded);
foreach (string s in FileuploadedContent)
{
NavigateContent(s);
}
}
}
private void NavigateContent(string lineasurl)
{
// Add an event handler that images the document after it loads.
try
{
webBrowser1.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler
(takescreen);
webBrowser1.Navigate(new Uri(lineasurl));
//webBrowser1.Navigate(lineasurl); also works
}
catch (System.UriFormatException)
{
return;
}
}
private void takescreen(object sender, WebBrowserDocumentCompletedEventArgs e)
{
//specifying sample values for image or screenshot size
int x = 600, y = 700;
Bitmap bitmap = new Bitmap(x, y);
webBrowser1.DrawToBitmap(bitmap, new Rectangle(0, 0, x, y));
//to give each screen a unique name - i append some no onto it
temp = temp + 1;
string TempFname = "Screenshotref" + temp.ToString() + "." + "jpg";
bitmap.Save(TempFname);
}
}
}