Apa cara yang benar untuk membuat aplikasi WPF satu contoh?

657

Menggunakan C # dan WPF di bawah .NET (daripada Windows Forms atau konsol), apa cara yang benar untuk membuat aplikasi yang hanya dapat dijalankan sebagai satu contoh?

Saya tahu ini ada hubungannya dengan beberapa hal mistis yang disebut mutex, jarang saya dapat menemukan seseorang yang mau berhenti dan menjelaskan apa salah satunya.

Kode juga perlu menginformasikan contoh yang sudah berjalan bahwa pengguna mencoba memulai yang kedua, dan mungkin juga meneruskan argumen baris perintah jika ada.

Nidonocu
sumber
14
Bukankah CLR secara otomatis merilis mutasi yang belum dirilis saat aplikasi berakhir?
Cocowalla
1
@Cocowalla: finalizer harus membuang mutex yang tidak dikelola kecuali jika tidak bisa mengetahui apakah mutex dibuat oleh aplikasi yang dikelola atau dilampirkan ke yang sudah ada.
Ignacio Soler Garcia
Hanya memiliki satu contoh aplikasi Anda masuk akal. Tetapi menyampaikan argumen ke aplikasi yang sudah ada tampak bagi saya agak konyol. Saya tidak bisa melihat alasan untuk melakukannya. Jika Anda mengaitkan aplikasi dengan ekstensi file, Anda harus membuka aplikasi sebanyak yang ingin pengguna buka dokumen. Itulah perilaku standar yang diharapkan setiap pengguna.
Eric Ouellet
9
@Cocowalla CLR tidak mengelola sumber daya asli. Namun, jika suatu proses berakhir, semua pegangan dibebaskan oleh sistem (OS, bukan CLR).
IInspectable
1
Saya lebih suka jawabannya oleh @ huseyint. Ini menggunakan kelas Microsoft 'SingleInstance.cs' sendiri, sehingga Anda tidak perlu khawatir tentang Mutex dan IntPtrs. Juga, tidak ada ketergantungan pada VisualBasic (yuk). Lihat codereview.stackexchange.com/questions/20871/… untuk lebih lanjut ...
Heliac

Jawaban:

537

Berikut ini adalah artikel yang sangat bagus mengenai solusi Mutex. Pendekatan yang dijelaskan oleh artikel ini menguntungkan karena dua alasan.

Pertama, tidak memerlukan ketergantungan pada perakitan Microsoft.VisualBasic. Jika proyek saya sudah memiliki ketergantungan pada majelis itu, saya mungkin akan menganjurkan menggunakan pendekatan yang ditunjukkan dalam jawaban lain . Tapi apa adanya, saya tidak menggunakan perakitan Microsoft.VisualBasic, dan saya lebih suka tidak menambahkan ketergantungan yang tidak perlu untuk proyek saya.

Kedua, artikel menunjukkan bagaimana membawa instance aplikasi yang ada ke latar depan ketika pengguna mencoba untuk memulai instance lain. Itu adalah sentuhan yang sangat bagus yang tidak dijawab oleh solusi Mutex lainnya yang dijelaskan di sini.


MEMPERBARUI

Sampai 8/1/2014, artikel yang saya tautkan di atas masih aktif, tetapi blog belum diperbarui dalam beberapa saat. Itu membuat saya khawatir bahwa pada akhirnya itu akan hilang, dan dengan itu, solusi yang disarankan. Saya mereproduksi konten artikel di sini untuk anak cucu. Kata-kata itu sepenuhnya milik pemilik blog di Sanity Free Coding .

Hari ini saya ingin memperbaiki beberapa kode yang melarang aplikasi saya menjalankan banyak instance dari dirinya sendiri.

Sebelumnya saya telah menggunakan System.Diagnostics.Process untuk mencari contoh myapp.exe saya dalam daftar proses. Sementara ini berhasil, ini membawa banyak overhead, dan saya ingin sesuatu yang lebih bersih.

Mengetahui bahwa saya dapat menggunakan mutex untuk ini (tetapi tidak pernah melakukannya sebelumnya) saya berangkat untuk mengurangi kode saya dan menyederhanakan hidup saya.

Di kelas utama aplikasi saya, saya membuat statis bernama Mutex :

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

Memiliki mutex bernama memungkinkan kita untuk menumpuk sinkronisasi di beberapa utas dan proses yang hanya ajaib yang saya cari.

Mutex.WaitOne memiliki kelebihan yang menentukan jumlah waktu untuk kami tunggu. Karena kita sebenarnya tidak ingin menyinkronkan kode kita (lebih lanjut hanya memeriksa jika sedang digunakan) kita menggunakan kelebihan dengan dua parameter: Mutex.WaitOne (Timepan Timespan, bool exitContext) . Tunggu satu mengembalikan true jika itu bisa masuk, dan false jika tidak. Dalam hal ini, kami tidak ingin menunggu sama sekali; Jika mutex kami digunakan, lewati, dan lanjutkan, jadi kami meneruskan TimeSpan.Zero (tunggu 0 milidetik), dan atur exitContext ke true sehingga kami dapat keluar dari konteks sinkronisasi sebelum kami mencoba mendapatkan kunci di atasnya. Dengan menggunakan ini, kita bungkus Aplikasi kita. Kode berjalan di dalam sesuatu seperti ini:

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");
        }
    }
}

Jadi, jika aplikasi kita berjalan, WaitOne akan mengembalikan false, dan kita akan mendapatkan kotak pesan.

Alih-alih menampilkan kotak pesan, saya memilih untuk menggunakan Win32 kecil untuk memberi tahu instance saya yang sedang berjalan bahwa seseorang lupa bahwa itu sudah berjalan (dengan membawa dirinya ke atas semua jendela lain). Untuk mencapai ini, saya menggunakan PostMessage untuk menyiarkan pesan khusus ke setiap jendela (pesan khusus didaftarkan dengan RegisterWindowMessage oleh aplikasi saya yang sedang berjalan, yang berarti hanya aplikasi saya yang tahu apa itu) kemudian contoh kedua saya keluar. Contoh aplikasi yang sedang berjalan akan menerima pemberitahuan itu dan memprosesnya. Untuk melakukan itu, saya mengesampingkan WndProc dalam bentuk utama saya dan mendengarkan notifikasi khusus saya. Ketika saya menerima pemberitahuan itu, saya menyetel properti TopMost formulir ke true untuk membawanya ke atas.

Inilah yang akhirnya saya dapatkan:

  • 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 (sisi depan parsial)
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;
    }
}
Matt Davis
sumber
5
Atas dasar bahwa jawaban ini menggunakan lebih sedikit kode dan lebih sedikit pustaka dan memberikan peningkatan ke fungsionalitas atas, saya akan menjadikan ini sebagai jawaban yang diterima baru. Jika ada yang tahu cara yang lebih benar untuk membawa formulir ke atas menggunakan API, silakan tambahkan itu.
Nidonocu
11
@BlueRaja, Anda memulai instance aplikasi pertama. Saat Anda memulai instance aplikasi kedua, ia mendeteksi bahwa instance lain sudah berjalan dan bersiap untuk dimatikan. Sebelum melakukannya, ia mengirim pesan asli "SHOWME" ke instance pertama, yang membawa instance pertama ke atas. Acara di .NET tidak memungkinkan komunikasi lintas proses, itulah sebabnya pesan asli digunakan.
Matt Davis
7
Apakah ada cara untuk melewatkan baris perintah dari instance lain, mungkin?
gyurisc
22
@Nam, Mutexkonstruktor hanya memerlukan string, sehingga Anda dapat memberikan nama string yang Anda inginkan, misalnya, "This Is My Mutex". Karena 'Mutex' adalah objek sistem yang tersedia untuk proses lain, Anda biasanya ingin nama tersebut unik sehingga tidak berbenturan dengan nama 'Mutex' lain pada sistem yang sama. Dalam artikel tersebut, string yang tampak samar adalah 'Guid'. Anda dapat membuat ini secara terprogram dengan menelepon System.Guid.NewGuid(). Dalam hal artikel, pengguna mungkin membuatnya melalui Visual Studio seperti yang ditunjukkan di sini: msdn.microsoft.com/en-us/library/ms241442(VS.80).aspx
Matt Davis
6
Apakah pendekatan mutex menganggap bahwa pengguna yang sama berusaha untuk memulai aplikasi lagi? Tentu saja membawa "contoh aplikasi yang ada ke latar depan" tidak masuk akal setelah 'pengguna beralih'
dumbledad
107

Anda bisa menggunakan kelas Mutex, tetapi Anda akan segera mengetahui bahwa Anda perlu mengimplementasikan kode untuk meneruskan argumen dan diri Anda sendiri. Yah, saya belajar trik ketika pemrograman di WinForms ketika saya membaca buku Chris Sell . Trik ini menggunakan logika yang sudah tersedia untuk kita dalam framework. Saya tidak tahu tentang Anda, tetapi ketika saya belajar tentang hal-hal yang dapat saya gunakan kembali dalam kerangka kerja, itu biasanya rute yang saya ambil alih-alih menciptakan kembali roda. Kecuali tentu saja itu tidak melakukan semua yang saya inginkan.

Ketika saya masuk ke WPF, saya menemukan cara untuk menggunakan kode yang sama, tetapi dalam aplikasi WPF. Solusi ini harus memenuhi kebutuhan Anda berdasarkan pertanyaan Anda.

Pertama, kita perlu membuat kelas aplikasi kita. Di kelas ini kita akan menimpa acara OnStartup dan membuat metode yang disebut Aktifkan, yang akan digunakan nanti.

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();
    }
}

Kedua, kita perlu membuat kelas yang dapat mengatur instance kita. Sebelum kita membahasnya, kita sebenarnya akan menggunakan kembali beberapa kode yang ada dalam perakitan Microsoft.VisualBasic. Karena, saya menggunakan C # dalam contoh ini, saya harus membuat referensi ke majelis. Jika Anda menggunakan VB.NET, Anda tidak perlu melakukan apa pun. Kelas yang akan kita gunakan adalah WindowsFormsApplicationBase dan mewarisi instance manager kita darinya dan kemudian memanfaatkan properti dan acara untuk menangani single instancing.

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();
    }
}

Pada dasarnya, kami menggunakan bit VB untuk mendeteksi instance tunggal dan memprosesnya. OnStartup akan diaktifkan ketika instance pertama dimuat. OnStartupNextInstance dipecat ketika aplikasi dijalankan kembali. Seperti yang Anda lihat, saya bisa mendapatkan apa yang diteruskan pada baris perintah melalui argumen event. Saya menetapkan nilai ke bidang contoh. Anda bisa menguraikan baris perintah di sini, atau Anda bisa meneruskannya ke aplikasi Anda melalui konstruktor dan panggilan ke metode Aktifkan.

Ketiga, saatnya untuk membuat EntryPoint kami. Alih-alih membuat aplikasi seperti yang biasa Anda lakukan, kami akan mengambil keuntungan dari SingleInstanceManager kami.

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

Yah, saya harap Anda bisa mengikuti semuanya dan bisa menggunakan implementasi ini dan menjadikannya milik Anda.

Dale Ragan
sumber
9
Saya akan tetap dengan solusi mutex karena tidak ada hubungannya dengan bentuk.
Steven Sudit
1
Saya telah menggunakan ini karena saya punya masalah dengan pendekatan lain, tapi saya cukup yakin ini menggunakan remote di bawah tenda. Aplikasi saya memiliki dua masalah terkait - beberapa pelanggan mengatakan itu mencoba menelepon ke rumah meskipun mereka sudah melarangnya. Ketika mereka terlihat lebih hati-hati, koneksi ke localhost. Namun, pada awalnya mereka tidak tahu itu. Juga, saya tidak dapat menggunakan remoting untuk tujuan yang berbeda (saya pikir?) Karena sudah digunakan untuk ini. Ketika saya mencoba pendekatan mutex, saya kemudian dapat menggunakan remoting lagi.
Richard Watson
4
Maafkan saya, tapi kecuali saya kehilangan sesuatu, Anda menghindari menulis 3 baris kode dan alih-alih Anda menggunakan kembali kerangka hanya untuk menulis kode yang cukup berat untuk melakukannya. Jadi di mana simpanannya?
greenoldman
2
mungkin melakukannya di winforms?
Jack
1
Jika Anda tidak memanggil InitializeComponent () pada contoh aplikasi, Anda tidak akan dapat menyelesaikan sumber daya ... _application = new SingleInstanceApplication (); _application.InitializeComponent (); _application.Run ();
Nick
84

Dari sini .

Penggunaan umum untuk lintas proses Mutex adalah untuk memastikan bahwa hanya instance dari program yang dapat berjalan pada satu waktu. Begini cara melakukannya:

class OneAtATimePlease {

  // Use a name unique to the application (eg include your company URL)
  static Mutex mutex = new Mutex (false, "oreilly.com OneAtATimeDemo");

  static void Main()
  {
    // Wait 5 seconds if contended – in case another instance
    // of the program is in the process of shutting down.
    if (!mutex.WaitOne(TimeSpan.FromSeconds (5), false))
    {
        Console.WriteLine("Another instance of the app is running. Bye!");
        return;
    }

    try
    {    
        Console.WriteLine("Running - press Enter to exit");
        Console.ReadLine();
    }
    finally
    {
        mutex.ReleaseMutex();
    }    
  }    
}

Fitur Mutex yang baik adalah bahwa jika aplikasi berhenti tanpa ReleaseMutex pertama kali dipanggil, CLR akan merilis Mutex secara otomatis.

jason saldo
sumber
5
Saya harus mengatakan, saya lebih suka jawaban ini daripada yang diterima hanya karena fakta bahwa itu tidak tergantung pada WinForms. Secara pribadi sebagian besar pengembangan saya telah pindah ke WPF dan saya tidak ingin harus menarik di pustaka WinForm untuk sesuatu seperti ini.
Switters
5
Tentu saja, untuk menjadi jawaban yang lengkap, Anda juga harus mendeskripsikan lewat argumen ke contoh lain :)
Simon Buchan
@Jason, bagus, terima kasih! Tapi saya lebih suka tidak melewati batas waktu. Ini sangat subyektif dan tergantung pada banyak variabel. Jika Anda ingin mengaktifkan aplikasi lain untuk memulai, lepaskan saja mutex Anda lebih cepat .. misalnya segera setelah pengguna mengonfirmasi tutup
Eric Ouellet
@EricOuellet: Hampir setiap program yang memiliki tab melakukan ini - Photoshop, Sublime Text, Chrome .... Jika Anda memiliki alasan yang bagus untuk memiliki proses "master" (misalkan Anda memiliki DB in-proc untuk pengaturan), Anda mungkin ingin memilikinya menunjukkan UI seolah-olah itu adalah proses baru juga.
Simon Buchan
@Simon, kamu benar. Saya hanya bertanya pada diri sendiri tentang hal yang sangat tua ... MDI vs SDI (Multi documentinterface vs Single document interface). Saat Anda berbicara tentang tab, Anda merujuk ke MDI. Pada tahun 1998, sebuah buku Microsoft menyarankan untuk menghilangkan setiap aplikasi MDI. Microsoft mengganti Word, Excel ... ke SDI yang menurut saya lebih sederhana dan lebih baik. Saya mengerti bahwa Chrome dan lainnya (sekarang IE) ingin kembali ke MDI. Saya pribadi (berdasarkan apa-apa / perasaan pribadi) bahwa masih lebih baik untuk membuka aplikasi baru ketika file assoc dipilih. Tapi saya lebih mengerti pertanyaan yang diajukan sekarang. Terima kasih!
Eric Ouellet
58

MSDN sebenarnya memiliki contoh aplikasi untuk C # dan VB untuk melakukan hal ini: http://msdn.microsoft.com/en-us/library/ms771662(v=VS.90).aspx

Teknik yang paling umum dan dapat diandalkan untuk mengembangkan deteksi instan adalah menggunakan infrastruktur remoting Microsoft .NET Framework (System.Remoting). Microsoft .NET Framework (versi 2.0) termasuk tipe, WindowsFormsApplicationBase, yang merangkum fungsi remoting yang diperlukan. Untuk memasukkan jenis ini ke dalam aplikasi WPF, suatu jenis perlu diturunkan darinya, dan digunakan sebagai shim antara metode titik masuk statis aplikasi, Utama, dan jenis Aplikasi aplikasi WPF. Shim mendeteksi ketika aplikasi pertama kali diluncurkan, dan ketika peluncuran berikutnya dicoba, dan hasil mengontrol jenis Aplikasi WPF untuk menentukan cara memproses peluncuran.

  • Untuk C # orang hanya mengambil napas dalam-dalam dan melupakan keseluruhan 'Saya tidak ingin menyertakan DLL VisualBasic'. Karena ini dan apa yang dikatakan Scott Hanselman dan fakta bahwa ini adalah solusi terbersih untuk masalah ini dan dirancang oleh orang-orang yang tahu lebih banyak tentang kerangka daripada Anda.
  • Dari sudut pandang kegunaan faktanya adalah jika pengguna Anda memuat aplikasi dan itu sudah terbuka dan Anda memberi mereka pesan kesalahan seperti 'Another instance of the app is running. Bye'maka mereka tidak akan menjadi pengguna yang sangat bahagia. Anda cukup HARUS (dalam aplikasi GUI) beralih ke aplikasi itu dan menyampaikan argumen yang diberikan - atau jika parameter baris perintah tidak memiliki arti maka Anda harus memunculkan aplikasi yang mungkin telah diminimalkan.

Kerangka kerja sudah memiliki dukungan untuk ini - hanya saja beberapa orang idiot menamai DLL Microsoft.VisualBasicdan itu tidak dimasukkan ke dalam Microsoft.ApplicationUtilsatau sesuatu seperti itu. Dapatkan lebih dari itu - atau buka Reflektor.

Kiat: Jika Anda menggunakan pendekatan ini seperti apa adanya, dan Anda sudah memiliki App.xaml dengan sumber daya, dll. Anda juga ingin melihatnya .

Simon_Weaver
sumber
Terima kasih telah menyertakan tautan 'lihat ini juga'. Itulah yang saya butuhkan. Omong-omong, solusi # 3 di tautan Anda adalah yang terbaik.
Eternal21
Saya juga seorang advokat untuk mendelegasikan kerangka kerja dan perpustakaan yang dirancang khusus jika memungkinkan.
Eniola
23

Kode ini harus menuju ke metode utama. Lihat di sini untuk informasi lebih lanjut tentang metode utama dalam WPF.

[DllImport("user32.dll")]
private static extern Boolean ShowWindow(IntPtr hWnd, Int32 nCmdShow);

private const int SW_SHOWMAXIMIZED = 3;

static void Main() 
{
    Process currentProcess = Process.GetCurrentProcess();
    var runningProcess = (from process in Process.GetProcesses()
                          where
                            process.Id != currentProcess.Id &&
                            process.ProcessName.Equals(
                              currentProcess.ProcessName,
                              StringComparison.Ordinal)
                          select process).FirstOrDefault();
    if (runningProcess != null)
    {
        ShowWindow(runningProcess.MainWindowHandle, SW_SHOWMAXIMIZED);
       return; 
    }
}

Metode 2

static void Main()
{
    string procName = Process.GetCurrentProcess().ProcessName;
    // get the list of all processes by that name

    Process[] processes=Process.GetProcessesByName(procName);

    if (processes.Length > 1)
    {
        MessageBox.Show(procName + " already running");  
        return;
    } 
    else
    {
        // Application.Run(...);
    }
}

Catatan: Metode di atas menganggap proses / aplikasi Anda memiliki nama unik. Karena menggunakan nama proses untuk mencari apakah ada prosesor yang ada. Jadi, jika aplikasi Anda memiliki nama yang sangat umum (yaitu: Notepad), pendekatan di atas tidak akan berfungsi.

CharithJ
sumber
1
Juga, ini tidak akan berfungsi jika ada program lain yang berjalan di komputer Anda dengan nama yang sama. ProcessNamemengembalikan nama file yang dapat dieksekusi dikurangi exe. Jika Anda membuat aplikasi yang disebut "Notepad", dan notepad windows sedang berjalan, ia akan mendeteksinya saat aplikasi Anda berjalan.
Jcl
1
Terima kasih atas jawaban ini. Saya telah menemukan begitu banyak pertanyaan serupa dan jawabannya selalu sangat rumit dan / atau membingungkan saya telah menemukan mereka tidak berguna. Yang ini (Metode # 1) sangat mudah, jelas dan sebagian besar sebenarnya membantu saya membuat kode saya berjalan.
ElDoRado1239
20

Yah, saya punya Kelas sekali pakai untuk ini yang bekerja dengan mudah untuk sebagian besar kasus penggunaan:

Gunakan seperti ini:

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

        // Initialize program here.
    }
}

Ini dia:

/// <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
}
Oliver Friedrich
sumber
1
yang ini cukup mudah untuk bekerja. Itu tidak akan menutup aplikasi kedua sampai saya mengubah Application.Exit (); untuk pengembalian sederhana; tapi selain itu bagus. Meskipun saya akui saya akan melihat solusi sebelumnya lebih dekat karena menggunakan antarmuka. blogs.microsoft.co.il/blogs/arik/archive/2010/05/28/…
hal9000
15

Yang baru yang menggunakan hal Mutex dan IPC, dan juga meneruskan argumen baris perintah apa pun ke instance yang berjalan, adalah WPF Single Instance Application .

huseyint
sumber
Saya menggunakan ini dengan sukses besar. Jika Anda memasukkan NamedPipes dengan ini, Anda juga bisa meneruskan argumen baris perintah ke aplikasi asli. Kelas, 'SingleInstance.cs', ditulis oleh Microsoft. Saya telah menambahkan tautan lain ke versi blog Arik Poznanski yang lebih mudah dibaca di CodeProject.
Heliac
1
Tautan sekarang terputus.
Mike Lowery
11

Kode C # .NET Single Instance Application yang merupakan referensi untuk jawaban yang ditandai adalah awal yang baik.

Namun, saya menemukan itu tidak menangani dengan baik kasus-kasus ketika contoh yang sudah ada memiliki dialog modal terbuka, apakah dialog itu dikelola (seperti Formulir lain seperti kotak tentang), atau yang tidak dikelola (seperti OpenFileDialog bahkan ketika menggunakan kelas .NET standar). Dengan kode asli, bentuk utama diaktifkan, tetapi modal yang tetap tidak aktif, yang terlihat aneh, ditambah pengguna harus mengkliknya untuk tetap menggunakan aplikasi.

Jadi, saya telah membuat kelas utilitas SingleInstance untuk menangani semua ini secara otomatis untuk aplikasi Winforms dan WPF.

Bentuk Win :

1) memodifikasi kelas Program seperti ini:

static class Program
{
    public static readonly SingleInstance Singleton = new SingleInstance(typeof(Program).FullName);

    [STAThread]
    static void Main(string[] args)
    {
        // NOTE: if this always return false, close & restart Visual Studio
        // this is probably due to the vshost.exe thing
        Singleton.RunFirstInstance(() =>
        {
            SingleInstanceMain(args);
        });
    }

    public static void SingleInstanceMain(string[] args)
    {
        // standard code that was in Main now goes here
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }
}

2) memodifikasi kelas jendela utama seperti ini:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    protected override void WndProc(ref Message m)
    {
        // if needed, the singleton will restore this window
        Program.Singleton.OnWndProc(this, m, true);

        // TODO: handle specific messages here if needed
        base.WndProc(ref m);
    }
}

WPF:

1) memodifikasi halaman App seperti ini (dan pastikan Anda mengatur action build-nya ke halaman untuk dapat mendefinisikan kembali metode Utama):

public partial class App : Application
{
    public static readonly SingleInstance Singleton = new SingleInstance(typeof(App).FullName);

    [STAThread]
    public static void Main(string[] args)
    {
        // NOTE: if this always return false, close & restart Visual Studio
        // this is probably due to the vshost.exe thing
        Singleton.RunFirstInstance(() =>
        {
            SingleInstanceMain(args);
        });
    }

    public static void SingleInstanceMain(string[] args)
    {
        // standard code that was in Main now goes here
        App app = new App();
        app.InitializeComponent();
        app.Run();
    }
}

2) memodifikasi kelas jendela utama seperti ini:

public partial class MainWindow : Window
{
    private HwndSource _source;

    public MainWindow()
    {
        InitializeComponent();
    }

    protected override void OnSourceInitialized(EventArgs e)
    {
        base.OnSourceInitialized(e);
        _source = (HwndSource)PresentationSource.FromVisual(this);
        _source.AddHook(HwndSourceHook);
    }

    protected virtual IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        // if needed, the singleton will restore this window
        App.Singleton.OnWndProc(hwnd, msg, wParam, lParam, true, true);

        // TODO: handle other specific message
        return IntPtr.Zero;
    }

Dan di sini adalah kelas utilitas:

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Threading;

namespace SingleInstanceUtilities
{
    public sealed class SingleInstance
    {
        private const int HWND_BROADCAST = 0xFFFF;

        [DllImport("user32.dll")]
        private static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);

        [DllImport("user32.dll", CharSet = CharSet.Unicode)]
        private static extern int RegisterWindowMessage(string message);

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

        public SingleInstance(string uniqueName)
        {
            if (uniqueName == null)
                throw new ArgumentNullException("uniqueName");

            Mutex = new Mutex(true, uniqueName);
            Message = RegisterWindowMessage("WM_" + uniqueName);
        }

        public Mutex Mutex { get; private set; }
        public int Message { get; private set; }

        public void RunFirstInstance(Action action)
        {
            RunFirstInstance(action, IntPtr.Zero, IntPtr.Zero);
        }

        // NOTE: if this always return false, close & restart Visual Studio
        // this is probably due to the vshost.exe thing
        public void RunFirstInstance(Action action, IntPtr wParam, IntPtr lParam)
        {
            if (action == null)
                throw new ArgumentNullException("action");

            if (WaitForMutext(wParam, lParam))
            {
                try
                {
                    action();
                }
                finally
                {
                    ReleaseMutex();
                }
            }
        }

        public static void ActivateWindow(IntPtr hwnd)
        {
            if (hwnd == IntPtr.Zero)
                return;

            FormUtilities.ActivateWindow(FormUtilities.GetModalWindow(hwnd));
        }

        public void OnWndProc(IntPtr hwnd, int m, IntPtr wParam, IntPtr lParam, bool restorePlacement, bool activate)
        {
            if (m == Message)
            {
                if (restorePlacement)
                {
                    WindowPlacement placement = WindowPlacement.GetPlacement(hwnd, false);
                    if (placement.IsValid && placement.IsMinimized)
                    {
                        const int SW_SHOWNORMAL = 1;
                        placement.ShowCmd = SW_SHOWNORMAL;
                        placement.SetPlacement(hwnd);
                    }
                }

                if (activate)
                {
                    SetForegroundWindow(hwnd);
                    FormUtilities.ActivateWindow(FormUtilities.GetModalWindow(hwnd));
                }
            }
        }

#if WINFORMS // define this for Winforms apps
        public void OnWndProc(System.Windows.Forms.Form form, int m, IntPtr wParam, IntPtr lParam, bool activate)
        {
            if (form == null)
                throw new ArgumentNullException("form");

            if (m == Message)
            {
                if (activate)
                {
                    if (form.WindowState == System.Windows.Forms.FormWindowState.Minimized)
                    {
                        form.WindowState = System.Windows.Forms.FormWindowState.Normal;
                    }

                    form.Activate();
                    FormUtilities.ActivateWindow(FormUtilities.GetModalWindow(form.Handle));
                }
            }
        }

        public void OnWndProc(System.Windows.Forms.Form form, System.Windows.Forms.Message m, bool activate)
        {
            if (form == null)
                throw new ArgumentNullException("form");

            OnWndProc(form, m.Msg, m.WParam, m.LParam, activate);
        }
#endif

        public void ReleaseMutex()
        {
            Mutex.ReleaseMutex();
        }

