我的c#WinForm解决方案包含几个项目,包括一个包含frmAdmin的Admin项目和一个包含frmUser的User项目。第三个项目包含frmTimer,它有一个定时启动frmUser的计时器。
我希望frmTimer在frmAdmin打开时不启动frmUser。
如果frmAdmin打开,我正在使用一个名为mutex来告诉frmTimer;但是,frmAdmin关闭后,互斥锁似乎不会被释放。
互斥体是在frmAdmin中创建的,代码如下:
public partial class frmAdmin : Form
{
Mutex m;
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
m = new Mutex(true, "frmAdmin");
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
m.ReleaseMutex();
MessageBox.Show("Debug 1 -- In the frmAdmin ONCLOSED Event."); //test code
Debug.WriteLine("Debug 1 -- In the frmAdmin ONCLOSED Event."); //test code
}
public frmAdmin(string strPassedFromLogin)
{
InitializeComponent();
<<Code snipped>>
}
private void frmAdmin_FormClosing(object sender, FormClosingEventArgs e)
{
//Start _ Added
bool mutexSet = true;
try
{
Mutex.OpenExisting("frmAdmin");
MessageBox.Show("Debug 2 -- In the frmAdmin FORMCLOSING Event."); //test code
}
catch (WaitHandleCannotBeOpenedException)
{
mutexSet = false;
}
if (mutexSet)
{
base.OnClosed(e);
m.ReleaseMutex();
}
//End _ Added
Application.Exit();
}
<<Code snipped>>
}
最初,我在frmAdmin_FormClosing方法中没有任何互斥代码(该方法只包含Application.Exit()行)。我添加了互斥代码以试图释放互斥锁,但它仍然没有被释放。
互斥体用于frmTimer,如下所示:
private void tmTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
bool adminIsOpen = true;
try
{
Mutex.OpenExisting("frmAdmin");
MessageBox.Show("Debug 3 -- Mutex exists: frmAdmin IS open."); //test code
}
catch (WaitHandleCannotBeOpenedException)
{
adminIsOpen = false;
MessageBox.Show("Debug 4 -- Mutex doesn't exists: frmAdmin is NOT open."); //test code
}
if (adminIsOpen == false)
{
//frmAdmin is closed; go ahead and open frmUser.
<<Code snipped>>
}
}
当我运行应用程序时,每次计时器触发时都会出现带有“Debug 4”文本的消息框,直到我打开frmAdmin(在密码验证后从frmLogin启动frmAdmin),然后在带有“Debug 3”的消息框上打开即使在我退出frmAdmin之后,每次计时器触发时都会出现文本。退出frmAdmin时,我看到带有“Debug 2”文本的消息框。我从未见过带有'Debug 1'文本的消息框(或输出窗口消息)。
似乎在frmAdmin关闭后互斥锁不会释放,这会阻止frmUser启动。
感谢任何帮助。
这是this问题的后续问题。
更新
这是我的代码,让它开始工作。我得到了它的工作,因为Hans Passant和Chris Taylor以及来自this帖子的Serhio的答案。
现在在frmAdmin中创建了互斥锁,代码如下:
Mutex m;
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
m = new Mutex(true, "frmAdmin");
}
//This 'OnClosed' event is skipped when this application is terminated using only Exit(); therefore, call Close() before calling Exit().
//The 'catch' code is added to insure the program keeps running in the event these exceptions occur.
protected override void OnClosed(EventArgs e)
{
if (m != null)
{
try
{
base.OnClosed(e);
m.ReleaseMutex();
m.Close();
}
catch (AbandonedMutexException)
{
//This catch is included to insure the program keeps running in the event this exception occurs.
}
catch (ApplicationException)
{
//This catch is included to insure the program keeps running in the event this exception occurs.
}
catch (SynchronizationLockException)
{
//This catch is included to insure the program keeps running in the event this exception occurs.
}
}
}
互斥体用于frmTimer,如下所示:
private void tmTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
bool adminIsOpen = false;
Mutex _muty = null;
try
{
//If the named mutex does not exist then OpenExisting will throw the 'WaitHandleCannotBeOpenedException',
//otherwise the mutex exists and Admin is open.
_muty = Mutex.OpenExisting("frmAdmin");
adminIsOpen = true;
_muty.Close();
}
catch (WaitHandleCannotBeOpenedException)
{
//This catch is thrown when Admin is not opened (keep 'adminIsOpen = false'). Do not delete this catch.
}
catch (AbandonedMutexException)
{
//This catch is included to insure the program keeps running in the event this exception occurs.
}
if (adminIsOpen == false)
{
//frmAdmin is closed; go ahead and open frmUser.
<<Code snipped>>
}
}
答案 0 :(得分:3)
问题是,一旦运行管理应用程序,Mutex就会存在,然后OpenExisting会成功。释放互斥锁不会破坏内核对象,它只是释放互斥锁上的保留,以便其他等待的线程可以执行。因此,随后的Mutex.OpenExisting调用会成功打开互斥锁。
如果您成功打开Mutex并且WaitOne返回false,您可能想要使用Mutex.WaitOne(TimeSpan),那么您知道您无法获取互斥锁,因此Admin应用程序仍然保留互斥锁。
答案 1 :(得分:3)
问题在于Elapsed事件处理程序,它使用Mutex.OpenExisting()检查互斥锁是否存在。当然存在。您实际上并未检查是否已发出信号。这需要调用WaitOne(0)方法。
还要注意在Timer.Elapsed事件中创建表单是不合适的。该事件运行一个线程池线程,它完全不适合充当UI线程。它具有错误的COM状态([STAThread]和Thread.SetApartmentState),这是一个在线程池线程上无法更改的属性。使用常规的Form.Timer,以便在程序的UI线程上创建表单。
编辑:还要注意不可避免的竞争,计时器可以在管理表单关闭之前创建一个微秒的用户表单。换句话说,您将拥有一个没有Admin表单的User表单,这是您编写此代码以防止的一个条件。这合适吗?在不同的过程中尝试形式互相影响是一个坏主意......