Home > Software engineering >  Unable to find RECT size with DwmGetWindowAttribute
Unable to find RECT size with DwmGetWindowAttribute

Time:08-24

I'm trying to find to find the size of the cmd.exe window which is started as a child. I like to use this size to resize my form accordingly. For some reason the size returned by DwmGetWindowAttribute is always zero, so I must be doing something wrong here, but I can't find it. Any help will be greatly appreciated. Kind regards, Eric

Imports System.Runtime.InteropServices
Public Class Form1
    Private WithEvents Tmr As New Timer With {.Interval = 100}
    Private Const HWND_BOTTOM As Integer = &H1
    Private WithEvents proc As New Process
    Public Const DWMWA_EXTENDED_FRAME_BOUNDS As Integer = 9
    <DllImport("user32.dll", EntryPoint:="SetParent")>
    Private Shared Function SetParent(ByVal hWndChild As IntPtr, ByVal hWndNewParent As IntPtr) As IntPtr
    End Function
    <DllImport("user32.dll", EntryPoint:="SetWindowPos")>
    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 UInteger) As <MarshalAs(UnmanagedType.Bool)> Boolean
    End Function
    <DllImport("dwmapi.dll")>
    Shared Function DwmGetWindowAttribute(ByVal hwnd As IntPtr, ByVal dwAttribute As Integer, ByRef pvAttribute As RECT, ByVal cbAttribute As Integer) As Integer
    End Function
    Public Structure RECT
        Public left, top, right, bottom As Integer
    End Structure
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.Text = "My title"
        proc.EnableRaisingEvents = True
        proc.StartInfo.FileName = "cmd"
        proc.Start()
        Tmr.Start()
    End Sub
    Private Sub Tmr_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Tmr.Tick
        If SetParent(proc.MainWindowHandle, Panel1.Handle) <> IntPtr.Zero Then
            Tmr.Stop()
            Dim Width As Integer
            Dim Hight As Integer
            Dim WindowRect As New RECT
            DwmGetWindowAttribute(proc.MainWindowHandle, DWMWA_EXTENDED_FRAME_BOUNDS, WindowRect, Marshal.SizeOf(WindowRect))
            Width = WindowRect.right - WindowRect.left
            Hight = WindowRect.bottom - WindowRect.top
            MessageBox.Show("Hight: " & Hight & " Width: " & Width)
            'Me.Size = New Size(Width, Hight)
            SetWindowPos(proc.MainWindowHandle, New IntPtr(HWND_BOTTOM), 0, 0, Panel1.ClientSize.Width, Panel1.ClientSize.Height, 0)
        End If
    End Sub
    Private Sub Proc_Exited(ByVal sender As Object, ByVal e As System.EventArgs) Handles proc.Exited
        Invoke(Sub() Close())
    End Sub
End Class

CodePudding user response:

After implementing all valuable additions of @Jimi, this is the code that works:

Imports System.Runtime.InteropServices
Public Class Form1
    Private WithEvents proc As New Process
    Public Const WM_NCLBUTTONDOWN As Long = &HA1
    Public Const SW_SHOWMAXIMIZED As UInt32 = 3
    Public Const WM_CLOSE = &H10
    Public Const DWMWA_EXTENDED_FRAME_BOUNDS As Integer = 9
    'Function to set the parent window
    Private Declare Function SetParent Lib "user32" (ByVal hWndChild As IntPtr, ByVal hWndNewParent As IntPtr) As IntPtr
    'Function to set the child window position
    Private Declare Function SetWindowPos Lib "user32" (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 UInteger) As <MarshalAs(UnmanagedType.Bool)> Boolean
    'Function to allow the child to be maximized
    Private Declare Function ShowWindow Lib "user32" (ByVal hwnd As IntPtr, ByVal nCmdShow As Int32) As Boolean
    'Function to retrieve the initail child size
    Private Declare Function DwmGetWindowAttribute Lib "dwmapi" (ByVal hwnd As IntPtr, ByVal dwAttribute As Integer, ByRef pvAttribute As RECT, ByVal cbAttribute As Integer) As Integer
    'Function to set focus to the child window
    Private Declare Function SetForegroundWindow Lib "user32" (ByVal hwnd As IntPtr) As Long
    'Function used to set terminate child window
    Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
    'Rectangle size, used later to resize the form
    Public Structure RECT
        Public left, top, right, bottom As Integer
    End Structure
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.Text = "My title"
        proc.EnableRaisingEvents = True
        proc.StartInfo.FileName = "cmd"
        proc.Start()
    End Sub
    Private Sub Tmr_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Shown
        'Wait for 200 ms for the form to load
        proc.WaitForExit(200)
        Dim WindowRect As New RECT
        Dim Width, Hight As Integer
        'Retrieve the initial size of the child window
        DwmGetWindowAttribute(proc.MainWindowHandle, DWMWA_EXTENDED_FRAME_BOUNDS, WindowRect, Marshal.SizeOf(WindowRect))
        Width = WindowRect.right - WindowRect.left
        Hight = WindowRect.bottom - WindowRect.top   23
        'Set the form size to the initial size of the child window
        Me.Size = New Size(Width, Hight)
        'When the child is started, move the child into the panel and maximize it
        If SetParent(proc.MainWindowHandle, Panel1.Handle) <> IntPtr.Zero Then
            SetWindowPos(proc.MainWindowHandle, IntPtr.Zero, 0, 0, Width, Height, 0)
            ShowWindow(proc.MainWindowHandle, SW_SHOWMAXIMIZED)
        End If
    End Sub
    'Exit form when child terminates
    Private Sub Proc_Exited(ByVal sender As Object, ByVal e As System.EventArgs) Handles proc.Exited
        Invoke(Sub() Close())
    End Sub
    'Set focus on child when the form is activated
    Private Sub Form1_UnFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Activated
        SetForegroundWindow(proc.MainWindowHandle)
    End Sub
    'Set focus on child when the parent titlebar is clicked
    Protected Overrides Sub DefWndProc(ByRef m As System.Windows.Forms.Message)
        If CLng(m.Msg) = WM_NCLBUTTONDOWN Then
            SetForegroundWindow(proc.MainWindowHandle)
        End If
        MyBase.DefWndProc(m)
    End Sub
    'Properly terminate child when the form is closed by the user
    Private Sub Form1_Closed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Closed
        SendMessage(proc.MainWindowHandle, WM_CLOSE, 0, 0)
    End Sub
End Class

Kind regards, Eric

  • Related