Browse Source

Move all code related to single instance logic to separate class (#2871)

pull/2877/head
Christoph Wille 3 years ago committed by GitHub
parent
commit
94982b5368
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 123
      ILSpy/App.xaml.cs
  2. 2
      ILSpy/MainWindow.xaml.cs
  3. 154
      ILSpy/SingleInstanceHandling.cs

123
ILSpy/App.xaml.cs

@ -24,7 +24,6 @@ using System.Linq; @@ -24,7 +24,6 @@ using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Documents;
@ -47,7 +46,6 @@ namespace ICSharpCode.ILSpy @@ -47,7 +46,6 @@ namespace ICSharpCode.ILSpy
{
internal static CommandLineArguments CommandLineArguments;
internal static readonly IList<ExceptionData> StartupExceptions = new List<ExceptionData>();
internal static Mutex SingleInstanceMutex;
public static ExportProvider ExportProvider { get; private set; }
public static IExportProviderFactory ExportProviderFactory { get; private set; }
@ -64,38 +62,14 @@ namespace ICSharpCode.ILSpy @@ -64,38 +62,14 @@ namespace ICSharpCode.ILSpy
var cmdArgs = Environment.GetCommandLineArgs().Skip(1);
App.CommandLineArguments = new CommandLineArguments(cmdArgs);
bool forceSingleInstance = (App.CommandLineArguments.SingleInstance ?? true)
&& !MiscSettingsPanel.CurrentMiscSettings.AllowMultipleInstances;
if (forceSingleInstance)
{
bool isFirst;
try
{
SingleInstanceMutex = new Mutex(initiallyOwned: true, @"Local\ILSpyInstance", out isFirst);
}
catch (WaitHandleCannotBeOpenedException)
{
isFirst = true;
}
if (!isFirst)
{
try
{
SingleInstanceMutex.WaitOne(10000);
}
catch (AbandonedMutexException)
{
// continue, there is no concurrent start happening.
}
}
cmdArgs = cmdArgs.Select(FullyQualifyPath);
string message = string.Join(Environment.NewLine, cmdArgs);
if (SendToPreviousInstance("ILSpy:\r\n" + message, !App.CommandLineArguments.NoActivate))
{
ReleaseSingleInstanceMutex();
Environment.Exit(0);
}
SingleInstanceHandling.ForceSingleInstance(cmdArgs);
}
InitializeComponent();
Resources.RegisterDefaultStyles();
@ -114,20 +88,6 @@ namespace ICSharpCode.ILSpy @@ -114,20 +88,6 @@ namespace ICSharpCode.ILSpy
ILSpyTraceListener.Install();
}
internal static void ReleaseSingleInstanceMutex()
{
var mutex = SingleInstanceMutex;
SingleInstanceMutex = null;
if (mutex == null)
{
return;
}
using (mutex)
{
mutex.ReleaseMutex();
}
}
static Assembly ResolvePluginDependencies(AssemblyLoadContext context, AssemblyName assemblyName)
{
var rootPath = Path.GetDirectoryName(typeof(App).Assembly.Location);
@ -210,22 +170,6 @@ namespace ICSharpCode.ILSpy @@ -210,22 +170,6 @@ namespace ICSharpCode.ILSpy
base.OnStartup(e);
}
string FullyQualifyPath(string argument)
{
// Fully qualify the paths before passing them to another process,
// because that process might use a different current directory.
if (string.IsNullOrEmpty(argument) || argument[0] == '/')
return argument;
try
{
return Path.Combine(Environment.CurrentDirectory, argument);
}
catch (ArgumentException)
{
return argument;
}
}
void DotNet40_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
{
// On .NET 4.0, an unobserved exception in a task terminates the process unless we mark it as observed
@ -282,67 +226,6 @@ namespace ICSharpCode.ILSpy @@ -282,67 +226,6 @@ namespace ICSharpCode.ILSpy
}
#endregion
#region Pass Command Line Arguments to previous instance
bool SendToPreviousInstance(string message, bool activate)
{
string ownProcessName;
using (var ownProcess = Process.GetCurrentProcess())
{
ownProcessName = ownProcess.ProcessName;
}
bool success = false;
NativeMethods.EnumWindows(
(hWnd, lParam) => {
string windowTitle = NativeMethods.GetWindowText(hWnd, 100);
if (windowTitle.StartsWith("ILSpy", StringComparison.Ordinal))
{
string processName = NativeMethods.GetProcessNameFromWindow(hWnd);
Debug.WriteLine("Found {0:x4}: '{1}' in '{2}'", hWnd, windowTitle, processName);
if (string.Equals(processName, ownProcessName, StringComparison.OrdinalIgnoreCase))
{
IntPtr result = Send(hWnd, message);
Debug.WriteLine("WM_COPYDATA result: {0:x8}", result);
if (result == (IntPtr)1)
{
if (activate)
NativeMethods.SetForegroundWindow(hWnd);
success = true;
return false; // stop enumeration
}
}
}
return true; // continue enumeration
}, IntPtr.Zero);
return success;
}
unsafe static IntPtr Send(IntPtr hWnd, string message)
{
const uint SMTO_NORMAL = 0;
CopyDataStruct lParam;
lParam.Padding = IntPtr.Zero;
lParam.Size = message.Length * 2;
fixed (char* buffer = message)
{
lParam.Buffer = (IntPtr)buffer;
IntPtr result;
// SendMessage with 3s timeout (e.g. when the target process is stopped in the debugger)
if (NativeMethods.SendMessageTimeout(
hWnd, NativeMethods.WM_COPYDATA, IntPtr.Zero, ref lParam,
SMTO_NORMAL, 3000, out result) != IntPtr.Zero)
{
return result;
}
else
{
return IntPtr.Zero;
}
}
}
#endregion
void Window_RequestNavigate(object sender, RequestNavigateEventArgs e)
{
ILSpy.MainWindow.Instance.NavigateTo(e);

2
ILSpy/MainWindow.xaml.cs

@ -581,7 +581,7 @@ namespace ICSharpCode.ILSpy @@ -581,7 +581,7 @@ namespace ICSharpCode.ILSpy
{
hwndSource.AddHook(WndProc);
}
App.ReleaseSingleInstanceMutex();
SingleInstanceHandling.ReleaseSingleInstanceMutex();
// Validate and Set Window Bounds
Rect bounds = Rect.Transform(sessionSettings.WindowBounds, source.CompositionTarget.TransformToDevice);
var boundsRect = new System.Drawing.Rectangle((int)bounds.Left, (int)bounds.Top, (int)bounds.Width, (int)bounds.Height);

154
ILSpy/SingleInstanceHandling.cs

@ -0,0 +1,154 @@ @@ -0,0 +1,154 @@
// Copyright (c) 2022 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
namespace ICSharpCode.ILSpy
{
internal static class SingleInstanceHandling
{
internal static Mutex SingleInstanceMutex;
internal static void ForceSingleInstance(IEnumerable<string> cmdArgs)
{
bool isFirst;
try
{
SingleInstanceMutex = new Mutex(initiallyOwned: true, @"Local\ILSpyInstance", out isFirst);
}
catch (WaitHandleCannotBeOpenedException)
{
isFirst = true;
}
if (!isFirst)
{
try
{
SingleInstanceMutex.WaitOne(10000);
}
catch (AbandonedMutexException)
{
// continue, there is no concurrent start happening.
}
}
cmdArgs = cmdArgs.Select(FullyQualifyPath);
string message = string.Join(Environment.NewLine, cmdArgs);
if (SendToPreviousInstance("ILSpy:\r\n" + message, !App.CommandLineArguments.NoActivate))
{
ReleaseSingleInstanceMutex();
Environment.Exit(0);
}
}
internal static string FullyQualifyPath(string argument)
{
// Fully qualify the paths before passing them to another process,
// because that process might use a different current directory.
if (string.IsNullOrEmpty(argument) || argument[0] == '/')
return argument;
try
{
return Path.Combine(Environment.CurrentDirectory, argument);
}
catch (ArgumentException)
{
return argument;
}
}
internal static void ReleaseSingleInstanceMutex()
{
var mutex = SingleInstanceMutex;
SingleInstanceMutex = null;
if (mutex == null)
{
return;
}
using (mutex)
{
mutex.ReleaseMutex();
}
}
#region Pass Command Line Arguments to previous instance
internal static bool SendToPreviousInstance(string message, bool activate)
{
string ownProcessName;
using (var ownProcess = Process.GetCurrentProcess())
{
ownProcessName = ownProcess.ProcessName;
}
bool success = false;
NativeMethods.EnumWindows(
(hWnd, lParam) => {
string windowTitle = NativeMethods.GetWindowText(hWnd, 100);
if (windowTitle.StartsWith("ILSpy", StringComparison.Ordinal))
{
string processName = NativeMethods.GetProcessNameFromWindow(hWnd);
Debug.WriteLine("Found {0:x4}: '{1}' in '{2}'", hWnd, windowTitle, processName);
if (string.Equals(processName, ownProcessName, StringComparison.OrdinalIgnoreCase))
{
IntPtr result = Send(hWnd, message);
Debug.WriteLine("WM_COPYDATA result: {0:x8}", result);
if (result == (IntPtr)1)
{
if (activate)
NativeMethods.SetForegroundWindow(hWnd);
success = true;
return false; // stop enumeration
}
}
}
return true; // continue enumeration
}, IntPtr.Zero);
return success;
}
unsafe static IntPtr Send(IntPtr hWnd, string message)
{
const uint SMTO_NORMAL = 0;
CopyDataStruct lParam;
lParam.Padding = IntPtr.Zero;
lParam.Size = message.Length * 2;
fixed (char* buffer = message)
{
lParam.Buffer = (IntPtr)buffer;
IntPtr result;
// SendMessage with 3s timeout (e.g. when the target process is stopped in the debugger)
if (NativeMethods.SendMessageTimeout(
hWnd, NativeMethods.WM_COPYDATA, IntPtr.Zero, ref lParam,
SMTO_NORMAL, 3000, out result) != IntPtr.Zero)
{
return result;
}
else
{
return IntPtr.Zero;
}
}
}
#endregion
}
}
Loading…
Cancel
Save