Restore style

This commit is contained in:
Jack251970 2025-03-22 13:29:47 +08:00
parent 9d81e60813
commit 48792b6a3b

View file

@ -6,137 +6,138 @@ using System.Windows;
// http://blogs.microsoft.co.il/arik/2010/05/28/wpf-single-instance-application/ // http://blogs.microsoft.co.il/arik/2010/05/28/wpf-single-instance-application/
// modified to allow single instace restart // modified to allow single instace restart
namespace Flow.Launcher.Helper; namespace Flow.Launcher.Helper
public interface ISingleInstanceApp
{ {
void OnSecondAppStarted(); public interface ISingleInstanceApp
} {
void OnSecondAppStarted();
/// <summary> }
/// This class checks to make sure that only one instance of
/// this application is running at a time. /// <summary>
/// </summary> /// This class checks to make sure that only one instance of
/// <remarks> /// this application is running at a time.
/// Note: this class should be used with some caution, because it does no /// </summary>
/// security checking. For example, if one instance of an app that uses this class /// <remarks>
/// is running as Administrator, any other instance, even if it is not /// Note: this class should be used with some caution, because it does no
/// running as Administrator, can activate it with command line arguments. /// security checking. For example, if one instance of an app that uses this class
/// For most apps, this will not be much of an issue. /// is running as Administrator, any other instance, even if it is not
/// </remarks> /// running as Administrator, can activate it with command line arguments.
public static class SingleInstance<TApplication> where TApplication: Application, ISingleInstanceApp /// For most apps, this will not be much of an issue.
{ /// </remarks>
#region Private Fields public static class SingleInstance<TApplication> where TApplication : Application, ISingleInstanceApp
{
/// <summary> #region Private Fields
/// String delimiter used in channel names.
/// </summary> /// <summary>
private const string Delimiter = ":"; /// String delimiter used in channel names.
/// </summary>
/// <summary> private const string Delimiter = ":";
/// Suffix to the channel name.
/// </summary> /// <summary>
private const string ChannelNameSuffix = "SingeInstanceIPCChannel"; /// Suffix to the channel name.
private const string InstanceMutexName = "Flow.Launcher_Unique_Application_Mutex"; /// </summary>
private const string ChannelNameSuffix = "SingeInstanceIPCChannel";
/// <summary> private const string InstanceMutexName = "Flow.Launcher_Unique_Application_Mutex";
/// Application mutex.
/// </summary> /// <summary>
internal static Mutex SingleInstanceMutex { get; set; } /// Application mutex.
/// </summary>
#endregion internal static Mutex SingleInstanceMutex { get; set; }
#region Public Methods #endregion
/// <summary> #region Public Methods
/// Checks if the instance of the application attempting to start is the first instance.
/// If not, activates the first instance. /// <summary>
/// </summary> /// Checks if the instance of the application attempting to start is the first instance.
/// <returns>True if this is the first instance of the application.</returns> /// If not, activates the first instance.
public static bool InitializeAsFirstInstance() /// </summary>
{ /// <returns>True if this is the first instance of the application.</returns>
// Build unique application Id and the IPC channel name. public static bool InitializeAsFirstInstance()
string applicationIdentifier = InstanceMutexName + Environment.UserName; {
// Build unique application Id and the IPC channel name.
string channelName = string.Concat(applicationIdentifier, Delimiter, ChannelNameSuffix); string applicationIdentifier = InstanceMutexName + Environment.UserName;
// Create mutex based on unique application Id to check if this is the first instance of the application. string channelName = string.Concat(applicationIdentifier, Delimiter, ChannelNameSuffix);
SingleInstanceMutex = new Mutex(true, applicationIdentifier, out var firstInstance);
if (firstInstance) // Create mutex based on unique application Id to check if this is the first instance of the application.
{ SingleInstanceMutex = new Mutex(true, applicationIdentifier, out var firstInstance);
_ = CreateRemoteServiceAsync(channelName); if (firstInstance)
return true; {
} _ = CreateRemoteServiceAsync(channelName);
else return true;
{ }
_ = SignalFirstInstanceAsync(channelName); else
return false; {
} _ = SignalFirstInstanceAsync(channelName);
} return false;
}
/// <summary> }
/// Cleans up single-instance code, clearing shared resources, mutexes, etc.
/// </summary> /// <summary>
public static void Cleanup() /// Cleans up single-instance code, clearing shared resources, mutexes, etc.
{ /// </summary>
SingleInstanceMutex?.ReleaseMutex(); public static void Cleanup()
} {
SingleInstanceMutex?.ReleaseMutex();
#endregion }
#region Private Methods #endregion
/// <summary> #region Private Methods
/// Creates a remote server pipe for communication.
/// Once receives signal from client, will activate first instance. /// <summary>
/// </summary> /// Creates a remote server pipe for communication.
/// <param name="channelName">Application's IPC channel name.</param> /// Once receives signal from client, will activate first instance.
private static async Task CreateRemoteServiceAsync(string channelName) /// </summary>
{ /// <param name="channelName">Application's IPC channel name.</param>
using NamedPipeServerStream pipeServer = new NamedPipeServerStream(channelName, PipeDirection.In); private static async Task CreateRemoteServiceAsync(string channelName)
while (true) {
{ using NamedPipeServerStream pipeServer = new NamedPipeServerStream(channelName, PipeDirection.In);
// Wait for connection to the pipe while (true)
await pipeServer.WaitForConnectionAsync(); {
// Wait for connection to the pipe
// Do an asynchronous call to ActivateFirstInstance function await pipeServer.WaitForConnectionAsync();
Application.Current?.Dispatcher.Invoke(ActivateFirstInstance);
// Do an asynchronous call to ActivateFirstInstance function
// Disconect client Application.Current?.Dispatcher.Invoke(ActivateFirstInstance);
pipeServer.Disconnect();
} // Disconect client
} pipeServer.Disconnect();
}
/// <summary> }
/// Creates a client pipe and sends a signal to server to launch first instance
/// </summary> /// <summary>
/// <param name="channelName">Application's IPC channel name.</param> /// Creates a client pipe and sends a signal to server to launch first instance
/// <param name="args"> /// </summary>
/// Command line arguments for the second instance, passed to the first instance to take appropriate action. /// <param name="channelName">Application's IPC channel name.</param>
/// </param> /// <param name="args">
private static async Task SignalFirstInstanceAsync(string channelName) /// Command line arguments for the second instance, passed to the first instance to take appropriate action.
{ /// </param>
// Create a client pipe connected to server private static async Task SignalFirstInstanceAsync(string channelName)
using NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", channelName, PipeDirection.Out); {
// Create a client pipe connected to server
// Connect to the available pipe using NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", channelName, PipeDirection.Out);
await pipeClient.ConnectAsync(0);
} // Connect to the available pipe
await pipeClient.ConnectAsync(0);
/// <summary> }
/// Activates the first instance of the application with arguments from a second instance.
/// </summary> /// <summary>
/// <param name="args">List of arguments to supply the first instance of the application.</param> /// Activates the first instance of the application with arguments from a second instance.
private static void ActivateFirstInstance() /// </summary>
{ /// <param name="args">List of arguments to supply the first instance of the application.</param>
// Set main window state and process command line args private static void ActivateFirstInstance()
if (Application.Current == null) {
{ // Set main window state and process command line args
return; if (Application.Current == null)
} {
return;
((TApplication)Application.Current).OnSecondAppStarted(); }
}
((TApplication)Application.Current).OnSecondAppStarted();
#endregion }
#endregion
}
} }