From 8a7ea75b0b00fa251ee7eda99a433d6c6e11d7f5 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard <julliard@winehq.org> Date: Fri, 22 Nov 2024 12:46:44 +0100 Subject: [PATCH] server: Add a new request to find sibling windows by class name. Use it to reimplement NtUserFindWindowEx(). --- dlls/win32u/window.c | 53 ++++++++++++++--------- include/wine/server_protocol.h | 23 +++++++++- server/protocol.def | 12 ++++++ server/request_handlers.h | 8 ++++ server/request_trace.h | 17 ++++++++ server/window.c | 77 ++++++++++++++++++++++++++++------ 6 files changed, 156 insertions(+), 34 deletions(-) diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index d0de4f2eab1..032ab96ad5c 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -3017,46 +3017,59 @@ NTSTATUS WINAPI NtUserBuildHwndList( HDESK desktop, HWND hwnd, BOOL children, BO HWND WINAPI NtUserFindWindowEx( HWND parent, HWND child, UNICODE_STRING *class, UNICODE_STRING *title, ULONG unk ) { - HWND *list; + user_handle_t *list; HWND retvalue = 0; - int i = 0, len = 0, title_len; - WCHAR *buffer = NULL; + int i = 0, size = 128, title_len; + ATOM atom = class ? get_int_atom_value( class ) : 0; + NTSTATUS status; - if (!parent && child) parent = get_desktop_window(); - else if (parent == HWND_MESSAGE) parent = get_hwnd_message_parent(); + /* empty class is not the same as NULL class */ + if (!atom && class && !class->Length) return 0; - if (title) + if (parent == HWND_MESSAGE) parent = get_hwnd_message_parent(); + + for (;;) { - len = title->Length / sizeof(WCHAR) + 1; /* one extra char to check for chars beyond the end */ - if (!(buffer = malloc( (len + 1) * sizeof(WCHAR) ))) return 0; - } + if (!(list = malloc( size * sizeof(*list) ))) return 0; - if (!(list = list_window_children( 0, parent, class, 0 ))) goto done; + SERVER_START_REQ( get_class_windows ) + { + req->parent = wine_server_user_handle( parent ); + req->child = wine_server_user_handle( child ); + req->atom = atom; + if (!atom && class) wine_server_add_data( req, class->Buffer, class->Length ); + wine_server_set_reply( req, list, size * sizeof(user_handle_t) ); + status = wine_server_call( req ); + size = reply->count; + } + SERVER_END_REQ; - if (child) - { - child = get_full_window_handle( child ); - while (list[i] && list[i] != child) i++; - if (!list[i]) goto done; - i++; /* start from next window */ + if (!status && size) break; + free( list ); + if (status != STATUS_BUFFER_TOO_SMALL) return 0; } if (title) { - while (list[i]) + int len = title->Length / sizeof(WCHAR) + 1; /* one extra char to check for chars beyond the end */ + WCHAR *buffer = malloc( (len + 1) * sizeof(WCHAR) ); + + if (!buffer) goto done; + while (i < size) { - title_len = NtUserInternalGetWindowText( list[i], buffer, len + 1 ); + title_len = NtUserInternalGetWindowText( wine_server_ptr_handle(list[i]), buffer, len + 1 ); if (title_len * sizeof(WCHAR) == title->Length && (!title_len || !wcsnicmp( buffer, title->Buffer, title_len ))) break; i++; } + free( buffer ); } - retvalue = list[i]; + + if (i < size) retvalue = wine_server_ptr_handle(list[i]); done: free( list ); - free( buffer ); return retvalue; } diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index f0b9571aaed..3c53a5dd0df 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -3498,6 +3498,24 @@ struct get_window_list_reply +struct get_class_windows_request +{ + struct request_header __header; + user_handle_t parent; + user_handle_t child; + atom_t atom; + /* VARARG(class,unicode_str); */ +}; +struct get_class_windows_reply +{ + struct reply_header __header; + int count; + /* VARARG(children,user_handles); */ + char __pad_12[4]; +}; + + + struct get_window_children_request { struct request_header __header; @@ -6023,6 +6041,7 @@ enum request REQ_set_parent, REQ_get_window_parents, REQ_get_window_list, + REQ_get_class_windows, REQ_get_window_children, REQ_get_window_children_from_point, REQ_get_window_tree, @@ -6321,6 +6340,7 @@ union generic_request struct set_parent_request set_parent_request; struct get_window_parents_request get_window_parents_request; struct get_window_list_request get_window_list_request; + struct get_class_windows_request get_class_windows_request; struct get_window_children_request get_window_children_request; struct get_window_children_from_point_request get_window_children_from_point_request; struct get_window_tree_request get_window_tree_request; @@ -6617,6 +6637,7 @@ union generic_reply struct set_parent_reply set_parent_reply; struct get_window_parents_reply get_window_parents_reply; struct get_window_list_reply get_window_list_reply; + struct get_class_windows_reply get_class_windows_reply; struct get_window_children_reply get_window_children_reply; struct get_window_children_from_point_reply get_window_children_from_point_reply; struct get_window_tree_reply get_window_tree_reply; @@ -6760,6 +6781,6 @@ union generic_reply struct set_keyboard_repeat_reply set_keyboard_repeat_reply; }; -#define SERVER_PROTOCOL_VERSION 850 +#define SERVER_PROTOCOL_VERSION 851 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/protocol.def b/server/protocol.def index 8082ad0f7cd..d564d1a3045 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -2583,6 +2583,18 @@ enum message_type @END +/* Get a list of the window siblings of a specified class */ +@REQ(get_class_windows) + user_handle_t parent; /* parent window */ + user_handle_t child; /* first child window */ + atom_t atom; /* class atom for the listed siblings */ + VARARG(class,unicode_str); /* class name */ +@REPLY + int count; /* total count of siblings */ + VARARG(children,user_handles); /* siblings handles */ +@END + + /* Get a list of the window children */ @REQ(get_window_children) obj_handle_t desktop; /* handle to desktop */ diff --git a/server/request_handlers.h b/server/request_handlers.h index 5f2a5518fe7..c254743a980 100644 --- a/server/request_handlers.h +++ b/server/request_handlers.h @@ -157,6 +157,7 @@ DECL_HANDLER(set_window_info); DECL_HANDLER(set_parent); DECL_HANDLER(get_window_parents); DECL_HANDLER(get_window_list); +DECL_HANDLER(get_class_windows); DECL_HANDLER(get_window_children); DECL_HANDLER(get_window_children_from_point); DECL_HANDLER(get_window_tree); @@ -452,6 +453,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_set_parent, (req_handler)req_get_window_parents, (req_handler)req_get_window_list, + (req_handler)req_get_class_windows, (req_handler)req_get_window_children, (req_handler)req_get_window_children_from_point, (req_handler)req_get_window_tree, @@ -1503,6 +1505,12 @@ C_ASSERT( offsetof(struct get_window_list_request, children) == 24 ); C_ASSERT( sizeof(struct get_window_list_request) == 32 ); C_ASSERT( offsetof(struct get_window_list_reply, count) == 8 ); C_ASSERT( sizeof(struct get_window_list_reply) == 16 ); +C_ASSERT( offsetof(struct get_class_windows_request, parent) == 12 ); +C_ASSERT( offsetof(struct get_class_windows_request, child) == 16 ); +C_ASSERT( offsetof(struct get_class_windows_request, atom) == 20 ); +C_ASSERT( sizeof(struct get_class_windows_request) == 24 ); +C_ASSERT( offsetof(struct get_class_windows_reply, count) == 8 ); +C_ASSERT( sizeof(struct get_class_windows_reply) == 16 ); C_ASSERT( offsetof(struct get_window_children_request, desktop) == 12 ); C_ASSERT( offsetof(struct get_window_children_request, parent) == 16 ); C_ASSERT( offsetof(struct get_window_children_request, atom) == 20 ); diff --git a/server/request_trace.h b/server/request_trace.h index 76adb66cec8..be545cd290b 100644 --- a/server/request_trace.h +++ b/server/request_trace.h @@ -1776,6 +1776,20 @@ static void dump_get_window_list_reply( const struct get_window_list_reply *req dump_varargs_user_handles( ", windows=", cur_size ); } +static void dump_get_class_windows_request( const struct get_class_windows_request *req ) +{ + fprintf( stderr, " parent=%08x", req->parent ); + fprintf( stderr, ", child=%08x", req->child ); + fprintf( stderr, ", atom=%04x", req->atom ); + dump_varargs_unicode_str( ", class=", cur_size ); +} + +static void dump_get_class_windows_reply( const struct get_class_windows_reply *req ) +{ + fprintf( stderr, " count=%d", req->count ); + dump_varargs_user_handles( ", children=", cur_size ); +} + static void dump_get_window_children_request( const struct get_window_children_request *req ) { fprintf( stderr, " desktop=%04x", req->desktop ); @@ -3489,6 +3503,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = (dump_func)dump_set_parent_request, (dump_func)dump_get_window_parents_request, (dump_func)dump_get_window_list_request, + (dump_func)dump_get_class_windows_request, (dump_func)dump_get_window_children_request, (dump_func)dump_get_window_children_from_point_request, (dump_func)dump_get_window_tree_request, @@ -3784,6 +3799,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = (dump_func)dump_set_parent_reply, (dump_func)dump_get_window_parents_reply, (dump_func)dump_get_window_list_reply, + (dump_func)dump_get_class_windows_reply, (dump_func)dump_get_window_children_reply, (dump_func)dump_get_window_children_from_point_reply, (dump_func)dump_get_window_tree_reply, @@ -4079,6 +4095,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = "set_parent", "get_window_parents", "get_window_list", + "get_class_windows", "get_window_children", "get_window_children_from_point", "get_window_tree", diff --git a/server/window.c b/server/window.c index 1ddf47c5bfd..07a35552cea 100644 --- a/server/window.c +++ b/server/window.c @@ -905,10 +905,11 @@ static int is_point_in_window( struct window *win, int *x, int *y, unsigned int } /* helper for get_window_list */ -static void append_window_to_list( struct window *win, struct thread *thread, user_handle_t *handles, - unsigned int *count, unsigned int max_count ) +static void append_window_to_list( struct window *win, struct thread *thread, atom_t atom, + user_handle_t *handles, unsigned int *count, unsigned int max_count ) { if (thread && win->thread != thread) return; + if (atom && get_class_atom( win->class ) != atom) return; if (*count < max_count) handles[*count] = win->handle; (*count)++; } @@ -918,7 +919,6 @@ static void get_window_list( struct desktop *desktop, struct window *win, struct int children, user_handle_t *handles, unsigned int *count, unsigned int max_count ) { - struct list *ptr; struct window *child; if (desktop) /* top-level windows of specified desktop */ @@ -926,35 +926,32 @@ static void get_window_list( struct desktop *desktop, struct window *win, struct if (children) return; if (!desktop->top_window) return; LIST_FOR_EACH_ENTRY( child, &desktop->top_window->children, struct window, entry ) - append_window_to_list( child, thread, handles, count, max_count ); + append_window_to_list( child, thread, 0, handles, count, max_count ); } else if (!win) /* top-level windows of current desktop */ { if (!(win = get_desktop_window( current ))) return; LIST_FOR_EACH_ENTRY( child, &win->children, struct window, entry ) - append_window_to_list( child, thread, handles, count, max_count ); + append_window_to_list( child, thread, 0, handles, count, max_count ); } else if (children) /* children (recursively) of specified window */ { LIST_FOR_EACH_ENTRY( child, &win->children, struct window, entry ) { - append_window_to_list( child, thread, handles, count, max_count ); + append_window_to_list( child, thread, 0, handles, count, max_count ); get_window_list( NULL, child, thread, TRUE, handles, count, max_count ); } } else if (!is_desktop_window( win )) /* siblings starting from specified window */ { - for (ptr = &win->entry; ptr; ptr = list_next( &win->parent->children, ptr )) - { - struct window *sibling = LIST_ENTRY( ptr, struct window, entry ); - append_window_to_list( sibling, thread, handles, count, max_count ); - } + for (child = win; child; child = get_next_window( child )) + append_window_to_list( child, thread, 0, handles, count, max_count ); } else /* desktop window siblings */ { - append_window_to_list( win, thread, handles, count, max_count ); + append_window_to_list( win, thread, 0, handles, count, max_count ); if (win == win->desktop->top_window && win->desktop->msg_window) - append_window_to_list( win->desktop->msg_window, thread, handles, count, max_count ); + append_window_to_list( win->desktop->msg_window, thread, 0, handles, count, max_count ); } } @@ -2477,6 +2474,60 @@ DECL_HANDLER(get_window_list) } +/* get a list of the window siblings of a specified class */ +DECL_HANDLER(get_class_windows) +{ + struct desktop *desktop = NULL; + struct window *parent = NULL, *win = NULL; + struct unicode_str cls_name = get_req_unicode_str(); + atom_t atom = req->atom; + user_handle_t *data; + unsigned int count = 0, max_count = get_reply_max_size() / sizeof(*data); + + if (cls_name.len && !(atom = find_global_atom( NULL, &cls_name ))) return; + if (req->parent && !(parent = get_window( req->parent ))) return; + + if (req->child) + { + if (!parent) parent = get_desktop_window( current ); + if (!(win = get_window( req->child ))) return; + if (win->parent != parent) return; + if (!(win = get_next_window( win ))) return; + } + else if (parent && !(win = get_first_child( parent ))) return; + + if (!win && !(desktop = get_thread_desktop( current, 0 ))) return; + + max_count = min( max_count, MAX_USER_HANDLES ); + if ((data = mem_alloc( max_count * sizeof(*data) ))) + { + if (desktop) /* top-level and message windows of current desktop */ + { + if (desktop->top_window) + for (win = get_first_child( desktop->top_window ); win; win = get_next_window( win )) + append_window_to_list( win, NULL, atom, data, &count, max_count ); + if (desktop->msg_window) + for (win = get_first_child( desktop->msg_window ); win; win = get_next_window( win )) + append_window_to_list( win, NULL, atom, data, &count, max_count ); + } + else + { + for ( ; win; win = get_next_window( win )) + append_window_to_list( win, NULL, atom, data, &count, max_count ); + } + if (count > max_count) + { + free( data ); + set_error( STATUS_BUFFER_TOO_SMALL ); + } + else set_reply_data_ptr( data, count * sizeof(*data) ); + reply->count = count; + } + + if (desktop) release_object( desktop ); +} + + /* get a list of the window children */ DECL_HANDLER(get_window_children) { -- GitLab