2009-12-22 17 views
7

Tôi đang làm việc trong một ứng dụng chỉ có một cá thể phải tồn tại tại bất kỳ thời điểm nào. Có rất nhiều khả năng để thực hiện điều này:Win32: Làm thế nào để có được quy trình/chủ sở hữu một mutex?

  • quá trình Kiểm tra chạy cho một phù hợp với tên EXE của chúng tôi (không đáng tin cậy)
  • Tìm cửa sổ chính (không đáng tin cậy, và tôi không luôn luôn có một cửa sổ chính)
  • Tạo một mutex có tên duy nhất (GUID)

Tùy chọn mutex dường như là đáng tin cậy nhất và thanh lịch nhất.

Tuy nhiên, trước khi phiên bản thứ hai của tôi chấm dứt, tôi muốn đăng thông báo lên phiên bản đã chạy. Đối với điều này, tôi cần một xử lý cho thread (hoặc quá trình) sở hữu mutex.

Tuy nhiên, có vẻ như không có chức năng API để có được người tạo/chủ sở hữu của một mutex cụ thể. Tôi chỉ cần nhìn nó? Có cách nào khác để chuyển sang luồng/quy trình này không? Có cách nào khác để giải quyết vấn đề này không?

Cập nhật: This guy chỉ cần phát thông báo tới tất cả quy trình đang chạy. Tôi đoán đó là có thể, nhưng tôi không thực sự thích nó ...

+0

Sao chép? http://stackoverflow.com/questions/19147/what-is-the-correct-way-to-create-a-single-instance-application –

+0

Không thực sự. Không có câu trả lời nào cho tôi biết cách xử lý quy trình. – Thomas

+0

Đây là bản sao, mặc dù nó chưa bao giờ được trả lời: http://stackoverflow.com/questions/541477/c-how-can-i-get-owners-name-for-a-mutex –

Trả lời

4

Tôi không nghĩ có một cách tầm thường để giải quyết chủ sở hữu thực sự của một Mutex, nhưng quá trình sở hữu nó có thể tạo ra các vật phẩm phụ khác có thời gian sống gắn liền với nó. Có rất nhiều cơ chế phù hợp để gọi lại quá trình mà không cần có cửa sổ chính.

  1. Đăng ký một đối tượng trong Bảng đối tượng đang chạy COM. Khách hàng không thể sở hữu Mutex có thể tra cứu chủ sở hữu thông qua ROT và gọi lại cho chủ sở hữu. Tệp Moniker phải phù hợp để đăng ký tại đây.
  2. Tạo một đoạn bộ nhớ dùng chung chứa chi tiết vị trí cho quy trình của chủ sở hữu. Từ đó, ghi vào bộ đệm xử lý quy trình và xử lý luồng của một chuỗi có thể nhận được thông báo cửa sổ, sau đó sử dụng PostThreadMessage() để gửi thông báo. Bất kỳ quá trình cạnh tranh nào khác cũng có thể mở bộ nhớ dùng chung cho chỉ đọc để xác định vị trí gửi tin nhắn cửa sổ.
  3. Nghe trong quy trình chủ sở hữu trên Ổ cắm hoặc Ống có tên. Có lẽ quá mức cần thiết và không phù hợp với nhu cầu của bạn.
  4. Sử dụng tệp được chia sẻ có khóa. Tôi không thích điều này bởi vì chủ sở hữu sẽ cần phải thăm dò ý kiến, và nó sẽ không xử lý một cách duyên dáng N tiềm năng các quy trình khác có thể cố gắng liên lạc với chủ sở hữu cùng một lúc.

Dưới đây là các liên kết tham chiếu cho hai tùy chọn đầu tiên.

  1. IRunningObjectTable @ MSDN, File Monikers @ MSDN
  2. Creating Named Shared Memory @ MSDN
2

Tôi chưa bao giờ thực sự hiểu được lý trí đằng sau bằng cách sử dụng một Mutex không có khả năng báo hiệu. Thay vào đó, tôi sẽ tạo một sự kiện (sử dụng CreateEvent) có các thuộc tính giống như tạo một mutex (tức là với tên nó có thể trả về đối tượng đã tồn tại) nhưng bạn có thể đặt cờ sự kiện trong tiến trình mới, miễn là bản gốc quá trình đang chờ trên cờ sự kiện, nó có thể được thông báo khi nó cần tự đánh thức.

+0

Tất nhiên, quy trình gốc không chặn việc chờ sự kiện; nó chỉ chạy vòng tròn trong vòng lặp tin nhắn của nó. (Tôi có thể kiểm tra mutex mỗi lần qua vòng lặp, nhưng điều đó cảm thấy xấu.) – Thomas

+0

Bạn luôn có thể chạy một chuỗi khác, để nó ngủ trên sự kiện và đăng lên vòng lặp tin nhắn khi có điều gì đó xảy ra. – Thanatos

+2

Ngoài ra còn có MsgWaitForMultipleObjects cho phép bạn đợi cho đến khi một tin nhắn cửa sổ đến hoặc một xử lý chờ đợi (nói và sự kiện hoặc mutex) được báo hiệu cho phép bạn làm điều đó trong một chủ đề. – tyranid

1

Bạn luôn có thể thực hiện theo cách UNIX và tạo tệp "pid", đặt id quá trình của cá thể hiện đang chạy vào tệp đó. Sau đó, xóa ứng dụng khi nó thoát.

Khi một trường hợp mới khởi động nó nên xác minh rằng quá trình trong file PID là thực sự sống cũng như (trong trường hợp ứng dụng thoát ra một cách bất thường và các tập tin không được xóa)

+0

+1. Bạn thậm chí có thể sử dụng khóa tệp trên tệp này thay vì một Mutex riêng biệt. –

+0

Tương tự như vậy, bạn có thể sử dụng bộ nhớ dùng chung và ghi PID ở đó không? – Thanatos

+0

Không cần phải đi qua hệ thống tập tin, thực sự ... một phần của bộ nhớ chia sẻ ('CreateFileMapping') sẽ làm tốt. Cảm ơn vì đã đưa tôi vào ca khúc này. Tôi sẽ chấp nhận điều này trừ khi có ai đó đưa ra một giải pháp tốt hơn. – Thomas

10

này sẽ giúp bạn bắt đầu theo yêu cầu ban đầu để có được một quá trình sở hữu một mutex.

Đó là trong C#, nhưng các cuộc gọi Win32 giống nhau.

class HandleInfo 
{ 
    [DllImport("ntdll.dll", CharSet = CharSet.Auto)] 
    public static extern uint NtQuerySystemInformation(int SystemInformationClass, IntPtr SystemInformation, int SystemInformationLength, out int ReturnLength); 

    [DllImport("kernel32.dll", SetLastError = true)] 
    internal static extern IntPtr VirtualAlloc(IntPtr address, uint numBytes, uint commitOrReserve, uint pageProtectionMode); 

    [DllImport("kernel32.dll", SetLastError=true)] 
    internal static extern bool VirtualFree(IntPtr address, uint numBytes, uint pageFreeMode); 

    [StructLayout(LayoutKind.Sequential)] 
    public struct SYSTEM_HANDLE_INFORMATION 
    { 
     public int ProcessId; 
     public byte ObjectTypeNumber; 
     public byte Flags; // 1 = PROTECT_FROM_CLOSE, 2 = INHERIT 
     public short Handle; 
     public int Object; 
     public int GrantedAccess; 
    } 

    static uint MEM_COMMIT = 0x1000; 
    static uint PAGE_READWRITE = 0x04; 
    static uint MEM_DECOMMIT = 0x4000; 
    static int SystemHandleInformation = 16; 
    static uint STATUS_INFO_LENGTH_MISMATCH = 0xC0000004; 

    public HandleInfo() 
    { 
     IntPtr memptr = VirtualAlloc(IntPtr.Zero, 100, MEM_COMMIT, PAGE_READWRITE); 

     int returnLength = 0; 
     bool success = false; 

     uint result = NtQuerySystemInformation(SystemHandleInformation, memptr, 100, out returnLength); 
     if (result == STATUS_INFO_LENGTH_MISMATCH) 
     { 
      success = VirtualFree(memptr, 0, MEM_DECOMMIT); 
      memptr = VirtualAlloc(IntPtr.Zero, (uint)(returnLength + 256), MEM_COMMIT, PAGE_READWRITE); 
      result = NtQuerySystemInformation(SystemHandleInformation, memptr, returnLength, out returnLength); 
     } 

     int handleCount = Marshal.ReadInt32(memptr); 
     SYSTEM_HANDLE_INFORMATION[] returnHandles = new SYSTEM_HANDLE_INFORMATION[handleCount]; 

     using (StreamWriter sw = new StreamWriter(@"C:\NtQueryDbg.txt")) 
     { 
      sw.WriteLine("@ Offset\tProcess Id\tHandle Id\tHandleType"); 
      for (int i = 0; i < handleCount; i++) 
      { 
       SYSTEM_HANDLE_INFORMATION thisHandle = (SYSTEM_HANDLE_INFORMATION)Marshal.PtrToStructure(
        new IntPtr(memptr.ToInt32() + 4 + i * Marshal.SizeOf(typeof(SYSTEM_HANDLE_INFORMATION))), 
        typeof(SYSTEM_HANDLE_INFORMATION)); 
       sw.WriteLine("{0}\t{1}\t{2}\t{3}", i.ToString(), thisHandle.ProcessId.ToString(), thisHandle.Handle.ToString(), thisHandle.ObjectTypeNumber.ToString()); 
      } 
     } 

     success = VirtualFree(memptr, 0, MEM_DECOMMIT); 
    } 
} 
+0

"NtQuerySystemInformation có thể được thay đổi hoặc không có sẵn trong các phiên bản tương lai của Windows. Các ứng dụng nên sử dụng các chức năng thay thế được liệt kê trong chủ đề này." Và SystemHandleInformation hoàn toàn không có giấy tờ. Yikes! – Thomas

+8

Thực ra, tôi chỉ đang cố gắng giúp bạn.Tôi đã phải đào một số mã C++ bí truyền của tôi từ cách trở lại, nhưng thay vì trudging trở lại vào VC6, tôi quyết định chuyển nó cho C# cho bạn và những người khác (để làm cho nó một chút hiện tại). Tôi đoán tôi không biết bạn không thể sử dụng công cụ không có giấy tờ, bạn chỉ cần hỏi nếu nó có thể ... – GalacticJello

+0

Từ mã này, sau đó bạn sẽ đi và gọi 'NtQueryMutant' để có được trạng thái chủ sở hữu và PID/ThreadID của chủ nhân. –

2

Tạo một chia sẻ vùng nhớ với tên cố định:

http://msdn.microsoft.com/en-us/library/aa366551%28VS.85%29.aspx

Sau đó, bạn có thể đặt bất kỳ cấu trúc mà bạn thích bên trong, bao gồm cả quá trình id, HWND, vv

Có một tùy chọn di động: tạo một ổ cắm trên cổng (có số cố định) và đợi (chấp nhận) trên đó. Ví dụ thứ hai của ứng dụng sẽ bị lỗi kể từ khi cổng đã được thực hiện. Sau đó, thể hiện thứ hai có thể kết nối với socket của thể hiện chính và gửi bất kỳ thông tin nào mong muốn.

Tôi hy vọng điều này sẽ giúp ...

+0

Nói chung, ổ cắm là xấu cho việc này. Họ yêu cầu một số số cổng phải được mã hóa cứng; nếu số này đã bị chiếm đóng thì sao? Tạo kết nối nghe cũng làm cho một ứng dụng rất đáng ngờ nếu các chức năng của nó không yêu cầu điều này. Tôi muốn xem xét, nói rằng, notepad chấp nhận các kết nối đến một phần mềm độc hại 100%. Miễn là phần mềm người dùng cuối được xem xét, giao tiếp ổ cắm chỉ OK cho các ứng dụng cụ thể như máy chủ và giao diện FileZilla. Tất nhiên, phần mềm doanh nghiệp có thể yêu cầu bất kỳ tùy chọn nào nó muốn. Tôi khuyên bạn nên đặt tên cho ống này. – Fr0sT