1. //
  2. // SingleInstance.cs
  3. //
  4. // Author: Shams Madhani {sdotmadhaniatgmxdotcom}
  5. //
  6. // Licensed under BSD or compatible license.
  7. // Redistribution and use in source and binary forms, with or without modification,
  8. // are permitted provided that the conditions set forth by BSD license are met.
  9. //
  10. // References: To be added..
  11. //
  12. using System;
  13. using System.IO;
  14. //using System.IO.Pipes;
  15. using System.Threading;
  16. using System.Collections.Generic;
  17. using System.Security.Permissions;
  18. namespace singleinstance
  19. {
  20. public class ArgumentsReceivedEventArgs : EventArgs
  21. {
  22. public String[] Args { get; set; }
  23. }
  24. public class SingleInstance : IDisposable
  25. {
  26. public static string argfilepath = Environment.CurrentDirectory;
  27. public static string argfile = "newargs.txt";
  28. public Mutex mutex = null;
  29. private Boolean ownsMutex = false;
  30. public Guid identifier = Guid.Empty;
  31. FileSystemWatcher watcher = new FileSystemWatcher();
  32. DateTime lastRead = DateTime.MinValue;
  33. /// <summary>
  34. /// Enforces single instance for an application.
  35. /// </summary>
  36. /// <param name="identifier">An identifier unique to this application.</param>
  37. public SingleInstance(Guid identifier)
  38. {
  39. this.identifier = identifier;
  40. mutex = new Mutex(true, identifier.ToString(), out ownsMutex);
  41. }
  42. /// <summary>
  43. /// Indicates whether this is the first instance of this application.
  44. /// </summary>
  45. public Boolean IsFirstInstance
  46. { get { return ownsMutex; } }
  47. /// <summary>
  48. /// Passes the given arguments to the first running instance of the application.
  49. /// </summary>
  50. /// <param name="arguments">The arguments to pass.</param>
  51. /// <returns>Return true if the operation succeded, false otherwise.</returns>
  52. public Boolean PassArgumentsToFirstInstance(String[] arguments)
  53. {
  54. if (IsFirstInstance)
  55. throw new InvalidOperationException("This is the first instance.");
  56. if (!File.Exists(argfile))
  57. File.Create(argfile);
  58. File.WriteAllText(argfile, "");
  59. foreach (string argument in arguments)
  60. {
  61. File.AppendAllText(argfile, argument+Environment.NewLine);
  62. }
  63. return false;
  64. }
  65. /// <summary>
  66. /// Listens for arguments being passed from successive instances of the applicaiton.
  67. /// </summary>
  68. public void ListenForArgumentsFromSuccessiveInstances()
  69. {
  70. if (!IsFirstInstance)
  71. throw new InvalidOperationException("This is not the first instance.");
  72. ThreadPool.QueueUserWorkItem(new WaitCallback(ListenForArguments));
  73. GC.KeepAlive(mutex);
  74. Thread.Sleep(1000);
  75. }
  76. /// <summary>
  77. /// Listens for arguments on a named pipe.
  78. /// </summary>
  79. /// <param name="state">State object required by WaitCallback delegate.</param>
  80. private void ListenForArguments(Object state)
  81. {
  82. if (!File.Exists(argfile))
  83. File.Create(argfile);
  84. watcher.Path = argfilepath;
  85. watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size;
  86. watcher.Filter = argfile;
  87. watcher.Changed += new FileSystemEventHandler(OnChanged);
  88. watcher.IncludeSubdirectories = false;
  89. watcher.EnableRaisingEvents = true;
  90. }
  91. private void OnChanged(Object state, FileSystemEventArgs e)
  92. {
  93. DateTime lastWriteTime = File.GetLastWriteTime(argfile);
  94. if (lastWriteTime != lastRead)
  95. {
  96. try
  97. {
  98. watcher.EnableRaisingEvents = false;
  99. Console.WriteLine("This is the first instance.");
  100. Thread.Sleep(1000);
  101. try
  102. {
  103. String line;
  104. List<String> arguments = new List<String>();
  105. using (StreamReader sr = new StreamReader(argfile))
  106. {
  107. while (sr.Peek() >= 0)
  108. {
  109. line = sr.ReadLine();
  110. arguments.Add(line);
  111. }
  112. sr.Dispose();
  113. }
  114. ThreadPool.QueueUserWorkItem(new WaitCallback(CallOnArgumentsReceived), arguments.ToArray());
  115. Thread.Sleep(1000);
  116. }
  117. catch (Exception exp1)
  118. {
  119. // Let the user know what went wrong.
  120. Console.WriteLine("The file could not be read:");
  121. Console.WriteLine(exp1.Message);
  122. }
  123. }
  124. finally
  125. {
  126. //Thread.Sleep(1000);
  127. watcher.EnableRaisingEvents = true;
  128. }
  129. lastRead = lastWriteTime;
  130. }
  131. }
  132. /// <summary>
  133. /// Calls the OnArgumentsReceived method casting the state Object to String[].
  134. /// </summary>
  135. /// <param name="state">The arguments to pass.</param>
  136. private void CallOnArgumentsReceived(Object state)
  137. {
  138. OnArgumentsReceived((String[])state);
  139. }
  140. /// <summary>
  141. /// Event raised when arguments are received from successive instances.
  142. /// </summary>
  143. public event EventHandler<ArgumentsReceivedEventArgs> ArgumentsReceived;
  144. /// <summary>
  145. /// Fires the ArgumentsReceived event.
  146. /// </summary>
  147. /// <param name="arguments">The arguments to pass with the ArgumentsReceivedEventArgs.</param>
  148. private void OnArgumentsReceived(String[] arguments)
  149. {
  150. if (ArgumentsReceived != null)
  151. ArgumentsReceived(this, new ArgumentsReceivedEventArgs() { Args = arguments });
  152. }
  153. #region IDisposable
  154. private Boolean disposed = false;
  155. protected virtual void Dispose(bool disposing)
  156. {
  157. if (!disposed)
  158. {
  159. if (mutex != null && ownsMutex)
  160. {
  161. mutex.ReleaseMutex();
  162. mutex = null;
  163. }
  164. disposed = true;
  165. }
  166. }
  167. ~SingleInstance()
  168. {
  169. Dispose(false);
  170. }
  171. public void Dispose()
  172. {
  173. Dispose(true);
  174. GC.SuppressFinalize(this);
  175. }
  176. #endregion
  177. }
  178. }

Single instance command line application in C# using Mono