Cegah beberapa contoh aplikasi tertentu di .NET?


123

Di .NET, apa cara terbaik untuk mencegah beberapa contoh aplikasi berjalan pada waktu yang bersamaan? Dan jika tidak ada teknik "terbaik", apa saja peringatan yang perlu dipertimbangkan dengan setiap solusi?

Jawaban:


151

Gunakan Mutex. Salah satu contoh di atas menggunakan GetProcessByName memiliki banyak peringatan. Berikut adalah artikel bagus tentang masalah ini:

http://odetocode.com/Blogs/scott/archive/2004/08/20/401.aspx

[STAThread]
static void Main() 
{
   using(Mutex mutex = new Mutex(false, "Global\\" + appGuid))
   {
      if(!mutex.WaitOne(0, false))
      {
         MessageBox.Show("Instance already running");
         return;
      }

      Application.Run(new Form1());
   }
}

private static string appGuid = "c0a76b5a-12ab-45c5-b9d9-d693faa6e7b9";

1
Menggunakan mutex juga berfungsi untuk kode non .net juga (meskipun sintaksnya akan bervariasi)
crashmstr

9
Berikut adalah versi yang sedikit lebih lengkap, dengan beberapa komentar bagus: stackoverflow.com/questions/229565/…
Richard Watson

2
@ClarkKent: Hanya string acak sehingga nama Mutex tidak akan bertabrakan dengan nama dari aplikasi lain.
jgauffin

Bisakah ini membatasi untuk sejumlah contoh juga?
Alejandro del Río

4
Untuk memiliki kontrol yang lebih baik saat membuat versi atau mengubah panduan aplikasi Anda, Anda dapat menggunakan: string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), true)[0]).Value;yang akan mendapatkan panduan rakitan pelaksana
ciosoriog

22
if (Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName).Length > 1)
{
  AppLog.Write("Application XXXX already running. Only one instance of this application is allowed", AppLog.LogMessageType.Warn);
  return;
}

1
Saya pikir itu seharusnya melihat ini: odetocode.com/blogs/scott/archive/2004/08/20/…
SubmarineX

@SubmarineX Bagaimana Anda yakin artikel ini benar? ini masih kontroversi antara mutex dan codeproject.com/Articles/4975/…
John Nguyen

Luar biasa Terima kasih yang sempurna atas waktu dan upaya Anda
Ahmed Mahmoud

2
-1 Meskipun ini adalah solusi cepat, sangat mudah juga untuk menghindari a.exe dapat diubah namanya menjadi b.exe dan keduanya akan berjalan. + kasus lain di mana ini tidak akan berfungsi sebagaimana mestinya. Gunakan dengan hati-hati!
Lars Nielsen

Ini tidak berfungsi dengan mono-develop. Di bawah lingkungan windows itu berfungsi dengan baik.
Tono Nam

20

Berikut adalah kode yang Anda perlukan untuk memastikan bahwa hanya satu instance yang berjalan. Ini adalah metode menggunakan mutex bernama.

public class Program
{
    static System.Threading.Mutex singleton = new Mutex(true, "My App Name");

    static void Main(string[] args)
    {
        if (!singleton.WaitOne(TimeSpan.Zero, true))
        {
            //there is already another instance running!
            Application.Exit();
        }
    }
}

2
Untuk aplikasi WPF gunakan Application.Current.Shutdown (); Metode ini bekerja dengan sangat baik. Terima kasih Terrapin.
Jeff

Di sini yang utama adalah Anda membuat mutex statis. Serta dalam kasus lain GC akan mengumpulkannya.
Oleksii

Saya menikmati kesederhanaan dan kejelasan nama Mutex. Kode ini ringkas dan efektif.
Chad

7

Hanselman memiliki posting tentang menggunakan kelas WinFormsApplicationBase dari rakitan Microsoft.VisualBasic untuk melakukan ini.


Saya telah menggunakannya selama beberapa tahun, tetapi sekarang saya ingin mengubah ke solusi berbasis Mutex. Saya memiliki pelanggan yang melaporkan masalah dengan ini dan saya curiga itu menggunakan Remoting untuk melakukannya.
Richard Watson

5

Sepertinya ada 3 teknik dasar yang telah disarankan sejauh ini.

  1. Berasal dari kelas Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase dan setel properti IsSingleInstance ke true. (Saya percaya peringatan di sini adalah bahwa ini tidak akan berfungsi dengan aplikasi WPF, bukan?)
  2. Gunakan mutex bernama dan periksa apakah sudah dibuat.
  3. Dapatkan daftar proses yang berjalan dan bandingkan nama-nama proses tersebut. (Ini memiliki peringatan mengharuskan nama proses Anda harus unik relatif terhadap proses lain yang berjalan pada mesin pengguna tertentu.)

Ada peringatan yang saya lewatkan?


3
Saya tidak berpikir 3 sangat efisien. Saya akan memilih Mutex, menggunakannya tanpa masalah berkali-kali. Saya tidak pernah menggunakan item 1 tidak yakin bagaimana yang terbang saat Anda berada di c #.
typemismatch

2
Opsi 1 masih berfungsi dengan WPF, hanya sedikit lebih terlibat. msdn.microsoft.com/en-us/library/ms771662.aspx
Graeme Bradbury

5

1 - Buat referensi di program.cs ->

using System.Diagnostics;

2 - Dimasukkan void Main()sebagai baris pertama kode ->

 if (Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName).Length >1)
                return;

Itu dia.


Apa bedanya ini dengan Mutex? Apakah ada tangkapan?
Helikopter Serang Harambe

Ini menggunakan nama proses. Jika nama tersebut diulang maka akan menghasilkan bendera palsu. Dalam kasus lain, ini lebih bersih dari mutex
magallanes

4

Menggunakan Visual Studio 2005 atau 2008 ketika Anda membuat proyek untuk eksekusi, pada jendela properti di dalam panel "Aplikasi" ada kotak centang bernama "Buat aplikasi contoh tunggal" yang dapat Anda aktifkan untuk mengonversi aplikasi pada aplikasi contoh tunggal .

Berikut adalah tangkapan dari jendela yang saya bicarakan: masukkan deskripsi gambar di sini Ini adalah proyek aplikasi Windows Visual Studio 2008.


3
Saya mencari kotak centang ini yang Anda sebutkan, untuk aplikasi C # / WPF saya, dan tidak ada.
HappyNomad

3
Saya juga tidak melihatnya di properti aplikasi VS 2008 C # / WinForms saya.
Jesse McGrew

Tidak di VS2005 juga. Dia pasti menyebut studio VB lama.
nawfal

Ya ada opsi, saya memodifikasi posting untuk menambahkan tangkapan jendela di mana Anda dapat menemukan opsi ini.
Doliveras

6
Opsi hanya ada untuk aplikasi VB.NET, bukan untuk C #. Rupanya opsi itu sendiri menggunakan kelas WinFormsApplicationBase dari rakitan Microsoft.VisualBasic.
amolbk

4

saya mencoba semua solusi di sini dan tidak ada yang berhasil dalam proyek C # .net 4.0 saya. Berharap dapat membantu seseorang di sini solusi yang berhasil untuk saya:

Sebagai variabel kelas utama:

private static string appGuid = "WRITE AN UNIQUE GUID HERE";
private static Mutex mutex;

Saat Anda perlu memeriksa apakah aplikasi sudah berjalan:

bool mutexCreated;
mutex = new Mutex(true, "Global\\" + appGuid, out mutexCreated);
if (mutexCreated)
    mutex.ReleaseMutex();

if (!mutexCreated)
{
    //App is already running, close this!
    Environment.Exit(0); //i used this because its a console app
}

Saya perlu menutup istance lain hanya dengan beberapa kondisi, ini bekerja dengan baik untuk tujuan saya


Anda tidak perlu benar-benar memperoleh mutex dan melepaskannya. Yang perlu Anda ketahui adalah jika aplikasi lain telah membuat objek (misalnya, jumlah referensi kernelnya> = 1).
Michael Goldshteyn


3

