Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/ntdll/tests/exception.c | 61 ++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+)
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index accb5d8646..c79fb8eb8d 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -53,6 +53,8 @@ static NTSTATUS (WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, static NTSTATUS (WINAPI *pNtSetInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG); static BOOL (WINAPI *pIsWow64Process)(HANDLE, PBOOL); static NTSTATUS (WINAPI *pNtClose)(HANDLE); +static NTSTATUS (WINAPI *pNtSuspendThread)(HANDLE thread, ULONG *count); +static NTSTATUS (WINAPI *pNtResumeThread)(HANDLE thread, ULONG *count);
#if defined(__x86_64__) typedef struct @@ -3135,6 +3137,61 @@ static void test_vectored_continue_handler(void) } #endif /* defined(__i386__) || defined(__x86_64__) */
+static DWORD WINAPI suspend_thread_test( void *arg ) +{ + HANDLE event = arg; + WaitForSingleObject(event, INFINITE); + return 0; +} + +static void test_suspend_thread(void) +{ + HANDLE thread, event; + NTSTATUS status; + ULONG count; + DWORD ret; + + status = NtSuspendThread(0, NULL); + ok(status == STATUS_INVALID_HANDLE, "Unexpected return value %#x.\n", status); + + status = NtResumeThread(0, NULL); + ok(status == STATUS_INVALID_HANDLE, "Unexpected return value %#x.\n", status); + + event = CreateEventW(NULL, FALSE, FALSE, NULL); + + thread = CreateThread(NULL, 0, suspend_thread_test, event, 0, NULL); + ok(thread != NULL, "Failed to create a thread.\n"); + + ret = WaitForSingleObject(thread, 0); + ok(ret == WAIT_TIMEOUT, "Unexpected status %d.\n", ret); + + status = NtResumeThread(thread, NULL); + ok(!status, "Unexpected status %#x.\n", status); + + status = NtResumeThread(thread, &count); + ok(!status, "Unexpected status %#x.\n", status); + ok(count == 0, "Unexpected suspended count %u.\n", count); + + status = NtSuspendThread(thread, NULL); + ok(!status, "Failed to suspend a thread, status %#x.\n", status); + + status = NtSuspendThread(thread, &count); + ok(!status, "Failed to suspend a thread, status %#x.\n", status); + ok(count == 1, "Unexpected suspended count %u.\n", count); + + status = NtResumeThread(thread, &count); + ok(!status, "Failed to resume a thread, status %#x.\n", status); + ok(count == 2, "Unexpected suspended count %u.\n", count); + + status = NtResumeThread(thread, NULL); + ok(!status, "Failed to resume a thread, status %#x.\n", status); + + SetEvent(event); + WaitForSingleObject(thread, INFINITE); + + CloseHandle(thread); +} + START_TEST(exception) { HMODULE hntdll = GetModuleHandleA("ntdll.dll"); @@ -3169,6 +3226,8 @@ START_TEST(exception) pNtSetInformationProcess = (void*)GetProcAddress( hntdll, "NtSetInformationProcess" ); pIsWow64Process = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process"); + pNtSuspendThread = (void *)GetProcAddress( hntdll, "NtSuspendThread" ); + pNtResumeThread = (void *)GetProcAddress( hntdll, "NtResumeThread" );
#ifdef __i386__ if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE; @@ -3253,6 +3312,7 @@ START_TEST(exception) test_dpe_exceptions(); test_prot_fault(); test_thread_context(); + test_suspend_thread();
#elif defined(__x86_64__) pRtlAddFunctionTable = (void *)GetProcAddress( hntdll, @@ -3294,6 +3354,7 @@ START_TEST(exception) test_prot_fault(); test_dpe_exceptions(); test_wow64_context(); + test_suspend_thread();
if (pRtlAddFunctionTable && pRtlDeleteFunctionTable && pRtlInstallFunctionTableCallback && pRtlLookupFunctionEntry) test_dynamic_unwind();
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/ntdll/process.c | 24 +++++-- dlls/ntdll/tests/exception.c | 116 ++++++++++++++++++++++++++++++++- include/wine/server_protocol.h | 32 ++++++++- server/process.c | 25 +++++++ server/protocol.def | 12 ++++ server/request.h | 8 +++ server/trace.c | 16 +++++ 7 files changed, 225 insertions(+), 8 deletions(-)
diff --git a/dlls/ntdll/process.c b/dlls/ntdll/process.c index 6c6c427372..573a7a771d 100644 --- a/dlls/ntdll/process.c +++ b/dlls/ntdll/process.c @@ -763,8 +763,16 @@ NTSTATUS WINAPI NtOpenProcess(PHANDLE handle, ACCESS_MASK access, */ NTSTATUS WINAPI NtResumeProcess( HANDLE handle ) { - FIXME("stub: %p\n", handle); - return STATUS_NOT_IMPLEMENTED; + NTSTATUS ret; + + SERVER_START_REQ( resume_process ) + { + req->handle = wine_server_obj_handle( handle ); + ret = wine_server_call( req ); + } + SERVER_END_REQ; + + return ret; }
/****************************************************************************** @@ -773,8 +781,16 @@ NTSTATUS WINAPI NtResumeProcess( HANDLE handle ) */ NTSTATUS WINAPI NtSuspendProcess( HANDLE handle ) { - FIXME("stub: %p\n", handle); - return STATUS_NOT_IMPLEMENTED; + NTSTATUS ret; + + SERVER_START_REQ( suspend_process ) + { + req->handle = wine_server_obj_handle( handle ); + ret = wine_server_call( req ); + } + SERVER_END_REQ; + + return ret; }
diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index c79fb8eb8d..95dff4fed5 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -55,6 +55,8 @@ static BOOL (WINAPI *pIsWow64Process)(HANDLE, PBOOL); static NTSTATUS (WINAPI *pNtClose)(HANDLE); static NTSTATUS (WINAPI *pNtSuspendThread)(HANDLE thread, ULONG *count); static NTSTATUS (WINAPI *pNtResumeThread)(HANDLE thread, ULONG *count); +static NTSTATUS (WINAPI *pNtSuspendProcess)(HANDLE process); +static NTSTATUS (WINAPI *pNtResumeProcess)(HANDLE process);
#if defined(__x86_64__) typedef struct @@ -160,6 +162,9 @@ static VOID (CDECL *pRtlUnwindEx)(VOID*, VOID*, EXCEPTION_RECORD*, VOID*, C static int (CDECL *p_setjmp)(_JUMP_BUFFER*); #endif
+static int my_argc; +static char** my_argv; + #ifdef __i386__
#ifndef __WINE_WINTRNL_H @@ -169,8 +174,6 @@ static int (CDECL *p_setjmp)(_JUMP_BUFFER*); #define MEM_EXECUTE_OPTION_PERMANENT 0x08 #endif
-static int my_argc; -static char** my_argv; static int test_stage;
static BOOL is_wow64; @@ -3192,6 +3195,102 @@ static void test_suspend_thread(void) CloseHandle(thread); }
+static const char *suspend_process_event_name = "suspend_process_event"; +static const char *suspend_process_event2_name = "suspend_process_event2"; + +static void suspend_process_proc(void) +{ + HANDLE event = OpenEventA(SYNCHRONIZE, FALSE, suspend_process_event_name); + HANDLE event2 = OpenEventA(SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, suspend_process_event2_name); + + ok(event != NULL, "Failed to open event handle.\n"); + ok(event2 != NULL, "Failed to open event handle.\n"); + + for (;;) + { + SetEvent(event2); + if (WaitForSingleObject(event, 100) == WAIT_OBJECT_0) + break; + } + + CloseHandle(event); + CloseHandle(event2); +} + +static void test_suspend_process(void) +{ + PROCESS_INFORMATION info; + char path_name[MAX_PATH]; + STARTUPINFOA startup; + HANDLE event, event2; + NTSTATUS status; + char **argv; + DWORD ret; + + event = CreateEventA(NULL, FALSE, FALSE, suspend_process_event_name); + ok(event != NULL, "Failed to create event.\n"); + + event2 = CreateEventA(NULL, FALSE, FALSE, suspend_process_event2_name); + ok(event2 != NULL, "Failed to create event.\n"); + + winetest_get_mainargs(&argv); + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + sprintf(path_name, "%s exception suspend_process", argv[0]); + + ret = CreateProcessA(NULL, path_name, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info), + ok(ret, "Failed to create target process.\n"); + + /* New process signals this event. */ + ResetEvent(event2); + ret = WaitForSingleObject(event2, INFINITE); + ok(ret == WAIT_OBJECT_0, "Wait failed, %#x.\n", ret); + + status = pNtSuspendProcess(0); + ok(status == STATUS_INVALID_HANDLE, "Unexpected status %#x.\n", status); + + status = pNtResumeProcess(info.hProcess); + ok(!status, "Failed to resume a process, status %#x.\n", status); + + ResetEvent(event2); + ret = WaitForSingleObject(event2, 200); + ok(ret == WAIT_OBJECT_0, "Wait failed.\n"); + + status = pNtSuspendProcess(info.hProcess); + ok(!status, "Failed to suspend a process, status %#x.\n", status); + + ResetEvent(event2); + ret = WaitForSingleObject(event2, 200); + ok(ret == WAIT_TIMEOUT, "Wait failed.\n"); + + status = pNtSuspendProcess(info.hProcess); + ok(!status, "Failed to suspend a process, status %#x.\n", status); + + status = pNtResumeProcess(info.hProcess); + ok(!status, "Failed to resume a process, status %#x.\n", status); + + ResetEvent(event2); + ret = WaitForSingleObject(event2, 200); + ok(ret == WAIT_TIMEOUT, "Wait failed.\n"); + + status = pNtResumeProcess(info.hProcess); + ok(!status, "Failed to resume a process, status %#x.\n", status); + + ResetEvent(event2); + ret = WaitForSingleObject(event2, 200); + ok(ret == WAIT_OBJECT_0, "Wait failed.\n"); + + SetEvent(event); + + winetest_wait_child_process(info.hProcess); + + CloseHandle(info.hProcess); + CloseHandle(info.hThread); + + CloseHandle(event); + CloseHandle(event2); +} + START_TEST(exception) { HMODULE hntdll = GetModuleHandleA("ntdll.dll"); @@ -3199,6 +3298,14 @@ START_TEST(exception) HMODULE hmsvcrt = LoadLibraryA("msvcrt.dll"); #endif
+ my_argc = winetest_get_mainargs( &my_argv ); + + if (my_argc >= 3 && !strcmp(my_argv[2], "suspend_process")) + { + suspend_process_proc(); + return; + } + code_mem = VirtualAlloc(NULL, 65536, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); if(!code_mem) { trace("VirtualAlloc failed\n"); @@ -3228,6 +3335,8 @@ START_TEST(exception) pIsWow64Process = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process"); pNtSuspendThread = (void *)GetProcAddress( hntdll, "NtSuspendThread" ); pNtResumeThread = (void *)GetProcAddress( hntdll, "NtResumeThread" ); + pNtSuspendProcess = (void *)GetProcAddress( hntdll, "NtSuspendProcess" ); + pNtResumeProcess = (void *)GetProcAddress( hntdll, "NtResumeProcess" );
#ifdef __i386__ if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE; @@ -3237,7 +3346,6 @@ START_TEST(exception) else skip("RtlAddVectoredExceptionHandler or RtlRemoveVectoredExceptionHandler not found\n");
- my_argc = winetest_get_mainargs( &my_argv ); if (my_argc >= 4) { void *addr; @@ -3313,6 +3421,7 @@ START_TEST(exception) test_prot_fault(); test_thread_context(); test_suspend_thread(); + test_suspend_process();
#elif defined(__x86_64__) pRtlAddFunctionTable = (void *)GetProcAddress( hntdll, @@ -3355,6 +3464,7 @@ START_TEST(exception) test_dpe_exceptions(); test_wow64_context(); test_suspend_thread(); + test_suspend_process();
if (pRtlAddFunctionTable && pRtlDeleteFunctionTable && pRtlInstallFunctionTableCallback && pRtlLookupFunctionEntry) test_dynamic_unwind(); diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 88b933cc14..c3d1bb6a59 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -5742,6 +5742,30 @@ struct terminate_job_reply };
+ +struct suspend_process_request +{ + struct request_header __header; + obj_handle_t handle; +}; +struct suspend_process_reply +{ + struct reply_header __header; +}; + + + +struct resume_process_request +{ + struct request_header __header; + obj_handle_t handle; +}; +struct resume_process_reply +{ + struct reply_header __header; +}; + + enum request { REQ_new_process, @@ -6040,6 +6064,8 @@ enum request REQ_set_job_limits, REQ_set_job_completion_port, REQ_terminate_job, + REQ_suspend_process, + REQ_resume_process, REQ_NB_REQUESTS };
@@ -6343,6 +6369,8 @@ union generic_request struct set_job_limits_request set_job_limits_request; struct set_job_completion_port_request set_job_completion_port_request; struct terminate_job_request terminate_job_request; + struct suspend_process_request suspend_process_request; + struct resume_process_request resume_process_request; }; union generic_reply { @@ -6644,8 +6672,10 @@ union generic_reply struct set_job_limits_reply set_job_limits_reply; struct set_job_completion_port_reply set_job_completion_port_reply; struct terminate_job_reply terminate_job_reply; + struct suspend_process_reply suspend_process_reply; + struct resume_process_reply resume_process_reply; };
-#define SERVER_PROTOCOL_VERSION 578 +#define SERVER_PROTOCOL_VERSION 579
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/process.c b/server/process.c index 473d3b1a27..9e3953dc59 100644 --- a/server/process.c +++ b/server/process.c @@ -1705,3 +1705,28 @@ DECL_HANDLER(set_job_completion_port)
release_object( job ); } + +/* suspend a process */ +DECL_HANDLER(suspend_process) +{ + struct process *process; + + if ((process = get_process_from_handle( req->handle, PROCESS_SUSPEND_RESUME ))) + { + suspend_process( process ); + release_object( process ); + } +} + +/* resume a process */ +DECL_HANDLER(resume_process) +{ + struct process *process; + + if ((process = get_process_from_handle( req->handle, PROCESS_SUSPEND_RESUME ))) + { + if (process->suspend > 0) + resume_process( process ); + release_object( process ); + } +} diff --git a/server/protocol.def b/server/protocol.def index b6ad514463..8a3ca0a28f 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3909,3 +3909,15 @@ struct handle_info obj_handle_t handle; /* handle to the job */ int status; /* process exit code */ @END + + +/* Suspend a process */ +@REQ(suspend_process) + obj_handle_t handle; /* process handle */ +@END + + +/* Resume a process */ +@REQ(resume_process) + obj_handle_t handle; /* process handle */ +@END diff --git a/server/request.h b/server/request.h index a3e95137e1..a21252c7a6 100644 --- a/server/request.h +++ b/server/request.h @@ -408,6 +408,8 @@ DECL_HANDLER(process_in_job); DECL_HANDLER(set_job_limits); DECL_HANDLER(set_job_completion_port); DECL_HANDLER(terminate_job); +DECL_HANDLER(suspend_process); +DECL_HANDLER(resume_process);
#ifdef WANT_REQUEST_HANDLERS
@@ -710,6 +712,8 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_set_job_limits, (req_handler)req_set_job_completion_port, (req_handler)req_terminate_job, + (req_handler)req_suspend_process, + (req_handler)req_resume_process, };
C_ASSERT( sizeof(affinity_t) == 8 ); @@ -2436,6 +2440,10 @@ C_ASSERT( sizeof(struct set_job_completion_port_request) == 32 ); C_ASSERT( FIELD_OFFSET(struct terminate_job_request, handle) == 12 ); C_ASSERT( FIELD_OFFSET(struct terminate_job_request, status) == 16 ); C_ASSERT( sizeof(struct terminate_job_request) == 24 ); +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 );
#endif /* WANT_REQUEST_HANDLERS */
diff --git a/server/trace.c b/server/trace.c index d4d0c3eb8d..5c6325a257 100644 --- a/server/trace.c +++ b/server/trace.c @@ -4582,6 +4582,16 @@ static void dump_terminate_job_request( const struct terminate_job_request *req fprintf( stderr, ", status=%d", req->status ); }
+static void dump_suspend_process_request( const struct suspend_process_request *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); +} + +static void dump_resume_process_request( const struct resume_process_request *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_exec_process_request, @@ -4879,6 +4889,8 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_set_job_limits_request, (dump_func)dump_set_job_completion_port_request, (dump_func)dump_terminate_job_request, + (dump_func)dump_suspend_process_request, + (dump_func)dump_resume_process_request, };
static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { @@ -5178,6 +5190,8 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { NULL, NULL, NULL, + NULL, + NULL, };
static const char * const req_names[REQ_NB_REQUESTS] = { @@ -5477,6 +5491,8 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "set_job_limits", "set_job_completion_port", "terminate_job", + "suspend_process", + "resume_process", };
static const struct
Nikolay Sivov nsivov@codeweavers.com wrote:
+static NTSTATUS (WINAPI *pNtSuspendThread)(HANDLE thread, ULONG *count); +static NTSTATUS (WINAPI *pNtResumeThread)(HANDLE thread, ULONG *count);
...
- pNtSuspendThread = (void *)GetProcAddress( hntdll, "NtSuspendThread" );
- pNtResumeThread = (void *)GetProcAddress( hntdll, "NtResumeThread" );
It would be helpful to actually use them in the test. Also it's would be interesting to create a suspended thread, and perform same kind of tests as with a not suspended one.