Win32: Как получить процесс/поток, который владеет взаимным исключением?

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

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

Взаимоисключающая опция кажется мне самым надежным и изящным.

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

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

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

7
задан Thomas 22 December 2009 в 16:08
поделиться

4 ответа

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

  1. Регистрация объекта в таблице объектов COM Running Object Table. Клиенты, которые не могут получить право собственности на Мьютекс, могут искать владельца через ROT и перезванивать ему. Файловый моникер должен быть пригоден для регистрации здесь.
  2. Создайте фрагмент разделяемой памяти, содержащий данные о местоположении для процесса владельца. Оттуда запишите в буфер дескриптор процесса и дескриптор потока, который может принимать сообщения windows, а затем используйте PostThreadMessage() для отправки уведомления. Любой другой конкурирующий процесс может открыть разделяемую память только для чтения, чтобы определить, куда послать сообщение windows.
  3. Listen in the owner process on a Socket или Named Pipe. Вероятно, это перебор и не подходит для ваших нужд.
  4. Используйте разделяемый файл с блокировкой. Мне это не нравится, потому что владельцу придется опрашивать, и он не будет грациозно обрабатывать N потенциальных других процессов, которые могут пытаться связаться с владельцем в то же время.

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

  1. IRunningObjectTable @ MSDN , File Monikers @ MSDN
  2. Creating Named Shared Memory @ MSDN
4
ответ дан 6 December 2019 в 10:00
поделиться

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

Это на 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);
    }
}
10
ответ дан 6 December 2019 в 10:00
поделиться

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

.
2
ответ дан 6 December 2019 в 10:00
поделиться

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

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

.
1
ответ дан 6 December 2019 в 10:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: