Click here to Skip to main content
15,891,316 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hello World,

I am facing an unusual behaviour in my code with (most probably) BeginInvoke. I have a login form on which an ID is generated each time user tries to login, the ID authorization is written to a file, which login form is watching using FileSystemWatcher. On authorization an event is generated which is captured by main form.

Now my issue is that old IDs show up at the time of verification, even though each time a new instance of LoginForm in created at the time of login process. First time the ID value in strID variable is correct but from then on for each consecutive login, previous IDs show up.

It is difficult for me to explain it in words correctly, so I am attaching the code for login form. The weird behaviour I am facing is at
C#
if(arrData[0] == this.strID)
part of GetAuthorizationStatus function. Please help me understand whether this is an expected behaviour or am I doing something wrong here.

Login form have 3 buttons : btnLogin for getting the ID, btnExit to exit the form and btnAuth for authenticating (I have put it here for testing, instead of another app/service)

Following is the code
C#
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.IO;
using System.Windows.Forms;

namespace SampleInvokeTest
{
	public static class IDGenerator
	{
		private static int iCurrentID;
		
		static IDGenerator()
		{
			iCurrentID = 0;
		}
		
		public static int GetNewID()
		{
			return ++iCurrentID;
		}
	}

    public class AuthEventArgs : EventArgs
    {
        private string strID;
        private DateTime dtLoginAt;
        private bool isAuthorized;

        public AuthEventArgs(string ID, DateTime LoginAt, bool isAuthorized)
        {
            this.strID = ID;
            this.dtLoginAt = LoginAt;
            this.isAuthorized = isAuthorized;
        }

        public string ID
        {
            get{ return this.strID; }
        }
        
        public DateTime LoginAt
        {
            get{ return this.dtLoginAt; }
        }

        public bool IsAuthorized
        {
            get{ return this.isAuthorized; }
        }
    }

    public partial class LoginForm : Form
    {
        delegate void ShowDelegate();

        private string strID;
        private const string FILE_PATH = @"E:\test.txt";
        private DateTime dtLastResultReadTime = DateTime.MinValue;
        private DateTime dtLoginAt = DateTime.MinValue;
        private bool isAuthorized = false;
        private string strLoginErrorMessage ="";

        public event EventHandler<AuthEventArgs> AuthOccurred; 
        
        public LoginForm()
		{
			InitializeComponent();
						
			FileSystemWatcher fswAuth = new FileSystemWatcher(Path.GetDirectoryName(FILE_PATH));
			fswAuth.Filter = Path.GetFileName(FILE_PATH);
			fswAuth.NotifyFilter = NotifyFilters.LastWrite;
			fswAuth.Changed+= new FileSystemEventHandler(fswAuth_Changed);
			fswAuth.EnableRaisingEvents = true;
			
			tmrTimeout.Interval = 5000;
			tmrTimeout.Tick+= new EventHandler(tmrTimeout_Tick);

            this.strID = "";
		}

        void fswAuth_Changed(object sender, FileSystemEventArgs e)
        {
            DateTime dtTempReadAt = File.GetLastWriteTime(FILE_PATH);
            if (dtLastResultReadTime < dtTempReadAt)
            {
                dtLastResultReadTime = dtTempReadAt;
                GetAuthorizationStatus();
            }
        }

