Getting Child Windows Using Process.GetProcess

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • TomLasky
    New Member
    • Nov 2008
    • 9

    Getting Child Windows Using Process.GetProcess

    Hi everyone, I ran into a road block and cant seem to find anything on google.

    I need to check all open windows/processes to see if certain software is running on the system. I was going to go the route of using core api from user32 for findwindow and so on, but then I came across using Process.Getproc esses and was very happy to see it was easy to get all processes and MainWindowTitle

    Code:
            Dim poc() As Process = Process.GetProcesses()
            For i As Integer = 0 To poc.Length - 1
    
                Try
                    msgbox poc(i).mainwindowtitle
                                    
                Catch ex As Exception
                    MsgBox(poc(i).ProcessName.ToString & " " & ex.Message)
    
                End Try
    
            Next
    However... if the software has child windows or multiple windows open it does not get the title of these "sub windows". An example is an application that has 2 windows open from the same exe. Is there anyway to grab all window titles of a process using this same logic?
  • nukefusion
    Recognized Expert New Member
    • Mar 2008
    • 221

    #2
    I'm not sure quite how to go about this using .NET's process class. You can however do this using P/Invoke. Code similar to the following should work:

    Code:
               private delegate bool EnumWindowsProc(IntPtr hWnd, int lParam);
    
            [DllImport("user32.dll", SetLastError=true)]
            private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
    
            [DllImport("USER32.DLL")]
            private static extern bool EnumWindows(EnumWindowsProc enumFunc, int lParam);
    
            [DllImport("USER32.DLL")]
            private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
    
            [DllImport("USER32.DLL")]
            private static extern int GetWindowTextLength(IntPtr hWnd);
    
            [DllImport("USER32.DLL")]
            private static extern bool IsWindowVisible(IntPtr hWnd);
    
            [DllImport("USER32.DLL")]
            private static extern IntPtr GetShellWindow();
    
            public IDictionary<IntPtr, string> GetOpenWindowsFromPID(int processID)
            {
                IntPtr hShellWindow = GetShellWindow();
                Dictionary<IntPtr, string> dictWindows = new Dictionary<IntPtr, string>();
    
                EnumWindows(delegate(IntPtr hWnd, int lParam)
                            {
                                if (hWnd == hShellWindow) return true;
                                if (!IsWindowVisible(hWnd)) return true;
    
                                int length = GetWindowTextLength(hWnd);
                                if (length == 0) return true;
    
                                uint windowPid;
                                GetWindowThreadProcessId(hWnd, out windowPid);                            
                                if (windowPid != processID) return true;
    
                                StringBuilder stringBuilder = new StringBuilder(length);
                                GetWindowText(hWnd, stringBuilder, length + 1);
                                dictWindows.Add(hWnd, stringBuilder.ToString());
                                return true;
                            }, 0);
    
                return dictWindows;
            }
    You can then use the Process class to get the Process ID of the application you are targeting and call the above code to get a list of windows associated it, something along the lines of:

    Code:
                Process[] processes = Process.GetProcessesByName("MyApp");
                foreach (Process process in processes)
                {
                    IDictionary<IntPtr, string> windows = this.GetOpenWindowsFromPID(process.Id);
                    foreach (KeyValuePair<IntPtr, string> kvp in windows)
                    {
                        // Do whatever you want here
                    }
                }
    If you want to target hidden windows also you can modify the code slightly.
    Hope it helps!

    Comment

    • r035198x
      MVP
      • Sep 2006
      • 13225

      #3
      If your goal is simply to determine if a particular program is running or not, then why do you need to find all windows? I'd expect that finding at least one window is sufficient.
      I will add also that not finding a window is not sufficient to determine that the program is not running.

      Comment

      • TomLasky
        New Member
        • Nov 2008
        • 9

        #4
        Do you have this in VB.NET form? I've been trying to do something similar using GetdesktopWindo w Hwnd and then using HWNDNEXT to loop through them all, the problem is I cant get the desktopwindow hwnd for some reason.

        I visited the pinvoke web site and found a lot of the findwindow api's and such, maybe im going about it the wrong way?

        Also, in response to the last post, yes I do need to know the programs thats are running but I also need to know if those programs are running other windows inside of their app. Problem is .NET's getprocesses just gets the mainwindowtitle and doesnt also provide childwindows... in my personal opinion it's a huge let down on the part of .NET. Everyone is always informing me to try and not use Win32API's directly because it's unmanaged and sloppy but in this instance im forced to use it. Anyway, any help would be appreciated, I'm on my 9th hour of google searching for a simple thing I could do in VB6 in 5 minutes.

        Comment

        • Plater
          Recognized Expert Expert
          • Apr 2007
          • 7872

          #5
          As pointed out, not finding the window doesn't mean the pogram is not running.
          Shouldn't you be looking for a particular .exe or some other more discriminate property to see if it is running?

          Comment

          • TomLasky
            New Member
            • Nov 2008
            • 9

            #6
            Here's what I'm doing:

            I'm managing instant message windows or trying to anyway. I need to get all open windows for a specific program. I can find if a application is running, thats not the problem. The problem is finding all open windows for that app. As of right now I can only get the active top most window of that app and thats it, but theres 20 more windows from the same app that are not being found. I understand software can be hidden and just because an open window isn't there doesn;t mean it's not running, understood but it has no bearing on what im trying to accomplish.

            Simply put... if you have 10 instant message windows open using AIM, I need to find the title text for all of those windows, not just the mainwindowtitle .

            I've been messing around with old school enumwindows but its not working in .NET, most likely im declaring the wrong type. As of now I butchered the code so badly I'm starting from scratch. I liked the first answer posted, makes sense im lost with delegates and need help in the area of vb.net.

            Comment

            • Plater
              Recognized Expert Expert
              • Apr 2007
              • 7872

              #7
              Ahh ok, you aren't so much interested in the running process, as what the process is running (all its windows)

              Yeah I am afraid you are stuck using win32_api for that unfortunatly

              Comment

              • TomLasky
                New Member
                • Nov 2008
                • 9

                #8
                I forgot to thank everyone for their help, I'm just burnt out pounding my head against the wall over this so I apologize for my lack of understanding. But sincerely, thank you for your time in helping me with this, it's much appreciated.

                Comment

                • TomLasky
                  New Member
                  • Nov 2008
                  • 9

                  #9
                  Plater, I was hoping it wouldn't be so! haha... oh well...

                  Any advice on proper logic to use to go about doing this?

                  I was trying to start from getting the desktop hwnd and then going hwndnext, I dont know... I'm brainless right now.

                  Comment

                  • nukefusion
                    Recognized Expert New Member
                    • Mar 2008
                    • 221

                    #10
                    Hi Tom,

                    Sorry, VB.NET really isn't my strong point but still, I've had a go at translating the code sample for you. I hope you can get some use out of it.

                    Code:
                    Imports System.Runtime.InteropServices
                    Imports System.Text
                    
                    Public Class Test
                    
                        <DllImport("USER32.DLL")> _
                        Private Shared Function GetShellWindow() As IntPtr
                        End Function
                    
                        <DllImport("USER32.DLL")> _
                        Private Shared Function GetWindowText(ByVal hWnd As IntPtr, ByVal lpString As StringBuilder, ByVal nMaxCount As Integer) As Integer
                        End Function
                    
                        <DllImport("USER32.DLL")> _
                        Private Shared Function GetWindowTextLength(ByVal hWnd As IntPtr) As Integer
                        End Function
                    
                        <DllImport("user32.dll", SetLastError:=True)> _
                        Private Shared Function GetWindowThreadProcessId(ByVal hWnd As IntPtr, <Out()> ByRef lpdwProcessId As UInt32) As UInt32
                        End Function
                    
                        <DllImport("USER32.DLL")> _
                        Private Shared Function IsWindowVisible(ByVal hWnd As IntPtr) As Boolean
                        End Function
                    
                        Private Delegate Function EnumWindowsProc(ByVal hWnd As IntPtr, ByVal lParam As Integer) As Boolean
                    
                        <DllImport("USER32.DLL")> _
                        Private Shared Function EnumWindows(ByVal enumFunc As EnumWindowsProc, ByVal lParam As Integer) As Boolean
                        End Function
                    
                        Private hShellWindow As IntPtr = GetShellWindow()
                        Private dictWindows As New Dictionary(Of IntPtr, String)
                        Private currentProcessID As Integer
                    
                    
                        Public Function GetOpenWindowsFromPID(ByVal processID As Integer) As IDictionary(Of IntPtr, String)
                            dictWindows.Clear()
                            currentProcessID = processID
                            EnumWindows(AddressOf enumWindowsInternal, 0)
                            Return dictWindows
                        End Function
                    
                        Private Function enumWindowsInternal(ByVal hWnd As IntPtr, ByVal lParam As Integer) As Boolean
                            If (hWnd <> hShellWindow) Then
                                Dim windowPid As UInt32
                                If Not IsWindowVisible(hWnd) Then
                                    Return True
                                End If
                                Dim length As Integer = GetWindowTextLength(hWnd)
                                If (length = 0) Then
                                    Return True
                                End If
                                GetWindowThreadProcessId(hWnd, windowPid)
                                If (windowPid <> currentProcessID) Then
                                    Return True
                                End If
                                Dim stringBuilder As New StringBuilder(length)
                                GetWindowText(hWnd, stringBuilder, (length + 1))
                                dictWindows.Add(hWnd, stringBuilder.ToString)
                            End If
                            Return True
                        End Function
                    End Class
                    You should be able to call it in a similar fashion, although I haven't tested this even though it does compile, i.e:

                    Code:
                    Dim processes As Process() = Process.GetProcessesByName("MyApp")
                    For Each process As Process In processes
                    	Dim windows As IDictionary(Of IntPtr, String) = GetOpenWindowsFromPID(process.Id)
                    	For Each kvp As KeyValuePair(Of IntPtr, String) In windows
                                            ' Do whatever you want here 
                    	Next
                    Next

                    Comment

                    • TomLasky
                      New Member
                      • Nov 2008
                      • 9

                      #11
                      NUKEFUSION!!!!

                      THANK YOU!!

                      Works perfectly! I CANNOT say Thank you enough for this!!

                      I combined your code with the code I had for cycling through the open processes, then I ran your code to evaluate each process individually and it works like a charm to gather each and every sub windows title!

                      You seriously saved me from going insane over this issue, now I understand how to tackle things of this nature. Keep up the excellent coding, simply brilliant!

                      Comment

                      • nukefusion
                        Recognized Expert New Member
                        • Mar 2008
                        • 221

                        #12
                        Your welcome. Glad you have managed to resolve your issue!

                        Comment

                        • kevinn7
                          New Member
                          • Jul 2009
                          • 8

                          #13
                          Originally posted by nukefusion
                          Your welcome. Glad you have managed to resolve your issue!
                          Nukefusion,

                          I managed to modify the code as below

                          Code:
                          Imports System.Runtime.InteropServices
                          Imports System.Text
                          
                          Public Class Form1
                          
                              Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
                                  For Each poc In Process.GetProcesses
                                      If poc.MainWindowTitle.Length > 1 Then
                          
                                          Dim windows As IDictionary(Of IntPtr, String) = GetOpenWindowsFromPID(poc.Id)
                                          For Each kvp As KeyValuePair(Of IntPtr, String) In windows
                                              poc.EnableRaisingEvents = True
                          
                          
                                              Try
                                                  ListBox1.Items.Add(" " & kvp.ToString)
                                              Catch ex As Exception
                                                  MsgBox(poc.ProcessName.ToString & " " & ex.Message)
                                              End Try
                                          Next
                          
                                      End If
                                      AddHandler poc.Exited, AddressOf OnProcessExit
                                  Next
                          
                              End Sub
                              <DllImport("USER32.DLL")> _
                              Private Shared Function GetShellWindow() As IntPtr
                              End Function
                          
                              <DllImport("USER32.DLL")> _
                              Private Shared Function GetWindowText(ByVal hWnd As IntPtr, ByVal lpString As StringBuilder, ByVal nMaxCount As Integer) As Integer
                              End Function
                          
                              <DllImport("USER32.DLL")> _
                              Private Shared Function GetWindowTextLength(ByVal hWnd As IntPtr) As Integer
                              End Function
                          
                              <DllImport("user32.dll", SetLastError:=True)> _
                              Private Shared Function GetWindowThreadProcessId(ByVal hWnd As IntPtr, <Out()> ByRef lpdwProcessId As UInt32) As UInt32
                              End Function
                          
                              <DllImport("USER32.DLL")> _
                              Private Shared Function IsWindowVisible(ByVal hWnd As IntPtr) As Boolean
                              End Function
                          
                              Private Delegate Function EnumWindowsProc(ByVal hWnd As IntPtr, ByVal lParam As Integer) As Boolean
                          
                              <DllImport("USER32.DLL")> _
                              Private Shared Function EnumWindows(ByVal enumFunc As EnumWindowsProc, ByVal lParam As Integer) As Boolean
                              End Function
                          
                              Private hShellWindow As IntPtr = GetShellWindow()
                              Private dictWindows As New Dictionary(Of IntPtr, String)
                              Private currentProcessID As Integer
                          
                          
                              Public Function GetOpenWindowsFromPID(ByVal processID As Integer) As IDictionary(Of IntPtr, String)
                                  dictWindows.Clear()
                                  currentProcessID = processID
                                  EnumWindows(AddressOf enumWindowsInternal, 0)
                                  Return dictWindows
                              End Function
                          
                              Private Function enumWindowsInternal(ByVal hWnd As IntPtr, ByVal lParam As Integer) As Boolean
                                  If (hWnd <> hShellWindow) Then
                                      Dim windowPid As UInt32
                                      If Not IsWindowVisible(hWnd) Then
                                          Return True
                                      End If
                                      Dim length As Integer = GetWindowTextLength(hWnd)
                                      If (length = 0) Then
                                          Return True
                                      End If
                                      GetWindowThreadProcessId(hWnd, windowPid)
                                      If (windowPid <> currentProcessID) Then
                                          Return True
                                      End If
                                      Dim stringBuilder As New StringBuilder(length)
                                      GetWindowText(hWnd, stringBuilder, (length + 1))
                                      dictWindows.Add(hWnd, stringBuilder.ToString)
                                  End If
                                  Return True
                              End Function
                              Private Sub OnProcessExit(ByVal sender As Object, ByVal e As EventArgs)
                                  ' Avoid late binding and Cast the Object to a Process. 
                                  Dim poc As Process = CType(sender, Process)
                                  ' Write the ExitCode 
                                  'ListBox4.Items.Add(p.ExitCode)
                                  ' Write the ExitTime 
                                  MsgBox(poc.ExitTime.ToString)
                              End Sub
                          End Class
                          As you can see, I've added the EnableRaisingEv ents and an eventhandler to pop up the exittime whenever a window closes.

                          The problem now is, the exittime popup appears only when I close all child programs of a program.

                          For example, if I have 3 IEs open, the exittime pops up after i close all 3 IEs.

                          My goal is to get the exittime on each of the IE. Any idea how I can achieve that?

                          Thanks a bunch =)

                          Comment

                          • Plater
                            Recognized Expert Expert
                            • Apr 2007
                            • 7872

                            #14
                            You'll have to find out when the window handle is destroyed right?

                            Comment

                            • kevinn7
                              New Member
                              • Jul 2009
                              • 8

                              #15
                              Originally posted by Plater
                              You'll have to find out when the window handle is destroyed right?
                              Yes exactly. I can't seem to get the idea on how I can enableraisingev ents on each kvp.value.

                              Any other work-around? thanks :)

                              Comment

                              Working...