代码之家  ›  专栏  ›  技术社区  ›  Thomas

win32:如何获取拥有互斥体的进程/线程?

  •  7
  • Thomas  · 技术社区  · 15 年前

    我正在处理一个应用程序,在任何给定的时间内只能存在一个实例。实现这一点有几种可能:

    • 检查正在运行的进程是否与我们的exe名称匹配(不可靠)
    • 找到主窗口(不可靠,我不总是有主窗口)
    • 创建具有唯一名称(guid)的互斥体

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

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

    但是,似乎没有API函数来获取给定互斥体的创建者/所有者。我只是忽略了它吗?有没有其他方法到达这个线程/进程?还有别的办法吗?

    更新 : This guy 只需向 全部的 正在运行进程。我想这是可能的,但我不太喜欢…

    5 回复  |  直到 12 年前
        1
  •  4
  •   takrl cck    12 年前

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

    1. 在运行对象表的COM中注册一个对象。无法取得互斥体所有权的客户机可以通过rot查找所有者,并回电给所有者。文件标记应该适合在此处注册。
    2. 创建一块共享内存,其中包含所有者进程的位置详细信息。从那里,将可以接收Windows消息的线程的进程句柄和线程句柄写入缓冲区,然后使用PostThreadMessage()发送通知。任何其他竞争进程都可以以只读方式打开共享内存,以确定向何处发送Windows消息。
    3. 在所有者进程中侦听套接字或命名管道。可能是杀伤力太大,不符合你的需要。
    4. 使用带锁定的共享文件。我不喜欢这样做,因为所有者需要进行投票,而且它不会优雅地处理可能同时尝试联系所有者的其他n个潜在进程。

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

    1. IRunningObjectTable @ MSDN , File Monikers @ MSDN
    2. Creating Named Shared Memory @ MSDN
        2
  •  10
  •   GalacticJello    15 年前

    这应该可以让您根据原始请求开始获取拥有互斥体的进程。

    它在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);
        }
    }
    
        3
  •  2
  •   tyranid    15 年前

    我从来没有真正理解使用一个没有信号能力的互斥体背后的合理性。我将创建一个事件(使用CreateEvent),它与创建互斥体具有相同的属性(即,它可以返回对象已经存在的名称),但您可以在新进程中设置事件标志,只要原始进程正在等待事件标志,它可以在需要唤醒自身时得到通知。

        4
  •  2
  •   Daniel Nikolić    14 年前

    创建一个 共享内存区 固定名称:

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

    然后你可以把你喜欢的任何结构放在里面,包括进程ID,hwnd等等。

    有一个便携的选择 :在端口上创建一个套接字(使用固定的数字),然后在其上等待(接受)。由于端口已被占用,应用程序的第二个实例将失败。然后第二个实例可以连接到主实例的套接字并发送所需的任何信息。

    我希望这有助于…

        5
  •  1
  •   Eric Petroelje    15 年前

    您总是可以用Unix的方式来做,并创建一个“pid”文件,将当前正在运行的实例的进程ID放入该文件中。然后让应用程序在退出时删除该文件。

    当一个新的实例启动时,它应该验证pid文件中的进程是否也处于活动状态(以防应用程序异常退出而文件没有被删除)。