Introduction
Programs like Microsoft Word used to use MDI for displaying multiple windows,
but users would often "lose" windows behind each other. Instead, each document
which is opened is now placed in its own window, and displayed in the task bar.
However, only one instance handles all the windows.
This code sample shows how to setup your own application with this functionality.
In particular, when the application is called with a file name as a command line argument,
it checks for a previous instance. If found, it uses interprocess communication, to pass the
file name to the running instance, then quits. The running application then opens a new window for the new file name.
Background
The interprocess communication uses code from the following article:
Please read for more details on the interprocess communication code.
Using the code
SingleInstance Project complies to SingleInstance.exe, which is the sample application.
TestHarness Project compiles to TestHarness.exe, which can be used to call SingleInstance.exe
from a windows form with various command line arguments.
There are various ways to make the main form invisible. Setting Visible = false
is not enough, as when the form is activated, it will become visible automatically.
Another trick is to set the form's Opacity = 0
, which prevent the form being "flashed"
when it starts up. You can also position the form so it out of the bounds of the screen.
Note that we must use an invisible form so we can receive window messages. I couldn't discover an
easy way of starting a message handling loop without including a form.
FrmMain.cs Make Invisible
public FrmMain()
{
InitializeComponent();
this.SetBounds( -1000, -1000, 100, 100 );
this.Visible = false;
initialize();
}
If we are the first instance, start
FrmMain
. If there is a previous instance running,
send it the file name which should be opened, and quit this instance.
FrmMain.cs Main method
static void Main(string[] args)
{
Process[] processes = Process.GetProcessesByName(
Process.GetCurrentProcess().ProcessName);
int length = processes.Length;
string fileName = string.Empty;
if ( args.Length > 0 )
fileName = args[0];
if ( length == 1 )
{
FrmMain frmMain = new FrmMain();
Form form = new FrmChild(fileName);
frmMain.AddForm( form );
Application.Run(frmMain);
return;
}
if ( length > 1 )
{
CopyData copyData = new CopyData();
copyData.Channels.Add("HcClientFrmMain");
copyData.Channels["HcClientFrmMain"].Send(fileName);
return;
}
}
During initialization, setup interprocess communication class, and get ready to handle receive message event.
FrmMain.cs Initialization
private void initialize()
{
forms = new ArrayList();
copyData = new CopyData();
copyData.AssignHandle(this.Handle);
copyData.DataReceived += new DataReceivedEventHandler(copyData_DataReceived);
}
When we (the previous instance) receive the file name, open a new window using the file name.
For demonstration purposes, the file name is just appended to the child window title.
FrmMain.cs Data Received Event Handler
private void copyData_DataReceived(object sender, DataReceivedEventArgs e)
{
if (e.ChannelName.Equals("HcClientFrmMain"))
{
string fileName = (string) e.Data;
FrmChild form = new FrmChild( fileName );
AddForm( form );
}
}
Note that
FrmMain
keeps track of the open child forms,
and when the last form closes,
FrmMain
closes.
FrmMain.cs AddForm
public void AddForm( Form form )
{
form.Closed += new EventHandler(OnFormClosed);
forms.Add ( form );
form.Show();
}
Points of Interest
Note that CopyData.Channels[x].Send( parameter )
can send any object which can
be serialized, not just a string
. Internally, it sends a <ode>
WM_COPYDATA
message
between the applications. This communication only works for processes running locally.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.