DOBON.NETプログラミング道掲示板
(現在 過去ログ4 を表示中)

[ 最新記事及び返信フォームをトピックトップへ ]

■34379 / inTopicNo.1)  プロセスからウインドウを取得
  
□投稿者/ /400 一般人(1回)-(2019/11/11(Mon) 23:26:51)
  • アイコン環境/言語:[vb.net2005] 
    分類:[.NET] 

    vb.net2005 winformです
    ウインドウの一覧を先に取得してそれをプロセスで束ねてく方法は分かるのですが、
    それだと時間がかかるのでプロセス一覧を先に取得して画面のあるものだけ
    ウインドウを取得したいのです
    ただそれだと一つのプロセスから複数のウインドウを表示しているような場合取得
    できません
    どのようにすればよいのでしょう

引用返信 削除キー/
■34381 / inTopicNo.2)  Re[1]: プロセスからウインドウを取得
□投稿者/ 魔界の仮面弁士 大御所(1254回)-(2019/11/12(Tue) 10:28:08)
  • アイコンNo34379に返信(/400さんの記事)
    > ウインドウの一覧を先に取得して
    ここでいうウィンドウとは、何を指していますか?

    トップレベルウィンドウ(EnumWindows)だけではなく、
    TextBox や Button、あるいは ComboBox のエディタ部とリスト部などといった、
    hWnd を持つオブジェクトすべてを指しているのでしょうか? (EnumChildWindows)


    > それをプロセスで束ねてく方法は分かるのですが、

    別案を求めるのであれば、そもそも現状、どうやって取得しているのかを
    示してもらった方が答えやすいです。たとえばこういった処理を行っているのでしょうか。
    https://www.atmarkit.co.jp/fdotnet/dotnettips/233enumwin/enumwin.html

    あるいは EnumWindows のコールバック結果を GetWindowThreadProcessId で判定しているとか?
    https://smdn.jp/programming/tips/enumwindows/



    > それだと時間がかかるので
    まず、列挙の目的は何でしょうか?
    そして現状の方法だとどの程度かかっていて、それをどの程度にまで短縮させたいのでしょうか?
    また、現状の処理で特に時間がかかっているのは、どの部分でしょうか?


    > ただそれだと一つのプロセスから複数のウインドウを表示しているような場合取得
    > できません
    どういう処理を書いているか分かりませんが、単一ウィンドウを取得する方法の場合、
    十分に短い時間で処理できているのでしょうか?

    ちなみに Process.MainWinowHandle プロパティの実装は、
    EnumWindows + GetWindowThreadProcessId で判定するという手法で実装されています。
    加えて、それがメインウィンドウであるかの判定のために、
    GetWindow(HWND, GW_OWNER) が IntPtr.Zero かつ IsWindowVisible(HWND) が True で
    あるかどうかも検査しています。アンマネージコードでも構わなければ、上記手法で
    最初に見つけたところで列挙を終えず、そのままコールバックを継続すればよいかと。
引用返信 削除キー/
■34390 / inTopicNo.3)  Re[2]: プロセスからウインドウを取得
□投稿者/ /400 一般人(4回)-(2019/11/14(Thu) 18:15:24)
  • アイコンNo34381に返信(魔界の仮面弁士さんの記事)

    レスありがとうございます

    > ここでいうウィンドウとは、何を指していますか?

    > まず、列挙の目的は何でしょうか?

    簡易的なタスクスイッチャーみたいなものを作ろうとしていて、例えばWZEditorというエディタがあるのですがタスクマネージャーのプロセスにはWZ_Main.exeだけがあり、アプリケーションの方には読ませたファイルの数だけ実際に見えているウインドウが並んでいるわけです("Hoge1.txt - WZ EDITOR" "Hoge2.txt - WZ EDITOR"みたいに)
    ところがプロセスのメインウインドウだとそのうちの1つしか取得できませんよね
    でプロセスが表示している全てのウインドウが取得出来ないかなと

    プロセスの取得はManagementObjectCollectionからプロセスIDを取得してそのプロセスIDからGetProcessByIdでプロセスを取得しています
    で、プロセスのMainWindowHandleがIntPtr.Zeroで無いものを処理しています

