ป้องกันหลายอินสแตนซ์ของแอพที่กำหนดใน. NET หรือไม่


123

ใน. NET วิธีใดดีที่สุดในการป้องกันไม่ให้แอปหลายอินสแตนซ์ทำงานพร้อมกัน และหากไม่มีเทคนิคที่ "ดีที่สุด" ข้อควรระวังในการพิจารณาแต่ละวิธีมีอะไรบ้าง?

คำตอบ:


151

ใช้ Mutex หนึ่งในตัวอย่างข้างต้นโดยใช้ GetProcessByName มีข้อแม้มากมาย นี่คือบทความที่ดีในหัวข้อนี้:

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
การใช้ mutex ก็ใช้ได้กับรหัสที่ไม่ใช่. net เช่นกัน (แม้ว่าไวยากรณ์จะแตกต่างกันไป)
crashmstr

9
นี่คือเวอร์ชันที่กรอกข้อมูลเพิ่มเติมเล็กน้อยพร้อมความคิดเห็นดีๆ: stackoverflow.com/questions/229565/…
Richard Watson

2
@ClarkKent: เพียงแค่สตริงสุ่มเพื่อให้ชื่อ Mutex ไม่ชนกับชื่อจากแอปพลิเคชันอื่น
jgauffin

สามารถ จำกัด จำนวนอินสแตนซ์ได้หรือไม่
Alejandro del Río

4
เพื่อให้สามารถควบคุมได้ดีขึ้นในขณะที่กำหนดเวอร์ชันหรือเปลี่ยนstring appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), true)[0]).Value;คู่มือแอปของคุณคุณสามารถใช้: ซึ่งจะได้รับคำแนะนำของแอสเซมบลีการดำเนินการ
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
ฉันคิดว่าควรจะเห็นสิ่งนี้: odetocode.com/blogs/scott/archive/2004/08/20/…
SubmarineX

@SubmarineX คุณแน่ใจได้อย่างไรว่าบทความนี้ถูกต้อง? ยังคงเป็นที่ถกเถียงกันระหว่าง mutex และcodeproject.com/Articles/4975/…
John Nguyen

Amazing ขอบคุณที่สมบูรณ์แบบสำหรับเวลาและความพยายามของคุณ
Ahmed Mahmoud

2
-1 แม้ว่านี่จะเป็นวิธีแก้ปัญหาที่รวดเร็ว แต่ก็ง่ายมากที่จะหลีกเลี่ยง a.exe สามารถเปลี่ยนชื่อเป็น b.exe และทั้งสองจะทำงาน + กรณีอื่น ๆ ที่ไม่ได้ผลตามที่ตั้งใจไว้ ใช้ด้วยความระมัดระวัง!
Lars Nielsen

สิ่งนี้ใช้ไม่ได้กับการพัฒนาแบบโมโน ภายใต้สภาพแวดล้อม windows มันทำงานได้ดีแม้ว่า
โตโน่นาม

20

นี่คือรหัสที่คุณต้องใช้เพื่อให้แน่ใจว่ามีเพียงอินสแตนซ์เดียวที่กำลังทำงานอยู่ นี่คือวิธีการใช้ชื่อ mutex

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
สำหรับแอปพลิเคชัน WPF ให้ใช้ Application.Current.Shutdown (); วิธีนี้ใช้ได้ผลเหมือนมีเสน่ห์ ขอบคุณ Terrapin
Jeff

สิ่งสำคัญคือคุณทำให้ mutex คงที่ เช่นเดียวกับในกรณีอื่น ๆ GC จะรวบรวมมัน
Oleksii

ฉันชอบความเรียบง่ายและชัดเจนของ Mutex ที่มีชื่อว่า รหัสนี้กระชับและมีประสิทธิภาพ
ชาด

7

Hanselman มีโพสต์เกี่ยวกับการใช้คลาส WinFormsApplicationBase จากแอสเซมบลี Microsoft.VisualBasic เพื่อทำสิ่งนี้


ฉันใช้มันมาสองสามปีแล้ว แต่ตอนนี้ฉันต้องการเปลี่ยนเป็นโซลูชันที่ใช้ Mutex ฉันมีลูกค้าที่รายงานปัญหาเกี่ยวกับสิ่งนี้และฉันสงสัยว่ากำลังใช้ Remoting เพื่อดำเนินการนี้
Richard Watson

5

ดูเหมือนว่าจะมีเทคนิคพื้นฐาน 3 อย่างที่แนะนำไปแล้ว

  1. มาจากคลาส Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase และตั้งค่าคุณสมบัติ IsSingleInstance เป็น true (ฉันเชื่อว่าข้อแม้ที่นี่จะใช้ไม่ได้กับแอปพลิเคชัน WPF ใช่หรือไม่)
  2. ใช้ชื่อ mutex และตรวจสอบว่าสร้างแล้วหรือยัง
  3. รับรายชื่อกระบวนการที่กำลังทำงานอยู่และเปรียบเทียบชื่อของกระบวนการ (ซึ่งมีข้อแม้ในการกำหนดให้ชื่อกระบวนการของคุณไม่ซ้ำกันเมื่อเทียบกับกระบวนการอื่น ๆ ที่ทำงานบนเครื่องของผู้ใช้ที่ระบุ)

ข้อควรระวังที่ฉันพลาด?


3
ฉันไม่คิดว่า 3 จะมีประสิทธิภาพมาก ฉันจะโหวตให้ Mutex ใช้โดยไม่มีปัญหาหลายครั้ง ฉันไม่เคยใช้ข้อ 1 ไม่แน่ใจว่ามันบินได้อย่างไรเมื่อคุณอยู่ใน c #
พิมพ์ดีด

2
ตัวเลือกที่ 1 ยังคงใช้งานได้กับ WPF ซึ่งเกี่ยวข้องมากกว่าเล็กน้อย msdn.microsoft.com/en-us/library/ms771662.aspx
Graeme Bradbury

5

1 - สร้างข้อมูลอ้างอิงใน program.cs ->

using System.Diagnostics;

2 - ใส่void Main()เป็นบรรทัดแรกของรหัส ->

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

แค่นั้นแหละ.


อะไรคือความแตกต่างของสิ่งนี้Mutex? มีการจับหรือไม่?
เฮลิคอปเตอร์โจมตีฮารัมเบ

โดยใช้ชื่อของกระบวนการ ถ้าชื่อซ้ำมันจะสร้างแฟล็กเท็จ ในกรณีอื่น ๆ มันสะอาดกว่า mutex
magallanes

4

การใช้ Visual Studio 2005 หรือ 2008 เมื่อคุณสร้างโครงการสำหรับปฏิบัติการบนหน้าต่างคุณสมบัติภายในแผง "แอปพลิเคชัน" จะมีกล่องกาเครื่องหมายชื่อ "สร้างแอปพลิเคชันอินสแตนซ์เดียว" ที่คุณสามารถเปิดใช้งานเพื่อแปลงแอปพลิเคชันบนแอปพลิเคชันอินสแตนซ์เดียว .

นี่คือการจับภาพหน้าต่างที่ฉันกำลังพูดถึง: ใส่คำอธิบายภาพที่นี่ นี่คือโครงการแอปพลิเคชัน Windows ของ Visual Studio 2008


3
ฉันมองหาช่องทำเครื่องหมายนี้ที่คุณพูดถึงสำหรับแอป C # / WPF ของฉัน แต่ไม่มีเลย
HappyNomad

3
ฉันไม่เห็นมันในคุณสมบัติของแอป VS 2008 C # / WinForms เช่นกัน
Jesse McGrew

ไม่มีใน VS2005 เช่นกัน เขาต้องพูดถึงสตูดิโอ VB เก่า
nawfal

ใช่มีตัวเลือกอยู่ฉันแก้ไขโพสต์เพื่อเพิ่มการจับภาพของหน้าต่างซึ่งคุณจะพบตัวเลือกนี้
Doliveras

6
ตัวเลือกนี้มีเฉพาะสำหรับแอปพลิเคชัน VB.NET ไม่ใช่สำหรับ C # เห็นได้ชัดว่าตัวเลือกนั้นใช้คลาส WinFormsApplicationBase จากแอสเซมบลี Microsoft.VisualBasic
amolbk

4

ฉันลองใช้วิธีแก้ปัญหาทั้งหมดที่นี่และไม่มีอะไรได้ผลในโครงการ C # .net 4.0 ของฉัน หวังว่าจะช่วยใครบางคนที่นี่วิธีแก้ปัญหาที่เหมาะกับฉัน:

เป็นตัวแปรคลาสหลัก:

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

เมื่อคุณต้องการตรวจสอบว่าแอปกำลังทำงานอยู่หรือไม่:

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
}

ฉันต้องการปิด istances อื่น ๆ ด้วยเงื่อนไขบางอย่างเท่านั้นซึ่งใช้ได้ดีสำหรับจุดประสงค์ของฉัน


คุณไม่จำเป็นต้องได้รับ mutex จริงและปล่อยมัน สิ่งที่คุณต้องรู้ก็คือหากแอปอื่นได้สร้างออบเจ็กต์แล้ว (เช่นจำนวนการอ้างอิงเคอร์เนล> = 1)
Michael Goldshteyn


3

หลังจากลองใช้วิธีแก้ปัญหาหลาย ๆ คำถามแล้ว ฉันลงเอยด้วยการใช้ตัวอย่างสำหรับWPFที่นี่: 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();  
        }  

    }          
}  

ใน App.xaml:

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

2

บทความนี้อธิบายวิธีสร้างแอปพลิเคชัน windows โดยควบคุมจำนวนอินสแตนซ์หรือเรียกใช้อินสแตนซ์เดียวเท่านั้น นี่เป็นความต้องการทั่วไปของแอปพลิเคชันทางธุรกิจ มีโซลูชันที่เป็นไปได้อื่น ๆ มากมายในการควบคุมสิ่งนี้

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


ลิงก์เสีย
ชาด

2

ใช้ VB.NET! ไม่มีจริงๆ ;)

ใช้ Microsoft.VisualBasic.ApplicationServices;

WindowsFormsApplicationBase จาก VB.Net มีคุณสมบัติ "SingleInstace" ซึ่งกำหนดอินสแตนซ์อื่น ๆ และปล่อยให้อินสแตนซ์เดียวทำงาน


2

นี่คือรหัสสำหรับ 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

นี่คือรหัสสำหรับ 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

(หมายเหตุ: นี่เป็นวิธีแก้ปัญหาที่สนุก! มันใช้ได้ แต่ใช้การออกแบบ GDI + ที่ไม่ดีเพื่อให้บรรลุเป้าหมายนี้)

ใส่รูปภาพลงในแอพของคุณและโหลดเมื่อเริ่มต้น กดค้างไว้จนกว่าแอปจะออก ผู้ใช้จะไม่สามารถเริ่มอินสแตนซ์ที่ 2 ได้ (แน่นอนว่าน้ำยา mutex สะอาดกว่ามาก)

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

นี่เป็นสิ่งที่ยอดเยี่ยมจริงๆในความเรียบง่ายและใช้งานได้กับไฟล์ทุกประเภทไม่ใช่แค่รูปภาพ ฉันรู้สึกว่าโซลูชัน Mutex ยังห่างไกลจากคำว่า "สะอาด" มันซับซ้อนมากและมีหลายวิธีที่จะล้มเหลวเนื่องจากไม่ได้ทำ "ถูกต้อง" นอกจากนี้ยังต้องใช้Main()วิธีการที่ขัดกับวิธีการทำงานของ WPF
Kyle Delaney

นี่เป็นเหมือนการใช้บั๊ก ใช้งานได้ แต่ไม่ได้ทำขึ้นเพื่อจุดประสงค์นั้น ฉันจะไม่ใช้มันเป็นมืออาชีพ
Bitterblue

ใช่โชคไม่ดีที่เราไม่มีวิธีแก้ปัญหานี้ที่มีประสิทธิภาพและเรียบง่ายโดยไม่ต้องอาศัยข้อยกเว้น
Kyle Delaney

แม้ว่าจะไม่ใช่ข้อผิดพลาดอย่างแน่นอน NET ยังคงทำงานตามที่ตั้งใจไว้
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!
}

จาก: การตรวจสอบอินสแตนซ์เดียวของ. NET Application

และ: Single Instance Application Mutex

คำตอบเดียวกับ @Smink และ @Imjustpondering with a twist:

คำถามที่พบบ่อยของ Jon Skeet เกี่ยวกับ C #เพื่อค้นหาว่าทำไมGC. KeepAliveจึงมีความสำคัญ


-1 เนื่องจากคุณไม่สามารถใช้บล็อกการใช้งานกับ mutex ซึ่งจะทำให้ KeepAlive ไม่จำเป็น และใช่ฉันคิดว่า John Skeet เข้าใจผิด เขาไม่ได้อธิบายรายละเอียดว่าทำไมการทิ้ง mutex ในกรณีนี้จึงเป็นเรื่องผิด

1

เพียงแค่ใช้ a StreamWriter, แล้วล่ะ?

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

และ

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


0

สิ่งนี้ใช้ได้ผลสำหรับฉันใน C # ที่บริสุทธิ์ try / catch คือเมื่อกระบวนการในรายการออกจากการวนซ้ำของคุณ

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

อย่าลืมคำนึงถึงความปลอดภัยเมื่อ จำกัด แอปพลิเคชันไว้ที่อินสแตนซ์เดียว:

บทความแบบเต็ม: https://blogs.msdn.microsoft.com/oldnewthing/20060620-13/?p=30813

เราใช้ชื่อ mutex ที่มีชื่อตายตัวเพื่อตรวจสอบว่ามีการเรียกใช้โปรแกรมอื่นอยู่หรือไม่ แต่นั่นก็หมายความว่าผู้โจมตีสามารถสร้าง mutex ขึ้นมาได้ก่อนดังนั้นจึงป้องกันไม่ให้โปรแกรมของเราทำงานได้เลย! ฉันจะป้องกันการโจมตีปฏิเสธการให้บริการประเภทนี้ได้อย่างไร

...

หากผู้โจมตีกำลังทำงานในบริบทความปลอดภัยเดียวกับโปรแกรมของคุณ (หรือจะ) กำลังทำงานอยู่คุณก็ไม่สามารถทำอะไรได้ ไม่ว่าคุณจะ "จับมือเป็นความลับ" เพื่อตรวจสอบว่าโปรแกรมอื่นของคุณกำลังทำงานอยู่หรือไม่ผู้โจมตีสามารถเลียนแบบได้ เนื่องจากมันทำงานในบริบทความปลอดภัยที่ถูกต้องจึงสามารถทำอะไรก็ได้ที่โปรแกรม "จริง" สามารถทำได้

...

เห็นได้ชัดว่าคุณไม่สามารถป้องกันตัวเองจากผู้โจมตีที่ใช้สิทธิ์การรักษาความปลอดภัยเดียวกันได้ แต่คุณยังสามารถป้องกันตัวเองจากผู้โจมตีที่ไม่มีสิทธิได้รับสิทธิพิเศษด้านความปลอดภัยอื่น ๆ

ลองตั้งค่า DACL บน mutex ของคุณนี่คือวิธี. NET: https://msdn.microsoft.com/en-us/library/system.security.accesscontrol.mutexsecurity(v=vs.110).aspx


0

คำตอบนี้ไม่ได้ผลสำหรับฉันเพราะฉันต้องการสิ่งนี้เพื่อทำงานภายใต้ Linux โดยใช้ monodevelop สิ่งนี้ใช้ได้ดีสำหรับฉัน:

เรียกวิธีนี้ว่ารหัสเฉพาะ

    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); 
        }
    }         
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.