Enumerating Child Windows

NachitoMax 411 Reputation points
2021-02-09T16:02:25.01+00:00

Hi

Im struggling to enumerate through the child windows of the parent application, i have looked but cant find anything that doesnt give me 'PinvokeStackImbalance' issue. Looking up the issue, i couldnt find an answer that i understood....

Here is some code i have tried, both give me the same error-

Public Class WindowHandleInfo

    Public Const LB_SETTABSTOPS = &H192
    Public Const MAX_PATH = 260

    Declare Function EnumWindows Lib "user32" (ByVal lpEnumFunc As Long,
                                               ByVal lParam As Long) As Long

    Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hwnd As Long,
                                                                      ByVal lpClassName As String,
                                                                      ByVal nMaxCount As Long) As Long

    Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long,
                                                                        ByVal lpString As String,
                                                                        ByVal cch As Long) As Long

    Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long,
                                                            ByVal lpdwProcessId As Long) As Long

    Declare Function SendMessageArray Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long,
                                                                         ByVal wMsg As Long,
                                                                         ByVal wParam As Long,
                                                                         lParam As Integer) As Long

    Public Function fEnumWindowsCallBack(ByVal hwnd As Long, ByVal lpData As Long) As Long

        Dim lResult As Long = 0
        Dim lThreadId As Long = 0
        Dim lProcessId As Long = 0
        Dim sWndName As String = String.Empty
        Dim sClassName As String = String.Empty

        fEnumWindowsCallBack = 1
        sClassName = Space$(MAX_PATH)
        sWndName = Space$(MAX_PATH)

        lResult = GetClassName(hwnd, sClassName, MAX_PATH)
        sClassName = Left$(sClassName, lResult)
        lResult = GetWindowText(hwnd, sWndName, MAX_PATH)
        sWndName = Left$(sWndName, lResult)

        lThreadId = GetWindowThreadProcessId(hwnd, lProcessId)

        Debug.Print(CStr(hwnd) &
                    vbTab & sClassName &
                    vbTab & CStr(lProcessId) &
                    vbTab & CStr(lThreadId) &
                    vbTab & sWndName)
    End Function

    Public Function fEnumWindows() As Boolean
        Dim hwnd As Long
        Call EnumWindows(AddressOf fEnumWindowsCallBack, hwnd)
    End Function

End Class


    Const GW_CHILD As Integer = 5
    Const GW_HWNDNEXT As Integer = 2

    Declare Function GetWindow Lib "user32" (ByVal hwnd As IntPtr, ByVal wCmd As Long) As IntPtr

Public Function GetWindows(ByVal ParentWindowHandle As IntPtr) As IntPtr()

        Dim ptrChild As IntPtr
        Dim ptrRet() As IntPtr
        Dim iCounter As Integer

        'get first child handle...
        ptrChild = GetWindow(ParentWindowHandle, GW_CHILD)

        'loop through and collect all child window handles...
        Do Until ptrChild.Equals(IntPtr.Zero)
            'process child...
            ReDim Preserve ptrRet(iCounter)
            ptrRet(iCounter) = ptrChild
            'get next child...
            ptrChild = GetWindow(ptrChild, GW_HWNDNEXT)
            iCounter += 1
        Loop

        'return...
        Return ptrRet

    End Function

is there a relatively straight forward way of enumerating the child windows? My thought process is-

Get the HWND of the parent application
Enumerate the windows to find the child windows by checking its parent Process id
if its a child, add it to a collection (or process it indoividually)
process the collection (move them all to the middle of the screen)

i'll keep looking but if anyone has any pointers, links or code sample, it would be much appreciated

Thanks

.NET
.NET
Microsoft Technologies based on the .NET software framework.
3,459 questions
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Castorix31 82,116 Reputation points
    2021-02-09T16:35:58.72+00:00

    Test with EnumChildWindows =>
    (add a button for the click)

    public partial class Form1 : Form  
    {        
        public delegate bool EnumChildCallback(IntPtr hwnd, ref IntPtr lParam);  
      
        [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)]  
        public static extern bool EnumChildWindows(IntPtr hWndParent, EnumChildCallback lpEnumFunc, ref IntPtr lParam);  
      
        [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)]  
        public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);  
      
        [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)]  
        public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);  
      
      
        public Form1()  
        {  
            InitializeComponent();  
        }  
      
        private void Form1_Load(object sender, EventArgs e)  
        {  
      
        }  
      
        private void button1_Click(object sender, EventArgs e)  
        {  
            EnumChildCallback ecb = new EnumChildCallback(EnumChildProc);  
            IntPtr pPtr = IntPtr.Zero;  
            EnumChildWindows(this.Handle, ecb, ref pPtr);  
        }  
      
        public static bool EnumChildProc(IntPtr hwndChild, ref IntPtr lParam)  
        {  
            StringBuilder sbBufferText = new StringBuilder(260);  
            GetClassName(hwndChild, sbBufferText, sbBufferText.Capacity);        
            StringBuilder sbBufferText2 = new StringBuilder(260);  
            GetWindowText(hwndChild, sbBufferText2, sbBufferText2.Capacity);  
            Console.WriteLine("Window : {0:x}", hwndChild.ToInt32());  
            Console.WriteLine("\tClass : {0}", sbBufferText.ToString());  
            Console.WriteLine("\tText : {0}", sbBufferText2.ToString());  
            return true;  
        }  
    }