Setelah mencoba beberapa solusi saya pertanyaannya. Saya akhirnya menggunakan contoh untuk WPF di sini: http://www.c-sharpcorner.com/UploadFile/f9f215/how-to-restrict-the-application-to-just-one-instance/

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

    protected override void OnStartup(StartupEventArgs e)  
    {  
        const string appName = "MyAppName";  
        bool createdNew;  

        _mutex = new Mutex(true, appName, out createdNew);  

        if (!createdNew)  
        {  
            //app is already running! Exiting the application  
            Application.Current.Shutdown();  
        }  

    }          
}  

Di App.xaml:

x:Class="*YourNameSpace*.App"
StartupUri="MainWindow.xaml"
Startup="App_Startup"


2

Gunakan VB.NET! Tidak benar-benar ;)

menggunakan Microsoft.VisualBasic.ApplicationServices;

WindowsFormsApplicationBase dari VB.Net memberi Anda Properti "SingleInstace", yang menentukan Instans lain dan membiarkan hanya satu Instance yang berjalan.


2

Ini adalah kode untuk VB.Net

Private Shared Sub Main()
    Using mutex As New Mutex(False, appGuid)
        If Not mutex.WaitOne(0, False) Then
              MessageBox.Show("Instance already running", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error)
            Return
        End If

        Application.Run(New Form1())
    End Using
End Sub

Ini adalah kode untuk C #