        public bool WaitForMutext(bool force, IntPtr wParam, IntPtr lParam)
        {
            bool b = PrivateWaitForMutext(force);
            if (!b)
            {
                PostMessage((IntPtr)HWND_BROADCAST, Message, wParam, lParam);
            }
            return b;
        }

        public bool WaitForMutext(IntPtr wParam, IntPtr lParam)
        {
            return WaitForMutext(false, wParam, lParam);
        }

        private bool PrivateWaitForMutext(bool force)
        {
            if (force)
                return true;

            try
            {
                return Mutex.WaitOne(TimeSpan.Zero, true);
            }
            catch (AbandonedMutexException)
            {
                return true;
            }
        }
    }

    // NOTE: don't add any field or public get/set property, as this must exactly map to Windows' WINDOWPLACEMENT structure
    [StructLayout(LayoutKind.Sequential)]
    public struct WindowPlacement
    {
        public int Length { get; set; }
        public int Flags { get; set; }
        public int ShowCmd { get; set; }
        public int MinPositionX { get; set; }
        public int MinPositionY { get; set; }
        public int MaxPositionX { get; set; }
        public int MaxPositionY { get; set; }
        public int NormalPositionLeft { get; set; }
        public int NormalPositionTop { get; set; }
        public int NormalPositionRight { get; set; }
        public int NormalPositionBottom { get; set; }

        [DllImport("user32.dll", SetLastError = true)]
        private static extern bool SetWindowPlacement(IntPtr hWnd, ref WindowPlacement lpwndpl);

        [DllImport("user32.dll", SetLastError = true)]
        private static extern bool GetWindowPlacement(IntPtr hWnd, ref WindowPlacement lpwndpl);

        private const int SW_SHOWMINIMIZED = 2;

        public bool IsMinimized
        {
            get
            {
                return ShowCmd == SW_SHOWMINIMIZED;
            }
        }

        public bool IsValid
        {
            get
            {
                return Length == Marshal.SizeOf(typeof(WindowPlacement));
            }
        }

        public void SetPlacement(IntPtr windowHandle)
        {
            SetWindowPlacement(windowHandle, ref this);
        }

        public static WindowPlacement GetPlacement(IntPtr windowHandle, bool throwOnError)
        {
            WindowPlacement placement = new WindowPlacement();
            if (windowHandle == IntPtr.Zero)
                return placement;

            placement.Length = Marshal.SizeOf(typeof(WindowPlacement));
            if (!GetWindowPlacement(windowHandle, ref placement))
            {
                if (throwOnError)
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                return new WindowPlacement();
            }
            return placement;
        }
    }

    public static class FormUtilities
    {
        [DllImport("user32.dll")]
        private static extern IntPtr GetWindow(IntPtr hWnd, int uCmd);

        [DllImport("user32.dll", SetLastError = true)]
        private static extern IntPtr SetActiveWindow(IntPtr hWnd);

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

        [DllImport("kernel32.dll")]
        public static extern int GetCurrentThreadId();

        private delegate bool EnumChildrenCallback(IntPtr hwnd, IntPtr lParam);

        [DllImport("user32.dll")]
        private static extern bool EnumThreadWindows(int dwThreadId, EnumChildrenCallback lpEnumFunc, IntPtr lParam);

        private class ModalWindowUtil
        {
            private const int GW_OWNER = 4;
            private int _maxOwnershipLevel;
            private IntPtr _maxOwnershipHandle;

            private bool EnumChildren(IntPtr hwnd, IntPtr lParam)
            {
                int level = 1;
                if (IsWindowVisible(hwnd) && IsOwned(lParam, hwnd, ref level))
                {
                    if (level > _maxOwnershipLevel)
                    {
                        _maxOwnershipHandle = hwnd;
                        _maxOwnershipLevel = level;
                    }
                }
                return true;
            }

            private static bool IsOwned(IntPtr owner, IntPtr hwnd, ref int level)
            {
                IntPtr o = GetWindow(hwnd, GW_OWNER);
                if (o == IntPtr.Zero)
                    return false;

                if (o == owner)
                    return true;

                level++;
                return IsOwned(owner, o, ref level);
            }

            public static void ActivateWindow(IntPtr hwnd)
            {
                if (hwnd != IntPtr.Zero)
                {
                    SetActiveWindow(hwnd);
                }
            }

            public static IntPtr GetModalWindow(IntPtr owner)
            {
                ModalWindowUtil util = new ModalWindowUtil();
                EnumThreadWindows(GetCurrentThreadId(), util.EnumChildren, owner);
                return util._maxOwnershipHandle; // may be IntPtr.Zero
            }
        }

        public static void ActivateWindow(IntPtr hwnd)
        {
            ModalWindowUtil.ActivateWindow(hwnd);
        }

        public static IntPtr GetModalWindow(IntPtr owner)
        {
            return ModalWindowUtil.GetModalWindow(owner);
        }
    }
}
Simon Mourier
sumber
10

Berikut adalah contoh yang memungkinkan Anda untuk memiliki satu instance aplikasi. Ketika instance baru dimuat, mereka meneruskan argumen mereka ke instance utama yang sedang berjalan.

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);
}
Nathan Moinvaziri
sumber
Ini adalah contoh yang sangat bagus dari apa yang harus saya lakukan. Nathan, apakah semua arg dikirimkan menggunakan metode ini? Saya memiliki 7 aplikasi ini dan saya pikir kode ini akan berfungsi.
kevp
1
Dalam contoh saya, hanya argumen pertama yang dikirim, tetapi dapat diubah sehingga semuanya dikirim.
Nathan Moinvaziri
8

Hanya beberapa pemikiran: Ada beberapa kasus ketika mengharuskan hanya satu contoh aplikasi tidak "lumpuh" karena beberapa orang akan Anda percaya. Aplikasi basis data, dll. Adalah urutan besarnya lebih sulit jika satu memungkinkan beberapa contoh aplikasi untuk satu pengguna untuk mengakses database (Anda tahu, semua itu memperbarui semua catatan yang terbuka dalam beberapa contoh aplikasi pada pengguna mesin, dll.). Pertama, untuk "masalah tabrakan nama, jangan gunakan nama yang dapat dibaca manusia - gunakan GUID sebagai gantinya atau, lebih baik GUID + nama yang dapat dibaca manusia. Peluang tabrakan nama baru saja lepas dari radar dan Mutex tidak peduli Seperti yang ditunjukkan oleh seseorang, serangan DOS akan menyedot, tetapi jika orang jahat telah kesulitan mendapatkan nama mutex dan memasukkannya ke dalam aplikasi mereka, Anda cukup banyak target dan harus melakukan JAUH lebih banyak untuk melindungi diri sendiri daripada hanya mengutak-atik nama mutex. Juga, jika seseorang menggunakan varian: Mutex baru (true, "some GUID plus Name", dari AIsFirstInstance), Anda sudah memiliki indikator untuk mengetahui apakah Mutex adalah instance pertama atau tidak.

Bruce
sumber
6

Begitu banyak jawaban untuk pertanyaan yang tampaknya sederhana ini. Hanya untuk sedikit mengguncang di sini adalah solusi saya untuk masalah ini.

