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?

C.Naga 76
sumber

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";
ImJustPondering
sumber
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;
}
Thomas Wagner
sumber
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();
        }
    }
}
Seibar
sumber
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.

bdukes
sumber
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?

C.Naga 76
sumber
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.

pengguna5289350
sumber
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.

Doliveras
sumber
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

HypeZ
sumber
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"
Kasper Halvas Jensen
sumber
2

Artikel ini hanya menjelaskan bagaimana Anda dapat membuat aplikasi windows dengan kontrol pada jumlah instance-nya atau menjalankan hanya satu instance. Ini adalah kebutuhan yang sangat khas dari aplikasi bisnis. Sudah ada banyak solusi lain yang mungkin untuk mengendalikan ini.

http://www.openwinforms.com/single_instance_application.html

Jorge Ferreira
sumber
Tautan rusak.
Chad
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.

MADMap
sumber
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());
    }
}
Shruti
sumber
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");
Bitterblue
sumber
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

Ric Tokyo
sumber
-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);
}
Divins Mathew
sumber
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 .

Tomer Gabel
sumber
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());
}
Derek Wade
sumber
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

tapir
sumber
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); 
        }
    }         
Tono Nam
sumber