private static void Main()
{
    using (Mutex mutex = new Mutex(false, appGuid)) {
        if (!mutex.WaitOne(0, false)) {
            MessageBox.Show("Instance already running", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
            return;
        }

        Application.Run(new Form1());
    }
}



1

(Catatan: ini adalah solusi yang menyenangkan! Ini berfungsi tetapi menggunakan desain GDI + yang buruk untuk mencapai ini.)

Masukkan gambar ke dalam aplikasi Anda dan muat saat memulai. Tahan sampai aplikasi keluar. Pengguna tidak akan dapat memulai instance kedua. (Tentu saja solusi mutex jauh lebih bersih)

private static Bitmap randomName = new Bitmap("my_image.jpg");

Ini sebenarnya brilian dalam kesederhanaannya, dan bekerja dengan hampir semua jenis file dan bukan hanya gambar. Saya merasa solusi Mutex jauh dari "bersih". Ini sangat rumit dan tampaknya ada banyak cara untuk gagal karena tidak melakukannya dengan "benar." Ini juga membutuhkan Main()metode yang bertentangan dengan bagaimana WPF seharusnya bekerja.
Kyle Delaney

Ini seperti penggunaan bug. Ini berfungsi tetapi tidak dibuat untuk tujuan itu. Saya tidak akan menggunakannya sebagai seorang profesional.
Bitterblue

Ya, sangat disayangkan kami tidak memiliki solusi yang efektif dan sederhana ini tanpa mengandalkan pengecualian.
Kyle Delaney

Padahal itu bukan bug. Itu masih. NET berperilaku sebagaimana mestinya.
Kyle Delaney

1
[STAThread]
static void Main()                  // args are OK here, of course
{
    bool ok;
    m = new System.Threading.Mutex(true, "YourNameHere", out ok);

    if (! ok)
    {
        MessageBox.Show("Another instance is already running.");
        return;
    }

    Application.Run(new Form1());   // or whatever was there

    GC.KeepAlive(m);                // important!
}

Dari: Memastikan satu contoh Aplikasi .NET

dan: Mutex Aplikasi Instance Tunggal

Jawaban yang sama dengan @Smink dan @Imjustpondering dengan twist:

FAQ Jon Skeet tentang C # untuk mencari tahu mengapa GC.KeepAlive penting


-1, karena Anda gagal menggunakan blok using dengan mutex, yang akan membuat KeepAlive menjadi berlebihan. Dan ya, menurut saya John Skeet salah yang ini. Dia tidak menjelaskan mengapa salah untuk membuang mutex dalam kasus ini.

1

Cukup menggunakan a StreamWriter, bagaimana dengan ini?

System.IO.File.StreamWriter OpenFlag = null;   //globally

dan

try
{
    OpenFlag = new StreamWriter(Path.GetTempPath() + "OpenedIfRunning");
}
catch (System.IO.IOException) //file in use
{
    Environment.Exit(0);
}

0

Biasanya ini dilakukan dengan Mutex bernama (gunakan Mutex baru ("nama aplikasi Anda", benar) dan periksa nilai yang dikembalikan), tetapi ada juga beberapa kelas dukungan di Microsoft.VisualBasic.dll yang dapat melakukannya untuk Anda .


0

Ini bekerja untuk saya di C # murni. coba / tangkap adalah ketika kemungkinan proses dalam daftar keluar selama loop Anda.

using System.Diagnostics;
....
[STAThread]
static void Main()
{
...
        int procCount = 0;
        foreach (Process pp in Process.GetProcesses())
        {
            try
            {
                if (String.Compare(pp.MainModule.FileName, Application.ExecutablePath, true) == 0)
                {
                    procCount++;                        
                    if(procCount > 1) {
                       Application.Exit();
                       return;
                    }
                }
            }
            catch { }
        }
        Application.Run(new Form1());
}

0

Pastikan untuk mempertimbangkan keamanan saat membatasi aplikasi ke satu instance:

Artikel lengkap: https://blogs.msdn.microsoft.com/oldnewthing/20060620-13/?p=30813

Kami menggunakan mutex bernama dengan nama tetap untuk mendeteksi apakah salinan lain dari program sedang berjalan. Tetapi itu juga berarti penyerang dapat membuat mutex terlebih dahulu, sehingga mencegah program kita berjalan sama sekali! Bagaimana cara mencegah serangan penolakan layanan jenis ini?

...

Jika penyerang berjalan dalam konteks keamanan yang sama dengan program Anda (atau akan) berjalan, maka tidak ada yang dapat Anda lakukan. Apapun "jabat tangan rahasia" yang Anda buat untuk menentukan apakah salinan lain dari program Anda sedang berjalan, penyerang dapat menirunya. Karena berjalan dalam konteks keamanan yang benar, ia dapat melakukan apa saja yang dapat dilakukan oleh program "sebenarnya".

...

Jelas Anda tidak dapat melindungi diri Anda dari penyerang yang menjalankan hak istimewa keamanan yang sama, tetapi Anda masih dapat melindungi diri Anda dari penyerang yang tidak memiliki hak istimewa yang menjalankan hak keamanan lainnya.

Coba atur DACL di mutex Anda, berikut cara .NET: https://msdn.microsoft.com/en-us/library/system.security.accesscontrol.mutexsecurity(v=vs.110).aspx


0

Tak satu pun dari jawaban ini berhasil untuk saya karena saya membutuhkan ini untuk bekerja di Linux menggunakan monodevelop. Ini bagus untuk saya:

Panggil metode ini dengan meneruskan ID unik

    public static void PreventMultipleInstance(string applicationId)
    {
        // Under Windows this is:
        //      C:\Users\SomeUser\AppData\Local\Temp\ 
        // Linux this is:
        //      /tmp/
        var temporaryDirectory = Path.GetTempPath();

        // Application ID (Make sure this guid is different accross your different applications!
        var applicationGuid = applicationId + ".process-lock";

        // file that will serve as our lock
        var fileFulePath = Path.Combine(temporaryDirectory, applicationGuid);

        try
        {
            // Prevents other processes from reading from or writing to this file
            var _InstanceLock = new FileStream(fileFulePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
            _InstanceLock.Lock(0, 0);
            MonoApp.Logger.LogToDisk(LogType.Notification, "04ZH-EQP0", "Aquired Lock", fileFulePath);

            // todo investigate why we need a reference to file stream. Without this GC releases the lock!
            System.Timers.Timer t = new System.Timers.Timer()
            {
                Interval = 500000,
                Enabled = true,
            };
            t.Elapsed += (a, b) =>
            {
                try
                {
                    _InstanceLock.Lock(0, 0);
                }
                catch
                {
                    MonoApp.Logger.Log(LogType.Error, "AOI7-QMCT", "Unable to lock file");
                }
            };
            t.Start();

        }
        catch
        {
            // Terminate application because another instance with this ID is running
            Environment.Exit(102534); 
        }
    }         
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.