Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PyGetMemory, PyGetBufferAddressAndLen, and PyGetString cannot recursively retrieve program descendant windows #2465

Open
yjkt opened this issue Jan 19, 2025 · 0 comments

Comments

@yjkt
Copy link

yjkt commented Jan 19, 2025

Expected behavior and actual behavior

Expected behavior

Using SECTION 2, the program is normal.

The program can run to print('Finished enumerating child windows') location。

xxxxxxxxxxxxxxxxxxxxxxxxx
Finished enumerating child windows
x: [8392952]

Actual behavior

Using SECTION 1, The program did not run to x and exited directly.

Steps to reproduce the problem

  1. Run the 3D-tool program and open any 3D model file.
  2. Click the Model Info button to open a panel
    Image
  3. Running the following script, the code in Section1 cannot correctly obtain the Combox hwnd for Not assigned, and the program exits. But using code of Section2 can work normally
import win32gui
import win32api
import ctypes

# Define the required message constants
CB_GETCURSEL = 0x0147
CB_GETLBTEXT = 0x0148
CB_GETLBTEXTLEN = 0x0149

def get_combo_box_selected_value(hwnd):
    # Get the currently selected index
    index = win32api.SendMessage(hwnd, CB_GETCURSEL, 0, 0)
    
    if index == -1:
        print("No item selected.")
        return None
    
    # Get the length of the selected item's text
    text_length = win32api.SendMessage(hwnd, CB_GETLBTEXTLEN, index, 0)
    
    if text_length == -1:
        print("Failed to get text length.")
        return None
    
    # SECTION 1: Using PyGetMemory, PyGetBufferAddressAndLen, and PyGetString
    # Create a buffer to store the text
    length = text_length + 1
    buf = b'\0' * length
    win32gui.PyGetMemory(id(buf), length)
    win32api.SendMessage(hwnd, CB_GETLBTEXT, index, buf)
    addr, length = win32gui.PyGetBufferAddressAndLen(buf)
    text = win32gui.PyGetString(addr, length - 1)

    # SECTION 2: Using ctypes
    # Create a buffer to store the text
    # buffer = ctypes.create_unicode_buffer(text_length + 1)
    # win32api.SendMessage(hwnd, CB_GETLBTEXT, index, buffer)
    # text = buffer.value
    
    return text

def enum_child_windows_callback(hwnd, x):
    # Get the class name and title of the child window
    class_name = win32gui.GetClassName(hwnd)
    if class_name == 'TComboBox':
        window_text = get_combo_box_selected_value(hwnd)
        print(f"ComboBox text: {window_text}")
    else:
        window_text = win32gui.GetWindowText(hwnd)
        print(f"Window text: {window_text}")

    if class_name == 'TComboBox' and window_text == 'Not assigned':
        print('xxxxxxxxxxxxxxxxxxxxxxxxx')
        x.append(hwnd)
        return False

    return True  # Continue enumerating child windows

def enum_all_child_windows(parent_hwnd, x):
    win32gui.EnumChildWindows(parent_hwnd, enum_child_windows_callback, x)

if __name__ == "__main__":
    calculator_hwnd = win32gui.FindWindow(None, "3D-Tool")

    if calculator_hwnd:
        print(f"Found main window: Handle={hex(calculator_hwnd)}")
        x = []
        enum_all_child_windows(calculator_hwnd, x)
        print('Finished enumerating child windows')
        print('x:', x)
    else:
        print("Could not find the specified window.")

System information

Python version and distribution:
miniconda Python 3.12.8 x64 for window 11 pro, 24H2

pywin32 version:
pywin32 308

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant