1: using System;
2: using System.Diagnostics;
3: using System.Runtime.Remoting;
4: using System.Runtime.Remoting.Channels;
5: using System.Runtime.Remoting.Channels.Ipc;
6: using System.Threading;
7:
8: namespace SingleInstanceApplication
9: {
10: /// <summary>
11: /// Application Instance Manager
12: /// </summary>
13: public static class ApplicationInstanceManager
14: {
15: /// <summary>
16: /// Creates the single instance.
17: /// </summary>
18: /// <param name="name">The name.</param>
19: /// <param name="callback">The callback.</param>
20: /// <returns></returns>
21: public static bool CreateSingleInstance(string name, EventHandler<InstanceCallbackEventArgs> callback)
22: {
23: EventWaitHandle eventWaitHandle = null;
24: string eventName = string.Format("{0}-{1}", Environment.MachineName, name);
25:
26: InstanceProxy.IsFirstInstance = false;
27: InstanceProxy.CommandLineArgs = Environment.GetCommandLineArgs();
28:
29: try
30: {
31: // try opening existing wait handle
32: eventWaitHandle = EventWaitHandle.OpenExisting(eventName);
33: }
34: catch
35: {
36: // got exception = handle wasn't created yet
37: InstanceProxy.IsFirstInstance = true;
38: }
39:
40: if (InstanceProxy.IsFirstInstance)
41: {
42: // init handle
43: eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName);
44:
45: // register wait handle for this instance (process)
46: ThreadPool.RegisterWaitForSingleObject(eventWaitHandle, WaitOrTimerCallback, callback, Timeout.Infinite, false);
47: eventWaitHandle.Close();
48:
49: // register shared type (used to pass data between processes)
50: RegisterRemoteType(name);
51: }
52: else
53: {
54: // pass console arguments to shared object
55: UpdateRemoteObject(name);
56:
57: // invoke (signal) wait handle on other process
58: if (eventWaitHandle != null) eventWaitHandle.Set();
59:
60:
61: // kill current process
62: Environment.Exit(0);
63: }
64:
65: return InstanceProxy.IsFirstInstance;
66: }
67:
68: /// <summary>
69: /// Updates the remote object.
70: /// </summary>
71: /// <param name="uri">The remote URI.</param>
72: private static void UpdateRemoteObject(string uri)
73: {
74: // register net-pipe channel
75: var clientChannel = new IpcClientChannel();
76: ChannelServices.RegisterChannel(clientChannel, true);
77:
78: // get shared object from other process
79: var proxy =
80: Activator.GetObject(typeof(InstanceProxy),
81: string.Format("ipc://{0}{1}/{1}", Environment.MachineName, uri)) as InstanceProxy;
82:
83: // pass current command line args to proxy
84: if (proxy != null)
85: proxy.SetCommandLineArgs(InstanceProxy.IsFirstInstance, InstanceProxy.CommandLineArgs);
86:
87: // close current client channel
88: ChannelServices.UnregisterChannel(clientChannel);
89: }
90:
91: /// <summary>
92: /// Registers the remote type.
93: /// </summary>
94: /// <param name="uri">The URI.</param>
95: private static void RegisterRemoteType(string uri)
96: {
97: // register remote channel (net-pipes)
98: var serverChannel = new IpcServerChannel(Environment.MachineName + uri);
99: ChannelServices.RegisterChannel(serverChannel, true);
100:
101: // register shared type
102: RemotingConfiguration.RegisterWellKnownServiceType(
103: typeof(InstanceProxy), uri, WellKnownObjectMode.Singleton);
104:
105: // close channel, on process exit
106: Process process = Process.GetCurrentProcess();
107: process.Exited += delegate { ChannelServices.UnregisterChannel(serverChannel); };
108: }
109:
110: /// <summary>
111: /// Wait Or Timer Callback Handler
112: /// </summary>
113: /// <param name="state">The state.</param>
114: /// <param name="timedOut">if set to <c>true</c> [timed out].</param>
115: private static void WaitOrTimerCallback(object state, bool timedOut)
116: {
117: // cast to event handler
118: var callback = state as EventHandler<InstanceCallbackEventArgs>;
119: if (callback == null) return;
120:
121: // invoke event handler on other process
122: callback(state,
123: new InstanceCallbackEventArgs(InstanceProxy.IsFirstInstance,
124: InstanceProxy.CommandLineArgs));
125: }
126: }
127: }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }