c# - ورفعه - كيفية عمل تطبيق اندرويد خاص بك




ما هي الطريقة الصحيحة لإنشاء تطبيق مثيل واحد؟ (20)

إليك مثال يتيح لك الحصول على مثيل واحد من التطبيق. عند تحميل أي مثيلات جديدة ، فإنها تقوم بتمرير الوسائط الخاصة بها إلى المثيل الرئيسي قيد التشغيل.

public partial class App : Application
{
    private static Mutex SingleMutex;
    public static uint MessageId;

    private void Application_Startup(object sender, StartupEventArgs e)
    {
        IntPtr Result;
        IntPtr SendOk;
        Win32.COPYDATASTRUCT CopyData;
        string[] Args;
        IntPtr CopyDataMem;
        bool AllowMultipleInstances = false;

        Args = Environment.GetCommandLineArgs();

        // TODO: Replace {00000000-0000-0000-0000-000000000000} with your application's GUID
        MessageId   = Win32.RegisterWindowMessage("{00000000-0000-0000-0000-000000000000}");
        SingleMutex = new Mutex(false, "AppName");

        if ((AllowMultipleInstances) || (!AllowMultipleInstances && SingleMutex.WaitOne(1, true)))
        {
            new Main();
        }
        else if (Args.Length > 1)
        {
            foreach (Process Proc in Process.GetProcesses())
            {
                SendOk = Win32.SendMessageTimeout(Proc.MainWindowHandle, MessageId, IntPtr.Zero, IntPtr.Zero,
                    Win32.SendMessageTimeoutFlags.SMTO_BLOCK | Win32.SendMessageTimeoutFlags.SMTO_ABORTIFHUNG,
                    2000, out Result);

                if (SendOk == IntPtr.Zero)
                    continue;
                if ((uint)Result != MessageId)
                    continue;

                CopyDataMem = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Win32.COPYDATASTRUCT)));

                CopyData.dwData = IntPtr.Zero;
                CopyData.cbData = Args[1].Length*2;
                CopyData.lpData = Marshal.StringToHGlobalUni(Args[1]);

                Marshal.StructureToPtr(CopyData, CopyDataMem, false);

                Win32.SendMessageTimeout(Proc.MainWindowHandle, Win32.WM_COPYDATA, IntPtr.Zero, CopyDataMem,
                    Win32.SendMessageTimeoutFlags.SMTO_BLOCK | Win32.SendMessageTimeoutFlags.SMTO_ABORTIFHUNG,
                    5000, out Result);

                Marshal.FreeHGlobal(CopyData.lpData);
                Marshal.FreeHGlobal(CopyDataMem);
            }

            Shutdown(0);
        }
    }
}

public partial class Main : Window
{
    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        HwndSource Source;

        Source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
        Source.AddHook(new HwndSourceHook(Window_Proc));
    }

    private IntPtr Window_Proc(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam, ref bool Handled)
    {
        Win32.COPYDATASTRUCT CopyData;
        string Path;

        if (Msg == Win32.WM_COPYDATA)
        {
            CopyData = (Win32.COPYDATASTRUCT)Marshal.PtrToStructure(lParam, typeof(Win32.COPYDATASTRUCT));
            Path = Marshal.PtrToStringUni(CopyData.lpData, CopyData.cbData / 2);

            if (WindowState == WindowState.Minimized)
            {
                // Restore window from tray
            }

            // Do whatever we want with information

            Activate();
            Focus();
        }

        if (Msg == App.MessageId)
        {
            Handled = true;
            return new IntPtr(App.MessageId);
        }

        return IntPtr.Zero;
    }
}

public class Win32
{
    public const uint WM_COPYDATA = 0x004A;

    public struct COPYDATASTRUCT
    {
        public IntPtr dwData;
        public int    cbData;
        public IntPtr lpData;
    }

    [Flags]
    public enum SendMessageTimeoutFlags : uint
    {
        SMTO_NORMAL             = 0x0000,
        SMTO_BLOCK              = 0x0001,
        SMTO_ABORTIFHUNG        = 0x0002,
        SMTO_NOTIMEOUTIFNOTHUNG = 0x0008
    }

    [DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
    public static extern uint RegisterWindowMessage(string lpString);
    [DllImport("user32.dll")]
    public static extern IntPtr SendMessageTimeout(
        IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam,
        SendMessageTimeoutFlags fuFlags, uint uTimeout, out IntPtr lpdwResult);
}

باستخدام C # و WPF ضمن .NET (بدلاً من Windows Forms أو وحدة التحكم) ، ما هي الطريقة الصحيحة لإنشاء تطبيق يمكن تشغيله فقط كمثيل واحد؟

أنا أعلم أن لها علاقة بشيء أسطوري يدعى كتكوت ، نادراً ما يمكنني أن أجد شخصًا يزعجني أن يوقف ويشرح ما هو واحد منها.

يحتاج الرمز أيضًا إلى إعلام المثيل الذي تم تشغيله بالفعل والذي حاول المستخدم تشغيله مرة أخرى ، وربما أيضًا تمرير أي وسيطات سطر أوامر إن وجدت.


حسنًا ، لدي فئة يمكن التخلص منها لهذا الأمر تعمل بسهولة لمعظم حالات الاستخدام:

استخدمه على النحو التالي:

static void Main()
{
    using (SingleInstanceMutex sim = new SingleInstanceMutex())
    {
        if (sim.IsOtherInstanceRunning)
        {
            Application.Exit();
        }

        // Initialize program here.
    }
}

ها هو:

/// <summary>
/// Represents a <see cref="SingleInstanceMutex"/> class.
/// </summary>
public partial class SingleInstanceMutex : IDisposable
{
    #region Fields

    /// <summary>
    /// Indicator whether another instance of this application is running or not.
    /// </summary>
    private bool isNoOtherInstanceRunning;

    /// <summary>
    /// The <see cref="Mutex"/> used to ask for other instances of this application.
    /// </summary>
    private Mutex singleInstanceMutex = null;

    /// <summary>
    /// An indicator whether this object is beeing actively disposed or not.
    /// </summary>
    private bool disposed;

    #endregion

    #region Constructor

    /// <summary>
    /// Initializes a new instance of the <see cref="SingleInstanceMutex"/> class.
    /// </summary>
    public SingleInstanceMutex()
    {
        this.singleInstanceMutex = new Mutex(true, Assembly.GetCallingAssembly().FullName, out this.isNoOtherInstanceRunning);
    }

    #endregion

    #region Properties

    /// <summary>
    /// Gets an indicator whether another instance of the application is running or not.
    /// </summary>
    public bool IsOtherInstanceRunning
    {
        get
        {
            return !this.isNoOtherInstanceRunning;
        }
    }

    #endregion

    #region Methods

    /// <summary>
    /// Closes the <see cref="SingleInstanceMutex"/>.
    /// </summary>
    public void Close()
    {
        this.ThrowIfDisposed();
        this.singleInstanceMutex.Close();
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            /* Release unmanaged ressources */

            if (disposing)
            {
                /* Release managed ressources */
                this.Close();
            }

            this.disposed = true;
        }
    }

    /// <summary>
    /// Throws an exception if something is tried to be done with an already disposed object.
    /// </summary>
    /// <remarks>
    /// All public methods of the class must first call this.
    /// </remarks>
    public void ThrowIfDisposed()
    {
        if (this.disposed)
        {
            throw new ObjectDisposedException(this.GetType().Name);
        }
    }

    #endregion
}

لدى MSDN بالفعل تطبيق نموذج لكل من C # و VB للقيام بهذا بالضبط: http://msdn.microsoft.com/en-us/library/ms771662(v=VS.90).aspx

التقنية الأكثر شيوعًا ووثوقًا لتطوير اكتشاف مثيل واحد هي استخدام البنية التحتية لـ Microsoft .NET Framework عن بُعد (System.Remoting). يتضمن Microsoft .NET Framework (الإصدار 2.0) نوعًا ، WindowsFormsApplicationBase ، والذي يقوم بتغليف وظائف الاتصال عن بُعد المطلوبة. لدمج هذا النوع في تطبيق WPF ، يجب اشتقاق منه من النوع ، واستخدامه كحلقة بين طريقة نقطة دخول التطبيق الثابت ، الرئيسي ، ونوع تطبيق تطبيق WPF. تقوم الرقاقة بالكشف عندما يتم تشغيل التطبيق لأول مرة ، وعند محاولة البدء في عمليات الإطلاق ، تتحكم الغلة في نوع تطبيق WPF لتحديد كيفية معالجة عمليات الإطلاق.

  • بالنسبة إلى الأشخاص C # ، خذ نفسًا عميقًا ونسيان الكل "لا أريد تضمين VisualBasic DLL". وبسبب this وما يقوله سكوت هانسنلمان وحقيقة أن هذا هو الحل الأنظف للمشكلة وصمم من قبل أشخاص يعرفون الكثير عن الإطار أكثر مما تفعلون.
  • من وجهة نظر قابلية الاستخدام ، تكون الحقيقة هي إذا كان مستخدمك يحمِّل تطبيقًا وكان مفتوحًا بالفعل وكنت تقدم لهم رسالة خطأ مثل 'Another instance of the app is running. Bye' 'Another instance of the app is running. Bye' لن يكون مستخدمًا سعيدًا جدًا. يجب عليك ببساطة (في تطبيق واجهة المستخدم الرسومية) التبديل إلى هذا التطبيق وتمريرها في الحجج المقدمة - أو إذا كانت معلمات سطر الأوامر لا معنى لها ثم يجب أن تنبثق التطبيق الذي قد يكون قد تم تصغيره.

الإطار لديه بالفعل دعم لهذا - فقط أن بعض أحمق يدعى DLL Microsoft.VisualBasic وأنه لم يحصل في Microsoft.ApplicationUtils أو شيء من هذا القبيل. الحصول على أكثر من ذلك - أو فتح العاكس.

نصيحة: إذا كنت تستخدم هذا الأسلوب تمامًا كما هو ، وكان لديك بالفعل App.xaml به موارد وما إلى ذلك ، فستحتاج إلى إلقاء نظرة على ذلك أيضًا .


مجرد بعض الأفكار: هناك حالات تتطلب فيها حالة واحدة فقط من التطبيق ليس "عرجاء" كما يعتقد البعض. تطبيقات قواعد البيانات ، وما إلى ذلك ، هي ترتيب من حجم أكثر صعوبة إذا كان أحد يسمح مثيلات متعددة من التطبيق لمستخدم واحد للوصول إلى قاعدة بيانات (كما تعلمون ، كل ذلك تحديث جميع السجلات المفتوحة في مثيلات متعددة من التطبيق على المستخدمين آلة ، وما إلى ذلك). أولاً ، بالنسبة لـ "اسم تضارب الأسماء ، لا تستخدم اسمًا مقروءًا بشريًا - استخدم GUID بدلاً من ذلك ، أو حتى GUID + اسم الإنسان الذي يمكن قراءته. فرص اصطدام الأسماء انقطعت فقط عن الرادار و Mutex لا يهتم وكما أشار أحدهم ، فإن هجوم DOS سوف يمتص ، ولكن إذا كان الشخص الخبيث قد واجه مشكلة في الحصول على اسم كائن المزامنة ودمجه في تطبيقه ، فأنت على الأغلب هدفاً على أي حال وسيتعين عليك فعل الكثير لحماية إذا كنت تستخدم نوعًا مختلفًا من: Mutex الجديد (صحيح ، "بعض GUID بالإضافة إلى الاسم" ، خارج AIsFirstInstance) ، يكون لديك بالفعل مؤشر على ما إذا كان Mutex هو المثيل الأول أم لا.


هنا article جيدة جدا بخصوص حل Mutex. النهج الموضح في المقالة مفيد لسببين.

أولاً ، لا يتطلب تبعية على التجميع Microsoft.VisualBasic. إذا كان مشروعي يعتمد بالفعل على ذلك التجمع ، ربما كنت سأدعو إلى استخدام النهج المبين في الإجابة المقبولة. ولكن كما هو ، لا أستخدم التجميع Microsoft.VisualBasic ، ولأفضل عدم إضافة تبعية لا لزوم لها لمشروعي.

ثانيًا ، يوضح المقال كيفية إحضار المثيل الموجود من التطبيق إلى المقدمة عندما يحاول المستخدم بدء مثيل آخر. هذه لمسة لطيفة للغاية لا تتناولها حلول Mutex الأخرى الموصوفة هنا.

تحديث

اعتبارًا من 8/1/2014 ، ما زالت المقالة التي ارتبط بها أعلاه نشطة ، ولكن لم يتم تحديث المدونة منذ فترة. وهذا يجعلني أخشى أن تختفي في النهاية ، ومعها ، الحل المدافع. أعيد إنتاج محتوى المقالة هنا للأجيال القادمة. الكلمات تنتمي فقط إلى مالك المدونة في Free Sanitation Free .

اليوم ، أردت أن أعيد صياغة بعض التعليمات البرمجية التي تمنع طلبي من تشغيل مثيلات متعددة لنفسه.

سابقا كان لي استخدام System.Diagnostics.Process للبحث عن مثيل myapp.exe في قائمة العمليات. في حين أن هذا يعمل ، فإنه يجلب الكثير من النفقات العامة ، وأردت شيء أنظف.

مع العلم أنني يمكن أن تستخدم كتكوت لهذا (ولكن لم تفعل ذلك من قبل) شرعت في خفض الكود الخاص بي وتبسيط حياتي.

في فئة التطبيق الرئيسي لدي خلقت ثابت اسمه Mutex :

static class Program
{
    static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
    [STAThread]
    ...
}

يسمح لنا وجود كائن مطفأ اسمه بتكديس التزامن عبر عدة خيوط وعمليات ، وهو السحر الذي أبحث عنه.

لدى Mutex.WaitOne زائد يحدد مقدار الوقت الذي Mutex.WaitOne . وبما أننا لا نرغب بالفعل في مزامنة رمزنا ( أكثر مجرد التحقق مما إذا كان قيد الاستخدام حاليًا) ، فإننا نستخدم الزائد مع معلمتين: Mutex.WaitOne (مهلة Timespan ، exit exitContext) . انتظر أحدًا يعود حقيقيًا إذا كان قادرًا على الدخول ، والخطأ إذا لم يكن. في هذه الحالة ، لا نريد الانتظار على الإطلاق ؛ إذا تم استخدام كائن المزامنة الخاص بنا ، فعليك تجاوزه ، والانتقال ، حتى نمرر في TimeSpan.Zero (انتظر 0 مللي ثانية) ، وقم بتعيين exitContext على true حتى نتمكن من الخروج من سياق المزامنة قبل أن نحاول تثبيت قفل عليه. باستخدام هذا ، سنقوم بتغليف رمز Application.Run الخاص بنا داخل شيء كالتالي:

static class Program
{
    static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
    [STAThread]
    static void Main() {
        if(mutex.WaitOne(TimeSpan.Zero, true)) {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
            mutex.ReleaseMutex();
        } else {
            MessageBox.Show("only one instance at a time");
        }
    }
}

لذا ، إذا كان تطبيقنا قيد التشغيل ، فإن WaitOne سيظهر خطأ ، وسنظهر لك مربع رسالة.

بدلاً من عرض مربع رسالة ، اخترت استخدام Win32 صغيرًا لإعلام مثيل التشغيل الخاص بي بأن أحد الأشخاص نسي أنه كان قيد التشغيل بالفعل (عن طريق جلب نفسه إلى أعلى جميع النوافذ الأخرى). ولتحقيق ذلك ، استخدمت PostMessage لبث رسالة مخصصة إلى كل نافذة (تم تسجيل الرسالة المخصصة مع RegisterWindowMessage خلال تطبيقي الجاري تشغيله ، وهو ما يعني أن التطبيق الخاص بي لا يعرف إلا ما هو موجود) ، ثم يتم إنهاء المثيل الثاني. سوف تتلقى مثيل التطبيق قيد التشغيل هذا الإعلام ومعالجته. من أجل القيام بذلك ، قمت WndProc في النموذج الرئيسي واستمعت لإشعاري المخصص. عندما تلقيت هذا الإشعار ، قمت بتعيين الخاصية TopMost للنموذج إلى true لإظهارها في الأعلى.

إليك ما انتهى به الأمر:

  • Program.cs
static class Program
{
    static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
    [STAThread]
    static void Main() {
        if(mutex.WaitOne(TimeSpan.Zero, true)) {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
            mutex.ReleaseMutex();
        } else {
            // send our Win32 message to make the currently running instance
            // jump on top of all the other windows
            NativeMethods.PostMessage(
                (IntPtr)NativeMethods.HWND_BROADCAST,
                NativeMethods.WM_SHOWME,
                IntPtr.Zero,
                IntPtr.Zero);
        }
    }
}
  • NativeMethods.cs
// this class just wraps some Win32 stuff that we're going to use
internal class NativeMethods
{
    public const int HWND_BROADCAST = 0xffff;
    public static readonly int WM_SHOWME = RegisterWindowMessage("WM_SHOWME");
    [DllImport("user32")]
    public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
    [DllImport("user32")]
    public static extern int RegisterWindowMessage(string message);
}
  • Form1.cs (الجانب الأمامي جزئي)
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    protected override void WndProc(ref Message m)
    {
        if(m.Msg == NativeMethods.WM_SHOWME) {
            ShowMe();
        }
        base.WndProc(ref m);
    }
    private void ShowMe()
    {
        if(WindowState == FormWindowState.Minimized) {
            WindowState = FormWindowState.Normal;
        }
        // get our current "TopMost" value (ours will always be false though)
        bool top = TopMost;
        // make our form jump to the top of everything
        TopMost = true;
        // set it back to whatever it was
        TopMost = top;
    }
}


يمكنك استخدام فئة Mutex ، لكنك ستكتشف قريبًا أنك ستحتاج إلى تنفيذ الشفرة لتمرير الحجج ومثلها. حسنا ، لقد تعلمت خدعة عند البرمجة في WinForms عندما قرأت كتاب Chris Sell . تستخدم هذه الحيلة المنطق المتوفر لدينا بالفعل في إطار العمل. لا أعرف عنك ، ولكن عندما أتعلم عن الأشياء التي يمكنني إعادة استخدامها في الإطار ، فهذا هو عادة الطريق الذي أتخذه بدلاً من إعادة اختراع العجلة. ما لم يكن بالطبع لا يفعل كل ما أريد.

عندما دخلت WPF ، توصلت إلى طريقة لاستخدام هذا الرمز نفسه ، ولكن في تطبيق WPF. يجب أن يلبي هذا الحل احتياجاتك استنادًا إلى سؤالك.

أولا ، نحن بحاجة إلى إنشاء فئة التطبيق لدينا. في هذه الفئة ، سنقوم بتجاوز الحدث OnStartup وإنشاء طريقة تسمى Activate ، والتي سيتم استخدامها لاحقًا.

public class SingleInstanceApplication : System.Windows.Application
{
    protected override void OnStartup(System.Windows.StartupEventArgs e)
    {
        // Call the OnStartup event on our base class
        base.OnStartup(e);

        // Create our MainWindow and show it
        MainWindow window = new MainWindow();
        window.Show();
    }

    public void Activate()
    {
        // Reactivate the main window
        MainWindow.Activate();
    }
}

ثانيًا ، سنحتاج إلى إنشاء فصل يمكنه إدارة حالاتنا. قبل أن نذهب من خلال ذلك ، سنقوم بالفعل بإعادة استخدام بعض التعليمات البرمجية الموجودة في تجميع Microsoft.VisualBasic. منذ ذلك الحين ، وأنا أستخدم C # في هذا المثال ، كان علي أن أشير إلى التجميع. إذا كنت تستخدم VB.NET ، لن تحتاج إلى القيام بأي شيء. الفئة التي نستخدمها هي WindowsFormsApplicationBase وترث مدير المثيل الخاص بنا ، ومن ثم الاستفادة من الخصائص والأحداث للتعامل مع المثيلات الفردية.

public class SingleInstanceManager : Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase
{
    private SingleInstanceApplication _application;
    private System.Collections.ObjectModel.ReadOnlyCollection<string> _commandLine;

    public SingleInstanceManager()
    {
        IsSingleInstance = true;
    }

    protected override bool OnStartup(Microsoft.VisualBasic.ApplicationServices.StartupEventArgs eventArgs)
    {
        // First time _application is launched
        _commandLine = eventArgs.CommandLine;
        _application = new SingleInstanceApplication();
        _application.Run();
        return false;
    }

    protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
    {
        // Subsequent launches
        base.OnStartupNextInstance(eventArgs);
        _commandLine = eventArgs.CommandLine;
        _application.Activate();
    }
}

بشكل أساسي ، نحن نستخدم وحدات بت VB للكشف عن مثيل واحد ومعالجتها وفقًا لذلك. سيتم تشغيل OnStartup عند تحميل المثيل الأول. يتم تشغيل OnStartupNextInstance عند إعادة تشغيل التطبيق مرة أخرى. كما ترون ، يمكنني الوصول إلى ما تم تمريره على سطر الأوامر من خلال حجج الحدث. أقوم بتعيين القيمة إلى حقل مثيل. يمكنك تحليل سطر الأوامر هنا ، أو يمكنك تمريره إلى التطبيق الخاص بك من خلال المُنشئ والمكالمة إلى طريقة Activate.

ثالثًا ، حان الوقت لإنشاء EntryPoint الخاصة بنا. بدلاً من تحديث التطبيق كما تفعل عادةً ، سنستفيد من SingleInstanceManager.

public class EntryPoint
{
    [STAThread]
    public static void Main(string[] args)
    {
        SingleInstanceManager manager = new SingleInstanceManager();
        manager.Run(args);
    }
}

حسنًا ، أتمنى أن تكون قادرًا على متابعة كل شيء وأن تكون قادرًا على استخدام هذا التطبيق وجعله ملكًا لك.


Update 2017-01-25. After trying few things, I decided to go with VisualBasic.dll it is easier and works better (at least for me). I let my previous answer just as reference...

Just as reference, this is how I did without passing arguments (which I can't find any reason to do so... I mean a single app with arguments that as to be passed out from one instance to another one). If file association is required, then an app should (per users standard expectation) be instanciated for each doc. If you have to pass args to existing app, I think I would used vb dll.

Not passing args (just single instance app), I prefer not registering a new Window message and not override the message loop as defined in Matt Davis Solution. Although it's not a big deal to add a VisualBasic dll, but I prefer not add a new reference just to do single instance app. Also, I do prefer instanciate a new class with Main instead of calling Shutdown from App.Startup override to ensure to exit as soon as possible.

In hope that anybody will like it... or will inspire a little bit :-)

Project startup class should be set as 'SingleInstanceApp'.

