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:
parent
8f8fa9182a
commit
02e3327f06
|
@ -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()
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 ### */
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue