Mutex not releasing

时光总嘲笑我的痴心妄想 提交于 2019-12-31 03:17:28

问题


My c# WinForm solution contains several projects including an Admin project containing frmAdmin and a User project containing frmUser. A third project contains frmTimer that has a timer that periodically launches frmUser.

I want frmTimer to not launch frmUser when frmAdmin is open.

I'm using a named mutex to tell frmTimer if frmAdmin is open; however, the mutex appears not to be released after frmAdmin is closed.

The mutex is created in frmAdmin with code like this:

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

Initially, I did not have any mutex code in the frmAdmin_FormClosing method (the method only contained the Application.Exit() line). I added the mutex code in an attempt to release the mutex, but it is still not being released.

The mutex is used in frmTimer like this:

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

When I run the application, the messagebox with the 'Debug 4' text appears each time the timer fires until I open frmAdmin (frmAdmin is launched from frmLogin after password verification), from then on the messagebox with the 'Debug 3' text appears each time the timer fires, even after I exit frmAdmin. When exiting frmAdmin, I see the messagebox with the 'Debug 2' text. I've never seen the messagebox (or an output window message) with the 'Debug 1' text.

It appears as though the mutex doesn't release after frmAdmin is closed and this prevents frmUser from launching.

Any help is appreciated.

This is a follow-up question to this question.

UPDATE

Here is my code after getting it to work. I got it to work because of the answers from Hans Passant and Chris Taylor and from Serhio from this post.

The mutex is now created in frmAdmin with code like this:

    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.
            }
        }
    }

The mutex is used in frmTimer like this:

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

回答1:


The problem is in the Elapsed event handler, it checks if the mutex exists with Mutex.OpenExisting(). Sure it exists. You are not actually checking if it is signaled. That takes calling its WaitOne(0) method.

Also beware that creating a form in a Timer.Elapsed event is quite inappropriate. That event runs a threadpool thread, it is not at all suitable to act as a UI thread. It has the wrong COM state ([STAThread] and Thread.SetApartmentState), a property you cannot change on a threadpool thread. Use a regular Form.Timer instead so that the form gets created on program's UI thread.

Edit: also beware of the inevitable race, the timer could create the User form one microsecond before the Admin form closes. In other words, you'll have a User form without an Admin form, the one condition you wrote this code to prevent. Is that appropriate? Trying forms in different processes to affect each other is a bad idea...




回答2:


The problem is that once you run the admin application the Mutex exists and then the OpenExisting succeeds. Releasing a Mutex does not destroy the Kernel object, it just releases the hold on the mutex so that other waiting threads can execute. Therefore susequent Mutex.OpenExisting calls open the mutex successfully.

You probably want to use Mutex.WaitOne(TimeSpan) if you successfully open the Mutex and if WaitOne returns false then you know you you could not acquire the mutex so the Admin application still hold the mutex.



来源:https://stackoverflow.com/questions/3275527/mutex-not-releasing

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!