public class SingleInstanceApp
{
    [STAThread]
    public static void Main(string[] args)
    {
        Mutex _mutexSingleInstance = new Mutex(true, "MonitorMeSingleInstance");

        if (_mutexSingleInstance.WaitOne(TimeSpan.Zero, true))
        {
            try
            {
                var app = new App();
                app.InitializeComponent();
                app.Run();

            }
            finally
            {
                _mutexSingleInstance.ReleaseMutex();
                _mutexSingleInstance.Close();
            }
        }
        else
        {
            MessageBox.Show("One instance is already running.");

            var processes = Process.GetProcessesByName(Assembly.GetEntryAssembly().GetName().Name);
            {
                if (processes.Length > 1)
                {
                    foreach (var process in processes)
                    {
                        if (process.Id != Process.GetCurrentProcess().Id)
                        {
                            WindowHelper.SetForegroundWindow(process.MainWindowHandle);
                        }
                    }
                }
            }
        }
    }
}

WindowHelper:

using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Threading;

namespace HQ.Util.Unmanaged
{
    public class WindowHelper
    {
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool SetForegroundWindow(IntPtr hWnd);

Here is my 2 cents

 static class Program
    {
        [STAThread]
        static void Main()
        {
            bool createdNew;
            using (new Mutex(true, "MyApp", out createdNew))
            {
                if (createdNew) {
                    Application.EnableVisualStyles();
                    Application.SetCompatibleTextRenderingDefault(false);
                    var mainClass = new SynGesturesLogic();
                    Application.ApplicationExit += mainClass.tray_exit;
                    Application.Run();
                }
                else
                {
                    var current = Process.GetCurrentProcess();
                    foreach (var process in Process.GetProcessesByName(current.ProcessName).Where(process => process.Id != current.Id))
                    {
                        NativeMethods.SetForegroundWindow(process.MainWindowHandle);
                        break;
                    }
                }
            }
        }
    }

Here is what I use. It combined process enumeration to perform switching and mutex to safeguard from "active clickers":

public partial class App
{
    [DllImport("user32")]
    private static extern int OpenIcon(IntPtr hWnd);

    [DllImport("user32.dll")]
    private static extern bool SetForegroundWindow(IntPtr hWnd);

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        var p = Process
           .GetProcessesByName(Process.GetCurrentProcess().ProcessName);
            foreach (var t in p.Where(t => t.MainWindowHandle != IntPtr.Zero))
            {
                OpenIcon(t.MainWindowHandle);
                SetForegroundWindow(t.MainWindowHandle);
                Current.Shutdown();
                return;
            }

            // there is a chance the user tries to click on the icon repeatedly
            // and the process cannot be discovered yet
            bool createdNew;
            var mutex = new Mutex(true, "MyAwesomeApp", 
               out createdNew);  // must be a variable, though it is unused - 
            // we just need a bit of time until the process shows up
            if (!createdNew)
            {
                Current.Shutdown();
                return;
            }

            new Bootstrapper().Run();
        }
    }

Here's the same thing implemented via Event.

public enum ApplicationSingleInstanceMode
{
    CurrentUserSession,
    AllSessionsOfCurrentUser,
    Pc
}

public class ApplicationSingleInstancePerUser: IDisposable
{
    private readonly EventWaitHandle _event;

    /// <summary>
    /// Shows if the current instance of ghost is the first
    /// </summary>
    public bool FirstInstance { get; private set; }

    /// <summary>
    /// Initializes 
    /// </summary>
    /// <param name="applicationName">The application name</param>
    /// <param name="mode">The single mode</param>
    public ApplicationSingleInstancePerUser(string applicationName, ApplicationSingleInstanceMode mode = ApplicationSingleInstanceMode.CurrentUserSession)
    {
        string name;
        if (mode == ApplicationSingleInstanceMode.CurrentUserSession)
            name = $"Local\\{applicationName}";
        else if (mode == ApplicationSingleInstanceMode.AllSessionsOfCurrentUser)
            name = $"Global\\{applicationName}{Environment.UserDomainName}";
        else
            name = $"Global\\{applicationName}";

        try
        {
            bool created;
            _event = new EventWaitHandle(false, EventResetMode.ManualReset, name, out created);
            FirstInstance = created;
        }
        catch
        {
        }
    }

    public void Dispose()
    {
        _event.Dispose();
    }
}

I added a sendMessage Method to the NativeMethods Class.

Apparently the postmessage method dosent work, if the application is not show in the taskbar, however using the sendmessage method solves this.

class NativeMethods
{
    public const int HWND_BROADCAST = 0xffff;
    public static readonly int WM_SHOWME = RegisterWindowMessage("WM_SHOWME");
    [DllImport("user32")]
    public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
    [DllImport("user32")]
    public static extern int RegisterWindowMessage(string message);
}

I like a solution to allow multiple Instances, if the exe is called from an other path. I modified CharithJ solution Method 1:

   static class Program {
    [DllImport("user32.dll")]
    private static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);
    [DllImport("User32.dll")]
    public static extern Int32 SetForegroundWindow(IntPtr hWnd);
    [STAThread]
    static void Main() {
        Process currentProcess = Process.GetCurrentProcess();
        foreach (var process in Process.GetProcesses()) {
            try {
                if ((process.Id != currentProcess.Id) && 
                    (process.ProcessName == currentProcess.ProcessName) &&
                    (process.MainModule.FileName == currentProcess.MainModule.FileName)) {
                    ShowWindow(process.MainWindowHandle, 5); // const int SW_SHOW = 5; //Activates the window and displays it in its current size and position. 
                    SetForegroundWindow(process.MainWindowHandle);
                    return;
                }
            } catch (Exception ex) {
                //ignore Exception "Access denied "
            }
        }

        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }
}


Normally, this is the code I use for single-instance Windows Forms applications:

[STAThread]
public static void Main()
{
    String assemblyName = Assembly.GetExecutingAssembly().GetName().Name;

    using (Mutex mutex = new Mutex(false, assemblyName))
    {
        if (!mutex.WaitOne(0, false))
        {
            Boolean shownProcess = false;
            Process currentProcess = Process.GetCurrentProcess();

            foreach (Process process in Process.GetProcessesByName(currentProcess.ProcessName))
            {
                if (!process.Id.Equals(currentProcess.Id) && process.MainModule.FileName.Equals(currentProcess.MainModule.FileName) && !process.MainWindowHandle.Equals(IntPtr.Zero))
                {
                    IntPtr windowHandle = process.MainWindowHandle;

                    if (NativeMethods.IsIconic(windowHandle))
                        NativeMethods.ShowWindow(windowHandle, ShowWindowCommand.Restore);

                    NativeMethods.SetForegroundWindow(windowHandle);

                    shownProcess = true;
                }
            }

            if (!shownProcess)
                MessageBox.Show(String.Format(CultureInfo.CurrentCulture, "An instance of {0} is already running!", assemblyName), assemblyName, MessageBoxButtons.OK, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button1, (MessageBoxOptions)0);
        }
        else
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form());
        }
    }
}

Where native components are:

[DllImport("User32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean IsIconic([In] IntPtr windowHandle);

[DllImport("User32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean SetForegroundWindow([In] IntPtr windowHandle);

[DllImport("User32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean ShowWindow([In] IntPtr windowHandle, [In] ShowWindowCommand command);

public enum ShowWindowCommand : int
{
    Hide                   = 0x0,
    ShowNormal             = 0x1,
    ShowMinimized          = 0x2,
    ShowMaximized          = 0x3,
    ShowNormalNotActive    = 0x4,
    Minimize               = 0x6,
    ShowMinimizedNotActive = 0x7,
    ShowCurrentNotActive   = 0x8,
    Restore                = 0x9,
    ShowDefault            = 0xA,
    ForceMinimize          = 0xB
}

Not using Mutex though, simple answer:

System.Diagnostics;    
...
string thisprocessname = Process.GetCurrentProcess().ProcessName;

if (Process.GetProcesses().Count(p => p.ProcessName == thisprocessname) > 1)
                return;

Put it inside the Program.Main() .
Example :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;

namespace Sample
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            //simple add Diagnostics namespace, and these 3 lines below 
            string thisprocessname = Process.GetCurrentProcess().ProcessName;
            if (Process.GetProcesses().Count(p => p.ProcessName == thisprocessname) > 1)
                return;

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Sample());
        }
    }
}

You can add MessageBox.Show to the if -statement and put "Application already running".
This might be helpful to someone.


The following code is my WCF named pipes solution to register a single-instance application. It's nice because it also raises an event when another instance attempts to start, and receives the command line of the other instance.

It's geared toward WPF because it uses the System.Windows.StartupEventHandler class, but this could be easily modified.

This code requires a reference to PresentationFramework , and System.ServiceModel .

الاستعمال:

class Program
{
    static void Main()
    {
        var applicationId = new Guid("b54f7b0d-87f9-4df9-9686-4d8fd76066dc");

        if (SingleInstanceManager.VerifySingleInstance(applicationId))
        {
            SingleInstanceManager.OtherInstanceStarted += OnOtherInstanceStarted;

            // Start the application
        }
    }

    static void OnOtherInstanceStarted(object sender, StartupEventArgs e)
    {
        // Do something in response to another instance starting up.
    }
}

مصدر الرمز:

/// <summary>
/// A class to use for single-instance applications.
/// </summary>
public static class SingleInstanceManager
{
  /// <summary>
  /// Raised when another instance attempts to start up.
  /// </summary>
  public static event StartupEventHandler OtherInstanceStarted;

  /// <summary>
  /// Checks to see if this instance is the first instance running on this machine.  If it is not, this method will
  /// send the main instance this instance's startup information.
  /// </summary>
  /// <param name="guid">The application's unique identifier.</param>
  /// <returns>True if this instance is the main instance.</returns>
  public static bool VerifySingleInstace(Guid guid)
  {
    if (!AttemptPublishService(guid))
    {
      NotifyMainInstance(guid);

      return false;
    }

    return true;
  }

  /// <summary>
  /// Attempts to publish the service.
  /// </summary>
  /// <param name="guid">The application's unique identifier.</param>
  /// <returns>True if the service was published successfully.</returns>
  private static bool AttemptPublishService(Guid guid)
  {
    try
    {
      ServiceHost serviceHost = new ServiceHost(typeof(SingleInstance));
      NetNamedPipeBinding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
      serviceHost.AddServiceEndpoint(typeof(ISingleInstance), binding, CreateAddress(guid));
      serviceHost.Open();

      return true;
    }
    catch
    {
      return false;
    }
  }

  /// <summary>
  /// Notifies the main instance that this instance is attempting to start up.
  /// </summary>
  /// <param name="guid">The application's unique identifier.</param>
  private static void NotifyMainInstance(Guid guid)
  {
    NetNamedPipeBinding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
    EndpointAddress remoteAddress = new EndpointAddress(CreateAddress(guid));
    using (ChannelFactory<ISingleInstance> factory = new ChannelFactory<ISingleInstance>(binding, remoteAddress))
    {
      ISingleInstance singleInstance = factory.CreateChannel();
      singleInstance.NotifyMainInstance(Environment.GetCommandLineArgs());
    }
  }

  /// <summary>
  /// Creates an address to publish/contact the service at based on a globally unique identifier.
  /// </summary>
  /// <param name="guid">The identifier for the application.</param>
  /// <returns>The address to publish/contact the service.</returns>
  private static string CreateAddress(Guid guid)
  {
    return string.Format(CultureInfo.CurrentCulture, "net.pipe://localhost/{0}", guid);
  }

  /// <summary>
  /// The interface that describes the single instance service.
  /// </summary>
  [ServiceContract]
  private interface ISingleInstance
  {
    /// <summary>
    /// Notifies the main instance that another instance of the application attempted to start.
    /// </summary>
    /// <param name="args">The other instance's command-line arguments.</param>
    [OperationContract]
    void NotifyMainInstance(string[] args);
  }

  /// <summary>
  /// The implementation of the single instance service interface.
  /// </summary>
  private class SingleInstance : ISingleInstance
  {
    /// <summary>
    /// Notifies the main instance that another instance of the application attempted to start.
    /// </summary>
    /// <param name="args">The other instance's command-line arguments.</param>
    public void NotifyMainInstance(string[] args)
    {
      if (OtherInstanceStarted != null)
      {
        Type type = typeof(StartupEventArgs);
        ConstructorInfo constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
        StartupEventArgs e = (StartupEventArgs)constructor.Invoke(null);
        FieldInfo argsField = type.GetField("_args", BindingFlags.Instance | BindingFlags.NonPublic);
        Debug.Assert(argsField != null);
        argsField.SetValue(e, args);

        OtherInstanceStarted(null, e);
      }
    }
  }
}

This is how I ended up taking care of this issue. Note that debug code is still in there for testing. This code is within the OnStartup in the App.xaml.cs file. (WPF)

        // Process already running ? 
        if (Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName).Length > 1)
        {

            // Show your error message
            MessageBox.Show("xxx is already running.  \r\n\r\nIf the original process is hung up you may need to restart your computer, or kill the current xxx process using the task manager.", "xxx is already running!", MessageBoxButton.OK, MessageBoxImage.Exclamation);

            // This process 
            Process currentProcess = Process.GetCurrentProcess();

            // Get all processes running on the local computer.
            Process[] localAll = Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName);

            // ID of this process... 
            int temp = currentProcess.Id;
            MessageBox.Show("This Process ID:  " + temp.ToString());

            for (int i = 0; i < localAll.Length; i++)
            {
                // Find the other process 
                if (localAll[i].Id != currentProcess.Id)
                {
                    MessageBox.Show("Original Process ID (Switching to):  " + localAll[i].Id.ToString());

                    // Switch to it... 
                    SetForegroundWindow(localAll[i].MainWindowHandle);

                }
            }

            Application.Current.Shutdown();

        }

This may have issues that I have not caught yet. If I run into any I'll update my answer.


Usually whenever we execute an .exe, every time it creates a separate windows process with its own address space, resources and so on. But we do not want this criteria as this would prevent us from creating single process. Single instance applications can be created using the Mutex in C# which is discussed in this article

Moreover if we want to bring the application on top we can do it using

 [DllImport("user32")]
 static extern IntPtr SetForegroundWindow(IntPtr hWnd);





mutex