Pergunta

Estou trabalhando em uma aplicação da qual apenas uma instância deve existir a qualquer momento. Existem várias possibilidades para conseguir isso:

  • Verifique os processos de execução de uma correspondência com o nome do nosso exe (não confiável)
  • Encontre a janela principal (não confiável, e eu nem sempre tenho uma janela principal)
  • Crie um mutex com um nome exclusivo (Guid)

A opção Mutex me parece a mais confiável e elegante.

No entanto, antes que minha segunda instância termine, quero postar uma mensagem na instância já em execução. Para isso, preciso de uma alça para o encadeamento (ou o processo) que possui o mutex.

No entanto, parece não haver função da API para obter o criador/proprietário de um determinado mutex. Estou apenas negligenciando isso? Existe outra maneira de chegar a este thread/processo? Existe outra maneira de fazer isso?

Atualizar: Esse cara Basta transmitir uma mensagem para tudo processos em execução. Eu acho que é possível, mas eu realmente não gosto ...

Foi útil?

Solução

Eu não acho que exista uma maneira trivial de resolver o proprietário real de um mutex, mas o processo que ele possui pode criar outros itens secundários cujas vidas estáes estão ligadas a ele. Existem muitos mecanismos adequados para ligar de volta através do processo sem ter uma janela principal.

  1. Registre um objeto na tabela de objeto com execução. Os clientes que não conseguem se apropriar do Mutex podem procurar o proprietário através da podridão e ligar de volta ao proprietário. Um apelido de arquivo deve ser adequado para registro aqui.
  2. Crie um pedaço de memória compartilhada contendo detalhes do local para o processo do proprietário. A partir daí, escreva no buffer o identificador de processo e o identificador de thread de um encadeamento que pode receber mensagens do Windows e, em seguida, use PostThReadMessage () para enviar uma notificação. Qualquer outro processo concorrente pode abrir a memória compartilhada apenas para leitura para determinar para onde enviar uma mensagem do Windows.
  3. Ouça no processo do proprietário em um soquete ou tubo nomeado. Provavelmente exagero e não é uma boa combinação para suas necessidades.
  4. Use um arquivo compartilhado com bloqueio. Não gosto disso, porque o proprietário precisará pesquisar e não lidará com noriosamente outros processos em potencial que podem estar tentando entrar em contato com o proprietário ao mesmo tempo.

Aqui estão os links de referência para as duas primeiras opções.

  1. IrunningObjecttable @ msdn , Monikers de arquivo @ msdn
  2. Criando memória compartilhada nomeada @ msdn

Outras dicas

Isso deve iniciar a solicitação original para obter um processo que possui um mutex.

Está em C#, mas as chamadas do Win32 são as mesmas.

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);
    }
}

Eu nunca entendi o racional por trás de usar um mutex que não tem capacidade de sinalização. Em vez disso, eu criaria um evento (usando createEvent), que possui as mesmas propriedades que a criação de um mutex (ou seja, com um nome que ele pode retornar que o objeto já existia), mas você pode definir o sinalizador do evento no novo processo, desde que o original O processo está aguardando o sinalizador do evento, pode ser notificado quando precisa se acordar.

Crie um Área de memória compartilhada com o nome fixo:

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

Em seguida, você pode colocar qualquer estrutura que desejar, incluindo ID do processo, hwnd etc.

Há uma opção portátil: Crie um soquete em uma porta (com um número fixo) e aguarde (aceite) nela. A segunda instância do aplicativo falhará, pois a porta já foi tomada. Em seguida, a segunda instância pode se conectar ao soquete da instância primária e enviar qualquer informação desejada.

Eu espero que isso ajude...

Você sempre pode fazê -lo da maneira Unix e criar um arquivo "PID", colocando o ID do processo da instância atualmente em execução nesse arquivo. Em seguida, peça ao aplicativo que exclua o arquivo quando sair.

Quando uma nova instância é iniciada, deve verificar se o processo no arquivo PID também está realmente vivo (caso o aplicativo sai anormalmente e o arquivo não seja excluído)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top