Why this application is taking so much of memory? - c#

So my problem is, i created one app for my personal use which fetch the html pages from some sites and then display it in a web browser after some alteration. Every thing is working fine but what perturbed me is the memory that it is taking. After querying for 3-4 terms, memory usage reaches to approximate 300-400 mb.
Some relevant code from the app is
void sentenceBox_Navigated(object sender, WebBrowserNavigatedEventArgs e)
{
GC.Collect();
}
HtmlDocument hd;
Word w=new Word();
private void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
status.Text = "Processing english req..";
if (checkInHis(queryTxt.Text))
{
sentenceBox.AllowNavigation = true;
richTextBox1.Text = w.engDefinition;
sentenceBox.DocumentText = w.engDefinition;
status.Text = "Word found in History.DONE!";
button1.Enabled = true;
return;
}
if (w == null || w.engWordProp != queryTxt.Text)
{
w.engWordProp=queryTxt.Text;
w.loadEngDefn();
w.engDefnLoadedEvent += new Word.engDefnLoaded(w_engDefnLoadedEvent);
return;
}
w.loadEngDefn();
w.engDefnLoadedEvent += new Word.engDefnLoaded(w_engDefnLoadedEvent);
}
void w_engDefnLoadedEvent(Word sender, EventArgs data)
{
sentenceBox.AllowNavigation = true;
sentenceBox.DocumentText = sender.engDefinition;
sender.engDefnLoadedEvent -= w_engDefnLoadedEvent;
button1.Enabled = true;
}
private void addToHistory(Word w)
{
status.Text = "Saving offline...";
if (!checkInHis(w.engWordProp))
{
history.Add(w);
// label1.Text = w.engWordProp + " saved in localdb. Database size: " + history.Count;
w = null;
}
else
{
// label1.Text = w.engWordProp + " Skipped. Database size: " + history.Count;
}
}
private Boolean checkInHis(string p)
{
status.Text = "checking offline storage...";
foreach (Word item in history)
{
if (item.engWordProp == p)
{
status.Text = "Word found in history.";
w = item;
return true;
}
}
status.Text = "Not found in offline database...";
return false;
}
private void sentenceBox_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
((WebBrowser)sender).AllowNavigation = false;
}
private void button2_Click_1(object sender, EventArgs e)
{
button2.Enabled = false;
status.Text = "Requesting hindi definition...";
if (checkInHis(queryTxt.Text))
{
sentenceBox.AllowNavigation = true;
sentenceBox.DocumentText = w.hindiDef;
status.Text = "DONE!";
button2.Enabled = true;
return;
}
if (w == null || w.engWordProp != queryTxt.Text)
{
w.engWordProp=queryTxt.Text;
w.loadHinDefn();
w.HindiDefLoadedEvent += new Word.hindiDefLoaded(w_HindiDefLoadedEvent);
return;
}
w.loadHinDefn();
w.HindiDefLoadedEvent += new Word.hindiDefLoaded(w_HindiDefLoadedEvent);
}
void w_HindiDefLoadedEvent(Word sender, EventArgs data)
{
sentenceBox.AllowNavigation = true;
sentenceBox.DocumentText = sender.hindiDef;
button2.Enabled = true;
sender.HindiDefLoadedEvent -= w_HindiDefLoadedEvent;
}
private void button3_Click(object sender, EventArgs e)
{
button3.Enabled = false;
saveWord(w);
button3.Enabled = true;
}
private void saveWord(Word w)
{
if (w.hindiDef == "")
{
w.loadHinDefn();
w.HindiDefLoadedEvent += new Word.hindiDefLoaded(w_HindiDefLoadedEventforHindiSave);
}
if (w.engDefinition == "")
{
w.loadEngDefn();
w.engDefnLoadedEvent += new Word.engDefnLoaded(w_engDefnLoadedEventforEnglishSave);
}
addToHistory(w);
}
void w_HindiDefLoadedEventforHindiSave(Word sender, EventArgs data)
{
sender.HindiDefLoadedEvent -= w_HindiDefLoadedEvent1;
sender.HindiDefLoadedEvent -= w_HindiDefLoadedEventforHindiSave;
}
void w_engDefnLoadedEventforEnglishSave(Word sender, EventArgs data)
{
sender.engDefnLoadedEvent -= w_engDefnLoadedEventforEnglishSave;
sender.engDefnLoadedEvent -= w_engDefnLoadedEventforEnglishSave;
}
void w_HindiDefLoadedEvent1(Word sender, EventArgs data)
{
saveWord(sender);
sender.HindiDefLoadedEvent -= w_HindiDefLoadedEvent1;
}
void w_engDefnLoadedEvent1(Word sender, EventArgs data)
{
sender.loadHinDefn();
sender.HindiDefLoadedEvent += new Word.hindiDefLoaded(w_HindiDefLoadedEvent1);
sender.engDefnLoadedEvent -= w_engDefnLoadedEvent1;
}
void initWord(String query)
{
queryTxt.Text = query;
w.engWordProp=queryTxt.Text;
w.loadEngDefn();
w.loadHinDefn();
w.engDefnLoadedEvent += new Word.engDefnLoaded(w_engDefnLoadedEvent);
w.HindiDefLoadedEvent += new Word.hindiDefLoaded(w_HindiDefLoadedEvent);
}
Word class
public Word(string q)
{
wb1 = new WebBrowser();
wb2=new WebBrowser();
engWord = q;
hindiDef = "";
engDefinition = "";
flagE = false;
flagH = false;
engUrl = "http://oxforddictionaries.com/definition/english/" + q + "?q=" + q;
hindiUrl = "http://dict.hinkhoj.com/hindi-dictionary.php?word=" + q;
wb1.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(wb_DocumentCompleted); ;
wb2.DocumentCompleted+=new WebBrowserDocumentCompletedEventHandler(wb_DocumentCompleted);
}
public delegate void engDefnLoaded(Word sender, EventArgs data);
public event engDefnLoaded engDefnLoadedEvent;
protected void onEngDefnLoadCompleated(Word sender, EventArgs data)
{
if (engDefnLoadedEvent!=null)
{
engDefnLoadedEvent(this,data);
}
}
public void loadEngDefn()
{
if (this.engDefinition=="")
{
// wb1 = new WebBrowser();
wb1.ScriptErrorsSuppressed = true;
wb1.Url = new Uri(this.engUrl);
}
else
{
if (engDefnLoadedEvent!=null)
{
engDefnLoadedEvent(this, new EventArgs());
}
}
}
public void loadHinDefn() {
if (this.hindiDef=="")
{
// wb2 = new WebBrowser();
wb2.ScriptErrorsSuppressed = true;
wb2.Url = new Uri(this.hindiUrl);
}
else
{
if (HindiDefLoadedEvent!=null)
{
HindiDefLoadedEvent(this, new EventArgs());
}
}
}
[NonSerialized]
HtmlDocument hd;
void wb_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
if (((WebBrowser)sender).ReadyState == WebBrowserReadyState.Complete)
{
hd = ((WebBrowser)sender).Document;
if (e.Url.ToString() == this.hindiUrl)
{
parsePage(hd.GetElementById("maint"), "hindi");
((WebBrowser)sender).DocumentCompleted -= wb_DocumentCompleted;
sender = null;
}
else
{
parsePage(hd.GetElementById("entryPageContent"), "eng");
((WebBrowser)sender).DocumentCompleted -= wb_DocumentCompleted;
sender = null;
}
}
}
private void parsePage(HtmlElement hd, string lan)
{
HtmlElementCollection he;
if (lan == "eng")
{
he = hd.GetElementsByTagName("section");
foreach (HtmlElement item in he)
{
this.engDefinition += item.InnerHtml + "<br>";
}
flagE = true;
engDefnLoadedEvent(this, new EventArgs());
wb1 = null;
wb1.Dispose();
return;
}
else
{
he = hd.GetElementsByTagName("div");
foreach (HtmlElement item in he)
{
if (item.GetAttribute("itemprop") == "itemListElement")
{
this.hindiDef += item.GetElementsByTagName("div")[0].InnerHtml + "<br>";
}
}
flagH = true;
HindiDefLoadedEvent(this,new EventArgs());
wb2 = null;
wb2.Dispose();
return;
}
}
Question: How to remove this memory leak issue ?
sample pic
After query 25 words.

First I'd like to point out that just because your application uses 300 - 400 MB of memory doesn't necessarily mean that you have a memory leak. Only if the memory keeps increasing with each requested page and is never released do you have a leak.
Second, in order to diagnose the problem you need to run a memory profiler. If you are using the Premium or Ultimate edition of Visual Studio, it has a memory profile feature. If not you can use either RedGate Memory Profile (14-day free trial) or similar software.
I would also add that the most common cause for leaks in .NET is the use of events where a short lived object attaches itself as an observer/handler to an event raised by a long lived object.

Well in the constructor of your Word class you have the following code:
wb1 = new WebBrowser();
wb2=new WebBrowser();
The WebBrowser class does is to instantiate some of the web browsing features of your local IE version.My guess is that WebBrowser being a part of the IE it has a high memory consumption.So imagine that you instantiate 2 WebBrowser objects for each word that you have.You could use a pool system for your WebBrowser objects, but i would replace the behavior of those with an WebClient object which is disposable.
P.S. The Garbage Collector system is a fine tuned system using GC.Collect(); it's like using a sledgehammer on your code.

Related

WinForms DataGridView EventHandling Issues

I got an issue with the event-handling in my class:
public partial class BadFiles : Form
{
private DataTable dt = new DataTable();
private string oldCellValue = string.Empty;
public BadFiles()
{
InitializeComponent();
dataGridBadFiles.EditMode = DataGridViewEditMode.EditOnKeystrokeOrF2;
Closing += new CancelEventHandler(BadFiles_Closing);
dataGridBadFiles.CellBeginEdit += new DataGridViewCellCancelEventHandler(BadFiles_CellBeginEdit);
dataGridBadFiles.CellValueChanged += new DataGridViewCellEventHandler(BadFiles_CellValueChanged);
dataGridBadFiles.CellValidating += new DataGridViewCellValidatingEventHandler(BadFiles_CellValidating);
dt.Columns.Add("Files");
for (int i = 0; i < GlobalVars.BadFileNames.Count; i++)
{
dt.Rows.Add(Path.GetFileName(GlobalVars.BadFileNames[i]));
}
dataGridBadFiles.DataSource = dt;
}
private void BadFiles_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e)
{
oldCellValue = dataGridBadFiles[e.ColumnIndex, e.RowIndex].Value.ToString();
}
private void BadFiles_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
{
if (string.IsNullOrEmpty(e.ToString()) == true || (FileTools.CheckFileName(e.ToString()) == false))
{
e.Cancel = true;
dataGridBadFiles[e.ColumnIndex, e.RowIndex].Value = oldCellValue;
}
}
private void BadFiles_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
var newCellValue = dataGridBadFiles[e.ColumnIndex, e.RowIndex].Value.ToString();
if (FileTools.CheckFileName(newCellValue) == true)
{
var m = new string[]
{
oldCellValue,
newCellValue
};
MessageBox.Show(string.Join(Environment.NewLine, m));
}
else
{
newCellValue = oldCellValue;
MessageBox.Show("Bad Value!");
}
}
private void BadFiles_Closing(object sender, CancelEventArgs e)
{
if(FileTools.CheckFileFolder(GlobalVars.DataInput).Count != 0)
{
e.Cancel = true;
MessageBox.Show("Please rename all files in the list!", "Rename Files", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
}
The CellValidating event doesn't fire/work, so i tested something else with the CellValueChanged event but that isn't working either. The class shows a Form with a GridView filled from a list with bad filenames. Those filenames must be renamed from the user with a check if the new filenames are OK before changing them. The problem is that the filename checking doesn't work because of the events not firing when supposed to.

How to move a function to a separate assembly from the interface [Error]

I am currently working on a file copying facility that allows me to select a source and a destination for the folders to be copied from and to. A progress bar is displayed after the user clicks on Copy.
The only issue is that All of my functions reside in one file which is form1.cs (as follows)
namespace CopyFacility
{
public partial class Form1 : Form
{
BackgroundWorker background = new BackgroundWorker();
FolderBrowserDialog folderBrowser = new FolderBrowserDialog();
OpenFileDialog openFile = new OpenFileDialog();
public Form1()
{
InitializeComponent();
background.WorkerSupportsCancellation = true;
background.WorkerReportsProgress = true;
background.DoWork += Background_DoWork;
background.RunWorkerCompleted += Background_RunWorkerCompleted;
background.ProgressChanged += Background_ProgressChanged;
}
string inputFile = null;
string outputFile = null;
private void CopyFile(string source, string destination, DoWorkEventArgs e)
{
FileStream fsOut = new FileStream(destination, FileMode.Create);
FileStream fsIn = new FileStream(source, FileMode.Open);
byte[] buffer = new byte[1048756];
int readBytes;
while((readBytes = fsIn.Read(buffer,0,buffer.Length)) > 0)
{
if(background.CancellationPending)
{
e.Cancel = true;
background.ReportProgress(0);
fsIn.Close();
fsOut.Close();
return;
}
else
{
fsOut.Write(buffer, 0, readBytes);
background.ReportProgress((int) (fsIn.Position * 100 / fsIn.Length));
}
fsOut.Write(buffer, 0, readBytes);
background.ReportProgress((int)(fsIn.Position * 100 / fsIn.Length));
}
fsIn.Close();
fsOut.Close();
}
private void Background_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
fileProgressBar.Value = e.ProgressPercentage;
}
private void Background_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if(e.Cancelled)
{
fileProgressBar.Visible = true;
lblMessage.Visible = true;
lblMessage.Text = "The process has been cancelled";
}
else
{
fileProgressBar.Visible = true;
lblMessage.Visible = true;
lblMessage.Text = "The process has been completed";
}
}
private void Background_DoWork(object sender, DoWorkEventArgs e)
{
CopyFile(inputFile, outputFile + #"\" + Path.GetFileName(inputFile),e);
}
private void btnCancel_Click(object sender, EventArgs e)
{
background.CancelAsync();
}
private void btnCopy_Click(object sender, EventArgs e)
{
if(background.IsBusy)
{
lblProgress.Visible = true;
}
else
{
fileProgressBar.Visible = true;
background.RunWorkerAsync();
}
}
private void btnSource_Click(object sender, EventArgs e)
{
if(openFile.ShowDialog() == DialogResult.OK )
{
inputFile = openFile.FileName;
btnSource.Text = inputFile;
}
}
private void btnDestination_Click(object sender, EventArgs e)
{
if (folderBrowser.ShowDialog() == DialogResult.OK)
{
outputFile = folderBrowser.SelectedPath;
btnDestination.Text = outputFile + #"\" + Path.GetFileName(inputFile);
}
}
}
}
I was wondering how I could go about putting the function "CopyFile" into it's own class that can be called whenever the button is clicked?
When I try creating a new class method and inserting the functions related to the copying function into a new class "CopyFunction.cs" , I get a following error from the code "InitializingComponent();" as follows
public CopyPresenter(BackgroundWorker background, FolderBrowserDialog folderBrwoser, OpenFileDialog openFile)
{
InitializeComponent();
background.WorkerSupportsCancellation = true;
background.WorkerReportsProgress = true;
background.DoWork += Background_DoWork;
background.RunWorkerCompleted += Background_RunWorkerCompleted;
background.ProgressChanged += Background_ProgressChanged;
}
The error says that the "InitializeComponent" doesn't exist in the current context.

Timer Run Once,Not continuous

I apologize for the use of language translation
I want to run the timer once. Next blogger.I'm saving it to the database.
Ongoing notification..One in 4 seconds
timer1 :Enabled ,Interval :4000
MyCode:
public bool InternetKontrol()
{
try
{
System.Net.Sockets.TcpClient kontrol_client = new System.Net.Sockets.TcpClient("www.google.com.tr", 80);
return true;
}
catch (Exception)
{
label1.Text = "";
return false;
}
}
private void timer1_Tick(object sender, EventArgs e)
{
if (InternetKontrol()==false)
{
NetGelenGiden ngg = new NetGelenGiden();
ngg.GidenZaman = "Net Gitti " + DateTime.Now.ToLongTimeString().ToString();
ngg.GelenZaman = "";
dat.NetGelenGidens.Add(ngg);
dat.SaveChanges();
notifyIcon1.Icon = SystemIcons.Information;
notifyIcon1.ShowBalloonTip(3000, "Net Durum", "Net Gitti " + DateTime.Now.ToLongTimeString().ToString(),ToolTipIcon.Info);
notifyIcon1.Visible = true;
label2.Text ="Net Gitti "+ DateTime.Now.ToLongTimeString().ToString();
}
I think you are defining Timer control in a Windows Forms (that's because you don't put the timer1 declaration).
At the beginning of your event timer1_Tick, disable or stop the timer:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
timer1.Start();
}
private bool InternetKontrol() { /* your code */ }
public bool flagInternetService = true;
private void timer1_Tick(object sender, EventArgs e)
{
var newResult = InternetKontrol();
var warn = false;
if (flagInternetService != newResult){
if (newResult == false) warn = true;
flagInternetService = newResult;
}
if (warn)
{
/* your code warning */
label2.Text = DateTime.Now.ToLongTimeString();
}
}
}

