Ngăn chặn nhiều phiên bản của một ứng dụng nhất định trong .NET?


123

Trong .NET, cách tốt nhất để ngăn nhiều phiên bản của một ứng dụng chạy cùng một lúc là gì? Và nếu không có kỹ thuật "tốt nhất", một số lưu ý cần xem xét với mỗi giải pháp là gì?

Câu trả lời:


151

Sử dụng Mutex. Một trong những ví dụ trên sử dụng GetProcessByName có nhiều cảnh báo. Đây là một bài viết tốt về chủ đề:

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
Sử dụng một mutex cũng hoạt động với mã không phải là mạng .net (mặc dù cú pháp sẽ thay đổi)
crashmstr

8
Đây là một phiên bản đầy đủ hơn một chút, với một số nhận xét tốt: stackoverflow.com/questions/229565/ mẹo
Richard Watson

2
@ClarkKent: Chỉ là một chuỗi ngẫu nhiên để tên Mutex không va chạm tên từ ứng dụng khác.
jgauffin

Điều này có thể hạn chế đến một số trường hợp nhất định không?
Alejandro del Río

3
Để kiểm soát tốt hơn trong khi tạo phiên bản hoặc thay đổi hướng dẫn ứng dụng của bạn, bạn có thể sử dụng: string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), true)[0]).Value;sẽ lấy hướng dẫn của hội đồng thi hành
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
Tôi nghĩ rằng nên xem cái này: odetocode.com/bloss/scott/archive/2004/08/20/NH
Tàu

@SubmarineX Làm thế nào bạn chắc chắn bài viết này là chính xác? vẫn còn tranh cãi giữa mutex và codeproject.com/Articles/4975/ Khăn
John Nguyen

Tuyệt vời Chỉ là cảm ơn hoàn hảo cho thời gian và nỗ lực của bạn
Ahmed Mahmoud

2
-1 Mặc dù đây là giải pháp nhanh chóng, nhưng cũng rất dễ dàng để phá vỡ a.exe có thể được đổi tên thành b.exe và cả hai sẽ chạy. + các trường hợp khác trong đó đơn giản là không hoạt động như dự định. Sử dụng cẩn thận!
Lars Nielsen

Điều này không làm việc với phát triển đơn. Trong môi trường windows, nó hoạt động rất tốt.
Tono Nam

20

Đây là mã bạn cần để đảm bảo rằng chỉ có một phiên bản đang chạy. Đây là phương pháp sử dụng một mutex có tên.

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
Đối với ứng dụng WPF, hãy sử dụng Application.Cản.Shutdown (); Phương pháp này hoạt động như một lá bùa. Cảm ơn Terrapin.
Jeff

Ở đây điều chính là bạn làm cho mutex tĩnh. Cũng như trong trường hợp khác, GC sẽ thu thập nó.
Oleksii

Tôi rất thích sự đơn giản và rõ ràng của Mutex có tên. Mã này ngắn gọn và hiệu quả.
Chad

7

Hanselman có một bài đăng về việc sử dụng lớp WinFormsApplicationBase từ hội đồng Microsoft.VisualBasic để làm điều này.


Tôi đã sử dụng nó trong một vài năm, nhưng hiện đang tìm cách thay đổi thành một giải pháp dựa trên Mutex. Tôi có khách hàng báo cáo sự cố với điều này và tôi nghi ngờ đó là sử dụng từ xa để thực hiện.
Richard Watson

5

Có vẻ như có 3 kỹ thuật cơ bản đã được đề xuất cho đến nay.

  1. Xuất phát từ lớp Microsoft.VisualBasic.ApplicationService.WindowsFormsApplicationBase và đặt thuộc tính IsSingleInstance thành true. (Tôi tin rằng một cảnh báo ở đây là điều này sẽ không hoạt động với các ứng dụng WPF, phải không?)
  2. Sử dụng một mutex có tên và kiểm tra xem nó đã được tạo chưa.
  3. Lấy danh sách các tiến trình đang chạy và so sánh tên của các tiến trình. (Điều này có sự cảnh báo về việc yêu cầu tên quy trình của bạn là duy nhất so với bất kỳ quy trình nào khác đang chạy trên một máy của người dùng cụ thể.)

Có bất kỳ cảnh báo nào tôi đã bỏ lỡ?


3
Tôi không nghĩ 3 là rất hiệu quả. Tôi đã bỏ phiếu cho Mutex, đã sử dụng nó mà không gặp vấn đề gì nhiều lần. Tôi chưa bao giờ sử dụng mục 1 không chắc chắn nó bay như thế nào khi bạn ở c #.
typemismatch

2
Tùy chọn 1 vẫn hoạt động với WPF, nó chỉ liên quan nhiều hơn một chút. msdn.microsoft.com/en-us/l Library / ms771662.aspx
Graeme Bradbury

5

1 - Tạo một tài liệu tham khảo trong chương trình.cs ->

using System.Diagnostics;

2 - Đặt vào void Main()làm dòng mã đầu tiên ->

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

Đó là nó.


Sự khác biệt của điều này là Mutexgì? Có bắt được không?
Máy bay trực thăng tấn công Harambe

Đó là sử dụng tên của quá trình. Nếu tên được lặp lại thì nó sẽ tạo ra một cờ giả. Trong mọi trường hợp khác, nó sạch hơn mutex
magallanes

4

Sử dụng Visual Studio 2005 hoặc 2008 khi bạn tạo dự án để thực thi, trên các cửa sổ thuộc tính bên trong bảng "Ứng dụng" có một hộp kiểm có tên là Tạo một ứng dụng cá thể đơn lẻ mà bạn có thể kích hoạt để chuyển đổi ứng dụng trên một ứng dụng cá thể .

Đây là một bản chụp cửa sổ mà tôi đang nói đến: nhập mô tả hình ảnh ở đây Đây là một dự án ứng dụng cửa sổ Visual Studio 2008.


3
Tôi đã tìm hộp kiểm này mà bạn đề cập, cho ứng dụng C # / WPF của tôi và không có cái nào cả.
HappyNomad

3
Tôi cũng không thấy nó trong các thuộc tính của ứng dụng VS 2008 C # / WinForms.
Jesse McGrew

Không phải trong VS2005 là tốt. Ông phải nhắc đến studio VB cũ.
nawfal

Có tùy chọn tồn tại, tôi đã sửa đổi bài đăng để thêm một bản chụp cửa sổ mà bạn có thể tìm thấy tùy chọn này.
Doliveras

6
Tùy chọn chỉ tồn tại cho ứng dụng VB.NET, không dành cho C #. Rõ ràng chính tùy chọn này sử dụng lớp WinFormsApplicationBase từ hội đồng Microsoft.VisualBasic.
amolbk

4

tôi đã thử tất cả các giải pháp ở đây và không có gì hoạt động trong dự án C # .net 4.0 của tôi. Hy vọng sẽ giúp được ai đó ở đây giải pháp hiệu quả cho tôi:

Là biến lớp chính:

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

Khi bạn cần kiểm tra xem ứng dụng đã chạy chưa:

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
}

Tôi cần phải đóng các istances khác chỉ với một số điều kiện, điều này hoạt động tốt cho mục đích của tôi


Bạn không cần phải thực sự có được mutex và phát hành nó. Tất cả những gì bạn cần biết là nếu một ứng dụng khác đã tạo đối tượng (nghĩa là số tham chiếu kernel của nó> = 1).
Michael Goldshteyn


3

Sau khi thử nhiều giải pháp tôi câu hỏi. Tôi đã kết thúc bằng cách sử dụng ví dụ cho WPF tại đây: 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();  
        }  

    }          
}  

Trong App.xaml:

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

2

Bài viết này chỉ giải thích cách bạn có thể tạo một ứng dụng windows với quyền kiểm soát số lượng phiên bản của nó hoặc chỉ chạy một thể hiện duy nhất. Đây là nhu cầu rất điển hình của một ứng dụng kinh doanh. Đã có rất nhiều giải pháp khả thi khác để kiểm soát điều này.

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


Liên kết bị hỏng.
Chad

2

Sử dụng VB.NET! Không: thực sự;)

sử dụng Microsoft.VisualBasic.ApplicationService;

WindowsFormsApplicationBase từ VB.Net cung cấp cho bạn Thuộc tính "SingleInstace", xác định các Trường hợp khác và chỉ cho phép một Trường hợp chạy.


2

Đây là mã cho 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

Đây là mã cho 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

(Lưu ý: đây là một giải pháp thú vị! Nó hoạt động nhưng sử dụng thiết kế GDI + xấu để đạt được điều này.)

Đặt một hình ảnh với ứng dụng của bạn và tải nó khi khởi động. Giữ nó cho đến khi ứng dụng thoát. Người dùng sẽ không thể bắt đầu phiên bản thứ 2. (Tất nhiên giải pháp mutex sạch hơn nhiều)

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

Điều này thực sự xuất sắc trong sự đơn giản của nó, và nó hoạt động với hầu hết mọi loại tệp và không chỉ hình ảnh. Tôi cảm thấy như giải pháp Mutex khác xa với "sạch". Nó cực kỳ phức tạp và rõ ràng có nhiều cách để nó thất bại vì không làm điều đó "đúng". Nó cũng đòi hỏi một Main()phương pháp đi ngược lại cách WPF hoạt động.
Kyle Delaney

Đây là loại lỗi như sử dụng. Nó hoạt động nhưng nó không được thực hiện cho mục đích đó. Tôi sẽ không sử dụng nó như một chuyên gia.
Bitterblue

Vâng, thật không may, chúng tôi không có giải pháp hiệu quả và đơn giản này mà không dựa vào ngoại lệ.
Kyle Delaney

Mặc dù nó không chính xác là một lỗi. Nó vẫn .NET hoạt động như dự định.
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!
}

Từ: Đảm bảo một phiên bản duy nhất của Ứng dụng .NET

và: Ứng dụng sơ thẩm Mutex

Câu trả lời tương tự như @Smink và @Imjustpondering với một twist:

Câu hỏi thường gặp của Jon Skeet về C # để tìm hiểu lý do tại sao GC.KeepAlive quan trọng


-1, vì bạn không sử dụng khối sử dụng với mutex, điều này sẽ khiến KeepAlive trở nên thừa. Và vâng, tôi nghĩ John Skeet đã sai điều này. Anh ta không nói rõ lý do tại sao sẽ sai khi loại bỏ mutex trong trường hợp này.

1

Đơn giản chỉ cần sử dụng một StreamWriter, làm thế nào về điều này?

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

Thông thường, nó được thực hiện với Mutex có tên (sử dụng Mutex mới ("tên ứng dụng của bạn", đúng) và kiểm tra giá trị trả về), nhưng cũng có một số lớp hỗ trợ trong Microsoft.VisualBasic.dll có thể làm điều đó cho bạn .


0

Điều này làm việc cho tôi trong C # tinh khiết. thử / bắt là khi có thể một quá trình trong danh sách thoát trong vòng lặp của bạn.

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

Hãy chắc chắn xem xét bảo mật khi giới hạn ứng dụng trong một trường hợp duy nhất:

Bài viết đầy đủ: https://bloss.msdn.microsoft.com/oldnewthing/20060620-13/?p=30813

Chúng tôi đang sử dụng một mutex có tên với một tên cố định để phát hiện xem một bản sao khác của chương trình có đang chạy hay không. Nhưng điều đó cũng có nghĩa là kẻ tấn công có thể tạo ra mutex trước, do đó ngăn chương trình của chúng tôi không chạy! Làm thế nào tôi có thể ngăn chặn loại tấn công từ chối dịch vụ này?

...

Nếu kẻ tấn công đang chạy trong cùng bối cảnh bảo mật như chương trình của bạn (hoặc sẽ) đang chạy, thì bạn không thể làm gì được. Bất cứ "cái bắt tay bí mật" nào bạn nghĩ ra để xác định xem một bản sao khác của chương trình của bạn có đang chạy hay không, kẻ tấn công có thể bắt chước nó. Vì nó đang chạy trong bối cảnh bảo mật chính xác, nó có thể làm bất cứ điều gì mà chương trình "thực" có thể làm.

...

Rõ ràng bạn không thể tự bảo vệ mình khỏi kẻ tấn công chạy cùng đặc quyền bảo mật, nhưng bạn vẫn có thể tự bảo vệ mình trước những kẻ tấn công không có đặc quyền chạy ở các đặc quyền bảo mật khác.

Hãy thử thiết lập một DACL trên mutex của bạn, đây là cách .NET: https://msdn.microsoft.com/en-us/l Library / system.security.accesscontrol.mutexsecurity (v = vs.110) .aspx


0

Không có câu trả lời nào phù hợp với tôi vì tôi cần nó để hoạt động trong Linux bằng cách sử dụng phát triển đơn. Điều này làm việc rất tốt cho tôi:

Gọi phương thức này là ID duy nhất

    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); 
        }
    }         
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.