Win32:如何获取拥有互斥锁的进程/线程?

Tho*_*mas 7 winapi multithreading mutex process single-instance

我正在处理一个应用程序,在任何给定时间只能存在一个实例.有几种可能性来实现这一目标:

  • 检查一个匹配EXE名称的运行进程(不可靠)
  • 找到主窗口(不可靠,我并不总是有一个主窗口)
  • 创建具有唯一名称(GUID)的互斥锁

互斥选项在我看来是最可靠和优雅的.

但是,在我的第二个实例终止之前,我想向已经运行的实例发布一条消息.为此,我需要一个拥有互斥锁的线程(或进程)的句柄.

但是,似乎没有API函数来获取给定互斥锁的创建者/所有者.我只是俯视它吗?有没有其他方法来到这个线程/进程?还有另一种方法可以解决这个问题吗?

更新:这个人只是向所有正在运行的进程广播一条消息.我想这是可能的,但我真的不喜欢它......

Gal*_*llo 10

这应该让您开始了原始请求,以获得拥有互斥锁的进程.

它在C#中,但Win32调用是相同的.

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 实际上,我只是想帮助你.我不得不从后面挖掘出一些深奥的C++代码,但是我决定将它移植到C#中,而不是跋涉回到VC6中(为了让它更新一些).我想我不知道你不能使用无证件的东西,你只是问是否有可能...... (8认同)

mek*_*ian 6

我不认为有一种简单的方法可以解决互斥锁的实际所有者,但是拥有它的进程可以创建其生命周期与其相关的其他次要项。有很多机制适合在没有主窗口的情况下跨进程回调。

  1. 在 COM 运行对象表中注册一个对象。无法获得互斥锁所有权的客户端可以通过 ROT 查找所有者并回调所有者。File Moniker 应该适合在此处注册。
  2. 创建包含所有者进程位置详细信息的共享内存块。从那里,将可以接收windows消息的线程的进程句柄和线程句柄写入缓冲区,然后使用PostThreadMessage()发送通知。任何其他竞争进程都可以将共享内存打开为只读,以确定向何处发送 Windows 消息。
  3. 侦听套接字或命名管道上的所有者进程。可能矫枉过正,不能很好地满足您的需求。
  4. 使用带锁定的共享文件。我不喜欢这个,因为所有者需要轮询,并且它不会优雅地处理 N 个可能同时尝试联系所有者的潜在其他进程。

以下是前两个选项的参考链接。

  1. IRunningObjectTable @ MSDN文件名字对象@ MSDN
  2. 创建命名共享内存@ MSDN