Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/kernel32/tests/thread.c | 132 +++++++++++++++++++++++++++++++++++ include/winternl.h | 9 ++- 2 files changed, 140 insertions(+), 1 deletion(-)
diff --git a/dlls/kernel32/tests/thread.c b/dlls/kernel32/tests/thread.c index ddbbec93ad..180eed8241 100644 --- a/dlls/kernel32/tests/thread.c +++ b/dlls/kernel32/tests/thread.c @@ -105,6 +105,8 @@ static NTSTATUS (WINAPI *pNtQueryInformationThread)(HANDLE,THREADINFOCLASS,PVOID static BOOL (WINAPI *pGetThreadGroupAffinity)(HANDLE,GROUP_AFFINITY*); static BOOL (WINAPI *pSetThreadGroupAffinity)(HANDLE,const GROUP_AFFINITY*,GROUP_AFFINITY*); static NTSTATUS (WINAPI *pNtSetInformationThread)(HANDLE,THREADINFOCLASS,LPCVOID,ULONG); +static HRESULT (WINAPI *pSetThreadDescription)(HANDLE,const WCHAR *); +static HRESULT (WINAPI *pGetThreadDescription)(HANDLE,WCHAR **);
static HANDLE create_target_process(const char *arg) { @@ -2111,6 +2113,133 @@ todo_wine CloseHandle(thread); }
+static void test_thread_description(void) +{ + THREAD_DESCRIPTION_INFORMATION *thread_desc; + static const WCHAR *desc = L"thread_desc"; + ULONG len, len2, desc_len; + NTSTATUS status; + char buff[128]; + WCHAR *ptr; + HRESULT hr; + + if (!pGetThreadDescription) + { + skip("Thread description API is not supported.\n"); + return; + } + + desc_len = lstrlenW(desc) * sizeof(*desc); + thread_desc = (THREAD_DESCRIPTION_INFORMATION *)buff; + + /* Initial description. */ + ptr = NULL; + hr = pGetThreadDescription(GetCurrentThread(), &ptr); + ok(hr == HRESULT_FROM_NT(STATUS_SUCCESS), "Failed to get thread description, hr %#x.\n", hr); + ok(!lstrcmpW(ptr, L""), "Unexpected description %s.\n", wine_dbgstr_w(ptr)); + LocalFree(ptr); + + len = 0; + status = pNtQueryInformationThread(GetCurrentThread(), ThreadDescription, NULL, 0, &len); + ok(status == STATUS_BUFFER_TOO_SMALL, "Unexpected status %#x.\n", status); + ok(len == sizeof(*thread_desc), "Unexpected structure length %u.\n", len); + + len2 = 0; + thread_desc->Length = 1; + thread_desc->Description = (WCHAR *)thread_desc; + status = pNtQueryInformationThread(GetCurrentThread(), ThreadDescription, thread_desc, len, &len2); + ok(!status, "Failed to get thread info, status %#x.\n", status); + ok(len2 == sizeof(*thread_desc), "Unexpected structure length %u.\n", len); + ok(!thread_desc->Length, "Unexpected description length %#x.\n", thread_desc->Length); + ok(thread_desc->Description == (WCHAR *)(thread_desc + 1), "Unexpected description string pointer %p, %p.\n", + thread_desc->Description, thread_desc); + + hr = pSetThreadDescription(GetCurrentThread(), NULL); + ok(hr == HRESULT_FROM_NT(STATUS_SUCCESS), "Failed to set thread description, hr %#x.\n", hr); + + hr = pSetThreadDescription(GetCurrentThread(), desc); + ok(hr == HRESULT_FROM_NT(STATUS_SUCCESS), "Failed to set thread description, hr %#x.\n", hr); + + ptr = NULL; + hr = pGetThreadDescription(GetCurrentThread(), &ptr); + ok(hr == HRESULT_FROM_NT(STATUS_SUCCESS), "Failed to get thread description, hr %#x.\n", hr); + ok(!lstrcmpW(ptr, desc), "Unexpected description %s.\n", wine_dbgstr_w(ptr)); + LocalFree(ptr); + + len = 0; + status = pNtQueryInformationThread(GetCurrentThread(), ThreadDescription, NULL, 0, &len); + ok(status == STATUS_BUFFER_TOO_SMALL, "Failed to get thread info, status %#x.\n", status); + ok(len == sizeof(*thread_desc) + desc_len, "Unexpected structure length %u.\n", len); + + len = 0; + status = pNtQueryInformationThread(GetCurrentThread(), ThreadDescription, buff, sizeof(buff), &len); + ok(!status, "Failed to get thread info.\n"); + ok(len == sizeof(*thread_desc) + desc_len, "Unexpected structure length %u.\n", len); + + ok(thread_desc->Length == (desc_len << 16 | desc_len), "Unexpected description length %#x.\n", + thread_desc->Length); + ok(thread_desc->Description == (WCHAR *)(thread_desc + 1), "Unexpected description string pointer %p, %p.\n", + thread_desc->Description, thread_desc); + ok(!memcmp(thread_desc->Description, desc, desc_len), "Unexpected description string.\n"); + + /* Partial results. */ + len = 0; + status = pNtQueryInformationThread(GetCurrentThread(), ThreadDescription, NULL, 0, &len); + ok(status == STATUS_BUFFER_TOO_SMALL, "Unexpected status %#x.\n", status); + ok(len == sizeof(*thread_desc) + desc_len, "Unexpected structure length %u.\n", len); + + status = pNtQueryInformationThread(GetCurrentThread(), ThreadDescription, buff, len - sizeof(WCHAR), &len); + ok(status == STATUS_BUFFER_TOO_SMALL, "Unexpected status %#x.\n", status); + ok(len == sizeof(*thread_desc) + desc_len, "Unexpected structure length %u.\n", len); + + /* Change description. */ + thread_desc->Length = 8 << 16 | 8; + lstrcpyW((WCHAR *)(thread_desc + 1), L"desc"); + + status = pNtSetInformationThread(GetCurrentThread(), ThreadDescription, thread_desc, sizeof(*thread_desc)); + ok(status == STATUS_SUCCESS, "Failed to set thread description, status %#x.\n", status); + + ptr = NULL; + hr = pGetThreadDescription(GetCurrentThread(), &ptr); + ok(hr == HRESULT_FROM_NT(STATUS_SUCCESS), "Failed to get thread description, hr %#x.\n", hr); + ok(!lstrcmpW(ptr, L"desc"), "Unexpected description %s.\n", wine_dbgstr_w(ptr)); + LocalFree(ptr); + + status = pNtSetInformationThread(GetCurrentThread(), ThreadDescription, thread_desc, sizeof(*thread_desc) - 1); + ok(status == STATUS_INFO_LENGTH_MISMATCH, "Unexpected status %#x.\n", status); + + status = NtSetInformationThread(GetCurrentThread(), ThreadDescription, NULL, sizeof(*thread_desc)); + ok(status == STATUS_ACCESS_VIOLATION, "Unexpected status %#x.\n", status); + + thread_desc->Description = NULL; + status = pNtSetInformationThread(GetCurrentThread(), ThreadDescription, thread_desc, sizeof(*thread_desc)); + ok(status == STATUS_ACCESS_VIOLATION, "Unexpected status %#x.\n", status); + + hr = pSetThreadDescription(GetCurrentThread(), NULL); + ok(hr == HRESULT_FROM_NT(STATUS_SUCCESS), "Failed to set thread description, hr %#x.\n", hr); + + ptr = NULL; + hr = pGetThreadDescription(GetCurrentThread(), &ptr); + ok(hr == HRESULT_FROM_NT(STATUS_SUCCESS), "Failed to get thread description, hr %#x.\n", hr); + ok(!lstrcmpW(ptr, L""), "Unexpected description %s.\n", wine_dbgstr_w(ptr)); + LocalFree(ptr); + + /* Set with 0 length/NULL pointer. */ + hr = pSetThreadDescription(GetCurrentThread(), L"123"); + ok(hr == HRESULT_FROM_NT(STATUS_SUCCESS), "Failed to set thread description, hr %#x.\n", hr); + + thread_desc->Length = 0; + thread_desc->Description = NULL; + status = pNtSetInformationThread(GetCurrentThread(), ThreadDescription, thread_desc, sizeof(*thread_desc)); + ok(!status, "Failed to set thread description, status %#x.\n", status); + + ptr = NULL; + hr = pGetThreadDescription(GetCurrentThread(), &ptr); + ok(hr == HRESULT_FROM_NT(STATUS_SUCCESS), "Failed to get thread description, hr %#x.\n", hr); + ok(!lstrcmpW(ptr, L""), "Unexpected description %s.\n", wine_dbgstr_w(ptr)); + LocalFree(ptr); +} + static void init_funcs(void) { HMODULE hKernel32 = GetModuleHandleA("kernel32.dll"); @@ -2147,6 +2276,8 @@ static void init_funcs(void)
X(GetThreadGroupAffinity); X(SetThreadGroupAffinity); + X(SetThreadDescription); + X(GetThreadDescription);
X(FlsAlloc); X(FlsFree); @@ -2223,6 +2354,7 @@ START_TEST(thread) test_ThreadErrorMode(); test_thread_fpu_cw(); test_thread_actctx(); + test_thread_description();
test_threadpool(); } diff --git a/include/winternl.h b/include/winternl.h index 8b895a9392..d5c8d76ccf 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -974,7 +974,7 @@ typedef enum _SYSTEM_INFORMATION_CLASS { } SYSTEM_INFORMATION_CLASS, *PSYSTEM_INFORMATION_CLASS;
typedef enum _THREADINFOCLASS { - ThreadBasicInformation, + ThreadBasicInformation = 0, ThreadTimes, ThreadPriority, ThreadBasePriority, @@ -1008,6 +1008,7 @@ typedef enum _THREADINFOCLASS { ThreadUmsInformation, ThreadCounterProfiling, ThreadIdealProcessorEx, + ThreadDescription = 38, MaxThreadInfoClass } THREADINFOCLASS;
@@ -1027,6 +1028,12 @@ typedef struct _THREAD_DESCRIPTOR_INFORMATION LDT_ENTRY Entry; } THREAD_DESCRIPTOR_INFORMATION, *PTHREAD_DESCRIPTOR_INFORMATION;
+typedef struct _THREAD_DESCRIPTION_INFORMATION +{ + DWORD Length; + WCHAR *Description; +} THREAD_DESCRIPTION_INFORMATION, *PTHREAD_DESCRIPTION_INFORMATION; + typedef struct _KERNEL_USER_TIMES { LARGE_INTEGER CreateTime; LARGE_INTEGER ExitTime;
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/ntdll/thread.c | 52 ++++++++++++++++++++++++++++++++++ include/wine/server_protocol.h | 17 ++++++----- server/protocol.def | 13 ++++++--- server/request.h | 2 ++ server/thread.c | 33 +++++++++++++++++++++ server/thread.h | 1 + server/trace.c | 4 +++ 7 files changed, 111 insertions(+), 11 deletions(-)
diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c index 621aaddfe3..4655d25245 100644 --- a/dlls/ntdll/thread.c +++ b/dlls/ntdll/thread.c @@ -1115,6 +1115,38 @@ NTSTATUS WINAPI NtQueryInformationThread( HANDLE handle, THREADINFOCLASS class, *(BOOL*)data = FALSE; if (ret_len) *ret_len = sizeof(BOOL); return STATUS_SUCCESS; + case ThreadDescription: + { + THREAD_DESCRIPTION_INFORMATION *info = data; + data_size_t len, desc_length = 0; + WCHAR *ptr; + + len = length >= sizeof(*info) ? length - sizeof(*info) : 0; + ptr = info ? (WCHAR *)(info + 1) : NULL; + + SERVER_START_REQ( get_thread_info ) + { + req->handle = wine_server_obj_handle( handle ); + req->mask = SET_THREAD_INFO_DESCRIPTION; + if (ptr && len) wine_server_set_reply( req, ptr, len ); + if (!(status = wine_server_call( req ))) + desc_length = reply->desc_length; + } + SERVER_END_REQ; + if (status == STATUS_SUCCESS) + { + if (desc_length <= len && info) + { + info->Length = desc_length << 16 | desc_length; + info->Description = ptr; + } + else + status = STATUS_BUFFER_TOO_SMALL; + + if (ret_len) *ret_len = desc_length + sizeof(*info); + } + } + return status; case ThreadPriority: case ThreadBasePriority: case ThreadImpersonationToken: @@ -1270,6 +1302,26 @@ NTSTATUS WINAPI NtSetInformationThread( HANDLE handle, THREADINFOCLASS class, SERVER_END_REQ; } return status; + case ThreadDescription: + { + const THREAD_DESCRIPTION_INFORMATION *info = data; + + if (length != sizeof(*info)) return STATUS_INFO_LENGTH_MISMATCH; + if (!info) return STATUS_ACCESS_VIOLATION; + if (info->Length >> 16 != (info->Length & 0xffff)) return STATUS_INVALID_PARAMETER; + if (info->Length && !info->Description) return STATUS_ACCESS_VIOLATION; + + SERVER_START_REQ( set_thread_info ) + { + req->handle = wine_server_obj_handle( handle ); + req->mask = SET_THREAD_INFO_DESCRIPTION; + if (info->Length) + wine_server_add_data( req, info->Description, info->Length & 0xffff ); + status = wine_server_call( req ); + } + SERVER_END_REQ; + } + return status; case ThreadBasicInformation: case ThreadTimes: case ThreadPriority: diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index d8f9816422..58e0dfc739 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -993,7 +993,7 @@ struct get_thread_info_request struct request_header __header; obj_handle_t handle; thread_id_t tid_in; - char __pad_20[4]; + int mask; }; struct get_thread_info_reply { @@ -1006,7 +1006,8 @@ struct get_thread_info_reply int exit_code; int priority; int last; - char __pad_52[4]; + data_size_t desc_length; + /* VARARG(desc,unicode_str); */ };
@@ -1034,16 +1035,18 @@ struct set_thread_info_request affinity_t affinity; client_ptr_t entry_point; obj_handle_t token; + /* VARARG(desc,unicode_str); */ char __pad_44[4]; }; struct set_thread_info_reply { struct reply_header __header; }; -#define SET_THREAD_INFO_PRIORITY 0x01 -#define SET_THREAD_INFO_AFFINITY 0x02 -#define SET_THREAD_INFO_TOKEN 0x04 -#define SET_THREAD_INFO_ENTRYPOINT 0x08 +#define SET_THREAD_INFO_PRIORITY 0x01 +#define SET_THREAD_INFO_AFFINITY 0x02 +#define SET_THREAD_INFO_TOKEN 0x04 +#define SET_THREAD_INFO_ENTRYPOINT 0x08 +#define SET_THREAD_INFO_DESCRIPTION 0x10
@@ -6691,6 +6694,6 @@ union generic_reply struct resume_process_reply resume_process_reply; };
-#define SERVER_PROTOCOL_VERSION 589 +#define SERVER_PROTOCOL_VERSION 590
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/protocol.def b/server/protocol.def index 3a0df20bdb..f2ea58ec82 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -935,6 +935,7 @@ struct rawinput_device @REQ(get_thread_info) obj_handle_t handle; /* thread handle */ thread_id_t tid_in; /* thread id (optional) */ + int mask; /* mask to request thread description */ @REPLY process_id_t pid; /* server process id */ thread_id_t tid; /* server thread id */ @@ -944,6 +945,8 @@ struct rawinput_device int exit_code; /* thread exit code */ int priority; /* thread priority level */ int last; /* last thread in process */ + data_size_t desc_length; /* description length in WCHARs */ + VARARG(desc,unicode_str); /* description string */ @END
@@ -964,11 +967,13 @@ struct rawinput_device affinity_t affinity; /* affinity mask */ client_ptr_t entry_point; /* thread entry point */ obj_handle_t token; /* impersonation token */ + VARARG(desc,unicode_str); /* description string */ @END -#define SET_THREAD_INFO_PRIORITY 0x01 -#define SET_THREAD_INFO_AFFINITY 0x02 -#define SET_THREAD_INFO_TOKEN 0x04 -#define SET_THREAD_INFO_ENTRYPOINT 0x08 +#define SET_THREAD_INFO_PRIORITY 0x01 +#define SET_THREAD_INFO_AFFINITY 0x02 +#define SET_THREAD_INFO_TOKEN 0x04 +#define SET_THREAD_INFO_ENTRYPOINT 0x08 +#define SET_THREAD_INFO_DESCRIPTION 0x10
/* Retrieve information about a module */ diff --git a/server/request.h b/server/request.h index 1303b35ef7..a824b4a4d0 100644 --- a/server/request.h +++ b/server/request.h @@ -840,6 +840,7 @@ C_ASSERT( FIELD_OFFSET(struct set_process_info_request, affinity) == 24 ); C_ASSERT( sizeof(struct set_process_info_request) == 32 ); C_ASSERT( FIELD_OFFSET(struct get_thread_info_request, handle) == 12 ); C_ASSERT( FIELD_OFFSET(struct get_thread_info_request, tid_in) == 16 ); +C_ASSERT( FIELD_OFFSET(struct get_thread_info_request, mask) == 20 ); C_ASSERT( sizeof(struct get_thread_info_request) == 24 ); C_ASSERT( FIELD_OFFSET(struct get_thread_info_reply, pid) == 8 ); C_ASSERT( FIELD_OFFSET(struct get_thread_info_reply, tid) == 12 ); @@ -849,6 +850,7 @@ C_ASSERT( FIELD_OFFSET(struct get_thread_info_reply, affinity) == 32 ); C_ASSERT( FIELD_OFFSET(struct get_thread_info_reply, exit_code) == 40 ); C_ASSERT( FIELD_OFFSET(struct get_thread_info_reply, priority) == 44 ); C_ASSERT( FIELD_OFFSET(struct get_thread_info_reply, last) == 48 ); +C_ASSERT( FIELD_OFFSET(struct get_thread_info_reply, desc_length) == 52 ); C_ASSERT( sizeof(struct get_thread_info_reply) == 56 ); C_ASSERT( FIELD_OFFSET(struct get_thread_times_request, handle) == 12 ); C_ASSERT( sizeof(struct get_thread_times_request) == 16 ); diff --git a/server/thread.c b/server/thread.c index e753c8d0dd..b0f3e9ed33 100644 --- a/server/thread.c +++ b/server/thread.c @@ -51,6 +51,7 @@ #include "request.h" #include "user.h" #include "security.h" +#include "unicode.h"
#ifdef __i386__ @@ -201,6 +202,7 @@ static inline void init_thread_structure( struct thread *thread ) thread->suspend = 0; thread->desktop_users = 0; thread->token = NULL; + thread->desc = NULL;
thread->creation_time = current_time; thread->exit_time = 0; @@ -336,6 +338,7 @@ static void cleanup_thread( struct thread *thread ) thread->inflight[i].client = thread->inflight[i].server = -1; } } + free( thread->desc ); thread->req_data = NULL; thread->reply_data = NULL; thread->request_fd = NULL; @@ -344,6 +347,7 @@ static void cleanup_thread( struct thread *thread ) thread->context = NULL; thread->suspend_context = NULL; thread->desktop = 0; + thread->desc = NULL; }
/* destroy a thread when its refcount is 0 */ @@ -551,6 +555,27 @@ static void set_thread_info( struct thread *thread, security_set_thread_token( thread, req->token ); if (req->mask & SET_THREAD_INFO_ENTRYPOINT) thread->entry_point = req->entry_point; + if (req->mask & SET_THREAD_INFO_DESCRIPTION) + { + WCHAR *desc = NULL; + data_size_t len = get_req_data_size() / sizeof(WCHAR); + + if (len) + { + if ((desc = mem_alloc( (len + 1) * sizeof(WCHAR) ))) + { + memcpy( desc, get_req_data(), len * sizeof(WCHAR) ); + desc[len] = 0; + free( thread->desc ); + thread->desc = desc; + } + } + else + { + free( thread->desc ); + thread->desc = NULL; + } + } }
/* stop a thread (at the Unix level) */ @@ -1436,6 +1461,14 @@ DECL_HANDLER(get_thread_info) reply->priority = thread->priority; reply->affinity = thread->affinity; reply->last = thread->process->running_threads == 1; + reply->desc_length = 0; + + if (thread->desc && req->mask & SET_THREAD_INFO_DESCRIPTION) + { + reply->desc_length = strlenW( thread->desc ) * sizeof(WCHAR); + if (get_reply_max_size() >= reply->desc_length) + set_reply_data( thread->desc, min( reply->desc_length, get_reply_max_size() )); + }
release_object( thread ); } diff --git a/server/thread.h b/server/thread.h index e10120dcf6..73b46d01be 100644 --- a/server/thread.h +++ b/server/thread.h @@ -88,6 +88,7 @@ struct thread timeout_t exit_time; /* Thread exit time */ struct token *token; /* security token associated with this thread */ struct list kernel_object; /* list of kernel object pointers */ + WCHAR *desc; /* thread description string */ };
struct thread_snapshot diff --git a/server/trace.c b/server/trace.c index 55d5e68962..f216233ca3 100644 --- a/server/trace.c +++ b/server/trace.c @@ -1411,6 +1411,7 @@ static void dump_get_thread_info_request( const struct get_thread_info_request * { fprintf( stderr, " handle=%04x", req->handle ); fprintf( stderr, ", tid_in=%04x", req->tid_in ); + fprintf( stderr, ", mask=%d", req->mask ); }
static void dump_get_thread_info_reply( const struct get_thread_info_reply *req ) @@ -1423,6 +1424,8 @@ static void dump_get_thread_info_reply( const struct get_thread_info_reply *req fprintf( stderr, ", exit_code=%d", req->exit_code ); fprintf( stderr, ", priority=%d", req->priority ); fprintf( stderr, ", last=%d", req->last ); + fprintf( stderr, ", desc_length=%u", req->desc_length ); + dump_varargs_unicode_str( ", desc=", cur_size ); }
static void dump_get_thread_times_request( const struct get_thread_times_request *req ) @@ -1444,6 +1447,7 @@ static void dump_set_thread_info_request( const struct set_thread_info_request * dump_uint64( ", affinity=", &req->affinity ); dump_uint64( ", entry_point=", &req->entry_point ); fprintf( stderr, ", token=%04x", req->token ); + dump_varargs_unicode_str( ", desc=", cur_size ); }
static void dump_get_dll_info_request( const struct get_dll_info_request *req )
Nikolay Sivov nsivov@codeweavers.com writes:
@@ -1436,6 +1461,14 @@ DECL_HANDLER(get_thread_info) reply->priority = thread->priority; reply->affinity = thread->affinity; reply->last = thread->process->running_threads == 1;
reply->desc_length = 0;
if (thread->desc && req->mask & SET_THREAD_INFO_DESCRIPTION)
{
reply->desc_length = strlenW( thread->desc ) * sizeof(WCHAR);
if (get_reply_max_size() >= reply->desc_length)
set_reply_data( thread->desc, min( reply->desc_length, get_reply_max_size() ));
}
I don't see why you need to check the mask for the get request. Also in general in the server we store the length with the strings, so that we don't need to recompute it.
On 11/25/19 3:36 PM, Alexandre Julliard wrote:
Nikolay Sivov nsivov@codeweavers.com writes:
@@ -1436,6 +1461,14 @@ DECL_HANDLER(get_thread_info) reply->priority = thread->priority; reply->affinity = thread->affinity; reply->last = thread->process->running_threads == 1;
reply->desc_length = 0;
if (thread->desc && req->mask & SET_THREAD_INFO_DESCRIPTION)
{
reply->desc_length = strlenW( thread->desc ) * sizeof(WCHAR);
if (get_reply_max_size() >= reply->desc_length)
set_reply_data( thread->desc, min( reply->desc_length, get_reply_max_size() ));
}
I don't see why you need to check the mask for the get request. Also in general in the server we store the length with the strings, so that we don't need to recompute it.
I'm using mask to return this string only when asked for, because get_thread_info() is used for several classes. Or do you mean the fact that it's requested could be inferred in some way? Maybe I only need to compare current length to get_reply_max_size(), as an indication that it was requested.
Regarding string length, it's not consistent apparently, it's used for dll names, but not for window text or console title.
Nikolay Sivov nsivov@codeweavers.com writes:
On 11/25/19 3:36 PM, Alexandre Julliard wrote:
Nikolay Sivov nsivov@codeweavers.com writes:
@@ -1436,6 +1461,14 @@ DECL_HANDLER(get_thread_info) reply->priority = thread->priority; reply->affinity = thread->affinity; reply->last = thread->process->running_threads == 1;
reply->desc_length = 0;
if (thread->desc && req->mask & SET_THREAD_INFO_DESCRIPTION)
{
reply->desc_length = strlenW( thread->desc ) * sizeof(WCHAR);
if (get_reply_max_size() >= reply->desc_length)
set_reply_data( thread->desc, min( reply->desc_length, get_reply_max_size() ));
}
I don't see why you need to check the mask for the get request. Also in general in the server we store the length with the strings, so that we don't need to recompute it.
I'm using mask to return this string only when asked for, because get_thread_info() is used for several classes. Or do you mean the fact that it's requested could be inferred in some way? Maybe I only need to compare current length to get_reply_max_size(), as an indication that it was requested.
Yes, if the caller doesn't want the description it won't pass a buffer.
Regarding string length, it's not consistent apparently, it's used for dll names, but not for window text or console title.
Yes, it's not 100% consistent, but most strings are stored with explicit length and without final null. This allows supporting embedded nulls where necessary.
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- ...api-ms-win-core-processthreads-l1-1-3.spec | 2 +- dlls/kernel32/kernel32.spec | 1 + dlls/kernel32/tests/thread.c | 2 +- dlls/kernelbase/kernelbase.spec | 2 +- dlls/kernelbase/thread.c | 58 ++++++++++++++++++- 5 files changed, 60 insertions(+), 5 deletions(-)
diff --git a/dlls/api-ms-win-core-processthreads-l1-1-3/api-ms-win-core-processthreads-l1-1-3.spec b/dlls/api-ms-win-core-processthreads-l1-1-3/api-ms-win-core-processthreads-l1-1-3.spec index 6f0c4fa752..9cc853d288 100644 --- a/dlls/api-ms-win-core-processthreads-l1-1-3/api-ms-win-core-processthreads-l1-1-3.spec +++ b/dlls/api-ms-win-core-processthreads-l1-1-3/api-ms-win-core-processthreads-l1-1-3.spec @@ -1,7 +1,7 @@ @ stub GetProcessDefaultCpuSets @ stub GetProcessInformation @ stub GetSystemCpuSetInformation -@ stub GetThreadDescription +@ stdcall GetThreadDescription(long ptr) kernel32.GetThreadDescription @ stub GetThreadSelectedCpuSets @ stub SetProcessDefaultCpuSets @ stub SetProcessInformation diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec index a26d65edf7..b500dbf95f 100644 --- a/dlls/kernel32/kernel32.spec +++ b/dlls/kernel32/kernel32.spec @@ -847,6 +847,7 @@ @ stdcall -import GetTempPathA(long ptr) @ stdcall -import GetTempPathW(long ptr) @ stdcall -import GetThreadContext(long ptr) +@ stdcall -import GetThreadDescription(long ptr) @ stdcall -import GetThreadErrorMode() @ stdcall -import GetThreadGroupAffinity(long ptr) @ stdcall -import GetThreadIOPendingFlag(long ptr) diff --git a/dlls/kernel32/tests/thread.c b/dlls/kernel32/tests/thread.c index 180eed8241..8ff34ba5f3 100644 --- a/dlls/kernel32/tests/thread.c +++ b/dlls/kernel32/tests/thread.c @@ -2125,7 +2125,7 @@ static void test_thread_description(void)
if (!pGetThreadDescription) { - skip("Thread description API is not supported.\n"); + win_skip("Thread description API is not supported.\n"); return; }
diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec index 7d20d3b112..6022d4ea37 100644 --- a/dlls/kernelbase/kernelbase.spec +++ b/dlls/kernelbase/kernelbase.spec @@ -711,7 +711,7 @@ @ stdcall GetTempPathA(long ptr) @ stdcall GetTempPathW(long ptr) @ stdcall GetThreadContext(long ptr) -# @ stub GetThreadDescription +@ stdcall GetThreadDescription(long ptr) @ stdcall GetThreadErrorMode() @ stdcall GetThreadGroupAffinity(long ptr) @ stdcall GetThreadIOPendingFlag(long ptr) diff --git a/dlls/kernelbase/thread.c b/dlls/kernelbase/thread.c index 345f44dff7..b8ce4bdb48 100644 --- a/dlls/kernelbase/thread.c +++ b/dlls/kernelbase/thread.c @@ -20,6 +20,7 @@
#include <stdarg.h> #include <string.h> +#include <limits.h>
#include "ntstatus.h" #define WIN32_NO_STATUS @@ -33,6 +34,7 @@ #include "wine/exception.h" #include "wine/asm.h" #include "wine/debug.h" +#include "wine/heap.h"
WINE_DEFAULT_DEBUG_CHANNEL(thread);
@@ -392,10 +394,62 @@ BOOL WINAPI DECLSPEC_HOTPATCH SetThreadContext( HANDLE thread, const CONTEXT *co */ HRESULT WINAPI /* DECLSPEC_HOTPATCH */ SetThreadDescription( HANDLE thread, PCWSTR description ) { - FIXME( "(%p %s): stub\n", thread, debugstr_w( description )); - return E_NOTIMPL; + THREAD_DESCRIPTION_INFORMATION info; + int length; + + TRACE( "(%p, %s)\n", thread, debugstr_w( description )); + + length = description ? lstrlenW( description ) * sizeof(WCHAR) : 0; + + if (length > USHRT_MAX) + return HRESULT_FROM_NT(STATUS_INVALID_PARAMETER); + + info.Length = length << 16 | length; + info.Description = (WCHAR *)description; + + return HRESULT_FROM_NT(NtSetInformationThread( thread, ThreadDescription, &info, sizeof(info) )); }
+/*********************************************************************** + * GetThreadDescription (kernelbase.@) + */ +HRESULT WINAPI /* DECLSPEC_HOTPATCH */ GetThreadDescription( HANDLE thread, WCHAR **description ) +{ + THREAD_DESCRIPTION_INFORMATION *info; + NTSTATUS status; + ULONG length; + + TRACE( "(%p, %p)\n", thread, description ); + + *description = NULL; + + length = 0; + status = NtQueryInformationThread( thread, ThreadDescription, NULL, 0, &length ); + if (status != STATUS_BUFFER_TOO_SMALL) + return HRESULT_FROM_NT(status); + + if (!(info = heap_alloc( length ))) + return HRESULT_FROM_NT(STATUS_NO_MEMORY); + + status = NtQueryInformationThread( thread, ThreadDescription, info, length, &length ); + if (!status) + { + length = info->Length & 0xffff; + + if (!(*description = LocalAlloc( 0, length + sizeof(WCHAR)))) + status = STATUS_NO_MEMORY; + else + { + if (length) + memcpy(*description, info->Description, length); + (*description)[length / sizeof(WCHAR)] = 0; + } + } + + heap_free(info); + + return HRESULT_FROM_NT(status); +}
/*********************************************************************** * SetThreadErrorMode (kernelbase.@)
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=60758
Your paranoid android.
=== debian10 (32 bit Chinese:China report) ===
kernel32: comm.c:918: Test failed: OutQueue should not be empty debugger.c:320: Test failed: GetThreadContext failed: 5
=== debian10 (32 bit WoW report) ===
kernel32: comm.c:918: Test failed: OutQueue should not be empty