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