ntdll: Implement NtGetNextThread().

Largely based on a patch by Nikolay Sivov for NtGetNextProcess().

Signed-off-by: Paul Gofman <pgofman@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Paul Gofman 2021-04-28 18:21:21 +03:00 committed by Alexandre Julliard
parent 8f8fa9182a
commit 02e3327f06
8 changed files with 232 additions and 1 deletions

View File

@ -223,6 +223,7 @@
@ stdcall -norelay -syscall NtGetContextThread(long ptr)
@ stdcall -syscall NtGetCurrentProcessorNumber()
# @ stub NtGetDevicePowerState
@ stdcall -syscall NtGetNextThread(ptr ptr long long long ptr)
@ stdcall -syscall NtGetNlsSectionPtr(long long long ptr ptr)
@ stub NtGetPlugPlayEvent
@ stdcall NtGetTickCount()

View File

@ -78,6 +78,8 @@ static void (WINAPI *pRtlWakeAddressAll)( const void * );
static void (WINAPI *pRtlWakeAddressSingle)( const void * );
static NTSTATUS (WINAPI *pNtOpenProcess)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES *, const CLIENT_ID * );
static NTSTATUS (WINAPI *pNtCreateDebugObject)( HANDLE *, ACCESS_MASK, OBJECT_ATTRIBUTES *, ULONG );
static NTSTATUS (WINAPI *pNtGetNextThread)(HANDLE process, HANDLE thread, ACCESS_MASK access, ULONG attributes,
ULONG flags, HANDLE *handle);
#define KEYEDEVENT_WAIT 0x0001
#define KEYEDEVENT_WAKE 0x0002
@ -2564,6 +2566,95 @@ static void test_object_types(void)
}
}
static DWORD WINAPI test_get_next_thread_proc( void *arg )
{
HANDLE event = (HANDLE)arg;
WaitForSingleObject(event, INFINITE);
return 0;
}
static void test_get_next_thread(void)
{
HANDLE hprocess = GetCurrentProcess();
HANDLE handle, thread, event, prev;
NTSTATUS status;
DWORD thread_id;
BOOL found;
if (!pNtGetNextThread)
{
win_skip("NtGetNextThread is not available.\n");
return;
}
event = CreateEventA(NULL, FALSE, FALSE, NULL);
thread = CreateThread( NULL, 0, test_get_next_thread_proc, event, 0, &thread_id );
status = pNtGetNextThread(hprocess, NULL, THREAD_QUERY_LIMITED_INFORMATION, OBJ_INHERIT, 0, NULL);
ok(status == STATUS_ACCESS_VIOLATION, "Got unexected status %#x.\n", status);
found = FALSE;
prev = NULL;
while (!(status = pNtGetNextThread(hprocess, prev, THREAD_QUERY_LIMITED_INFORMATION, OBJ_INHERIT, 0, &handle)))
{
if (prev)
{
if (GetThreadId(handle) == thread_id)
found = TRUE;
pNtClose(prev);
}
else
{
ok(GetThreadId(handle) == GetCurrentThreadId(), "Got unexpected thread id %04x, current %04x.\n",
GetThreadId(handle), GetCurrentThreadId());
}
prev = handle;
handle = (HANDLE)0xdeadbeef;
}
pNtClose(prev);
ok(!handle, "Got unexpected handle %p.\n", handle);
ok(status == STATUS_NO_MORE_ENTRIES, "Unexpected status %#x.\n", status);
ok(found, "Thread not found.\n");
handle = (HANDLE)0xdeadbeef;
status = pNtGetNextThread((void *)0xdeadbeef, 0, PROCESS_QUERY_LIMITED_INFORMATION, OBJ_INHERIT, 0, &handle);
ok(status == STATUS_INVALID_HANDLE, "Unexpected status %#x.\n", status);
ok(!handle, "Got unexpected handle %p.\n", handle);
handle = (HANDLE)0xdeadbeef;
status = pNtGetNextThread(hprocess, (void *)0xdeadbeef, PROCESS_QUERY_LIMITED_INFORMATION, OBJ_INHERIT, 0, &handle);
ok(status == STATUS_INVALID_HANDLE, "Unexpected status %#x.\n", status);
ok(!handle, "Got unexpected handle %p.\n", handle);
/* Reversed search is only supported on recent enough Win10. */
status = pNtGetNextThread(hprocess, 0, PROCESS_QUERY_LIMITED_INFORMATION, OBJ_INHERIT, 1, &handle);
ok(!status || broken(status == STATUS_INVALID_PARAMETER), "Unexpected status %#x.\n", status);
if (!status)
pNtClose(handle);
status = pNtGetNextThread(hprocess, 0, PROCESS_QUERY_LIMITED_INFORMATION, OBJ_INHERIT, 2, &handle);
ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %#x.\n", status);
SetEvent(event);
WaitForSingleObject(thread, INFINITE);
found = FALSE;
prev = NULL;
while (!(status = pNtGetNextThread(hprocess, prev, THREAD_QUERY_LIMITED_INFORMATION, OBJ_INHERIT, 0, &handle)))
{
if (prev)
pNtClose(prev);
if (GetThreadId(handle) == thread_id)
found = TRUE;
prev = handle;
}
pNtClose(prev);
ok(found, "Thread not found.\n");
CloseHandle(thread);
}
START_TEST(om)
{
HMODULE hntdll = GetModuleHandleA("ntdll.dll");
@ -2615,6 +2706,7 @@ START_TEST(om)
pRtlWakeAddressSingle = (void *)GetProcAddress(hntdll, "RtlWakeAddressSingle");
pNtOpenProcess = (void *)GetProcAddress(hntdll, "NtOpenProcess");
pNtCreateDebugObject = (void *)GetProcAddress(hntdll, "NtCreateDebugObject");
pNtGetNextThread = (void *)GetProcAddress(hntdll, "NtGetNextThread");
test_case_sensitive();
test_namespace_pipe();
@ -2632,4 +2724,5 @@ START_TEST(om)
test_wait_on_address();
test_process();
test_object_types();
test_get_next_thread();
}

View File

@ -1382,3 +1382,31 @@ ULONG WINAPI NtGetCurrentProcessorNumber(void)
/* fallback to the first processor */
return 0;
}
/******************************************************************************
* NtGetNextThread (NTDLL.@)
*/
NTSTATUS WINAPI NtGetNextThread( HANDLE process, HANDLE thread, ACCESS_MASK access, ULONG attributes,
ULONG flags, HANDLE *handle )
{
HANDLE ret_handle = 0;
NTSTATUS ret;
TRACE( "process %p, thread %p, access %#x, attributes %#x, flags %#x, handle %p.\n",
process, thread, access, attributes, flags, handle );
SERVER_START_REQ( get_next_thread )
{
req->process = wine_server_obj_handle( process );
req->last = wine_server_obj_handle( thread );
req->access = access;
req->attributes = attributes;
req->flags = flags;
if (!(ret = wine_server_call( req ))) ret_handle = wine_server_ptr_handle( reply->handle );
}
SERVER_END_REQ;
*handle = ret_handle;
return ret;
}

View File

@ -5374,6 +5374,24 @@ struct resume_process_reply
};
struct get_next_thread_request
{
struct request_header __header;
obj_handle_t process;
obj_handle_t last;
unsigned int access;
unsigned int attributes;
unsigned int flags;
};
struct get_next_thread_reply
{
struct reply_header __header;
obj_handle_t handle;
char __pad_12[4];
};
enum request
{
REQ_new_process,
@ -5649,6 +5667,7 @@ enum request
REQ_terminate_job,
REQ_suspend_process,
REQ_resume_process,
REQ_get_next_thread,
REQ_NB_REQUESTS
};
@ -5929,6 +5948,7 @@ union generic_request
struct terminate_job_request terminate_job_request;
struct suspend_process_request suspend_process_request;
struct resume_process_request resume_process_request;
struct get_next_thread_request get_next_thread_request;
};
union generic_reply
{
@ -6207,11 +6227,12 @@ union generic_reply
struct terminate_job_reply terminate_job_reply;
struct suspend_process_reply suspend_process_reply;
struct resume_process_reply resume_process_reply;
struct get_next_thread_reply get_next_thread_reply;
};
/* ### protocol_version begin ### */
#define SERVER_PROTOCOL_VERSION 696
#define SERVER_PROTOCOL_VERSION 697
/* ### protocol_version end ### */

View File

@ -3687,3 +3687,15 @@ struct handle_info
@REQ(resume_process)
obj_handle_t handle; /* process handle */
@END
/* Iterate thread list for process */
@REQ(get_next_thread)
obj_handle_t process; /* process handle */
obj_handle_t last; /* thread handle to start with */
unsigned int access; /* desired access for returned handle */
unsigned int attributes; /* returned handle attributes */
unsigned int flags; /* controls iteration direction */
@REPLY
obj_handle_t handle; /* next thread handle */
@END

