Search code examples
vb.netwindowsprocesswindow

Topmost window over everything, except child processes


My program is full screen and topmost, and I would like all the child processes of that window to be above my program's main window. The processes are unknown, and external.

I can launch the process using System.Diagnostics.Process.Start(exeName,procArgs).WaitForExit(), but from there I am stuck.


Solution

  • Basically you use the SetParent() API to make the external app a child of yours. Here I'm also using the GetWindowRect() and SetWindowPos() APIs to keep the window in the same launch position after its parent is changed. Finally, you need to keep track of the processes and close them manually so they do not become orphaned when the form is closed:

    Imports System.ComponentModel
    Imports System.IO
    Imports System.Runtime.InteropServices
    Imports System.Text.RegularExpressions
    
    Public Class Form1
    
        Private Const SWP_NOSIZE As Integer = &H0001
    
        <StructLayout(LayoutKind.Sequential)>
        Public Structure RECT
            Public Left As Integer, Top As Integer, Right As Integer, Bottom As Integer
        End Structure
    
        <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
        Public Shared Function SetParent(ByVal hWndChild As IntPtr, ByVal hWndNewParent As IntPtr) As IntPtr
        End Function
    
        <DllImport("user32.dll")>
        Private Shared Function GetWindowRect(ByVal hWnd As IntPtr, ByRef lpRect As RECT) As Boolean
        End Function
    
        <DllImport("user32.dll", SetLastError:=True)>
        Private Shared Function SetWindowPos(ByVal hWnd As IntPtr, ByVal hWndInsertAfter As IntPtr, ByVal X As Integer, ByVal Y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal uFlags As Integer) As Boolean
        End Function
    
        Private Ps As New List(Of Process)
    
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            Dim exeName As String = "Notepad"
            Dim procArgs As String = ""
            LaunchExe(exeName, procArgs)
        End Sub
    
        Private Sub LaunchExe(ByVal exeName As String, ByVal procArgs As String)
            Try
                Dim p As Process = System.Diagnostics.Process.Start(exeName, procArgs)
                If Not IsNothing(p) Then
                    p.WaitForInputIdle()
                    Dim rc As RECT
                    GetWindowRect(p.MainWindowHandle, rc)
                    Dim pt As New Point(rc.Left, rc.Top)
                    pt = Me.PointToClient(pt)
                    SetParent(p.MainWindowHandle, Me.Handle)
                    SetWindowPos(p.MainWindowHandle, 0, pt.X, pt.Y, 0, 0, SWP_NOSIZE)
                    Ps.Add(p)
                End If
            Catch ex As Exception
                MessageBox.Show(exeName & vbCrLf & procArgs, "Error Starting Application")
            End Try
        End Sub
    
        Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
            Me.Close()
        End Sub
    
        Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
            For Each P As Process In Ps
                If Not P.HasExited Then
                    P.CloseMainWindow()
                End If
            Next
        End Sub
    
    End Class