質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.10%

UnityでSystem.Diagnostics.Processを使ってhWndを取得したい

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 205

Kayun1102

score 9

実現したいこと

起動しているプロセスの内、hWndを持つものだけをリストアップしたい。
hWndと合わせてモジュール名・パス、PIDを取得したい。
終了したものは随時リストから外したい。(決まった動作を行う直前だけ更新でも構わないが、実行時間は短ければ短いほど良い)
Unityを用いて以上のことを実装したい。

試したこと+問題点

モジュール名が簡単に取得できるという観点からSystem.Diagnostics.Processを使うことにした。
hWnd・モジュール名・パス・PIDを格納できる構造体の配列を作り、GetProcesses()で取得したものをhWndがあるものだけ格納するプログラムを書いた。

public class Hoge : MonoBehaviour{
    public ActiveApp[] activeApps = new ActiveApp[1000];
    [Serializable]
    public struct ActiveApp {
        private IntPtr hWnd;
        public IntPtr HWnd {
            set {
                _hWnd = value.ToInt32();    //Unityのインスペクターに表示させたかった。
                hWnd = value;
            }
            get { return hWnd; }
        }
        public Process process;
        public int pID;
        public string exeName;
        public string exePath;
        public int _hWnd;
    }

    public void ModuleName() {
        try {
            Process[] process = Process.GetProcesses();
            if (process.Length > activeApps.Length) throw new IndexOutOfRangeException(message: "Apps too much!");
            int x = 0;
            for (int i = 0; i < process.Length; i++) {
                if (!process[i].HasExited) {
                    if (!(process[i].MainWindowHandle == IntPtr.Zero)) {
                        activeApps[x].exePath = process[i].MainModule.FileName;
                        activeApps[x].exeName = process[i].MainModule.ModuleName;
                        activeApps[x].HWnd = process[i].MainWindowHandle;
                        x++;
                    }
                }
            }
        }
        catch (Exception e) {
            UnityEngine.Debug.LogError(e.Message);
        }
    }
}


usingは省いてあります。なぜかこのやり方だとhWndの部分が常に0が返されてしまう。
モジュール名等ほかの情報はちゃんと得られました。
Unity Version: 2019.3.13f1

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.IO;

namespace GetWindowProcesses
{
    class Program
    {
        public delegate bool EnumWindowsDelegate(IntPtr hWnd, IntPtr lparam);

        [DllImport("user32.dll")]
        private static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private extern static bool EnumWindows(EnumWindowsDelegate lpEnumFunc, IntPtr lparam);

        private static bool EnumWindowCallBack(IntPtr hWnd, IntPtr lparam)
        {
            int pID;
            GetWindowThreadProcessId(hWnd, out pID);
            _DictHWnd[pID] = hWnd;
            return true;
        }

        private static Dictionary<int, IntPtr> _DictHWnd = new Dictionary<int, IntPtr>();

        [Serializable]
        public struct ActiveApp
        {
            private IntPtr hWnd;
            public IntPtr HWnd
            {
                set
                {
                    _hWnd = value.ToInt32();
                    hWnd = value;
                }
                get { return hWnd; }
            }

            public int pID;
            public string exeName;
            public string exePath;
            public int _hWnd;
        }

        static void Main(string[] args)
        {
            var watch = Stopwatch.StartNew();
            EnumWindows(new EnumWindowsDelegate(EnumWindowCallBack), IntPtr.Zero);
            var listApp = new List<ActiveApp>();
            var procs = Process.GetProcesses();
            foreach (var proc in procs)
            {
                if (_DictHWnd.ContainsKey(proc.Id))
                {
                    var app = new ActiveApp(){ pID = proc.Id };

                    try
                    {
                        app.exePath = proc.MainModule.FileName;
                        app.exeName = proc.MainModule.ModuleName;
                        app.HWnd = _DictHWnd[proc.Id];
                        listApp.Add(app);
                    }
                    catch
                    {
                    }
                }                
            }           
            watch.Stop();

            Console.WriteLine($"process time {watch.ElapsedMilliseconds} msec");

            foreach (var app in listApp)
            {
                Console.WriteLine($"pID:{app.pID} hWnd:{app.HWnd} exePath:{app.exePath.Substring(0, 4)}*****");
            }

            Console.ReadKey();
        }
    }
}

実行結果

process time 209 msec
pID:6752 hWnd:65664 exePath:C:\W*****
pID:6608 hWnd:131100 exePath:C:\W*****
…(中略)
pID:8996 hWnd:2426832 exePath:C:\P*****
  1. EnumWindowsで取得したhWndを、プロセスIDをキーにしてDictionaryに格納
  2. Process.GetProcessesでプロセス一覧を取得
  3. 取得したプロセス一覧のプロセスIDでDictionaryからhWndを検索
  4. hWndとEXE名が取得出来たらListに格納(EXE名取得で例外が発生した場合は何もしない)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/01 08:26

    回答ありがとうございます。自分のプログラムを10回走らせて平均を取ったところ5632msec、上記のコードで3015msecでした。System.Diagnostics.Processを使ってとなるとたぶんこの辺が頭打ちなのかもしれません。タイトルから大きくずれてしまうことになりそうなので、解決とします。

    キャンセル

  • 2020/07/01 10:04

    アンチウイルスソフト等によっては他プロセス情報の取得を阻害・遅延するものもあったり、セキュリティ要件や環境依存の問題も色々ありそうです

    キャンセル

+1

MainWindowHandle は取得できるとは限りません。

EnumWindows でトップレベルのウインドウを列挙してプロセス ID の等しいものを取得しては?

参考URL:「表示されているウィンドウを列挙する」
https://smdn.jp/programming/tips/enumwindows/

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/06/30 23:38 編集

    GetProcessesしてから、プロセスIDでhWndと紐づけした方がいいかもしれませんね

    キャンセル

  • 2020/07/01 01:19

    たぶん、例外処理に時間がかかっていると思われます。
    管理者権限で実行するとどうでしょうか?

    キャンセル

  • 2020/07/01 08:28

    管理者権限をつけて実行してみましたが、さほど変わりませんでした。限界を感じたので、System.Diagnostics.Process以外の方法を検討します。ありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.10%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる