10

私は、常に1つのインスタンスのみが存在する必要があるアプリケーションを使用しています。これを達成するためのいくつかの可能性があります:

  • EXEの名前に一致するプロセスがないか実行中のプロセスを確認してください(信頼性が低い)
  • メインウィンドウを見つける(信頼性が低く、メインウィンドウが常にあるとは限りません)
  • 一意の名前(GUID)でミューテックスを作成する

ミューテックスオプションは、私にとって最も信頼性が高くエレガントなようです。

ただし、2番目のインスタンスが終了する前に、すでに実行中のインスタンスにメッセージを投稿したいと思います。このために、ミューテックスを所有するスレッド(またはプロセス)へのハンドルが必要です。

ただし、特定のミューテックスの作成者/所有者を取得するためのAPI関数はないようです。私はそれを見落としているだけですか?このスレッド/プロセスに到達する別の方法はありますか?これについて別の方法はありますか?

更新この男は、実行中のすべてのプロセスにメッセージをブロードキャストするだけです。それは可能だと思いますが、私はそれが本当に好きではありません...

4

6 に答える 6

11

これにより、ミューテックスを所有するプロセスを取得するための元のリクエストを開始できます。

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);
    }
}
于 2009-12-22T17:59:55.487 に答える
7

ミューテックスの実際の所有者を解決する簡単な方法はないと思いますが、ミューテックスを所有するプロセスは、ライフタイムがそれに関連付けられている他のセカンダリアイテムを作成できます。メインウィンドウがなくてもプロセス間でコールバックするのに適したメカニズムはたくさんあります。

  1. COM実行オブジェクトテーブルにオブジェクトを登録します。Mutexの所有権を取得できないクライアントは、ROTを介して所有者を検索し、所有者にコールバックできます。ここでの登録には、ファイルモニカーが適しているはずです。
  2. 所有者プロセスの場所の詳細を含む共有メモリのチャンクを作成します。そこから、Windowsメッセージを受信できるスレッドのプロセスハンドルとスレッドハンドルをバッファに書き込み、PostThreadMessage()を使用して通知を送信します。他の競合するプロセスは、共有メモリを読み取り専用で開いて、Windowsメッセージの送信先を決定する場合があります。
  3. ソケットまたは名前付きパイプの所有者プロセスでリッスンします。おそらくやり過ぎであり、あなたのニーズにぴったりではありません。
  4. ロック付きの共有ファイルを使用します。所有者がポーリングする必要があり、同時に所有者に連絡しようとしている可能性のあるN個の潜在的な他のプロセスを適切に処理しないため、私はこれが好きではありません。

最初の2つのオプションの参照リンクは次のとおりです。

  1. IRunningObjectTable @ MSDNファイルモニカ@ MSDN
  2. 名前付き共有メモリの作成@MSDN
于 2009-12-22T16:35:07.773 に答える
2

シグナリング機能を持たないMutexを使用する理由を本当に理解したことはありません。代わりに、ミューテックスの作成と同じプロパティを持つイベントを(CreateEventを使用して)作成します(つまり、オブジェクトがすでに存在していることを返すことができる名前を使用します)が、元のプロセスである限り、新しいプロセスでイベントフラグを設定できますプロセスはイベントフラグを待機しており、ウェイクアップする必要があるときに通知を受けることができます。

于 2009-12-22T16:18:33.593 に答える
2

いつでもUNIXの方法で実行し、「pid」ファイルを作成して、現在実行中のインスタンスのプロセスIDをそのファイルに入れることができます。次に、アプリが終了したときにファイルを削除します。

新しいインスタンスが起動すると、PIDファイル内のプロセスも実際に動作していることを確認する必要があります(アプリが異常終了し、ファイルが削除されない場合)

于 2009-12-22T16:22:44.963 に答える
2

固定名で共有メモリ領域を作成します。

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

次に、プロセスID、HWNDなど、好きな構造を内部に配置できます。

移植可能なオプションがあります:ポート(固定番号)にソケットを作成し、それで待機(受け入れ)します。ポートがすでに使用されているため、アプリの2番目のインスタンスは失敗します。次に、2番目のインスタンスは1次インスタンスのソケットに接続し、必要な情報を送信できます。

これがお役に立てば幸いです...

于 2010-11-18T15:13:03.143 に答える
0

私も同様の問題を抱えていました。アプリの単一インスタンスが実行されている場合に返される関数が必要です。次に、アプリを前面に表示する別の関数。ここで、最初に、すでに実行中のウィンドウのHWNDを推測​​する必要があります。

FindWindowは非常に時間がかかります。ウィンドウのタイトルは変更される可能性があり、別のウィンドウが同じクラスとタイトルを使用している可能性があります。

次に、ミューテックスを使用して追加のデータを保存できるのではないかと思いました。しかし、ユーザーデータをミューテックスオブジェクトまたはイベントオブジェクトのどこに保存できるかわかりません。ただし、ミューテックスは、ミューテックスがどのスレッドに属しているか、したがってどのプロセスに属しているかを認識しています。しかし、あなたが言ったように、APIは存在しないようです。

ここでは、多くの新しく複雑な外観の方法が提案されています。単にファイルを使用するという例外があります。そこで、別のメソッドである一時レジストリキーを追加したいと思います。

私はすでにhkeyライブラリを構築しているので、この方法は私にとって最も簡単です。しかし、win32レジストリAPIは、恐ろしい見た目の共有メモリ方式と比較して非常に単純です。

于 2021-08-02T06:07:23.087 に答える