引用返信 削除キー/
■34391 / inTopicNo.4)  Re[3]: プロセスからウインドウを取得
□投稿者/ 魔界の仮面弁士 大御所(1259回)-(2019/11/14(Thu) 20:31:01)
  • アイコンNo34390に返信(/400さんの記事)
    > プロセスの取得はManagementObjectCollectionからプロセスIDを取得して
    WMI からのアプローチなのですね。
    使っているのは、root\CIMV2 名前空間の Win32_Process クラスあたりでしょうか。


    > そのプロセスIDからGetProcessByIdでプロセスを取得しています
    新しいプロセスの起動等を検知するために、__InstanceCreationEvent / __InstanceDeletionEvent を
    仕掛けているといった事情があるのなら、WMI を使う価値はあるかもしれませんが、
    単に Process クラスのインスタンスを得ることが目的なのだとしたら、プロセスの列挙は
    WMI を経由させずとも、最初から Process.GetProcesses を使った方が手っ取り早い気がします。


    ただ、最終的に必要なのはウィンドウハンドル(あるいはそのタイトル?)のようなので、その場合、
    前回の回答で述べた EnumWindows + GetWindowThreadProcessId で探すことになりそうです。
    ここの Tips 集にある [プロセス]>[ウィンドウのタイトルからプロセスを探す] でも使われていますね。


    とはいっても、最初の質問で
    >>> ウインドウの一覧を先に取得してそれをプロセスで束ねてく方法は分かるのですが、
    との事ですので、恐らくその方法は既にご存知なのかと思います。

    ウィンドウ数が膨大で列挙が遅いというのであれば、列挙完了まで待たせるのではなく、
    探索はワーカースレッドに任せ、対象プロセスのウィンドウだったものが
    逐次通知されてくるようにするのはどうでしょうか。
引用返信 削除キー/
■34392 / inTopicNo.5)  Re[4]: プロセスからウインドウを取得
□投稿者/ 魔界の仮面弁士 大御所(1260回)-(2019/11/14(Thu) 21:47:22)
  • アイコン
    No34391に追記(魔界の仮面弁士の記事)
    >>>> ウインドウの一覧を先に取得してそれをプロセスで束ねてく方法は
    >>>> 分かるのですが、それだと時間がかかるので
    > ウィンドウ数が膨大で列挙が遅いというのであれば、
    
    手元の VB2005 で試してみましたが、数百ミリ秒以内に列挙が完了しており、
    言うほど時間がかかるようには思えませんでした。
    
    比較のため、そちらで実装したコードを見せて頂けないでしょうか?
    
    
    Option Strict On
    Imports System.Runtime.InteropServices
    Imports System.Text
    Imports System.ComponentModel
    Public Class Form1
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            Dim dt As New DataTable()
            dt.Columns.Add("PID", GetType(Integer))
            dt.Columns.Add("ProcessName", GetType(String))
            dt.Columns.Add("HWND", GetType(IntPtr))
            dt.Columns.Add("Title", GetType(String))
            EnumWindows(AddressOf EnumWindowCallBack, dt)
            Me.DataGridView1.DataSource = dt
            Me.DataGridView1.Columns("PID").DefaultCellStyle.Format = "X8"
            Me.DataGridView1.Columns("HWND").DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
        End Sub
    
        Private Function EnumWindowCallBack(ByVal hWnd As IntPtr, ByVal lparam As DataTable) As Boolean
            If GetWindow(hWnd) = IntPtr.Zero AndAlso IsWindowVisible(hWnd) Then
                Dim processId As Integer
                GetWindowThreadProcessId(hWnd, processId)
                Dim procName As String = ""
                Try
                    'procName = Process.GetProcessById(processId).ProcessName
                Catch
                End Try
                Dim Title As String = ""
                'Dim textLen As Integer = GetWindowTextLength(hWnd)
                'If textLen > 0 Then
                '    Dim tsb As New StringBuilder(textLen + 1)
                '    GetWindowText(hWnd, tsb, tsb.Capacity)
                '    Title = tsb.ToString(0, textLen)
                'End If
                lparam.Rows.Add(processId, procName, hWnd, Title)
            End If
            Return True
        End Function
        <DllImport("user32.dll", CharSet:=CharSet.Unicode, EntryPoint:="GetWindowTextW", SetLastError:=True)> _
        Private Shared Function GetWindowText(ByVal hWnd As IntPtr, ByVal lpString As StringBuilder, ByVal nMaxCount As Integer) As Integer
        End Function
        <DllImport("user32.dll", CharSet:=CharSet.Unicode, EntryPoint:="GetWindowTextLengthW", SetLastError:=True)> _
        Private Shared Function GetWindowTextLength(ByVal hWnd As IntPtr) As Integer
        End Function
        Private Delegate Function EnumWindowsDelegate(ByVal hWnd As IntPtr, ByVal lparam As DataTable) As Boolean
        <DllImport("user32.dll")> _
        Private Shared Function EnumWindows(ByVal lpEnumFunc As EnumWindowsDelegate, ByVal lparam As DataTable) As Boolean
        End Function
        <DllImport("user32.dll", SetLastError:=True)> _
        Private Shared Function GetWindowThreadProcessId(ByVal hWnd As IntPtr, ByRef lpdwProcessId As Integer) As Integer
        End Function
        Private Const GW_OWNER As Integer = 4
        <DllImport("user32.dll")> _
        Private Shared Function GetWindow(ByVal hWnd As IntPtr, Optional ByVal uCmd As Integer = GW_OWNER) As IntPtr
        End Function
        <DllImport("user32.dll")> _
        Private Shared Function IsWindowVisible(ByVal hWnd As IntPtr) As Boolean
        End Function
    End Class

引用返信 削除キー/



トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

Mode/  Pass/


- Child Tree -