Win32: как получить процесс / поток, которому принадлежит мьютекс?



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

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

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

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

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

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

188   5  

5 ответов:

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

  1. зарегистрируйте объект в таблице com Running Object. Клиенты, которые не могут стать владельцами мьютекса, могут искать владельца через ROT и перезванивать владельцу. Имя файла должно быть подходит для регистрации здесь.
  2. Создайте фрагмент общей памяти, содержащий сведения о местоположении для процесса владельца. Оттуда запишите в буфер дескриптор процесса и дескриптор потока потока, который может получать сообщения windows, а затем используйте PostThreadMessage() для отправки уведомления. Любой другой конкурирующий процесс может открыть общую память только для чтения, чтобы определить, куда отправить сообщение windows.
  3. прослушивание в процессе владельца сокета или именованного канала. Наверное, перебор и не очень подходит для ваших нужд.
  4. Используйте общий файл с блокировкой. Мне это не нравится, потому что владельцу нужно будет опрашивать, и он не будет изящно обрабатывать N потенциальных других процессов, которые могут одновременно пытаться связаться с владельцем.

Здесь приведены ссылки на первые два варианта.

  1. Irunningobjectable @ MSDN , Моникеры файлов @ MSDN
  2. создание именованной общей памяти @ MSDN

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

Это в 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);
    }
}

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

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

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

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

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

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

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

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

    Ничего не найдено.

Добавить ответ:
Отменить.