BeginInvoke的奇怪行为 [英] Weird behaviour with BeginInvoke

查看:67
本文介绍了BeginInvoke的奇怪行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Hello World,



我的代码中遇到了一种异常行为(很可能是)BeginInvoke。我有一个登录表单,每次用户尝试登录时都会生成一个ID,ID授权会写入一个文件,登录表单正在使用FileSystemWatcher查看。在授权时,生成由主表单捕获的事件。



现在我的问题是,在验证时会显示旧ID,即使每次登录过程中都创建了一个新的LoginForm实例。第一次strID变量中的ID值是正确的,但从那时起,每次连续登录时,都会显示先前的ID。



我很难用正确的文字解释它,所以我附上登录表格的代码。我面临的奇怪行为是

  if (arrData [ 0 ] ==  .strID)

GetAuthorizationStatus函数的一部分。请帮助我理解这是否是预期的行为或我在这里做错了什么。



登录表单有3个按钮:btnLogin用于获取ID,btnExit用于退出form和btnAuth进行身份验证(我把它放在这里进行测试,而不是另一个应用程序/服务)



以下是代码

< pre lang =c#> 使用系统;
使用 System.Collections.Generic;
使用 System.ComponentModel;
使用 System.Drawing;
使用 System.Text;
使用 System.IO;
使用 System.Windows.Forms;

命名空间 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;
.isAuthorized = isAuthorized;
}

public string ID
{
获取 {返回 。 strID; }
}

public DateTime LoginAt
{
get { return this .dtLoginAt; }
}

public bool IsAuthorized
{
获取 {返回 .isAuthorized; }
}
}

public partial < span class =code-keyword> 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);

.strID = ;
}

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

私有 void GetAuthorizationStatus( )
{
if (InvokeRequired)
{
BeginInvoke( new ShowDelegate(GetAuthorizationStatus));
}
else
{
string strData = ;
tmrTimeout.Enabled = false ;

使用(FileStream stream = new FileStream(FILE_PATH,FileMode.Open, FileAccess.Read,FileShare.ReadWrite))
{
使用(StreamReader streamReader = new StreamReader(stream))
{
try
{
strData = streamReader.ReadToEnd();
}
catch (例外)
{
// 日志错误
}
}
}

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

if (arrData.Length == 2
{

if (arrData [ 0 ] == this .strID)
{
if (arrData [ 1 ] == Y
{
tmrTimeout。已启用= false ;
.lblWait.Visible = false ;
.btnExit.Enabled = true ;
.btnLogin.Enabled = false ;
.isAuthorized = true ;
this .strLoginErrorMessage = ;

onAuthOccurred( new AuthEventArgs( this .strID, this .dtLoginAt, this .isAuthorized));
this .Close();
}
其他
{
// < span class =code-comment>授权失败

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

// 也清除局部变量
.strID = ;
this .dtLoginAt = DateTime.MinValue;
.isAuthorized = false ;
this .strLoginErrorMessage = 授权被拒绝。 ;
}
}
}

如果(!this.isAuthorized&&& this .strLoginErrorMessage!=
{
MessageBox.Show( this .strLoginErrorMessage, 授权被拒绝,MessageBoxButtons.OK,MessageBoxIcon.Information);
}
}
}

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

// 也清除局部变量
.strID = ;
.isAuthorized = false ;
this .dtLoginAt = DateTime.MinValue;

MessageBox.Show( 等待授权时发生超时。 授权超时,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();

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

private void btnExit_Click( object sender,EventArgs e)
{
if (MessageBox.Show( 是否要取消登录? 取消登录,MessageBoxButtons.OKCancel,MessageBoxIcon.Question)== DialogResult.OK)
{
// 清除本地变量
.strID = ;
.isAuthorized = false ;
this .dtLoginAt = DateTime.MinValue;
// 引发事件并退出
onAuthOccurred( new AuthEventArgs( this .strID, this .dtLoginAt, .isAuthorized));
this .Close();
}
}

void BtnAuthClick( object sender,EventArgs e)
{
File.WriteAllText(FILE_PATH, string .Format( {0},Y .strID));
}
}
}

解决方案

在代码中发现问题。我同时在其他网站上寻找解决方案,以下是Hans Passant对stackoverflow的评论



这个程序有一个非常讨厌的bug,它没有处理当窗口关闭时FSW。它只是保持驾驶,继续观察事件。这通常会导致一个ObjectDisposedException跳闸,它没有必要。当然你会使用陈旧的ID。更糟糕的是当你发生什么再次显示该表单,你将有两个FSW写入文件。这不是它结束的地方,当你同时运行程序两次时完成失败的鲸鱼。你需要认真地重新思考你将如何做到这一点,它现在的方式无法正常工作。



我必须简单地将FSW作为私有变量并在我关闭登录表单时处理FSW这就解决了这个问题。



感谢您提供有用的评论并尝试使用代码Florian Braun。非常感谢。


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

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

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

解决方案

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.


这篇关于BeginInvoke的奇怪行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