2014-12-31 23:07:19 +13:00
|
|
|
|
#region License Information (GPL v3)
|
2014-09-13 23:52:12 +12:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
ShareX - A program that allows you to take screenshots and share any file type
|
2020-02-05 20:19:48 +13:00
|
|
|
|
Copyright (c) 2007-2020 ShareX Team
|
2014-09-13 23:52:12 +12:00
|
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
|
modify it under the terms of the GNU General Public License
|
|
|
|
|
as published by the Free Software Foundation; either version 2
|
|
|
|
|
of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
|
|
|
|
|
|
Optionally you can also view the license at <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#endregion License Information (GPL v3)
|
|
|
|
|
|
2020-04-22 11:03:18 +12:00
|
|
|
|
using Newtonsoft.Json;
|
2014-09-13 23:52:12 +12:00
|
|
|
|
using System;
|
2020-04-22 11:03:18 +12:00
|
|
|
|
using System.IO;
|
|
|
|
|
using System.IO.Pipes;
|
|
|
|
|
using System.Text;
|
2014-09-13 23:52:12 +12:00
|
|
|
|
using System.Threading;
|
|
|
|
|
|
2016-03-20 23:27:47 +13:00
|
|
|
|
namespace ShareX.HelpersLib
|
2014-09-13 23:52:12 +12:00
|
|
|
|
{
|
2016-03-17 22:03:08 +13:00
|
|
|
|
public class ApplicationInstanceManager : IDisposable
|
2014-09-13 23:52:12 +12:00
|
|
|
|
{
|
2016-03-20 23:44:20 +13:00
|
|
|
|
private static readonly string MutexName = "82E6AC09-0FEF-4390-AD9F-0DD3F5561EFC";
|
|
|
|
|
private static readonly string AppName = "ShareX";
|
2020-04-22 11:03:18 +12:00
|
|
|
|
private static readonly string PipeName = $"{Environment.MachineName}-{Environment.UserName}-{AppName}";
|
|
|
|
|
private static readonly string SemaphoreName = PipeName + "Semaphore";
|
2016-03-04 12:57:21 +13:00
|
|
|
|
|
2016-03-20 23:44:20 +13:00
|
|
|
|
public bool IsSingleInstance { get; private set; }
|
2016-03-18 13:10:56 +13:00
|
|
|
|
public bool IsFirstInstance { get; private set; }
|
|
|
|
|
|
2016-03-17 22:03:08 +13:00
|
|
|
|
private Mutex mutex;
|
|
|
|
|
private Semaphore semaphore;
|
2020-04-22 11:03:18 +12:00
|
|
|
|
private NamedPipeServerStream pipeServer;
|
2016-03-17 22:03:08 +13:00
|
|
|
|
|
2016-03-20 23:44:20 +13:00
|
|
|
|
public ApplicationInstanceManager(bool isSingleInstance, string[] args, EventHandler<InstanceCallbackEventArgs> callback)
|
2016-03-17 22:03:08 +13:00
|
|
|
|
{
|
2016-03-20 23:44:20 +13:00
|
|
|
|
IsSingleInstance = isSingleInstance;
|
|
|
|
|
|
|
|
|
|
mutex = new Mutex(false, MutexName);
|
2016-03-17 22:03:08 +13:00
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
2016-03-18 13:10:56 +13:00
|
|
|
|
IsFirstInstance = mutex.WaitOne(100, false);
|
2016-03-17 22:03:08 +13:00
|
|
|
|
|
2016-03-20 23:44:20 +13:00
|
|
|
|
if (IsSingleInstance && !IsFirstInstance)
|
2016-03-17 22:03:08 +13:00
|
|
|
|
{
|
2016-03-29 23:43:30 +13:00
|
|
|
|
CreateMultipleInstance(args);
|
2016-03-17 22:03:08 +13:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (AbandonedMutexException)
|
|
|
|
|
{
|
|
|
|
|
// Log the mutex was abandoned in another process, it will still get acquired
|
2017-03-29 08:59:17 +13:00
|
|
|
|
DebugHelper.WriteLine("Single instance mutex found abandoned from another process.");
|
2016-03-18 13:10:56 +13:00
|
|
|
|
IsFirstInstance = true;
|
2016-03-17 22:03:08 +13:00
|
|
|
|
}
|
|
|
|
|
|
2016-03-20 23:44:20 +13:00
|
|
|
|
CreateFirstInstance(callback);
|
2016-03-17 22:03:08 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
2016-03-18 13:10:56 +13:00
|
|
|
|
if (IsFirstInstance)
|
2016-03-17 22:03:08 +13:00
|
|
|
|
{
|
2020-04-23 04:26:56 +12:00
|
|
|
|
mutex.ReleaseMutex();
|
2016-03-17 22:03:08 +13:00
|
|
|
|
}
|
2020-04-23 04:26:56 +12:00
|
|
|
|
|
|
|
|
|
mutex.Dispose();
|
|
|
|
|
semaphore?.Dispose();
|
|
|
|
|
pipeServer?.Dispose();
|
2016-03-17 22:03:08 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CreateFirstInstance(EventHandler<InstanceCallbackEventArgs> callback)
|
2014-09-13 23:52:12 +12:00
|
|
|
|
{
|
2016-03-29 23:43:30 +13:00
|
|
|
|
try
|
2014-09-13 23:52:12 +12:00
|
|
|
|
{
|
2020-04-22 11:03:18 +12:00
|
|
|
|
semaphore = new Semaphore(1, 1, SemaphoreName, out var createdNew);
|
|
|
|
|
// Mixing single instance and multi instance (via command line parameter) copies of the program can
|
|
|
|
|
// result in CreateFirstInstance being called if it isn't really the first one. Make sure this is
|
|
|
|
|
// really first instance by detecting if the semaphore was created
|
|
|
|
|
if (!createdNew)
|
2014-09-13 23:52:12 +12:00
|
|
|
|
{
|
2020-04-22 11:03:18 +12:00
|
|
|
|
return;
|
2014-09-13 23:52:12 +12:00
|
|
|
|
}
|
2020-04-22 11:03:18 +12:00
|
|
|
|
|
|
|
|
|
CreateServer(callback);
|
2016-03-29 23:43:30 +13:00
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
DebugHelper.WriteException(e);
|
2014-09-13 23:52:12 +12:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-29 23:43:30 +13:00
|
|
|
|
private void CreateMultipleInstance(string[] args)
|
2014-09-13 23:52:12 +12:00
|
|
|
|
{
|
2016-03-29 23:43:30 +13:00
|
|
|
|
try
|
2014-09-13 23:52:12 +12:00
|
|
|
|
{
|
2020-04-22 11:03:18 +12:00
|
|
|
|
semaphore = Semaphore.OpenExisting(SemaphoreName);
|
2020-04-23 04:26:56 +12:00
|
|
|
|
|
|
|
|
|
// Wait until the server is ready to accept data
|
2020-04-22 11:03:18 +12:00
|
|
|
|
semaphore.WaitOne();
|
|
|
|
|
SendDataToServer(args);
|
2014-09-13 23:52:12 +12:00
|
|
|
|
}
|
2016-03-29 23:43:30 +13:00
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
DebugHelper.WriteException(e);
|
|
|
|
|
}
|
2014-09-13 23:52:12 +12:00
|
|
|
|
|
2016-03-11 10:29:01 +13:00
|
|
|
|
Environment.Exit(0);
|
2014-09-13 23:52:12 +12:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-22 11:03:18 +12:00
|
|
|
|
private void SendDataToServer(string[] args)
|
2014-09-13 23:52:12 +12:00
|
|
|
|
{
|
2020-04-22 11:03:18 +12:00
|
|
|
|
using (var pipeClient = new NamedPipeClientStream(".", PipeName, PipeDirection.Out))
|
|
|
|
|
{
|
|
|
|
|
pipeClient.Connect();
|
2014-09-13 23:52:12 +12:00
|
|
|
|
|
2020-04-22 11:03:18 +12:00
|
|
|
|
var pipeData = new InstanceCallbackEventArgs
|
|
|
|
|
{
|
|
|
|
|
CommandLineArgs = args
|
|
|
|
|
};
|
2014-09-13 23:52:12 +12:00
|
|
|
|
|
2020-04-22 11:03:18 +12:00
|
|
|
|
var bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(pipeData));
|
|
|
|
|
pipeClient.Write(bytes, 0, bytes.Length);
|
2014-09-13 23:52:12 +12:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-22 11:03:18 +12:00
|
|
|
|
private void CreateServer(EventHandler<InstanceCallbackEventArgs> callback)
|
2014-09-13 23:52:12 +12:00
|
|
|
|
{
|
2020-04-22 11:03:18 +12:00
|
|
|
|
pipeServer = new NamedPipeServerStream(PipeName, PipeDirection.In, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
|
|
|
|
|
pipeServer.BeginWaitForConnection(ConnectionCallback, callback);
|
2014-09-13 23:52:12 +12:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-22 11:03:18 +12:00
|
|
|
|
private void ConnectionCallback(IAsyncResult ar)
|
2014-09-13 23:52:12 +12:00
|
|
|
|
{
|
2020-04-22 11:03:18 +12:00
|
|
|
|
try
|
2016-03-17 22:03:08 +13:00
|
|
|
|
{
|
2020-04-22 11:03:18 +12:00
|
|
|
|
pipeServer.EndWaitForConnection(ar);
|
2020-04-26 16:21:20 +12:00
|
|
|
|
}
|
|
|
|
|
catch (ObjectDisposedException)
|
|
|
|
|
{
|
|
|
|
|
// Operation got aborted as part of program exit.
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-04-22 11:03:18 +12:00
|
|
|
|
|
2020-04-26 16:21:20 +12:00
|
|
|
|
var callback = ar.AsyncState as EventHandler<InstanceCallbackEventArgs>;
|
|
|
|
|
var sr = new StreamReader(pipeServer, Encoding.UTF8);
|
|
|
|
|
try
|
|
|
|
|
{
|
2020-04-22 11:03:18 +12:00
|
|
|
|
if (callback != null)
|
2016-03-18 13:10:56 +13:00
|
|
|
|
{
|
2020-04-22 11:03:18 +12:00
|
|
|
|
var data = sr.ReadToEnd();
|
|
|
|
|
callback(this, JsonConvert.DeserializeObject<InstanceCallbackEventArgs>(data));
|
2016-03-18 13:10:56 +13:00
|
|
|
|
}
|
2016-03-17 22:03:08 +13:00
|
|
|
|
}
|
2020-04-22 11:03:18 +12:00
|
|
|
|
finally
|
|
|
|
|
{
|
2020-04-23 04:26:56 +12:00
|
|
|
|
// Close the existing server
|
2020-04-22 11:03:18 +12:00
|
|
|
|
sr.Dispose();
|
2020-04-23 04:26:56 +12:00
|
|
|
|
|
|
|
|
|
// Create a new server
|
2020-04-22 11:03:18 +12:00
|
|
|
|
CreateServer(callback);
|
2020-04-23 04:26:56 +12:00
|
|
|
|
|
|
|
|
|
// Signal that we are ready to accept a new connection
|
|
|
|
|
semaphore.Release();
|
2020-04-22 11:03:18 +12:00
|
|
|
|
}
|
2016-03-20 23:27:47 +13:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class InstanceCallbackEventArgs : EventArgs
|
|
|
|
|
{
|
2020-04-22 11:03:18 +12:00
|
|
|
|
[JsonProperty]
|
|
|
|
|
public string[] CommandLineArgs { get; internal set; }
|
2016-03-20 23:27:47 +13:00
|
|
|
|
}
|
2014-09-13 23:52:12 +12:00
|
|
|
|
}
|