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

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

新規登録して質問してみよう
ただいま回答率
85.35%
Windows 10

Windows 10は、マイクロソフト社がリリースしたOSです。Modern UIを標準画面にした8.1から、10では再びデスクトップ主体に戻され、UIも変更されています。PCやスマホ、タブレットなど様々なデバイスに幅広く対応していることが特徴です。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

Q&A

解決済

2回答

2495閲覧

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

Kayun1102

総合スコア9

Windows 10

Windows 10は、マイクロソフト社がリリースしたOSです。Modern UIを標準画面にした8.1から、10では再びデスクトップ主体に戻され、UIも変更されています。PCやスマホ、タブレットなど様々なデバイスに幅広く対応していることが特徴です。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

0グッド

0クリップ

投稿2020/06/29 13:07

編集2020/06/29 13:12

実現したいこと

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

試したこと+問題点

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

C#

1public class Hoge : MonoBehaviour{ 2 public ActiveApp[] activeApps = new ActiveApp[1000]; 3 [Serializable] 4 public struct ActiveApp { 5 private IntPtr hWnd; 6 public IntPtr HWnd { 7 set { 8 _hWnd = value.ToInt32(); //Unityのインスペクターに表示させたかった。 9 hWnd = value; 10 } 11 get { return hWnd; } 12 } 13 public Process process; 14 public int pID; 15 public string exeName; 16 public string exePath; 17 public int _hWnd; 18 } 19 20 public void ModuleName() { 21 try { 22 Process[] process = Process.GetProcesses(); 23 if (process.Length > activeApps.Length) throw new IndexOutOfRangeException(message: "Apps too much!"); 24 int x = 0; 25 for (int i = 0; i < process.Length; i++) { 26 if (!process[i].HasExited) { 27 if (!(process[i].MainWindowHandle == IntPtr.Zero)) { 28 activeApps[x].exePath = process[i].MainModule.FileName; 29 activeApps[x].exeName = process[i].MainModule.ModuleName; 30 activeApps[x].HWnd = process[i].MainWindowHandle; 31 x++; 32 } 33 } 34 } 35 } 36 catch (Exception e) { 37 UnityEngine.Debug.LogError(e.Message); 38 } 39 } 40} 41

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

CS

1using System; 2using System.Collections.Generic; 3using System.Runtime.InteropServices; 4using System.Diagnostics; 5using System.IO; 6 7namespace GetWindowProcesses 8{ 9 class Program 10 { 11 public delegate bool EnumWindowsDelegate(IntPtr hWnd, IntPtr lparam); 12 13 [DllImport("user32.dll")] 14 private static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId); 15 16 [DllImport("user32.dll")] 17 [return: MarshalAs(UnmanagedType.Bool)] 18 private extern static bool EnumWindows(EnumWindowsDelegate lpEnumFunc, IntPtr lparam); 19 20 private static bool EnumWindowCallBack(IntPtr hWnd, IntPtr lparam) 21 { 22 int pID; 23 GetWindowThreadProcessId(hWnd, out pID); 24 _DictHWnd[pID] = hWnd; 25 return true; 26 } 27 28 private static Dictionary<int, IntPtr> _DictHWnd = new Dictionary<int, IntPtr>(); 29 30 [Serializable] 31 public struct ActiveApp 32 { 33 private IntPtr hWnd; 34 public IntPtr HWnd 35 { 36 set 37 { 38 _hWnd = value.ToInt32(); 39 hWnd = value; 40 } 41 get { return hWnd; } 42 } 43 44 public int pID; 45 public string exeName; 46 public string exePath; 47 public int _hWnd; 48 } 49 50 static void Main(string[] args) 51 { 52 var watch = Stopwatch.StartNew(); 53 EnumWindows(new EnumWindowsDelegate(EnumWindowCallBack), IntPtr.Zero); 54 var listApp = new List<ActiveApp>(); 55 var procs = Process.GetProcesses(); 56 foreach (var proc in procs) 57 { 58 if (_DictHWnd.ContainsKey(proc.Id)) 59 { 60 var app = new ActiveApp(){ pID = proc.Id }; 61 62 try 63 { 64 app.exePath = proc.MainModule.FileName; 65 app.exeName = proc.MainModule.ModuleName; 66 app.HWnd = _DictHWnd[proc.Id]; 67 listApp.Add(app); 68 } 69 catch 70 { 71 } 72 } 73 } 74 watch.Stop(); 75 76 Console.WriteLine($"process time {watch.ElapsedMilliseconds} msec"); 77 78 foreach (var app in listApp) 79 { 80 Console.WriteLine($"pID:{app.pID} hWnd:{app.HWnd} exePath:{app.exePath.Substring(0, 4)}*****"); 81 } 82 83 Console.ReadKey(); 84 } 85 } 86}

実行結果

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/06/30 15:25

編集2020/06/30 15:30
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Kayun1102

2020/06/30 23:26

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

退会済みユーザー

2020/07/01 01:04

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

0

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

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

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

投稿2020/06/29 23:05

KOZ6.0

総合スコア2707

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Kayun1102

2020/06/29 23:29

回答ありがとうございます。 実はそのやり方で切り抜けようとしたのですが、 PIDが一致するものをforeachなどのループで調べることになると思うんですが、1回更新するのに5秒ほどかかってしまい、どうにかならないものかと思案しておりました。winAPIでPIDから直接モジュール名が取得できればそれで済む話なのですが、難解でよくわからなかったので、楽しようとしたらできなかった感じですね。
KOZ6.0

2020/06/30 00:44

目的(起動しているプロセスの内、hWndを持つものだけをリストアップしたい)を考えると (1) ウインドウを列挙する(EnumWindows) (2) ウインドウハンドルから プロセスIDを取得(GetWindowThreadProcessId) (3) プロセスID から Process を取得する(Process.GetProcessById) の手順が最速かと思います。
Kayun1102

2020/06/30 12:10

返信ありがとうございます。これは行けるかもしれないと思い以下のようなコードに書き換えました。 ``` public class EventManager : MonoBehaviour { [DllImport("user32.dll")] private static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId); public delegate bool EnumWindowsDelegate(IntPtr hWnd, IntPtr lparam); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public extern static bool EnumWindows(EnumWindowsDelegate lpEnumFunc, IntPtr lparam); [Serializable] public struct ActiveApp { private IntPtr hWnd; public IntPtr HWnd { set { _hWnd = value.ToInt32(); hWnd = value; } get { return hWnd; } } public Process process; public int pID; public string exeName; public string exePath; public int _hWnd; } public ActiveApp[] activeApps = new ActiveApp[1000]; void Start() { EnumWindows(new EnumWindowsDelegate(EnumWindowCallBack), IntPtr.Zero); } private bool EnumWindowCallBack(IntPtr hWnd, IntPtr lparam) { int pID; GetWindowThreadProcessId(hWnd, out pID); try { Process process = Process.GetProcessById(pID); activeApps[appCount].exeName = process.MainModule.ModuleName; activeApps[appCount].exePath = process.MainModule.FileName; } catch (Exception e) { UnityEngine.Debug.LogError(e); //なぜか見つからない場合がある return true; //見なかったことにして次へ進む } activeApps[appCount].pID = pID; activeApps[appCount].HWnd = hWnd; appCount++; return true; //trueで次のhWnd } } ``` これがですね、1回取得するのに20秒くらいかかっちゃいました。おそらく、Process.GetProcessById()は、そこそこ時間がかかるため、Process.GetProcesses()でまとめて取得するより時間がかかっちゃうんだと思います。後者は自分の非効率に見えるfor文での照合でもおよそ5秒で済んでいましたから……
退会済みユーザー

退会済みユーザー

2020/06/30 15:17 編集

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

2020/06/30 16:19

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

2020/06/30 23:28

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問