Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Make executable singleton
#1
Is there any way to make an executable single launch/singleton, in the sense that it checks if an instance is running already, and if so that the second instance aborts loading?
#2
Code:
Copy      Help
// script "Singleton exe - simple.cs"
/*/ role exeProgram; ifRunning run; /*/
script.single("unique-mutex-name");
dialog.show("test");

Code:
Copy      Help
// script "Singleton exe - AppSingleInstance.cs"
/*/ role exeProgram; ifRunning run; /*/
if (script.testing) args = new[] { "test", "args" };

if (AppSingleInstance.AlreadyRunning("unique-mutex-name", args)) {
    print.it("already running");
    return;
}


var b = new wpfBuilder("Window").WinSize(400);
b.R.AddOkCancel();
b.End();
AppSingleInstance.Notified += a => {
    print.it("AppSingleInstance.Notified", a);
    b.Window.Activate();
};

if (!b.ShowDialog()) return;

/// <summary>
///
Implements "single instance process" feature.
/// This class will be added to the Au library in next version.
/// </summary>
///
<seealso cref="script.single"/>
public static class AppSingleInstance {
    static Mutex _mutex;
    static wnd _wNotify;
    
    /// <summary>
    ///
Detects whether a process of this app is already running.
    /// </summary>
    ///
<param name="mutex">A unique string to use for mutex name (see <see cref="Mutex(bool, string, out bool)"/>). If prefix <c>@"Global\"</c> used, detects processes in all user sessions.</param>
    ///
<param name="notifyArgs">
    ///
If not null:
    /// <br/>• If already running, sends it to that process, which receives it in <see cref="Notified"/> event.
    /// <br/>• Else enables <b>Notified</b> event in this process.
    /// </param>
    ///
<param name="waitMS">Milliseconds to wait until this process can run. No timeout if -1.</param>
    ///
<returns>True if already running.</returns>
    ///
<exception cref="InvalidOperationException">This function already called.</exception>
    ///
<exception cref="Exception">Exceptions of <see cref="Mutex(bool, string, out bool)"/>.</exception>
    public static bool AlreadyRunning(string mutex, IEnumerable<string> notifyArgs = null, int waitMS = 0) {
        var m = new Mutex(true, mutex, out bool createdNew);
        if (null != Interlocked.CompareExchange(ref _mutex, m, null)) { m.Dispose(); throw new InvalidOperationException(); }
        
        if (!createdNew && waitMS != 0) {
            try { createdNew = m.WaitOne(waitMS); }
            catch (AbandonedMutexException) { createdNew = true; }
        }

        
        if (notifyArgs != null) {
            if (createdNew) {
                WndUtil.RegisterWindowClass(mutex, _WndProc);
                _wNotify = WndUtil.CreateMessageOnlyWindow(mutex, "AppSingleInstance");
                WndCopyData.EnableReceivingWM_COPYDATA();
            }
else {
                var w = wnd.findFast("AppSingleInstance", mutex, messageOnly: true);
                if (!w.Is0) WndCopyData.Send<char>(w, 1, string.Join('\0', notifyArgs));
            }
        }

        
        return !createdNew;
    }

    
    static nint _WndProc(wnd w, int msg, nint wp, nint lp) {
        if (msg == api.WM_COPYDATA) {
            var x = new WndCopyData(lp);
            if (x.DataId == 1) {
                Notified?.Invoke(x.GetString().Split('\0'));
            }
        }

        return api.DefWindowProc(w, msg, wp, lp);
    }

    
    /// <summary>
    ///
When <see cref="AlreadyRunning"/> in new process detected that this process is running.
    /// Receives <i>notifyArgs</i> passed to it.
    /// </summary>
    ///
<remarks>
    ///
To enable this event, call <see cref="AlreadyRunning"/> with non-null <i>notifyArgs</i>. The event handler runs in the same thread. The thread must dispatch Windows messages (for example show a window or dialog, or call <see cref="wait.doEvents(int)"/>).
    /// </remarks>
    public static event Action<string[]> Notified;
    
    unsafe class api : NativeApi {
        [
DllImport("user32.dll", EntryPoint = "DefWindowProcW")]
        internal static extern nint DefWindowProc(wnd hWnd, int Msg, nint wParam, nint lParam);
        
        internal const int WM_COPYDATA = 0x4A;
    }
}
#3
Thanks. That works :-).

I really appreciate this!


Forum Jump:


Users browsing this thread: 1 Guest(s)