10
votes

WIN32: Comment obtenir le processus / le fil qui possède un mutex?

Je travaille une application dont une seule instance doit exister à tout moment. Il existe plusieurs possibilités d'accomplir ceci:

  • Vérifiez les processus d'exécution pour une correspondance nom de notre EXE (non fiable)
  • Trouver la fenêtre principale (peu fiable, et je n'ai pas toujours une fenêtre principale)
  • Créez un mutex avec un nom unique (GUID)

    L'option mutex me semble le plus fiable et le plus élégant.

    Cependant, avant que ma deuxième instance se termine, je souhaite poster un message à l'instance déjà exécutée. Pour cela, j'ai besoin d'une poignée sur le fil (ou le processus) qui possède le mutex.

    Cependant, il ne semble y avoir aucune fonction API pour obtenir le créateur / propriétaire d'un mutex donné. Suis-je juste surplombant? Y a-t-il une autre façon d'arriver à ce fil / processus? Y a-t-il une autre façon d'aller à ce sujet?

    update : Ce gars Il suffit de diffuser un message à tous les processus d'exécution . Je suppose que c'est possible, mais je n'aime pas vraiment ça ...


6 commentaires

Dupliquer? Stackoverflow.com/Questtions/19147/...


Pas vraiment. Aucune des réponses ne me dit comment obtenir le processus de processus.


Voici un duplicata, même s'il n'a jamais été répondu: Stactoverflow.com/questions/541477/...


Eh bien, on a répondu, mais probablement pas dans la façon dont le questionneur espérait que ce serait :)


Je n'appellerais pas cela une duplication exacte non plus; Bien que cela réponde à ma question principale, il ne fournit pas une solution alternative - quelque chose que j'ai explicitement demandé.


Ne pas mettre cela comme une réponse (puisque ce n'est pas une réponse), mais cela peut être d'intérêt: msdn.microsoft.com/en-us/library/aa446629%28vs.85%29.aspx montre comment obtenir le propriétaire (au sens de l'utilisateur) de Un verrouillage de fichier et vous pouvez appliquer la même chose à un mutex. Ne vous donne toujours pas de pid cependant.


6 Réponses :


2
votes

Je n'ai jamais vraiment compris le rationnel derrière l'utilisation d'un mutex qui n'a aucune capacité de signalisation. Je créerais plutôt un événement (utilisant CreateEeEvent) qui a les mêmes propriétés que la création d'un mutex (c'est-à-dire avec un nom, il peut renvoyer que l'objet existait déjà), mais vous pouvez définir le drapeau de l'événement dans le nouveau processus, aussi longtemps que l'original. Le processus attend sur le drapeau de l'événement, il peut être notifié quand il doit se réveiller.


3 commentaires

Bien sûr, le processus d'origine ne bloque pas l'attente d'un événement; Il s'agit simplement de courir des cercles dans sa boucle de message. (Je pourrais vérifier le mutex à chaque fois par la boucle, mais cela ressent le mal.)


Vous pouvez toujours exécuter un autre fil, dormez-le sur l'événement et postez-le à la boucle de message lorsque quelque chose se passe.


Il existe également MSGWAITEAlformultiPleObjects, ce qui vous permet d'attendre un message Windows ou une poignée d'attente (disons et événement ou mutex) à signaler vous permettant de le faire dans un fil.



2
votes

Vous pouvez toujours faire la manière UNIX et créer un fichier "PID", mettre l'ID de processus de l'instance actuellement exécutée dans ce fichier. Demandez ensuite à l'application supprimer le fichier quand il quitte.

Lorsqu'une nouvelle instance démarre, il doit également vérifier que le processus dans le fichier PID est également vivant également (au cas où l'application quitte anormalement et que le fichier ne soit pas supprimé)


3 commentaires

+1. Vous pouvez même utiliser un verrou de fichier sur ce fichier au lieu d'un mutex séparé.


Semblable à cela, pourriez-vous utiliser une mémoire partagée et écrire le PID là-bas?


Pas besoin de passer par le système de fichiers, réellement ... un morceau de mémoire partagée ( CreateFilemapping ) fera très bien. Merci de me mettre sur cette piste. J'accepterai cela à moins que quelqu'un ne propose une meilleure solution.



6
votes

Je ne pense pas qu'il existe un moyen trivial de résoudre le propriétaire réel d'un mutex, mais le processus qui le possède peut créer d'autres éléments secondaires dont la durée de vie est liée. Il existe de nombreux mécanismes qui conviennent à la remise en arrière sans avoir une fenêtre principale.

  1. enregistrer un objet dans la table d'objet COM. Les clients incapables de prendre la propriété du mutex peuvent rechercher le propriétaire via la pourriture et rappeler au propriétaire. Un fichier moniker doit convenir à l'enregistrement ici.
  2. Créez un morceau de mémoire partagée contenant des détails de localisation pour le processus de propriétaire. À partir de là, écrivez dans la mémoire tampon la poignée de processus et la poignée de filetage d'un fil pouvant recevoir des messages Windows, puis utilisez le postTheADMessage () pour envoyer une notification. Tout autre processus concurrent peut ouvrir la mémoire partagée pour être en lecture seule pour déterminer où envoyer un message Windows.
  3. Écoutez le processus propriétaire sur une prise ou un tuyau nommé. Probablement surkill et pas un bon match pour vos besoins.
  4. Utilisez un fichier partagé avec verrouillage. Je n'aime pas cela parce que le propriétaire devra sonder le sondage, et il ne traitera pas de manière gracieuse n d'autres processus potentiels qui pourraient essayer de contacter le propriétaire en même temps.

    Voici des liens de référence pour les deux premières options.

    1. IrunningObjectTable @ msdn , Fichier Monikers @ MSDN
    2. Création de mémoire partagée nommée @ MSDN

0 commentaires

11
votes

Cela devrait vous aider à démarrer sur la demande initiale pour obtenir un processus qui possède un mutex.

C'est dans C #, mais les appels Win32 sont les mêmes. P>

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 commentaires

"NtquerySysteminformation peut être modifiée ou indisponible dans les futures versions de Windows. Les applications doivent utiliser les fonctions alternatives énumérées dans cette rubrique." Et la sociétéHandleInformation est entièrement non documentée. Yikes!


En fait, j'essayais juste de vous aider. J'ai dû creuser une partie de mon code ésotérique C ++ de Way, mais au lieu de faire tomber dans la VC6, j'ai décidé de le porter à C # pour vous et d'autres (pour en faire un peu plus de courant). Je suppose que je ne savais pas que vous ne pouviez pas utiliser de choses sans papiers, vous venez de demander si c'était possible ...


À partir de ce code, vous alliez alors appeler NTQuéryMutant pour obtenir le statut du propriétaire et le PID / Threadid du propriétaire.



2
votes

Créer une zone de mémoire partagée avec le nom fixe:

http://msdn.microsoft.com/ EN-US / Bibliothèque / AA366551% 28VS.85% 29.aspx

Ensuite, vous pouvez mettre n'importe quelle structure que vous aimez à l'intérieur, y compris ID de processus, HWND, etc.

Il y a une option portable : Créez une prise sur un port (avec un numéro fixe) et attendez (accepter) dessus. La deuxième instance de l'application échouera car le port est déjà pris. Ensuite, la deuxième instance peut se connecter à la prise de l'instance principale et envoyer toute information souhaitée.

J'espère que cela vous aidera ...


1 commentaires

En général, les sockets sont mauvais pour cela. Ils exigent que certains points de port soient codés durs; Et si ce nombre serait déjà occupé? Créer une connexion d'écoute rend également une application très méfiante si ses fonctions ne nécessitent pas cela. Je considérerais, disons, le bloc-notes qui accepte des connexions entrantes un logiciel 100% malveillant. Tant que le logiciel utilisateur final est pris en compte, la communication Socket est OK uniquement pour des applications spécifiques telles que FileZilla Server et Interface. Bien sûr, le logiciel Enterprise pourrait avoir besoin d'une option qu'il souhaite. Je recommanderais des pipes nommées pour cela.



0
votes

J'ai eu des problèmes similaires. Je veux une fonction qui retourne si une seule instance d'une application est en cours d'exécution. Ensuite, une autre fonction pour apporter l'application à l'avant. Dans lequel je dois d'abord déduire le HWND de la fenêtre déjà courante.

Recherchewindow suce gros fois. Les titres de fenêtre peuvent changer, une autre fenêtre pourrait utiliser la même classe et le même titre, etc.

Puis je pensais que des données supplémentaires pouvaient être stockées avec un mutex. Mais je ne vois pas où les données utilisateur peuvent être stockées dans un objet mutex ou un objet événement. Mais un mutex sait quel fil il appartient et donc quel processus appartient. Mais comme vous l'avez dit, l'API ne semble pas exister.

De nombreuses méthodes neuves et complexes ont été suggérées ici; avec l'exécution d'utiliser simplement un fichier. Donc, je souhaite ajouter une autre méthode, des clés de registre temporaires.

Cette méthode est la plus facile pour moi lorsque j'ai déjà construit une bibliothèque HKEY. Mais l'API Registry Win32 est assez simple par rapport à la méthode de mémoire partagée horrible.


0 commentaires