From f5ce4fcf49801499de72685e98a1ad164b45e3e7 Mon Sep 17 00:00:00 2001 From: Jaex Date: Sat, 13 Sep 2014 14:52:12 +0300 Subject: [PATCH] fixed #280: Reverted single instance codes --- HelpersLib/HelpersLib.csproj | 2 + .../ApplicationInstanceManager.cs | 122 +++++++++++++++ .../InstanceProxy.cs | 41 +++-- ShareX/Forms/MainForm.cs | 9 +- ShareX/Program.cs | 141 ++++++++---------- ShareX/ShareX.csproj | 1 - 6 files changed, 215 insertions(+), 101 deletions(-) create mode 100644 HelpersLib/SingleInstanceApplication/ApplicationInstanceManager.cs rename ShareX/ShareXApplicationBase.cs => HelpersLib/SingleInstanceApplication/InstanceProxy.cs (53%) diff --git a/HelpersLib/HelpersLib.csproj b/HelpersLib/HelpersLib.csproj index 35ec7661b..59bc87017 100644 --- a/HelpersLib/HelpersLib.csproj +++ b/HelpersLib/HelpersLib.csproj @@ -314,6 +314,8 @@ + + UserControl diff --git a/HelpersLib/SingleInstanceApplication/ApplicationInstanceManager.cs b/HelpersLib/SingleInstanceApplication/ApplicationInstanceManager.cs new file mode 100644 index 000000000..ca1e4dea8 --- /dev/null +++ b/HelpersLib/SingleInstanceApplication/ApplicationInstanceManager.cs @@ -0,0 +1,122 @@ +#region License Information (GPL v3) + +/* + ShareX - A program that allows you to take screenshots and share any file type + Copyright (C) 2007-2014 ShareX Developers + + 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 . +*/ + +#endregion License Information (GPL v3) + +using System; +using System.Diagnostics; +using System.Runtime.Remoting; +using System.Runtime.Remoting.Channels; +using System.Runtime.Remoting.Channels.Ipc; +using System.Threading; +using System.Windows.Forms; + +namespace SingleInstanceApplication +{ + public static class ApplicationInstanceManager + { + [DebuggerStepThrough] + public static bool CreateSingleInstance(string name, EventHandler callback, string[] args) + { + string eventName = string.Format("{0}-{1}", Environment.MachineName, name); + + InstanceProxy.IsFirstInstance = false; + InstanceProxy.CommandLineArgs = args; + + try + { + using (EventWaitHandle eventWaitHandle = EventWaitHandle.OpenExisting(eventName)) + { + UpdateRemoteObject(name); + + if (eventWaitHandle != null) eventWaitHandle.Set(); + } + + Environment.Exit(0); + } + catch + { + InstanceProxy.IsFirstInstance = true; + + using (EventWaitHandle eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName)) + { + ThreadPool.RegisterWaitForSingleObject(eventWaitHandle, WaitOrTimerCallback, callback, Timeout.Infinite, false); + } + + RegisterRemoteType(name); + } + + return InstanceProxy.IsFirstInstance; + } + + public static bool CreateSingleInstance(EventHandler callback, string[] args) + { + try + { + return CreateSingleInstance(Application.ProductName, callback, args); + } + catch + { + } + + return true; + } + + private static void UpdateRemoteObject(string uri) + { + IpcClientChannel clientChannel = new IpcClientChannel(); + ChannelServices.RegisterChannel(clientChannel, true); + + InstanceProxy proxy = Activator.GetObject(typeof(InstanceProxy), string.Format("ipc://{0}{1}/{1}", Environment.MachineName, uri)) as InstanceProxy; + + if (proxy != null) + { + proxy.SetCommandLineArgs(InstanceProxy.IsFirstInstance, InstanceProxy.CommandLineArgs); + } + + ChannelServices.UnregisterChannel(clientChannel); + } + + private static void RegisterRemoteType(string uri) + { + IpcServerChannel serverChannel = new IpcServerChannel(Environment.MachineName + uri); + ChannelServices.RegisterChannel(serverChannel, true); + + RemotingConfiguration.RegisterWellKnownServiceType(typeof(InstanceProxy), uri, WellKnownObjectMode.Singleton); + + Process process = Process.GetCurrentProcess(); + process.Exited += delegate + { + ChannelServices.UnregisterChannel(serverChannel); + }; + } + + private static void WaitOrTimerCallback(object state, bool timedOut) + { + EventHandler callback = state as EventHandler; + if (callback == null) return; + + callback(state, new InstanceCallbackEventArgs(InstanceProxy.IsFirstInstance, InstanceProxy.CommandLineArgs)); + } + } +} \ No newline at end of file diff --git a/ShareX/ShareXApplicationBase.cs b/HelpersLib/SingleInstanceApplication/InstanceProxy.cs similarity index 53% rename from ShareX/ShareXApplicationBase.cs rename to HelpersLib/SingleInstanceApplication/InstanceProxy.cs index 337480839..fb6b0ca72 100644 --- a/ShareX/ShareXApplicationBase.cs +++ b/HelpersLib/SingleInstanceApplication/InstanceProxy.cs @@ -1,4 +1,4 @@ -#region License Information (GPL v3) +#region License Information (GPL v3) /* ShareX - A program that allows you to take screenshots and share any file type @@ -23,27 +23,36 @@ You should have received a copy of the GNU General Public License #endregion License Information (GPL v3) -using Microsoft.VisualBasic.ApplicationServices; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Windows.Forms; +using System.Security.Permissions; -namespace ShareX +namespace SingleInstanceApplication { - internal class ShareXApplicationBase : WindowsFormsApplicationBase + [Serializable] + [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] + internal class InstanceProxy : MarshalByRefObject { - public ShareXApplicationBase(bool isSingleInstance) - { - IsSingleInstance = isSingleInstance; - EnableVisualStyles = true; - } + public static bool IsFirstInstance { get; internal set; } - public new Form MainForm + public static string[] CommandLineArgs { get; internal set; } + + public void SetCommandLineArgs(bool isFirstInstance, string[] commandLineArgs) { - get { return base.MainForm; } - set { base.MainForm = value; } + IsFirstInstance = isFirstInstance; + CommandLineArgs = commandLineArgs; } } + + public class InstanceCallbackEventArgs : EventArgs + { + internal InstanceCallbackEventArgs(bool isFirstInstance, string[] commandLineArgs) + { + IsFirstInstance = isFirstInstance; + CommandLineArgs = commandLineArgs; + } + + public bool IsFirstInstance { get; private set; } + + public string[] CommandLineArgs { get; private set; } + } } \ No newline at end of file diff --git a/ShareX/Forms/MainForm.cs b/ShareX/Forms/MainForm.cs index db53acb6e..7f098bfbf 100644 --- a/ShareX/Forms/MainForm.cs +++ b/ShareX/Forms/MainForm.cs @@ -41,7 +41,7 @@ namespace ShareX { public partial class MainForm : HotkeyForm { - public ManualResetEvent ReadyWaitHandle { get; private set; } + public bool IsReady { get; private set; } private bool forceClose; private UploadInfoManager uim; @@ -49,7 +49,6 @@ public partial class MainForm : HotkeyForm public MainForm() { - ReadyWaitHandle = new ManualResetEvent(false); InitControls(); HandleCreated += MainForm_HandleCreated; } @@ -63,7 +62,7 @@ private void MainForm_HandleCreated(object sender, EventArgs e) AutoCheckUpdate(); #endif - ReadyWaitHandle.Set(); + IsReady = true; DebugHelper.WriteLine("Startup time: {0} ms", Program.StartTimer.ElapsedMilliseconds); @@ -678,7 +677,7 @@ private void MainForm_Resize(object sender, EventArgs e) private void MainForm_LocationChanged(object sender, EventArgs e) { - if (ReadyWaitHandle.WaitOne(0) && WindowState == FormWindowState.Normal) + if (IsReady && WindowState == FormWindowState.Normal) { Program.Settings.MainFormPosition = Location; } @@ -686,7 +685,7 @@ private void MainForm_LocationChanged(object sender, EventArgs e) private void MainForm_SizeChanged(object sender, EventArgs e) { - if (ReadyWaitHandle.WaitOne(0) && WindowState == FormWindowState.Normal) + if (IsReady && WindowState == FormWindowState.Normal) { Program.Settings.MainFormSize = Size; } diff --git a/ShareX/Program.cs b/ShareX/Program.cs index 3816617d6..4f1c11a57 100644 --- a/ShareX/Program.cs +++ b/ShareX/Program.cs @@ -24,16 +24,14 @@ You should have received a copy of the GNU General Public License #endregion License Information (GPL v3) using HelpersLib; -using Microsoft.VisualBasic.ApplicationServices; +using SingleInstanceApplication; using System; using System.Diagnostics; using System.IO; -using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Text; using System.Threading; -using System.Threading.Tasks; using System.Windows.Forms; using UploadersLib; @@ -231,6 +229,7 @@ public static string Title } public static string[] Arguments { get; private set; } + public static bool IsMultiInstance { get; private set; } public static bool IsPortable { get; private set; } public static bool IsSilentRun { get; private set; } public static bool IsSandbox { get; private set; } @@ -249,8 +248,6 @@ public static string Title public static HotkeyManager HotkeyManager { get; set; } public static WatchFolderManager WatchFolderManager { get; set; } - private static ShareXApplicationBase applicationBase; - [STAThread] private static void Main(string[] args) { @@ -263,24 +260,15 @@ private static void Main(string[] args) if (CheckAdminTasks()) return; // If ShareX opened just for be able to execute task as Admin - bool forceMultiInstance = CLIHelper.CheckArgs(Arguments, "multi", "m"); + IsMultiInstance = CLIHelper.CheckArgs(Arguments, "multi", "m"); - Application.SetCompatibleTextRenderingDefault(false); - applicationBase = new ShareXApplicationBase(!forceMultiInstance); - applicationBase.Startup += StartupHandler; - applicationBase.StartupNextInstance += StartupNextInstanceHandler; - applicationBase.Shutdown += ShutdownHandler; - try + if (IsMultiInstance || ApplicationInstanceManager.CreateSingleInstance(SingleInstanceCallback, Arguments)) { - applicationBase.Run(Arguments); - } - catch (CantStartSingleInstanceException) - { - MessageBox.Show("Couldn't launch the application."); + Run(); } } - private static void StartupHandler(object sender, StartupEventArgs e) + private static void Run() { string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value.ToString(); @@ -308,6 +296,9 @@ private static void StartupHandler(object sender, StartupEventArgs e) } } + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + DebugHelper.WriteLine("{0} started", Title); DebugHelper.WriteLine("Operating system: " + Environment.OSVersion.VersionString); DebugHelper.WriteLine("Command line: " + Environment.CommandLine); @@ -326,75 +317,24 @@ private static void StartupHandler(object sender, StartupEventArgs e) DebugHelper.WriteLine("MainForm init started"); MainForm = new MainForm(); - applicationBase.MainForm = MainForm; DebugHelper.WriteLine("MainForm init finished"); if (Settings == null) { SettingsResetEvent.WaitOne(); } + + Application.Run(MainForm); + + if (WatchFolderManager != null) WatchFolderManager.Dispose(); + SaveSettings(); + BackupSettings(); + + DebugHelper.WriteLine("ShareX closing"); + DebugHelper.Logger.SaveLog(LogsFilePath); } } - private static void StartupNextInstanceHandler(object sender, StartupNextInstanceEventArgs e) - { - e.BringToForeground = false; - - string[] args = e.CommandLine.ToArray(); - - if (MainForm.ReadyWaitHandle.WaitOne(0)) - { - DoStartupNextInstance(args); - } - else - { - TaskEx.Run(() => WaitFormLoad(args)); - } - } - - private static void WaitFormLoad(string[] args) - { - try - { - if (MainForm.ReadyWaitHandle.WaitOne(5000)) - { - MainForm.InvokeSafe(() => DoStartupNextInstance(args)); - } - } - catch { } - } - - private static void DoStartupNextInstance(string[] args) - { - if (args == null || args.Length == 0) - { - if (MainForm.niTray != null && MainForm.niTray.Visible) - { - // Workaround for Windows startup tray icon bug - MainForm.niTray.Visible = false; - MainForm.niTray.Visible = true; - } - - MainForm.ShowActivate(); - } - else if (MainForm.Visible) - { - MainForm.ShowActivate(); - } - - MainForm.UseCommandLineArgs(args); - } - - private static void ShutdownHandler(object sender, EventArgs e) - { - if (WatchFolderManager != null) WatchFolderManager.Dispose(); - SaveSettings(); - BackupSettings(); - - DebugHelper.WriteLine("ShareX closing"); - DebugHelper.Logger.SaveLog(LogsFilePath); - } - public static void LoadSettings() { LoadProgramSettings(); @@ -500,7 +440,7 @@ private static void Application_ThreadException(object sender, ThreadExceptionEv OnError(e.Exception); } - private static void CurrentDomain_UnhandledException(object sender, System.UnhandledExceptionEventArgs e) + private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { OnError((Exception)e.ExceptionObject); } @@ -513,6 +453,49 @@ private static void OnError(Exception e) } } + private static void SingleInstanceCallback(object sender, InstanceCallbackEventArgs args) + { + if (WaitFormLoad(5000)) + { + Action d = () => + { + if (args.CommandLineArgs == null || args.CommandLineArgs.Length < 1) + { + if (MainForm.niTray != null && MainForm.niTray.Visible) + { + // Workaround for Windows startup tray icon bug + MainForm.niTray.Visible = false; + MainForm.niTray.Visible = true; + } + + MainForm.ShowActivate(); + } + else if (MainForm.Visible) + { + MainForm.ShowActivate(); + } + + MainForm.UseCommandLineArgs(args.CommandLineArgs); + }; + + MainForm.InvokeSafe(d); + } + } + + private static bool WaitFormLoad(int wait) + { + Stopwatch timer = Stopwatch.StartNew(); + + while (timer.ElapsedMilliseconds < wait) + { + if (MainForm != null && MainForm.IsReady) return true; + + Thread.Sleep(10); + } + + return false; + } + public static void ConfigureUploadersConfigWatcher() { if (Program.Settings.DetectUploaderConfigFileChanges && uploaderConfigWatcher == null) diff --git a/ShareX/ShareX.csproj b/ShareX/ShareX.csproj index 5a401638d..03368c315 100644 --- a/ShareX/ShareX.csproj +++ b/ShareX/ShareX.csproj @@ -228,7 +228,6 @@ True Resources.resx -