More Zero deployment

G

Gary

Can anyone suggest a way of making sure that no more that
one instance of a zero deployed app can be run?

Can't use the standard method of checking if a process
already exists because it executes as IIEXEC and not
the .exe name.

Any suggestions?

Cheers

Gary
 
B

Bart Jacobs

If your app is untrusted, you can use isolated storage and
FileMode.CreateNew to find out if you're the instance that should
display the GUI. But activating the main window of another instance does
not seem to be possible (short of polling the isolated store or
communicating via the originating server).

If your app is fully trusted, you can use a combination of mutexes, a
semaphore, and broadcast window messages. (You could also try to use
FindWindow, but that would be more work, I think.)

Specifically, you would use:
- two named mutexes, called e.g. MyAppStartMutex and MyAppGuiMutex
(instances of System.Threading.Mutex);
- one named semaphore, called e.g. MyAppSemaphore (created using
CreateSemaphore in kernel32.dll);
- and one window message ID, called e.g. MyAppMessage, registered using
the RegisterWindowMessage user32.dll API.

When an instance starts, it first acquires the MyAppStartMutex. Then, it
broadcasts a MyAppMessage to all top-level windows on the user's desktop
(using the PostMessage user32.dll API). Then, it waits for any of the
MyAppSemaphore and the MyAppGuiMutex (using WaitHandle.WaitAny). If
MyAppSemaphore is signaled, the instance can die because this means some
other instance received the MyAppMessage and activated its main window.
If MyAppGuiMutex is signaled, the instance can show the GUI itself.

In the former case, the MyAppStartMutex is released when the instance
dies. In the latter case, the MyAppStartMutex should be released only
after the instance has a message queue and a top-level window, so that
MyAppMessages sent by other instances will be received.

The instance should listen for MyAppMessages by adding a message filter
to the System.Windows.Forms.Application.

When the instance receives a MyAppMessage, it activates its main window
and signals the MyAppSemaphore.

This scheme is intended to guarantee that 1) no two instances show a GUI
at any one time, and 2) soon after an instance is started, either a new
GUI is shown or an existing GUI is activated and the instance dies.

Sample code:

---------------------------------------------
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace NoTouchOneInstance
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;

private OneInstanceHelper helper;

public Form1(OneInstanceHelper helper)
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

this.helper = helper;
helper.ActivationRequest += new EventHandler(OnActivationRequest);
}

void OnActivationRequest(object source, EventArgs args)
{
Activate();
}

protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
helper.CanReceiveTopLevelMessages();
}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 266);
this.Name = "Form1";
this.Text = "No-Touch One Instance";

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
using (OneInstanceHelper helper = new
OneInstanceHelper("NoTouchOneInstanceTest"))
{
if (!helper.TryActivateExistingInstance())
{
Application.Run(new Form1(helper));
}
else
{
MessageBox.Show("An existing instance was activated.");
}
}
}
}
}

---------------------------------------------

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;

namespace NoTouchOneInstance
{
/// <summary>
/// Provides support for making sure that only one instance of an
application
/// runs at any time.
/// </summary>
public sealed class OneInstanceHelper : IDisposable
{
[DllImport("user32.dll", SetLastError=true)]
static extern int RegisterWindowMessage(string name);

[DllImport("user32.dll", SetLastError=true)]
static extern bool PostMessage(IntPtr handle, int msg, int wParam, int
lParam);

class MessageFilter : IMessageFilter
{
OneInstanceHelper owner;

internal MessageFilter(OneInstanceHelper owner)
{
this.owner = owner;
}

#region IMessageFilter Members

public bool PreFilterMessage(ref Message m)
{
if (m.Msg == owner.msg)
{
if (owner.ActivationRequest != null)
owner.ActivationRequest(owner, new EventArgs());
owner.semaphore.Release();
}

return false;
}

#endregion

}

const int HWND_BROADCAST = 0xffff;

string name;
int msg;
Mutex guiMutex;
Mutex startMutex;
Semaphore semaphore;
MessageFilter messageFilter;

public event EventHandler ActivationRequest;

public OneInstanceHelper(string name)
{
this.name = name;
}

/// <summary>
/// Attempts to active an existing instance of this application.
/// </summary>
/// <returns><c>true</c> if an existing instance was activated;
/// false if this instance is the first instance.</returns>
public bool TryActivateExistingInstance()
{
msg = RegisterWindowMessage(name);
if (msg == 0)
throw new Win32Exception();
startMutex = new Mutex(false, name + "StartMutex");
startMutex.WaitOne();
if (!PostMessage(new IntPtr(HWND_BROADCAST), msg, 0, 0))
throw new Win32Exception();
guiMutex = new Mutex(false, name + "GuiMutex");
semaphore = new Semaphore(0, int.MaxValue, name + "Semaphore");
int result = WaitHandle.WaitAny(new WaitHandle[] {semaphore, guiMutex});
bool success = (result & 63) == 0;
if (!success)
{
messageFilter = new MessageFilter(this);
Application.AddMessageFilter(messageFilter);
}
return success;
}

public void CanReceiveTopLevelMessages()
{
startMutex.Close();
startMutex = null;
}

#region IDisposable Members

public void Dispose()
{
if (startMutex != null)
startMutex.Close();
if (guiMutex != null)
guiMutex.Close();
if (semaphore != null)
semaphore.Close();
}

#endregion
}
}

---------------------------------------------

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Threading;

namespace NoTouchOneInstance
{
/// <summary>
/// Encapsulates a Windows API semaphore.
/// </summary>
public sealed class Semaphore : WaitHandle
{
[DllImport("kernel32.dll", SetLastError=true)]
static extern IntPtr CreateSemaphore(IntPtr securityAttributes, int
initialCount, int maximumCount, string name);

[DllImport("kernel32.dll", SetLastError=true)]
static extern bool ReleaseSemaphore(HandleRef handle, int
releaseCount, out int previousCount);

// TODO: Ensure finalization-safety and disposal-safety.
// TODO: Make sure that Win32 errors are not ignored.
public Semaphore(int initialCount, int maximumCount, string name)
{
IntPtr handle = CreateSemaphore(IntPtr.Zero, initialCount,
maximumCount, name);
if (handle == IntPtr.Zero)
throw new Win32Exception();
Handle = handle;
// The following call prevents the scenario where the object is finalized
// before the new handle is assigned to the Handle property.
// In other words, the following call ensures that the finalizer for this
// object sees the newly created handle and, as a result, the handle will
// be closed when the AppDomain is unloaded (or earlier).
GC.KeepAlive(this);
}

public void Release(int releaseCount, out int previousCount)
{
if (!ReleaseSemaphore(new HandleRef(this, Handle), releaseCount, out
previousCount))
throw new Win32Exception();
}

public void Release()
{
int previousCount;
Release(1, out previousCount);
}
}
}

Hope this helps...

Bart
 
Y

Ying-Shen Yu[MSFT]

Hi Gary,
Bart has given an excellent reply, does it solve your problem?
I'll monitor this issue for a few days, if you have any questions on this
issue, please be free to reply in this group.
Thank you!

Bart, thank you very much for your great sharing!


Best regards,

Ying-Shen Yu [MSFT]
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security

This posting is provided "AS IS" with no warranties and confers no rights.
You should not reply this mail directly, "Online" should be removed before
sending, Thanks!
 
G

Gary

Thanks for the very comprehensive response Bart.

Much appreciated.

Gary
-----Original Message-----
If your app is untrusted, you can use isolated storage and
FileMode.CreateNew to find out if you're the instance that should
display the GUI. But activating the main window of another instance does
not seem to be possible (short of polling the isolated store or
communicating via the originating server).

If your app is fully trusted, you can use a combination of mutexes, a
semaphore, and broadcast window messages. (You could also try to use
FindWindow, but that would be more work, I think.)

Specifically, you would use:
- two named mutexes, called e.g. MyAppStartMutex and MyAppGuiMutex
(instances of System.Threading.Mutex);
- one named semaphore, called e.g. MyAppSemaphore (created using
CreateSemaphore in kernel32.dll);
- and one window message ID, called e.g. MyAppMessage, registered using
the RegisterWindowMessage user32.dll API.

When an instance starts, it first acquires the MyAppStartMutex. Then, it
broadcasts a MyAppMessage to all top-level windows on the user's desktop
(using the PostMessage user32.dll API). Then, it waits for any of the
MyAppSemaphore and the MyAppGuiMutex (using WaitHandle.WaitAny). If
MyAppSemaphore is signaled, the instance can die because this means some
other instance received the MyAppMessage and activated its main window.
If MyAppGuiMutex is signaled, the instance can show the GUI itself.

In the former case, the MyAppStartMutex is released when the instance
dies. In the latter case, the MyAppStartMutex should be released only
after the instance has a message queue and a top-level window, so that
MyAppMessages sent by other instances will be received.

The instance should listen for MyAppMessages by adding a message filter
to the System.Windows.Forms.Application.

When the instance receives a MyAppMessage, it activates its main window
and signals the MyAppSemaphore.

This scheme is intended to guarantee that 1) no two instances show a GUI
at any one time, and 2) soon after an instance is started, either a new
GUI is shown or an existing GUI is activated and the instance dies.

Sample code:

---------------------------------------------
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace NoTouchOneInstance
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;

private OneInstanceHelper helper;

public Form1(OneInstanceHelper helper)
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

this.helper = helper;
helper.ActivationRequest += new EventHandler(OnActivationRequest);
}

void OnActivationRequest(object source, EventArgs args)
{
Activate();
}

protected override void OnHandleCreated (EventArgs e)
{
base.OnHandleCreated(e);
helper.CanReceiveTopLevelMessages ();
}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose ();
}
}
base.Dispose( disposing );
}

#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 266);
this.Name = "Form1";
this.Text = "No-Touch One Instance";

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
using (OneInstanceHelper helper = new
OneInstanceHelper("NoTouchOneInstanceTest"))
{
if (! helper.TryActivateExistingInstance())
{
Application.Run (new Form1(helper));
}
else
{
MessageBox.Show
("An existing instance was activated.");
}
}
}
}
}

---------------------------------------------

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;

namespace NoTouchOneInstance
{
/// <summary>
/// Provides support for making sure that only one instance of an
application
/// runs at any time.
/// </summary>
public sealed class OneInstanceHelper : IDisposable
{
[DllImport("user32.dll", SetLastError=true)]
static extern int RegisterWindowMessage (string name);

[DllImport("user32.dll", SetLastError=true)]
static extern bool PostMessage(IntPtr
handle, int msg, int wParam, int
lParam);

class MessageFilter : IMessageFilter
{
OneInstanceHelper owner;

internal MessageFilter (OneInstanceHelper owner)
{
this.owner = owner;
}

#region IMessageFilter Members

public bool PreFilterMessage(ref Message m)
{
if (m.Msg == owner.msg)
{
if
(owner.ActivationRequest != null)owner.ActivationRequest(owner, new EventArgs());
owner.semaphore.Release();
}

return false;
}

#endregion

}

const int HWND_BROADCAST = 0xffff;

string name;
int msg;
Mutex guiMutex;
Mutex startMutex;
Semaphore semaphore;
MessageFilter messageFilter;

public event EventHandler ActivationRequest;

public OneInstanceHelper(string name)
{
this.name = name;
}

/// <summary>
/// Attempts to active an existing instance of this application.
/// </summary>
/// <returns><c>true</c> if an existing instance was activated;
/// false if this instance is the first
instance. said:
public bool TryActivateExistingInstance()
{
msg = RegisterWindowMessage(name);
if (msg == 0)
throw new Win32Exception();
startMutex = new Mutex(false, name + "StartMutex");
startMutex.WaitOne();
if (!PostMessage(new IntPtr (HWND_BROADCAST), msg, 0, 0))
throw new Win32Exception();
guiMutex = new Mutex(false, name + "GuiMutex");
semaphore = new Semaphore(0,
int.MaxValue, name + "Semaphore");
int result = WaitHandle.WaitAny
(new WaitHandle[] {semaphore, guiMutex});
bool success = (result & 63) == 0;
if (!success)
{
messageFilter = new MessageFilter(this);Application.AddMessageFilter(messageFilter);
}
return success;
}

public void CanReceiveTopLevelMessages()
{
startMutex.Close();
startMutex = null;
}

#region IDisposable Members

public void Dispose()
{
if (startMutex != null)
startMutex.Close();
if (guiMutex != null)
guiMutex.Close();
if (semaphore != null)
semaphore.Close();
}

#endregion
}
}

---------------------------------------------

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Threading;

namespace NoTouchOneInstance
{
/// <summary>
/// Encapsulates a Windows API semaphore.
/// </summary>
public sealed class Semaphore : WaitHandle
{
[DllImport("kernel32.dll", SetLastError=true)]
static extern IntPtr CreateSemaphore
(IntPtr securityAttributes, int
initialCount, int maximumCount, string name);

[DllImport("kernel32.dll", SetLastError=true)]
static extern bool ReleaseSemaphore (HandleRef handle, int
releaseCount, out int previousCount);

// TODO: Ensure finalization-safety and disposal-safety.
// TODO: Make sure that Win32 errors are not ignored.
public Semaphore(int initialCount, int maximumCount, string name)
{
IntPtr handle = CreateSemaphore (IntPtr.Zero, initialCount,
maximumCount, name);
if (handle == IntPtr.Zero)
throw new Win32Exception();
Handle = handle;
// The following call prevents the
scenario where the object is finalized
// before the new handle is
assigned to the Handle property.
// In other words, the following
call ensures that the finalizer for this
// object sees the newly created
handle and, as a result, the handle will
// be closed when the AppDomain is unloaded (or earlier).
GC.KeepAlive(this);
}

public void Release(int releaseCount, out int previousCount)
{
if (!ReleaseSemaphore(new HandleRef
(this, Handle), releaseCount, out
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top