View File

@ -392,6 +392,7 @@ DECL_HANDLER(get_job_info);
DECL_HANDLER(terminate_job);
DECL_HANDLER(suspend_process);
DECL_HANDLER(resume_process);
DECL_HANDLER(get_next_thread);
#ifdef WANT_REQUEST_HANDLERS
@ -671,6 +672,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
(req_handler)req_terminate_job,
(req_handler)req_suspend_process,
(req_handler)req_resume_process,
(req_handler)req_get_next_thread,
};
C_ASSERT( sizeof(abstime_t) == 8 );
@ -2226,6 +2228,14 @@ C_ASSERT( FIELD_OFFSET(struct suspend_process_request, handle) == 12 );
C_ASSERT( sizeof(struct suspend_process_request) == 16 );
C_ASSERT( FIELD_OFFSET(struct resume_process_request, handle) == 12 );
C_ASSERT( sizeof(struct resume_process_request) == 16 );
C_ASSERT( FIELD_OFFSET(struct get_next_thread_request, process) == 12 );
C_ASSERT( FIELD_OFFSET(struct get_next_thread_request, last) == 16 );
C_ASSERT( FIELD_OFFSET(struct get_next_thread_request, access) == 20 );
C_ASSERT( FIELD_OFFSET(struct get_next_thread_request, attributes) == 24 );
C_ASSERT( FIELD_OFFSET(struct get_next_thread_request, flags) == 28 );
C_ASSERT( sizeof(struct get_next_thread_request) == 32 );
C_ASSERT( FIELD_OFFSET(struct get_next_thread_reply, handle) == 8 );
C_ASSERT( sizeof(struct get_next_thread_reply) == 16 );
#endif /* WANT_REQUEST_HANDLERS */

View File

@ -1925,3 +1925,52 @@ DECL_HANDLER(get_selector_entry)
release_object( thread );
}
}
/* Iterate thread list for process. Use global thread list to also
* return terminated but not yet destroyed threads. */
DECL_HANDLER(get_next_thread)
{
struct thread *thread;
struct process *process;
struct list *ptr;
if (req->flags > 1)
{
set_error( STATUS_INVALID_PARAMETER );
return;
}
if (!(process = get_process_from_handle( req->process, PROCESS_QUERY_INFORMATION )))
return;
if (!req->last)
{
ptr = req->flags ? list_tail( &thread_list ) : list_head( &thread_list );
}
else if ((thread = get_thread_from_handle( req->last, 0 )))
{
ptr = req->flags ? list_prev( &thread_list, &thread->entry )
: list_next( &thread_list, &thread->entry );
release_object( thread );
}
else
{
release_object( process );
return;
}
while (ptr)
{
thread = LIST_ENTRY( ptr, struct thread, entry );
if (thread->process == process)
{
reply->handle = alloc_handle( current->process, thread, req->access, req->attributes );
release_object( process );
return;
}
ptr = req->flags ? list_prev( &thread_list, &thread->entry )
: list_next( &thread_list, &thread->entry );
}
set_error( STATUS_NO_MORE_ENTRIES );
release_object( process );
}

View File

@ -4436,6 +4436,20 @@ static void dump_resume_process_request( const struct resume_process_request *re
fprintf( stderr, " handle=%04x", req->handle );
}
static void dump_get_next_thread_request( const struct get_next_thread_request *req )
{
fprintf( stderr, " process=%04x", req->process );
fprintf( stderr, ", last=%04x", req->last );
fprintf( stderr, ", access=%08x", req->access );
fprintf( stderr, ", attributes=%08x", req->attributes );
fprintf( stderr, ", flags=%08x", req->flags );
}
static void dump_get_next_thread_reply( const struct get_next_thread_reply *req )
{
fprintf( stderr, " handle=%04x", req->handle );
}
static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
(dump_func)dump_new_process_request,
(dump_func)dump_get_new_process_info_request,
@ -4710,6 +4724,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
(dump_func)dump_terminate_job_request,
(dump_func)dump_suspend_process_request,
(dump_func)dump_resume_process_request,
(dump_func)dump_get_next_thread_request,
};
static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
@ -4986,6 +5001,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
NULL,
NULL,
NULL,
(dump_func)dump_get_next_thread_reply,
};
static const char * const req_names[REQ_NB_REQUESTS] = {
@ -5262,6 +5278,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
"terminate_job",
"suspend_process",
"resume_process",
"get_next_thread",
};
static const struct