Win32: Как получить процесс / поток, который владеет мьютексом? - PullRequest
7 голосов
/ 22 декабря 2009

Я работаю с приложением, для которого в любой момент времени должен существовать только один экземпляр. Есть несколько возможностей сделать это:

  • Проверка запущенных процессов на соответствие имени нашего EXE-файла (ненадежно)
  • Найти главное окно (ненадежно, и у меня не всегда есть главное окно)
  • Создать мьютекс с уникальным именем (GUID)

Вариант мьютекса кажется мне самым надежным и элегантным.

Однако, прежде чем мой второй экземпляр завершится, я хочу отправить сообщение уже запущенному экземпляру. Для этого мне нужен дескриптор потока (или процесса), которому принадлежит мьютекс.

Однако, похоже, нет API-функции для получения создателя / владельца данного мьютекса. Я просто пропускаю это? Есть ли другой способ попасть в этот поток / процесс? Есть ли другой способ сделать это?

Обновление : Этот парень просто передает сообщение всем запущенным процессам. Я думаю, это возможно, но мне это не очень нравится ...

Ответы [ 5 ]

10 голосов
/ 22 декабря 2009

Это должно привести вас к исходному запросу, чтобы получить процесс, который владеет мьютексом.

Это в 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);
    }
}
4 голосов
/ 22 декабря 2009

Я не думаю, что существует тривиальный способ разрешения фактического владельца Mutex, но процесс, который владеет им, может создавать другие второстепенные элементы, время жизни которых связано с ним. Существует множество механизмов, которые подходят для обратного вызова между процессами без основного окна.

  1. Зарегистрировать объект в таблице запущенных объектов COM. Клиенты, которые не могут стать владельцами Mutex, могут найти владельца через ROT и перезвонить владельцу. File Moniker должен подходить для регистрации здесь.
  2. Создать кусок общей памяти, содержащий сведения о местоположении для процесса владельца. Оттуда запишите в буфер дескриптор процесса и дескриптор потока, который может получать сообщения Windows, а затем используйте PostThreadMessage () для отправки уведомления. Любой другой конкурирующий процесс может открыть общую память только для чтения, чтобы определить, куда отправить сообщение Windows.
  3. Прослушивание процесса владельца на сокете или именованном канале. Вероятно, излишнее и не очень подходит для ваших нужд.
  4. Использование общего файла с блокировкой. Мне это не нравится, потому что владельцу нужно будет опросить, и он не будет корректно обрабатывать N потенциальных других процессов, которые могут одновременно пытаться связаться с владельцем.

Вот ссылки на первые две опции.

  1. IRunningObjectTable @ MSDN , File Monikers @ MSDN
  2. Создание именованной общей памяти @ MSDN
2 голосов
/ 18 ноября 2010

Создать область общей памяти с фиксированным именем:

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

Затем вы можете поместить любую понравившуюся вам структуру, включая идентификатор процесса, HWND и т. Д.

Существует портативная опция : создать сокет для порта (с фиксированным номером) и подождать (принять) его. Второй экземпляр приложения потерпит неудачу, так как порт уже занят. Затем второй экземпляр может подключиться к сокету основного экземпляра и отправить любую необходимую информацию.

Надеюсь, это поможет ...

2 голосов
/ 22 декабря 2009

Я никогда не понимал, что стоит за использованием Mutex, который не имеет возможности сигнализации. Вместо этого я бы создал событие (используя CreateEvent), которое имеет те же свойства, что и создание мьютекса (т. Е. С именем, которое он может вернуть, что объект уже существовал), но вы можете установить флаг события в новом процессе, если оригинал процесс ожидает на флаге события, он может быть уведомлен, когда ему нужно разбудить себя.

1 голос
/ 22 декабря 2009

Вы всегда можете сделать это в стиле UNIX и создать файл «pid», поместив в этот файл идентификатор процесса запущенного экземпляра. Затем приложение удалит файл при его выходе.

Когда запускается новый экземпляр, он должен убедиться, что процесс в PID-файле также действительно активен (в случае ненормального завершения работы приложения и удаления файла)

...