Membuat Mutex bisa menyusahkan karena JIT-er hanya melihat Anda menggunakannya untuk sebagian kecil kode Anda dan ingin menandainya sebagai siap untuk pengumpulan sampah. Cukup banyak yang ingin pintar Anda berpikir Anda tidak akan menggunakan Mutex selama itu. Pada kenyataannya Anda ingin bertahan di Mutex ini selama aplikasi Anda berjalan. Cara terbaik untuk memberi tahu pengumpul sampah untuk meninggalkan Anda Mutex sendirian adalah dengan memerintahkannya untuk tetap hidup meskipun keluar dari berbagai generasi pengumpulan garasi. Contoh:

var m = new Mutex(...);
...
GC.KeepAlive(m);

Saya mengangkat ide dari halaman ini: http://www.ai.uga.edu/~mc/SingleInstance.html

Peter
sumber
3
Bukankah lebih mudah untuk menyimpan salinannya bersama di kelas aplikasi?
rossisdead
6

Sepertinya ada cara yang sangat baik untuk menangani ini:

Aplikasi Instansi Tunggal WPF

Ini memberikan kelas yang dapat Anda tambahkan yang mengelola semua mutex dan pesan perpesanan untuk menyederhanakan implementasi Anda ke titik di mana itu hanya sepele.

Joel Barsotti
sumber
Ini sepertinya tidak membawa jendela yang ada ke latar depan ketika saya mencobanya.
RandomEngy
6

Kode berikut adalah solusi pipa bernama WCF saya untuk mendaftarkan aplikasi satu contoh. Itu bagus karena itu juga memunculkan suatu peristiwa ketika instance lain mencoba untuk memulai, dan menerima baris perintah dari instance lainnya.

Ini diarahkan ke WPF karena menggunakan System.Windows.StartupEventHandlerkelas, tetapi ini dapat dengan mudah dimodifikasi.

Kode ini membutuhkan referensi ke PresentationFramework, dan System.ServiceModel.

Pemakaian:

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.
    }
}

Kode sumber:

/// <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);
      }
    }
  }
}
Dan
sumber
5

Anda tidak boleh menggunakan mutex yang bernama untuk mengimplementasikan aplikasi instance tunggal (atau setidaknya tidak untuk kode produksi). Kode berbahaya dapat dengan mudah DoS ( Denial of Service ) ...

Matt Davison
sumber
8
"Anda seharusnya tidak pernah menggunakan mutex bernama" - tidak pernah mengatakan tidak pernah. Jika kode berbahaya dijalankan pada mesin saya, saya mungkin sudah disembunyikan.
Joe
Sebenarnya itu bahkan tidak harus berupa kode jahat. Bisa saja itu tabrakan nama yang tidak disengaja.
Matt Davison
Lalu apa yang harus kamu lakukan?
Kevin Berridge
Pertanyaan yang lebih baik adalah alasan apa yang memungkinkan Anda menginginkan perilaku itu. Jangan mendesain aplikasi Anda sebagai aplikasi instance =). Saya tahu itu adalah jawaban yang lemah tetapi dari sudut pandang desain hampir selalu merupakan jawaban yang benar. Tanpa tahu lebih banyak tentang aplikasi, sulit untuk mengatakan lebih banyak.
Matt Davison
2
Setidaknya di bawah Windows, Mutex memiliki kontrol akses, jadi orang dapat mempermainkan objek Anda. Untuk nama tabrakan sendiri, itu sebabnya UUID / GUID di mana diciptakan.
NuSkooler
5

Lihatlah kode berikut. Ini adalah solusi yang bagus dan sederhana untuk mencegah beberapa contoh aplikasi WPF.

private void Application_Startup(object sender, StartupEventArgs e)
{
    Process thisProc = Process.GetCurrentProcess();
    if (Process.GetProcessesByName(thisProc.ProcessName).Length > 1)
    {
        MessageBox.Show("Application running");
        Application.Current.Shutdown();
        return;
    }

    var wLogin = new LoginWindow();

    if (wLogin.ShowDialog() == true)
    {
        var wMain = new Main();
        wMain.WindowState = WindowState.Maximized;
        wMain.Show();
    }
    else
    {
        Application.Current.Shutdown();
    }
}
carlito
sumber
4

Inilah yang saya gunakan. Ini menggabungkan proses penghitungan untuk melakukan peralihan dan mutex untuk melindungi dari "clickers aktif":

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();
        }
    }
Sergey Aldoukhov
sumber
4

Saya menemukan solusi yang lebih sederhana, mirip dengan Dale Ragan, tetapi sedikit dimodifikasi. Ini praktis semua yang Anda butuhkan dan didasarkan pada kelas standar Microsoft WindowsFormsApplicationBase.

Pertama, Anda membuat kelas SingleInstanceController, yang dapat Anda gunakan di semua aplikasi instance tunggal lainnya, yang menggunakan Formulir Windows:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.VisualBasic.ApplicationServices;


namespace SingleInstanceController_NET
{
    public class SingleInstanceController
    : WindowsFormsApplicationBase
    {
        public delegate Form CreateMainForm();
        public delegate void StartNextInstanceDelegate(Form mainWindow);
        CreateMainForm formCreation;
        StartNextInstanceDelegate onStartNextInstance;
        public SingleInstanceController(CreateMainForm formCreation, StartNextInstanceDelegate onStartNextInstance)
        {
            // Set whether the application is single instance
            this.formCreation = formCreation;
            this.onStartNextInstance = onStartNextInstance;
            this.IsSingleInstance = true;

            this.StartupNextInstance += new StartupNextInstanceEventHandler(this_StartupNextInstance);                      
        }

        void this_StartupNextInstance(object sender, StartupNextInstanceEventArgs e)
        {
            if (onStartNextInstance != null)
            {
                onStartNextInstance(this.MainForm); // This code will be executed when the user tries to start the running program again,
                                                    // for example, by clicking on the exe file.
            }                                       // This code can determine how to re-activate the existing main window of the running application.
        }

        protected override void OnCreateMainForm()
        {
            // Instantiate your main application form
            this.MainForm = formCreation();
        }

        public void Run()
        {
            string[] commandLine = new string[0];
            base.Run(commandLine);
        }
    }
}

Maka Anda dapat menggunakannya dalam program Anda sebagai berikut:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using SingleInstanceController_NET;

namespace SingleInstance
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        static Form CreateForm()
        {
            return new Form1(); // Form1 is used for the main window.
        }

        static void OnStartNextInstance(Form mainWindow) // When the user tries to restart the application again,
                                                         // the main window is activated again.
        {
            mainWindow.WindowState = FormWindowState.Maximized;
        }
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);            
            SingleInstanceController controller = new SingleInstanceController(CreateForm, OnStartNextInstance);
            controller.Run();         
        }
    }
}

Baik program dan solusi SingleInstanceController_NET ​​harus referensi Microsoft.VisualBasic. Jika Anda hanya ingin mengaktifkan kembali aplikasi yang berjalan sebagai jendela normal ketika pengguna mencoba untuk me-restart program yang sedang berjalan, parameter kedua di SingleInstanceController bisa menjadi nol. Dalam contoh yang diberikan, jendela dimaksimalkan.

