如何通过全局屏幕坐标获取Win32控件的HWND?
问题背景
我要给Windows应用自动化项目实现类似Spy的功能。应用A(C#或C编写)需要自动化现有Win32应用B(非WPF/OpenGL应用),B里有个需要捕获的picture-box,它的运行时精确尺寸和位置未知,由B动态创建调整,但可以确定某个全局屏幕坐标(x,y)始终在这个控件范围内。
核心问题
我能通过user32.dll的FindWindow获取B的主窗口句柄,但B内picture-box的HWND对A来说是未知的。当B最大化且处于前台时,能不能通过这个全局屏幕坐标(x,y)获取到该picture-box的HWND?
尝试过的方案及问题
我试过用user32.dll的WindowFromPoint函数,但没成功,代码如下:
void* window_hwnd = FindWindow(null, "<title of the window>"); SendMessage(window_hwnd, WM_SYSCOMMAND, SC_RESTORE, 0); ShowWindow(window_hwnd, SW_MAXIMIZE); SetForegroundWindow(window_hwnd); SetActiveWindow(window_hwnd); SetWindowPos(window_hwnd, null, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); RedrawWindow(window_hwnd, null, null, RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN); void* control_hwnd = WindowFromPoint(300, 300);
理论上这段代码应该返回坐标(300,300)处控件的HWND,但实际control_hwnd和window_hwnd的值完全相同。
解决办法
1. 检查坐标是否正确
WindowFromPoint接收的是全局屏幕坐标,如果你写的(300,300)是相对于主窗口客户区的坐标,得先转成屏幕坐标:
POINT clientPoint = {300, 300}; ClientToScreen(window_hwnd, &clientPoint); HWND control_hwnd = WindowFromPoint(clientPoint);
2. 用ChildWindowFromPointEx精准查找子控件
这个函数专门用来在父窗口内定位子控件,还能跳过不可见的窗口,比WindowFromPoint更适合找子控件:
// 先把屏幕坐标转成主窗口客户区的相对坐标 POINT screenPoint = {300, 300}; ScreenToClient(window_hwnd, &screenPoint); // 查找所有可见子窗口,返回最顶层的那个 HWND control_hwnd = ChildWindowFromPointEx(window_hwnd, screenPoint, CWP_ALL | CWP_SKIPINVISIBLE);
3. 枚举子控件筛选
如果目标picture-box是标准Win32静态控件(类名是Static),可以枚举主窗口的所有子控件,结合位置判断:
BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam) { char className[256]; GetClassNameA(hwnd, className, sizeof(className)); // 匹配标准picture-box的类名 if (strcmp(className, "Static") == 0) { RECT rect; GetWindowRect(hwnd, &rect); // 检查目标坐标是否在控件范围内 if (PtInRect(&rect, (POINT){300, 300})) { *(HWND*)lParam = hwnd; return FALSE; // 找到就停止枚举 } } return TRUE; } HWND targetHwnd = NULL; EnumChildWindows(window_hwnd, EnumChildProc, (LPARAM)&targetHwnd); // targetHwnd就是目标控件句柄(找到的话)
4. 等待窗口稳定
代码里先还原再最大化,可能窗口还没完成布局就执行了WindowFromPoint,可以加个短延时确保控件加载到位:
ShowWindow(window_hwnd, SW_MAXIMIZE); UpdateWindow(window_hwnd); Sleep(100); // 延时时间可根据实际情况调整
内容的提问来源于stack exchange,提问作者unknown6656
相关产品推荐
相关产品推荐