WebClient Retry

Is it possible to retry a webclient request? On the odd occasion my application will throw an error when attempting to connect to an xml web service but if I retry, it works OK. I'd like it to retry 2 times before throwing an error unless someone has a better solution :)
private void ApplicationBarLogin_Click(object sender, EventArgs e)
{
settings.UsernameSetting = Username.Text;
if (RememberPassword.IsChecked == true)
{
settings.PasswordSetting = Password.Password;
settings.RememberPasswordSetting = true;
}
else
{
settings.RememberPasswordSetting = false;
}
WebClient internode = new WebClient();
internode.Credentials = new NetworkCredential(settings.UsernameSetting, settings.PasswordSetting);
internode.DownloadStringCompleted += new DownloadStringCompletedEventHandler(internode_DownloadStringCompleted);
internode.DownloadStringAsync(new Uri("https://customer-webtools-api.internode.on.net/api/v1.5/"));
}
public void internode_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show(e.Error.Message);
}
else
{
MessageBox.Show("Authentication successfull.");
}
}
If you get a failure, you could re-issue the request. By keeping count of the number of times you re-issue the request you can determine when to show the user an error. Here is a quick modification to your code to demonstrate what I mean.
private void ApplicationBarLogin_Click(object sender, EventArgs e)
{
settings.UsernameSetting = Username.Text;
if (RememberPassword.IsChecked == true)
{
settings.PasswordSetting = Password.Password;
settings.RememberPasswordSetting = true;
}
else
{
settings.RememberPasswordSetting = false;
}
WebClient internode = new WebClient();
internode.Credentials = new NetworkCredential(settings.UsernameSetting, settings.PasswordSetting);
internode.DownloadStringCompleted += new DownloadStringCompletedEventHandler(internode_DownloadStringCompleted);
internode.DownloadStringAsync(new Uri("https://customer-webtools-api.internode.on.net/api/v1.5/"));
}
private int _retryCount = 0;
public void internode_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error != null)
{
_retryCount++;
if (_retryCount < 3)
{
WebClient internode = (WebClient)sender;
internode.DownloadStringAsync(new Uri("https://customer-webtools-api.internode.on.net/api/v1.5/"));
}
else
{
_retryCount = 0;
MessageBox.Show(e.Error.Message);
}
}
else
{
_retryCount = 0;
MessageBox.Show("Authentication successfull.");
}
}
WebClient doesn't have any built in retry functionality.
You should look to build the retry logic yourself before, probably, informing the user of the problem.

