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 -norelay -syscall NtGetContextThread(long ptr)
|
||||||
@ stdcall -syscall NtGetCurrentProcessorNumber()
|
@ stdcall -syscall NtGetCurrentProcessorNumber()
|
||||||
# @ stub NtGetDevicePowerState
|
# @ stub NtGetDevicePowerState
|
||||||
|
@ stdcall -syscall NtGetNextThread(ptr ptr long long long ptr)
|
||||||
@ stdcall -syscall NtGetNlsSectionPtr(long long long ptr ptr)
|
@ stdcall -syscall NtGetNlsSectionPtr(long long long ptr ptr)
|
||||||
@ stub NtGetPlugPlayEvent
|
@ stub NtGetPlugPlayEvent
|
||||||
@ stdcall NtGetTickCount()
|
@ stdcall NtGetTickCount()
|
||||||
|
|
|
@ -78,6 +78,8 @@ static void (WINAPI *pRtlWakeAddressAll)( const void * );
|
||||||
static void (WINAPI *pRtlWakeAddressSingle)( const void * );
|
static void (WINAPI *pRtlWakeAddressSingle)( const void * );
|
||||||
static NTSTATUS (WINAPI *pNtOpenProcess)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES *, const CLIENT_ID * );
|
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 *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_WAIT 0x0001
|
||||||
#define KEYEDEVENT_WAKE 0x0002
|
#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)
|
START_TEST(om)
|
||||||
{
|
{
|
||||||
HMODULE hntdll = GetModuleHandleA("ntdll.dll");
|
HMODULE hntdll = GetModuleHandleA("ntdll.dll");
|
||||||
|
@ -2615,6 +2706,7 @@ START_TEST(om)
|
||||||
pRtlWakeAddressSingle = (void *)GetProcAddress(hntdll, "RtlWakeAddressSingle");
|
pRtlWakeAddressSingle = (void *)GetProcAddress(hntdll, "RtlWakeAddressSingle");
|
||||||
pNtOpenProcess = (void *)GetProcAddress(hntdll, "NtOpenProcess");
|
pNtOpenProcess = (void *)GetProcAddress(hntdll, "NtOpenProcess");
|
||||||
pNtCreateDebugObject = (void *)GetProcAddress(hntdll, "NtCreateDebugObject");
|
pNtCreateDebugObject = (void *)GetProcAddress(hntdll, "NtCreateDebugObject");
|
||||||
|
pNtGetNextThread = (void *)GetProcAddress(hntdll, "NtGetNextThread");
|
||||||
|
|
||||||
test_case_sensitive();
|
test_case_sensitive();
|
||||||
test_namespace_pipe();
|
test_namespace_pipe();
|
||||||
|
@ -2632,4 +2724,5 @@ START_TEST(om)
|
||||||
test_wait_on_address();
|
test_wait_on_address();
|
||||||
test_process();
|
test_process();
|
||||||
test_object_types();
|
test_object_types();
|
||||||
|
test_get_next_thread();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1382,3 +1382,31 @@ ULONG WINAPI NtGetCurrentProcessorNumber(void)
|
||||||
/* fallback to the first processor */
|
/* fallback to the first processor */
|
||||||
return 0;
|
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
|
enum request
|
||||||
{
|
{
|
||||||
REQ_new_process,
|
REQ_new_process,
|
||||||
|
@ -5649,6 +5667,7 @@ enum request
|
||||||
REQ_terminate_job,
|
REQ_terminate_job,
|
||||||
REQ_suspend_process,
|
REQ_suspend_process,
|
||||||
REQ_resume_process,
|
REQ_resume_process,
|
||||||
|
REQ_get_next_thread,
|
||||||
REQ_NB_REQUESTS
|
REQ_NB_REQUESTS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5929,6 +5948,7 @@ union generic_request
|
||||||
struct terminate_job_request terminate_job_request;
|
struct terminate_job_request terminate_job_request;
|
||||||
struct suspend_process_request suspend_process_request;
|
struct suspend_process_request suspend_process_request;
|
||||||
struct resume_process_request resume_process_request;
|
struct resume_process_request resume_process_request;
|
||||||
|
struct get_next_thread_request get_next_thread_request;
|
||||||
};
|
};
|
||||||
union generic_reply
|
union generic_reply
|
||||||
{
|
{
|
||||||
|
@ -6207,11 +6227,12 @@ union generic_reply
|
||||||
struct terminate_job_reply terminate_job_reply;
|
struct terminate_job_reply terminate_job_reply;
|
||||||
struct suspend_process_reply suspend_process_reply;
|
struct suspend_process_reply suspend_process_reply;
|
||||||
struct resume_process_reply resume_process_reply;
|
struct resume_process_reply resume_process_reply;
|
||||||
|
struct get_next_thread_reply get_next_thread_reply;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ### protocol_version begin ### */
|
/* ### protocol_version begin ### */
|
||||||
|
|
||||||
#define SERVER_PROTOCOL_VERSION 696
|
#define SERVER_PROTOCOL_VERSION 697
|
||||||
|
|
||||||
/* ### protocol_version end ### */
|
/* ### protocol_version end ### */
|
||||||
|
|
||||||
|
|
|
@ -3687,3 +3687,15 @@ struct handle_info
|
||||||
@REQ(resume_process)
|
@REQ(resume_process)
|
||||||
obj_handle_t handle; /* process handle */
|
obj_handle_t handle; /* process handle */
|
||||||
@END
|
@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(terminate_job);
|
||||||
DECL_HANDLER(suspend_process);
|
DECL_HANDLER(suspend_process);
|
||||||
DECL_HANDLER(resume_process);
|
DECL_HANDLER(resume_process);
|
||||||
|
DECL_HANDLER(get_next_thread);
|
||||||
|
|
||||||
#ifdef WANT_REQUEST_HANDLERS
|
#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_terminate_job,
|
||||||
(req_handler)req_suspend_process,
|
(req_handler)req_suspend_process,
|
||||||
(req_handler)req_resume_process,
|
(req_handler)req_resume_process,
|
||||||
|
(req_handler)req_get_next_thread,
|
||||||
};
|
};
|
||||||
|
|
||||||
C_ASSERT( sizeof(abstime_t) == 8 );
|
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( sizeof(struct suspend_process_request) == 16 );
|
||||||
C_ASSERT( FIELD_OFFSET(struct resume_process_request, handle) == 12 );
|
C_ASSERT( FIELD_OFFSET(struct resume_process_request, handle) == 12 );
|
||||||
C_ASSERT( sizeof(struct resume_process_request) == 16 );
|
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 */
|
#endif /* WANT_REQUEST_HANDLERS */
|
||||||
|
|
||||||
|
|
|
@ -1925,3 +1925,52 @@ DECL_HANDLER(get_selector_entry)
|
||||||
release_object( thread );
|
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 );
|
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] = {
|
static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
|
||||||
(dump_func)dump_new_process_request,
|
(dump_func)dump_new_process_request,
|
||||||
(dump_func)dump_get_new_process_info_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_terminate_job_request,
|
||||||
(dump_func)dump_suspend_process_request,
|
(dump_func)dump_suspend_process_request,
|
||||||
(dump_func)dump_resume_process_request,
|
(dump_func)dump_resume_process_request,
|
||||||
|
(dump_func)dump_get_next_thread_request,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
|
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,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
|
(dump_func)dump_get_next_thread_reply,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char * const req_names[REQ_NB_REQUESTS] = {
|
static const char * const req_names[REQ_NB_REQUESTS] = {
|
||||||
|
@ -5262,6 +5278,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
|
||||||
"terminate_job",
|
"terminate_job",
|
||||||
"suspend_process",
|
"suspend_process",
|
||||||
"resume_process",
|
"resume_process",
|
||||||
|
"get_next_thread",
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct
|
static const struct
|
||||||
|
|
Loading…
Reference in New Issue