        private void GetAuthorizationStatus()
        {
            if (InvokeRequired)
            {
                BeginInvoke(new ShowDelegate(GetAuthorizationStatus));
            }
            else
            {
                string strData = ",";
				tmrTimeout.Enabled = false;
				
                using (FileStream stream = new FileStream(FILE_PATH, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                {
                    using (StreamReader streamReader = new StreamReader(stream))
                    {
                        try
                        {
                            strData = streamReader.ReadToEnd();                            
                        }
                        catch (Exception)
                        {
                            //log error
                        }
                    }
                }

                string[] arrData = strData.Split(new char[] { ',' });

                if (arrData.Length == 2)
                {
                    
                	if(arrData[0] == this.strID)
                	{
	                    if (arrData[1] == "Y")
	                    {
	                        tmrTimeout.Enabled = false;
	                        this.lblWait.Visible = false;
	                        this.btnExit.Enabled = true;
	                        this.btnLogin.Enabled = false;
	                        this.isAuthorized = true;
	                        this.strLoginErrorMessage = "";
	                    
	                        onAuthOccurred(new AuthEventArgs(this.strID, this.dtLoginAt, this.isAuthorized));
	                        this.Close();
	                    }
	                    else
	                    {
	                        //Authorization failed
	                        tmrTimeout.Enabled = false;
	                        this.lblWait.Visible = false;
	                        this.btnExit.Enabled = true;
	                        this.btnLogin.Enabled = true;
	
	                        //also clear local variables
	                        this.strID = "";
	                        this.dtLoginAt = DateTime.MinValue;
	                        this.isAuthorized = false;
	                        this.strLoginErrorMessage = "Authorization denied.";
	                    }
                	}
                }

                if (!this.isAuthorized && this.strLoginErrorMessage != "")
                {
                    MessageBox.Show(this.strLoginErrorMessage, "Authorization denied", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
        }

        void tmrTimeout_Tick(object sender, EventArgs e)
        {
            tmrTimeout.Enabled = false;
            this.lblWait.Visible = false;
            this.btnExit.Enabled = true;
            this.btnLogin.Enabled = true;

            //also clear local variables
            this.strID="";
            this.isAuthorized = false;
            this.dtLoginAt = DateTime.MinValue;

            MessageBox.Show("Timeout occurred while waiting for authorization.", "Authorization timeout", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void onAuthOccurred(AuthEventArgs e)
        {
            EventHandler<AuthEventArgs> handler = AuthOccurred;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        private void btnLogin_Click(object sender, EventArgs e)
        {
            this.dtLoginAt = DateTime.Now;
            this.strID = IDGenerator.GetNewID().ToString();

            this.btnLogin.Enabled = false;
            this.btnExit.Enabled = false;
            tmrTimeout.Enabled = true;
            lblWait.Visible = true;
        }

        private void btnExit_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show("Do you wish to cancel login?", "Cancel Login", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK)
            {
                //Clear local variables
                this.strID="";
                this.isAuthorized = false;
                this.dtLoginAt = DateTime.MinValue;
                //raise event and exit
                onAuthOccurred(new AuthEventArgs(this.strID, this.dtLoginAt, this.isAuthorized));
                this.Close();
            }
        }
		
		void BtnAuthClick(object sender, EventArgs e)
		{
			File.WriteAllText(FILE_PATH,string.Format("{0},Y",this.strID));
		}
    }
}
Posted
Updated 22-Apr-15 1:55am
v3
Comments
Florian Braun 22-Apr-15 7:48am    
I tried to compile your code but it seems the most interesting part is missing:
this.strID = IDGenerator.GetNewID().ToString();

as far as I understand your problem you get the same value for this.strID each time you press btnLogin? So I need more information about this IDGenerator and the GetNewID. Because as far as i can see thats where the problem is?
EkBanda 22-Apr-15 7:53am    
Thanks for the reply. Sorry, forgot to add ID generator code. As I was creating a sample project for testing the behaviour, I have kept it as simple as possible. I have updated the code in original question.
Florian Braun 22-Apr-15 8:32am    
hey... i tested this code and it works fine so far... i get a new ID each time i open the login form and i get a valid authentication with your samle
EkBanda 22-Apr-15 8:35am    
Hmmm.. I have tested it on .net 4.0, may I know which framework version are you using? Also, following is the code of main form calling login form.

void BtnShowLoginClick(object sender, EventArgs e)
{
LoginForm f1 = new LoginForm();
f1.AuthOccurred+= new EventHandler<autheventargs>(f1_AuthOccurred);
f1.ShowDialog();
f1.Dispose();
}

void f1_AuthOccurred(object sender, AuthEventArgs e)
{
this.txtResult.Text += string.Format("{0},{1},{2},{3}",e.ID, e.IsAuthorized, e.LoginAt.ToString(),Environment.NewLine);
}

Also, have you checked by creating a breakpoint, at the point in code I have mentioned in my original post?

Please try this by joining these two checks in one check case

if(arrData[0] == this.strID) and if (arrData[1] == "Y")
to
if(arrData[0] == this.strID && arrData[1] == "Y")

Thanks & Regards
Florian Braun 22-Apr-15 8:47am    
i tried net 4.5 and net4.0
worked both times.

this is my main form:
void Button1Click(object sender, EventArgs e)
{
var mf=new MainForm();
mf.AuthOccurred+=onAuthOccurred;
mf.ShowDialog();
mf.Dispose();
}
void onAuthOccurred(object sender, AuthEventArgs e)
{
MessageBox.Show(String.Format("Auth: {0}, {1}, {2}",e.ID,e.IsAuthorized,e.LoginAt));
}

i set a breakpoint to the first if(arrData[0]==this.strID) and checkedthe contend of both arrData and this.strID. of course it was equal each time ind increasing by one each time...

I also couldnt find any mistake in code which would explain what happens.

1 solution

Found the problem in the code. I was simultaneously looking for the solution on other sites also and following is the comment by Hans Passant on stackoverflow

"This program has a very nasty bug, it does not dispose the FSW when the window closes. It just keeps motoring, continuing to watch for events. That usually tends to trip an ObjectDisposedException, it doesn't have to. Of course you'll use a stale ID. Much worse is what happens when you show the form again, you'll have two FSWs writing to the file. That's not where it ends, complete fail whale when you run the program twice at the same time. You need to seriously re-think how you are going to do this, it cannot work the way you have it now."

I have to simply make the FSW a private variable and dispose the FSW at the time I close my login form and that solved the problem.

Thanks for the helpful comments and trying out the code Florian Braun. Much appreciated.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900