C#, passing events doesn't work

I have this example code that DOES work.
main form:
FileTransferManager fm = new FileTransferManager();
...
public FrmMain()
{
InitializeComponent();
...
fm.OnFile += fm_OnFile;
}
...
void fm_OnFile(object sender, FileTransferEventArgs e)
{
var recvFile = new FrmReceiveFile(fm, e);
recvFile.Show();
e.Accept = true;
}
and FrmReceiveFile:
public partial class FrmReceiveFile : Form
{
private FileTransferManager fm;
private FileTransferEventArgs ftea;
public FrmReceiveFile(FileTransferManager ftm, FileTransferEventArgs fea)
{
InitializeComponent();
fm = ftm;
ftea = fea;
Text = "File transfer: " + ftea.Jid;
lblSize.Text = Util.HumanReadableFileSize(ftea.FileSize);
lblFileName.Text = ftea.Filename;
lblDescription.Text = ftea.Description;
fm.OnError += fm_OnError;
fm.OnEnd += fm_OnEnd;
fm.OnStart += fm_OnStart;
fm.OnProgress += fm_OnProgress;
}
void fm_OnStart(object sender, FileTransferEventArgs e)
{
MessageBox.Show("file transfer started"); ///// THIS APPEARS & EVERYTHING WORKS!
if (e.Sid != ftea.Sid)
return;
}
...
And here is my code, all in one form, yet somehow it does not work.
public partial class Form1 : Form
{
private string sid = "";
FileTransferManager fmout = new FileTransferManager(); //// this FileTransferManager is for outgoing files
FileTransferManager fmin = new FileTransferManager(); //// this FileTransferManager is for incomeing files
FileTransferEventArgs fta = new FileTransferEventArgs();
Jid _jid = new Jid();
public Form1()
{
InitializeComponent();
fmout.OnError += fmout_OnError;
fmout.OnEnd += fmout_OnEnd;
fmout.OnStart += fmout_OnStart;
fmout.OnProgress += fmout_OnProgress;
fmout.XmppClient = xmppClient;
fmin.XmppClient = xmppClient;
fmin.OnFile += fmin_OnFile;
fmin.OnEnd += fmin_OnEnd;
fmin.OnStart += fmin_OnStart;
fmin.OnProgress += fmin_OnProgress;
}
////////////////////////////////////////////////////////////////////////////////////////////
void fmin_OnFile(object sender, FileTransferEventArgs e)
{
DisplayEvent("INCOMING FILE: " + e.Filename + " - " + e.FileSize); ///// THIS APPEARS CORRECTLY
e.Accept = true;
fta = e;
}
void fmin_OnStart(object sender, FileTransferEventArgs e) /// THIS WON'T START! :(
{
MessageBox.Show("Incoming file!"); /// THIS WON'T START! :(
if (e.Sid != fta.Sid)
return;
}
Looks like e.Accept = true; does not launch fmin_OnStart ... any ideas what might be the problem?
Thanks!
The difference (that can be made out from the code you have shared) in the two pieces of code is that in the first one you are registering the "fm.OnStart += fm_OnStart" event when your "OnFileHandler" is called, while in the other one (not working) you are doing that upfront, even when your OnFileHandler is not called.
Though, as an user of FileTransferManager, i dont think that should matter.
Still, you can try the same thing in the second code.. so do it as below.
void fmin_OnFile(object sender, FileTransferEventArgs e)
{ fmin.OnStart += fmin_OnStart;
DisplayEvent("INCOMING FILE: " + e.Filename + " - " + e.FileSize);
e.Accept = true; fta = e; }
If that works, i would rather question the programmer of FileTransferManager.

Categories