Announcement

Collapse
No announcement yet.

Child process window handle

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Child process window handle

    Does anyone know a neat way of getting the handle of the main
    window of a child process?

    This is the rather messy way I do it now. I launch the child
    PB/DLL exe from the parent PB/DLL exe with CreateProcess. The
    child process creates its main window and records the handle to
    a memory mapped file in a DLL. Meanwhile, the parent process
    waits for input idle (WaitForInputIdle) with a time-out of one
    second and then calls the same DLL to retrieve the handle. It
    works but I'm sure this method cannot be safe.

    Thanks, Keith

    ------------------

  • #2
    Off the top of my head, you could use EnumWindows to enumerate all of the top level windows. Use GetWindowThreadProcessId to get the process ID that created each window, and when you find one that matches the Process ID that was returned by CreateProcess... Bingo.

    Of course whatever you do you will need to wait for the window to be created before you can start scanning for it.

    -- Eric


    ------------------
    Perfect Sync Development Tools
    Perfect Sync Web Site
    Contact Us: mailto:[email protected][email protected]</A>



    [This message has been edited by Eric Pearson (edited May 15, 2001).]
    "Not my circus, not my monkeys."

    Comment


    • #3
      Keith,

      If I totally understand what's going on, the GetParent function
      retrieves a handle to the specified child window's
      parent window. Give that a try!!!!

      Cheers,
      Cecil

      ------------------

      Comment


      • #4
        If both apps are own, it's easy to coordinate

        Parent.Bas
        Code:
           #Compile Exe
           #Register None
           #Dim All
           #Include "win32api.inc"
        
           CallBack Function MainDlgProc
              Select Case CbMsg
                 Case %WM_COMMAND
                    Dim i As Long, hWndChild As Static Long
                    If CbCtl = %IDOK Then i = Shell ("Child.Exe &H" + Hex$(CbHndl, 8))
                 Case %WM_USER + 401
                    hWndChild = CbWparam: SetWindowText CbHndl, "I am a parent. Child reported hWnd = &H" + Hex$(hWndChild, 8)
               End Select
           End Function
        
           Function PbMain
              Local hDlg As Long
              Dialog New 0, "Testing", , , 250, 40, %WS_CAPTION Or %WS_SYSMENU To hDlg
              Control Add Button, hDlg, %IDOK, "Start Child.Exe", 10, 10, 230, 15
              Dialog Show Modal hDlg Call MainDlgProc ' Show window
           End Function
        Child.Bas
        Code:
           #Compile Exe
           #Register None
           #Dim All
           #Include "win32api.inc"
        
           CallBack Function DlgProc
              Select Case CbMsg
                 Case %WM_INITDIALOG
                   PostMessage CbHndl, %WM_USER + 501, 0, 0
                Case %WM_USER + 501   
                   SendMessage Val(Command$), %WM_USER + 401, CbHndl, 0
                   SetForegroundWindow Val(Command$)
              End Select
           End Function
        
           Function PbMain
              Local hDlg As Long
              Dialog New 0, "", 0, 0, 200, 80, %WS_CAPTION Or %WS_SYSMENU To hDlg
              SetWindowText hDlg, "I am a child (hwnd = &H" + Hex$(hDlg, 8) + ")"
              Dialog Show Modal hDlg Call DlgProc ' Show window
           End Function
        If second app is not own, it's possible to use a hook (somehow I posted a sample)

        ------------------
        E-MAIL: [email protected]

        Comment


        • #5
          Eric

          Your scheme makes sense to me and I'm trying it. Cannot quite get
          it to work properly yet as I seem to picking up wrong window handle.
          I will try again tomorrow. Many thanks.

          Semen

          Your replies are always very interesting. I'm already using the
          same interprocess communication as you suggest for other purposes
          and so maybe I will use your method if I fail to get Eric's
          aproach working. Again, many thanks.

          Keith

          ------------------

          Comment


          • #6
            EnumWindows is not a problem
            Code:
               #Compile Exe
               #Dim All
               #Register None
               #Include "Win32Api.Inc"
            
               Global hNotepad As Long, hDlg As Long
               
               Function EnumWindowsCallBack(ByVal hwnd As Long, ByVal lParam As Long) As Long
                   Local kProc As Long
                   GetWindowThreadProcessId hWnd, kproc
                   If kProc = lParam Then hNotePad = hWnd
                   Function = 1
                End Function
            
               CallBack Function StartNotepad
                  Dim eProc As Long
                  Dim proc As PROCESS_INFORMATION
                  Dim start As STARTUPINFO
                  start.cb = SizeOf(start)
                  If CreateProcess (ByVal 0&, "Notepad.exe", ByVal 0&, ByVal 0&, 1&, _
                     %NORMAL_PRIORITY_CLASS, ByVal 0&, ByVal 0&, start, proc) Then eProc = proc.dwProcessId: _
                     CloseHandle proc.hThread: CloseHandle proc.hProcess Else Exit Function
                     
                  While hNotepad = 0
                     Sleep 50
                     EnumWindows CodePtr(EnumWindowsCallBack), eProc
                  Wend
                  
                  ShowWindow CbHndl, 0
                  MessageBox CbHndl, "Click Ok to close Notepad's window", "Semen", %MB_TASKMODAL
                  ShowWindow CbHndl, 1
                  SendMessage hNotePad, %WM_SYSCOMMAND, %SC_CLOSE, 0
                  hNotePad = 0
                  
               End Function
            
               Function PbMain
                  Dialog New hDlg, "test", , , 70, 30, %WS_SYSMENU Or %WS_CAPTION, %WS_EX_TOPMOST To hdlg
                  Control Add Button, hDlg, %IDOK, "Start Notepad", 10, 10, 50, 15 Call StartNotePad
                  Dialog Show Modal hDlg
               End Function
            CBT- hook ... I found, but it was global (because I integrated own DLL).
            So, here it's not comfortable way.

            [This message has been edited by Semen Matusovski (edited May 15, 2001).]

            Comment


            • #7
              Semen

              I think I'm doing the same, as follows.

              Code:
              GLOBAL g_tempDword AS DWORD
              
              FUNCTION CheckForProcess (BYVAL hWnd AS LONG, BYVAL procID AS LONG) AS LONG
                LOCAL wProcID AS LONG
              
                CALL GetWindowThreadProcessId (hWnd, wProcID)
              
                IF wProcID = procID THEN
                  g_tempDword = hWnd
                  FUNCTION = %FALSE         'Stop enumerating
                ELSE
                  FUNCTION = %TRUE
                END IF
              END FUNCTION
              
              FUNCTION ProcessMainWindow (BYVAL procId AS LONG) AS LONG
              
                g_tempDword = %NULL
                CALL EnumWindows (CODEPTR(CheckForProcess), procId)
                FUNCTION = g_tempDword
              END FUNCTION
              First I call CreateProcess to launch the process and then I do

              Code:
                CALL WaitForInputIdle (pi.hProcess, 1000)  'We wait for upto 1 s
              
                g_hWndInspector = ProcessMainWindow (pi.dwProcessId)
              Can't see why its not working. I think its getting a window of the
              child exe but not the main window.

              Keith

              ------------------

              Comment


              • #8
                Keith --
                Do you use WaitForInputIdle after or before CloseHandle pi.hProcess ?
                If after, I guess that WaitForInputIdle returns "failed" (-1).
                At least, this so under Win2000.

                ------------------
                E-MAIL: [email protected]

                Comment


                • #9
                  Semen

                  I use WaitForInputIdle before CloseHandle as follows.

                  Code:
                    CALL WaitForInputIdle (pi.hProcess, 1000)  'We wait for upto 1 s
                    
                    'Get inspector main window handle  
                  ' g_hWndInspector = GetProcHWin()                      'Old method 
                    g_hWndInspector = ProcessMainWindow (pi.dwProcessId) 'New method
                  
                    'Close process and thread handles (unless we need them)
                    CALL CloseHandle (pi.hProcess)
                    CALL CloseHandle (pi.hThread)
                  The old method (commented out) worked fine. The new method returns
                  a window handle but its not the main process window. When I get back
                  to my development machine I will find out what window it is picking up.
                  In the meantime, many thanks to you and Eric.

                  Keith

                  ------------------

                  Comment


                  • #10
                    Here is an update to enumerating process windows (for anyone still interested).

                    I've checked and the EnumWindows function is passing parent and child window
                    handles back to the callback function. But hang on, the MSDN documentation
                    says that EnumWindows does not enumerate child windows. My process creates
                    a total of 6 windows before settling into input idle, a main window and 5
                    child windows, and I'm seeing all 6 in the callback.

                    So, I've double checked to make sure I'm including the %WS_CHILD style for
                    all except the main window and that I'm specifying a valid parent handle
                    (handle of main window, of course). It seems correct.

                    In my callback function I return %FALSE to end enumeration when I get the
                    first window belonging to the process. If instead I return %TRUE so that
                    enumeration continues, I get the last window belonging to the process.
                    In neither case is it the main window and I wouldn't expect it to be
                    because EnumWindows does not guarantee to enumerate in any particular order,
                    which is fair enough.

                    I will try calling GetParent(hWnd) in my callback function and checking if
                    the return value is %NULL. If it is then presumable that must be the main
                    window (ie. the only one without a parent). But I'm still puzzled why
                    EnumWindows is enumerating parent and children.

                    Keith

                    ------------------


                    [This message has been edited by Keith Waters (edited May 17, 2001).]

                    Comment


                    • #11
                      EnumWindows enumerates top-level windows... there may be more than one top-level window per app or process.

                      ------------------
                      Lance
                      PowerBASIC Support
                      mailto:[email protected][email protected]</A>
                      Lance
                      mailto:[email protected]

                      Comment


                      • #12
                        Yes Lance, I agree and the documentation emphasises by saying, quote
                        "The EnumWindows function does not enumerate child windows.".
                        My app only creates one top-level, all other windows are children.
                        Is the documentation wrong here?

                        Keith

                        ------------------


                        [This message has been edited by Keith Waters (edited May 17, 2001).]

                        Comment


                        • #13
                          Meanwhile EnumWindows enumerates some child system windows (under Windows2000 - classname ComboLBox).

                          One thought - wrong combinations of styles (for example, %WS_CHILD together with %WS_POPUP)
                          Code:
                             #Compile Exe
                             #Dim All
                             #Register None
                             #Include "Win32Api.Inc"
                             
                             Function EnumWindowsCallBack(ByVal hwnd As Long, ByVal lParam As Long) As Long
                                 Local kProc As Long
                                 GetWindowThreadProcessId hWnd, kproc
                                 If kProc = GetCurrentProcessId Then _
                                    If (GetWindowLong(hWnd, %GWL_STYLE) And %WS_CHILD) = %WS_CHILD Then MsgBox "Child " + Hex$(hWnd)
                                 Function = 1
                              End Function
                             CallBack Function DlgProc
                                Dim i As Long, hdlg(5) As Long
                                Select Case CbMsg
                                   Case %WM_INITDIALOG
                                      For i = 1 To 5
                                         Dialog New CbHndl, "test" + Str$(i), 30 * i, 30 * i, 70, 100, %WS_CHILD Or %WS_POPUP Or %WS_BORDER To hdlg(i)
                                         Dialog Show Modeless hDlg(i)
                                      Next
                                End Select
                             End Function
                             CallBack Function StartNotepad
                                EnumWindows CodePtr(EnumWindowsCallBack), 0
                                Dialog End CbHndl
                             End Function
                             Function PbMain
                                Dim hDlg As Long
                                Dialog New 0, "test", , , 400, 300, %WS_POPUP To hdlg
                                Control Add Button, hDlg, %IDOK, "Start Notepad", 200, 100, 50, 15 Call StartNotePad
                                Dialog Show Modal hDlg Call DlgProc
                             End Function


                          ------------------
                          E-MAIL: [email protected]

                          Comment


                          • #14
                            Semen

                            I've just checked for %WS_POPUP combined with %WS_CHILD and found
                            none. I'll check for other wrong combinations but I would have
                            thought if ther were any I'd have noticed other strange effects.
                            Thanks for alerting me to that possibility, tho.

                            Also, I'd thought of checking the window style GetWindowLong as
                            you suggest and I also thought of using GetParent. But the
                            central issue is why should all this be necessary when EnumWindows
                            is supposed to enumerate only top-level windows (ie. those with
                            no parent except the desktop). I feel I must be doing something
                            wrong but cannot trace it.

                            Thank anyway, Keith

                            ------------------

                            Comment


                            • #15
                              The update.

                              The following worked.

                              Code:
                              GLOBAL g_tempDword AS DWORD
                                
                              FUNCTION CheckForProcess (BYVAL hWnd AS LONG, BYVAL procID AS LONG) AS LONG
                                LOCAL wProcID AS LONG
                               
                                CALL GetWindowThreadProcessId (hWnd, wProcID)
                               
                                IF wProcID = procID THEN           'If window of process
                                  IF %NULL = GetParent(hWnd) THEN  'If has no parent
                                    g_tempDword = hWnd             'Note window handle
                                    FUNCTION = %FALSE              'Stop enumerating
                                    EXIT FUNCTION
                                  END IF
                                END IF
                                FUNCTION = %TRUE                   'Continue enumerating
                              END FUNCTION
                               
                              FUNCTION ProcessMainWindow (BYVAL procId AS LONG) AS LONG
                                g_tempDword = %NULL
                                CALL EnumWindows (CODEPTR(CheckForProcess), procId)
                                FUNCTION = g_tempDword
                              END FUNCTION
                              The following didn't work because more than one window in the enumeration
                              passed the "(GetWindowLong (hWnd, %GWL_STYLE) AND %WS_CHILD) = 0" test
                              (sounds like Lance's point).

                              Code:
                              GLOBAL g_tempDword AS DWORD
                               
                              FUNCTION CheckForProcess (BYVAL hWnd AS LONG, BYVAL procID AS LONG) AS LONG
                                LOCAL wProcID AS LONG
                               
                                CALL GetWindowThreadProcessId (hWnd, wProcID)
                               
                                IF wProcID = procID THEN
                                  IF (GetWindowLong (hWnd, %GWL_STYLE) AND %WS_CHILD) = 0 THEN
                                    g_tempDword = hWnd        'Note window handle
                                    FUNCTION = %FALSE         'Stop enumerating
                                    EXIT FUNCTION
                                  END IF
                                END IF
                                FUNCTION = %TRUE            'Continue enumerating
                              END FUNCTION
                               
                              FUNCTION ProcessMainWindow (BYVAL procId AS LONG) AS LONG
                                g_tempDword = %NULL
                                CALL EnumWindows (CODEPTR(CheckForProcess), procId)
                                FUNCTION = g_tempDword
                              END FUNCTION
                              Keith

                              ------------------

                              Comment

                              Working...
                              X