Mikhail Semenov
sumber
4

Pembaruan 2017-01-25. Setelah mencoba beberapa hal, saya memutuskan untuk menggunakan VisualBasic.dll lebih mudah dan berfungsi lebih baik (setidaknya untuk saya). Saya membiarkan jawaban saya sebelumnya hanya sebagai referensi ...

Sama seperti referensi, ini adalah bagaimana saya melakukannya tanpa melewati argumen (yang saya tidak dapat menemukan alasan untuk melakukannya ... Maksud saya satu aplikasi dengan argumen yang akan diteruskan dari satu contoh ke yang lain). Jika asosiasi file diperlukan, maka aplikasi harus (sesuai standar harapan pengguna) akan dipasang untuk setiap dokumen. Jika Anda harus meneruskan args ke aplikasi yang ada, saya pikir saya akan menggunakan vb dll.

Tidak lewat args (hanya aplikasi instance tunggal), saya lebih suka tidak mendaftarkan pesan Window baru dan tidak mengesampingkan loop pesan seperti yang didefinisikan dalam Matt Davis Solution. Meskipun bukan masalah besar untuk menambahkan VisualBasic dll, tapi saya lebih suka tidak menambahkan referensi baru hanya untuk melakukan aplikasi instance tunggal. Juga, saya lebih suka menginisiasi kelas baru dengan Main daripada memanggil Shutdown dari App.Startup menimpa untuk memastikan untuk keluar sesegera mungkin.

Dengan harapan siapa pun akan menyukainya ... atau akan sedikit menginspirasi :-)

Kelas startup proyek harus ditetapkan sebagai '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);
Eric Ouellet
sumber
3

Tidak menggunakan Mutex, jawaban sederhana:

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

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

Letakkan di dalam Program.Main().
Contoh :

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());
        }
    }
}

Anda dapat menambahkan MessageBox.Showke- ifpernyataan dan menempatkan "Aplikasi sudah berjalan".
Ini mungkin bermanfaat bagi seseorang.

newbieguy
sumber
4
Jika dua proses dimulai pada saat yang sama, mereka dapat melihat dua proses aktif dan berhenti sendiri.
PADA
@AT Ya benar, ini juga dapat membantu aplikasi yang berjalan sebagai Administrator atau lainnya
newbieguy
Jika Anda membuat salinan aplikasi Anda dan mengganti namanya, Anda dapat menjalankan yang asli dan salinannya secara bersamaan.
Dominique Bijnens
2

Pendekatan berbasis-mutex tidak lintas-platform karena mutex bernama tidak global dalam Mono. Pendekatan berbasis proses-enumerasi tidak memiliki sinkronisasi dan dapat mengakibatkan perilaku yang salah (mis. Beberapa proses yang dimulai pada saat yang sama semua dapat berakhir sendiri tergantung pada waktu). Pendekatan berbasis sistem windowing tidak diinginkan dalam aplikasi konsol. Solusi ini, dibangun di atas jawaban Divin, mengatasi semua masalah ini:

using System;
using System.IO;

namespace TestCs
{
    public class Program
    {
        // The app id must be unique. Generate a new guid for your application. 
        public static string AppId = "01234567-89ab-cdef-0123-456789abcdef";

        // The stream is stored globally to ensure that it won't be disposed before the application terminates.
        public static FileStream UniqueInstanceStream;

        public static int Main(string[] args)
        {
            EnsureUniqueInstance();

            // Your code here.

            return 0;
        }

        private static void EnsureUniqueInstance()
        {
            // Note: If you want the check to be per-user, use Environment.SpecialFolder.ApplicationData instead.
            string lockDir = Path.Combine(
                Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
                "UniqueInstanceApps");
            string lockPath = Path.Combine(lockDir, $"{AppId}.unique");

            Directory.CreateDirectory(lockDir);

            try
            {
                // Create the file with exclusive write access. If this fails, then another process is executing.
                UniqueInstanceStream = File.Open(lockPath, FileMode.Create, FileAccess.Write, FileShare.None);

                // Although only the line above should be sufficient, when debugging with a vshost on Visual Studio
                // (that acts as a proxy), the IO exception isn't passed to the application before a Write is executed.
                UniqueInstanceStream.Write(new byte[] { 0 }, 0, 1);
                UniqueInstanceStream.Flush();
            }
            catch
            {
                throw new Exception("Another instance of the application is already running.");
            }
        }
    }
}
DI
sumber
2

Saya menggunakan Mutex dalam solusi saya untuk mencegah beberapa kejadian.

static Mutex mutex = null;
//A string that is the name of the mutex
string mutexName = @"Global\test";
//Prevent Multiple Instances of Application
bool onlyInstance = false;
mutex = new Mutex(true, mutexName, out onlyInstance);

if (!onlyInstance)
{
  MessageBox.Show("You are already running this application in your system.", "Already Running..", MessageBoxButton.OK);
  Application.Current.Shutdown();
}
Wisnu Babu
sumber
1

Gunakan solusi mutex:

using System;
using System.Windows.Forms;
using System.Threading;

namespace OneAndOnlyOne
{
static class Program
{
    static String _mutexID = " // generate guid"
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        Boolean _isNotRunning;
        using (Mutex _mutex = new Mutex(true, _mutexID, out _isNotRunning))
        {
            if (_isNotRunning)
            {
                Application.Run(new Form1());
            }
            else
            {
                MessageBox.Show("An instance is already running.");
                return;
            }
        }
    }
}
}
Cornel Marian
sumber
1

Berikut ini adalah solusi ringan yang saya gunakan yang memungkinkan aplikasi untuk membawa jendela yang sudah ada ke latar depan tanpa menggunakan pesan jendela khusus atau secara membabi buta mencari nama proses.

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

static readonly string guid = "<Application Guid>";

static void Main()
{
    Mutex mutex = null;
    if (!CreateMutex(out mutex))
        return;

    // Application startup code.

    Environment.SetEnvironmentVariable(guid, null, EnvironmentVariableTarget.User);
}

static bool CreateMutex(out Mutex mutex)
{
    bool createdNew = false;
    mutex = new Mutex(false, guid, out createdNew);

    if (createdNew)
    {
        Process process = Process.GetCurrentProcess();
        string value = process.Id.ToString();

        Environment.SetEnvironmentVariable(guid, value, EnvironmentVariableTarget.User);
    }
    else
    {
        string value = Environment.GetEnvironmentVariable(guid, EnvironmentVariableTarget.User);
        Process process = null;
        int processId = -1;

        if (int.TryParse(value, out processId))
            process = Process.GetProcessById(processId);

        if (process == null || !SetForegroundWindow(process.MainWindowHandle))
            MessageBox.Show("Unable to start application. An instance of this application is already running.");
    }

    return createdNew;
}

Sunting: Anda juga dapat menyimpan dan menginisialisasi mutex dan dibuat Baru secara statis, tetapi Anda harus membuang / melepaskan mutex secara eksplisit setelah Anda selesai menggunakannya. Secara pribadi, saya lebih suka menjaga mutex lokal karena akan secara otomatis dibuang walaupun aplikasi ditutup tanpa pernah mencapai akhir Main.

Jason Lim
sumber
1

Anda juga dapat menggunakan CodeFluent Runtime yang merupakan seperangkat alat gratis. Ini menyediakan kelas SingleInstance untuk mengimplementasikan aplikasi contoh tunggal.

Antoine Diekmann
sumber
1

Saya menambahkan Metode sendMessage ke Kelas NativeMethods.

Tampaknya dosis metode postmessage berfungsi, jika aplikasi tidak ditampilkan di taskbar, namun menggunakan metode sendmessage memecahkan ini.

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);
}
Martin Bech
sumber
1

Inilah hal yang sama diterapkan melalui Acara.

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();
    }
}
Siarhei Kuchuk
sumber
1

[Saya telah memberikan kode contoh untuk aplikasi konsol dan WPF di bawah ini.]

Anda hanya perlu memeriksa nilai createdNewvariabel (contoh di bawah!), Setelah Anda membuat instance Mutex bernama.

Boolean createdNewakan mengembalikan false:

jika instance Mutex bernama "YourApplicationNameHere" sudah dibuat di sistem di suatu tempat

Boolean createdNewakan mengembalikan true:

jika ini adalah Mutex pertama bernama "YourApplicationNameHere" pada sistem.


Aplikasi konsol - Contoh:

static Mutex m = null;

static void Main(string[] args)
{
    const string mutexName = "YourApplicationNameHere";
    bool createdNew = false;

    try
    {
        // Initializes a new instance of the Mutex class with a Boolean value that indicates 
        // whether the calling thread should have initial ownership of the mutex, a string that is the name of the mutex, 
        // and a Boolean value that, when the method returns, indicates whether the calling thread was granted initial ownership of the mutex.

        using (m = new Mutex(true, mutexName, out createdNew))
        {
            if (!createdNew)
            {
                Console.WriteLine("instance is alreday running... shutting down !!!");
                Console.Read();
                return; // Exit the application
            }

            // Run your windows forms app here
            Console.WriteLine("Single instance app is running!");
            Console.ReadLine();
        }


    }
    catch (Exception ex)
    {

        Console.WriteLine(ex.Message);
        Console.ReadLine();
    }
}

WPF-Contoh:

public partial class App : Application
{
static Mutex m = null;

protected override void OnStartup(StartupEventArgs e)
{

    const string mutexName = "YourApplicationNameHere";
    bool createdNew = false;

    try
    {
        // Initializes a new instance of the Mutex class with a Boolean value that indicates 
        // whether the calling thread should have initial ownership of the mutex, a string that is the name of the mutex, 
        // and a Boolean value that, when the method returns, indicates whether the calling thread was granted initial ownership of the mutex.

        m = new Mutex(true, mutexName, out createdNew);

        if (!createdNew)
        {
            Current.Shutdown(); // Exit the application
        }

    }
    catch (Exception)
    {
        throw;
    }

    base.OnStartup(e);
}


protected override void OnExit(ExitEventArgs e)
{
    if (m != null)
    {
        m.Dispose();
    }
    base.OnExit(e);
}
}
Legenda
sumber
1

Solusi hemat waktu untuk C # Winforms ...

Program.cs:

using System;
using System.Windows.Forms;
// needs reference to Microsoft.VisualBasic
using Microsoft.VisualBasic.ApplicationServices;  

namespace YourNamespace
{
    public class SingleInstanceController : WindowsFormsApplicationBase
    {
        public SingleInstanceController()
        {
            this.IsSingleInstance = true;
        }

        protected override void OnStartupNextInstance(StartupNextInstanceEventArgs e)
        {
            e.BringToForeground = true;
            base.OnStartupNextInstance(e);
        }

        protected override void OnCreateMainForm()
        {
            this.MainForm = new Form1();
        }
    }

    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            string[] args = Environment.GetCommandLineArgs();
            SingleInstanceController controller = new SingleInstanceController();
            controller.Run(args);
        }
    }
}
AJBauer
sumber
1

Silakan periksa solusi yang diusulkan dari sini yang menggunakan semaphore untuk menentukan apakah instance yang sudah berjalan, berfungsi untuk aplikasi WPF dan dapat meneruskan argumen dari instance kedua ke instance yang sudah berjalan dengan menggunakan TcpListener dan TcpClient:

Ini juga berfungsi untuk .NET Core, tidak hanya untuk .NET Framework.

Alexandru Dicu
sumber
1

Saya tidak dapat menemukan solusi singkat di sini jadi saya harap seseorang akan menyukai ini:

DIPERBARUI 2018-09-20

Masukkan kode ini di Program.cs:

using System.Diagnostics;

static void Main()
{
    Process thisProcess = Process.GetCurrentProcess();
    Process[] allProcesses = Process.GetProcessesByName(thisProcess.ProcessName);
    if (allProcesses.Length > 1)
    {
        // Don't put a MessageBox in here because the user could spam this MessageBox.
        return;
    }

    // Optional code. If you don't want that someone runs your ".exe" with a different name:

    string exeName = AppDomain.CurrentDomain.FriendlyName;
    // in debug mode, don't forget that you don't use your normal .exe name.
    // Debug uses the .vshost.exe.
    if (exeName != "the name of your executable.exe") 
    {
        // You can add a MessageBox here if you want.
        // To point out to users that the name got changed and maybe what the name should be or something like that^^ 
        MessageBox.Show("The executable name should be \"the name of your executable.exe\"", 
            "Wrong executable name", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return;
    }

    // Following code is default code:
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new MainForm());
}
Deniz
sumber
Ini akan memperkenalkan kondisi balapan. Harus menggunakan mutex.
georgiosd
1
tidak ada jaminan bahwa jika Anda memutar dua instance sekaligus, ini akan berhasil. Seperti memperbarui variabel dari dua utas yang berbeda. Bisnis yang berisiko dan rumit. Gunakan kekuatannya, Luke :)
georgiosd
@georgios ah, saya mengerti maksud Anda. Seperti jika seseorang memulai .exe dan mengubah namanya. Ya ini akan menjadi cara untuk memulainya lebih sering, tetapi biasanya .exe tidak berfungsi, jika namanya diubah. Saya akan memperbarui jawaban saya ^^ Terima kasih Luke: D untuk menunjukkannya :)
Deniz
1
Bukan hanya itu @Deniz. Jika Anda memulai dua proses dengan sangat cepat, ada kemungkinan daftar proses atau metode pengambilannya akan dieksekusi sementara masih ada hanya satu yang muncul. Ini mungkin kasus tepi yang tidak relevan bagi Anda tetapi ini menjadi pertanyaan umum ...
georgiosd
@georgiosd Bisakah Anda membuktikannya? Karena Iv'e mengujinya hanya untukmu hehe. Tetapi itu tidak mungkin bagi saya, bahkan sangat "sangat cepat"! : P Jadi saya tidak mengerti mengapa Anda percaya pada sesuatu yang tidak terjadi dan bahkan tidak suka kode tidak bersalah ini: D
Deniz