I'm thinking how to implement CreateRemoteThread and besides fix memory management functions. The complete (afaik) list includes: RtlCreateUserThread NtAllocateVirtualMemory NtFreeVirtualMemory NtProtectVirtualMemory NtQueryVirtualMemory NtLockVirtualMemory (do nothing?) NtUnlockVirtualMemory (do nothing?) NtFlushVirtualMemory NtMapViewOfSection NtUnmapViewOfSection
Suggested implementation. Let's add two groups of handlers to the wineserver, something like remote_operation_xxxx and remote_operation_xxxx_complete. remote_operation should do the following: 1) suspend_for_ptrace(); 2) inject a piece of code into required process and start its execution; 3) resume_after_ptrace(); 4) place calling thread into suspended state (or into some wait state?)
remote_operation_complete should prepare reply and resume thread suspended by remote_operation. Injected code should call required function and then remote_operation_complete in context of required process. The question is: how to correctly get address of function? imho possible solutions are: 1) assume ntdll loaded at the same address for all processes -- unreliable; 2) get dll base address from per-process dll list and parse ELF by hand -- too complicated (?); 3) pass relative offsets and add them later to the ntdll's base address -- unreliable a bit: ntdll may be replaced, although, it is unlikely; 4) extend the struct process (server/process.h) and the request init_process with pointers to required functions -- most reliable and simple but looks ugly. What do you think?
Monday, June 21, 2004 4:00 AM "Alexander Yaworsky" yaworsky@migusoft.ru Wrote:
I'm thinking how to implement CreateRemoteThread and besides fix memory
management functions.
The complete (afaik) list includes: RtlCreateUserThread NtAllocateVirtualMemory NtFreeVirtualMemory NtProtectVirtualMemory NtQueryVirtualMemory NtLockVirtualMemory (do nothing?) NtUnlockVirtualMemory (do nothing?) NtFlushVirtualMemory NtMapViewOfSection NtUnmapViewOfSection
Suggested implementation. Let's add two groups of handlers to the wineserver, something like
remote_operation_xxxx
and remote_operation_xxxx_complete. remote_operation should do the following:
- suspend_for_ptrace();
- inject a piece of code into required process and start its execution;
- resume_after_ptrace();
- place calling thread into suspended state (or into some wait state?)
remote_operation_complete should prepare reply and resume thread suspended
by remote_operation.
Injected code should call required function and then
remote_operation_complete
in context of required process. The question is: how to correctly get address of function? imho possible solutions are:
- assume ntdll loaded at the same address for all processes --
unreliable;
- get dll base address from per-process dll list and parse ELF by hand --
too complicated (?);
- pass relative offsets and add them later to the ntdll's base address --
unreliable
a bit: ntdll may be replaced, although, it is unlikely;
- extend the struct process (server/process.h) and the request
init_process with
pointers to required functions -- most reliable and simple but looks
ugly.
What do you think?
Mike Hearn has a couple of suggestions here: http://www.winehq.org/hypermail/wine-devel/2004/05/0164.html I have a VB6 app (United Devices Agent) that requires this api in order to function correctly and have been attempting to learn C for the past month or so and starting to get a handle on it but I have a lot to learn about posix and signaling yet and generally how things work. If you need or would like a volunteer to help test and troubleshoot using an actual app, my hand is up :-). Meantime, I'm continuing to try to learn how all this works
Thanks, Roger
Hello
This is a basic framework. Not finished, not really tested -- just stable point. It does no harm for applications I'm using. I would be glad to get some comments and suggestions.
This patch does not contain files generated by tools/make_requests
Index: dlls/kernel/kernel_private.h =================================================================== RCS file: /home/wine/wine/dlls/kernel/kernel_private.h,v retrieving revision 1.20 diff -u -r1.20 kernel_private.h --- dlls/kernel/kernel_private.h 14 May 2004 21:43:18 -0000 1.20 +++ dlls/kernel/kernel_private.h 23 Aug 2004 09:28:39 -0000 @@ -91,4 +91,6 @@ } INSTANCEDATA; #include "poppack.h"
+extern void CALLBACK THREAD_Start( void *ptr ); + #endif Index: dlls/kernel/process.c =================================================================== RCS file: /home/wine/wine/dlls/kernel/process.c,v retrieving revision 1.71 diff -u -r1.71 process.c --- dlls/kernel/process.c 18 Aug 2004 21:03:32 -0000 1.71 +++ dlls/kernel/process.c 23 Aug 2004 09:29:03 -0000 @@ -894,6 +894,7 @@ { req->peb = peb; req->ldt_copy = &wine_ldt_copy; + req->kernel_thread_start = THREAD_Start; if ((ret = !wine_server_call_err( req ))) { main_exe_file = reply->exe_file; Index: dlls/kernel/thread.c =================================================================== RCS file: /home/wine/wine/dlls/kernel/thread.c,v retrieving revision 1.19 diff -u -r1.19 thread.c --- dlls/kernel/thread.c 7 Jul 2004 00:49:34 -0000 1.19 +++ dlls/kernel/thread.c 23 Aug 2004 09:29:08 -0000 @@ -90,13 +90,13 @@ * * Start execution of a newly created thread. Does not return. */ -static void CALLBACK THREAD_Start( void *ptr ) +void CALLBACK THREAD_Start( void *ptr ) { struct new_thread_info *info = ptr; LPTHREAD_START_ROUTINE func = info->func; void *arg = info->arg;
- RtlFreeHeap( GetProcessHeap(), 0, info ); + VirtualFree( info, 0, MEM_RELEASE );
if (TRACE_ON(relay)) DPRINTF("%04lx:Starting thread (entryproc=%p)\n", GetCurrentThreadId(), func ); @@ -121,39 +121,8 @@ LPTHREAD_START_ROUTINE start, LPVOID param, DWORD flags, LPDWORD id ) { - HANDLE handle; - CLIENT_ID client_id; - NTSTATUS status; - SIZE_T stack_reserve = 0, stack_commit = 0; - struct new_thread_info *info; - - if (!(info = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*info) ))) - { - SetLastError( ERROR_NOT_ENOUGH_MEMORY ); - return 0; - } - info->func = start; - info->arg = param; - - if (flags & STACK_SIZE_PARAM_IS_A_RESERVATION) stack_reserve = stack; - else stack_commit = stack; - - status = RtlCreateUserThread( GetCurrentProcess(), NULL, (flags & CREATE_SUSPENDED) != 0, - NULL, stack_reserve, stack_commit, - THREAD_Start, info, &handle, &client_id ); - if (status == STATUS_SUCCESS) - { - if (id) *id = (DWORD)client_id.UniqueThread; - if (sa && (sa->nLength >= sizeof(*sa)) && sa->bInheritHandle) - SetHandleInformation( handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT ); - } - else - { - RtlFreeHeap( GetProcessHeap(), 0, info ); - SetLastError( RtlNtStatusToDosError(status) ); - handle = 0; - } - return handle; + return CreateRemoteThread( GetCurrentProcess(), + sa, stack, start, param, flags, id ); }
@@ -168,16 +137,68 @@ * Success: Handle to the new thread. * Failure: NULL. Use GetLastError() to find the error cause. * - * BUGS - * Unimplemented */ HANDLE WINAPI CreateRemoteThread( HANDLE hProcess, SECURITY_ATTRIBUTES *sa, SIZE_T stack, LPTHREAD_START_ROUTINE start, LPVOID param, DWORD flags, LPDWORD id ) { - FIXME("(): stub, Write Me.\n"); + extern BOOL __wine_is_current_process( HANDLE hProcess ); + + HANDLE handle; + CLIENT_ID client_id; + NTSTATUS status; + SIZE_T stack_reserve = 0, stack_commit = 0; + struct new_thread_info *info; + PRTL_THREAD_START_ROUTINE start_addr; + + if (!(info = VirtualAllocEx( hProcess, NULL, sizeof(*info), + MEM_COMMIT, PAGE_READWRITE ))) + return 0; + + if( __wine_is_current_process( hProcess ) ) + { + info->func = start; + info->arg = param; + start_addr = (void*) THREAD_Start; + } + else + { + struct new_thread_info local_info; + DWORD written; + + local_info.func = start; + local_info.arg = param; + if( ! WriteProcessMemory( hProcess, &info, &local_info, + sizeof(struct new_thread_info), &written ) ) + return NULL; + SERVER_START_REQ( get_kernel_thread_start ) + { + req->handle = hProcess; + if (!(status = wine_server_call( req ))) + start_addr = (PRTL_THREAD_START_ROUTINE) reply->address; + } + SERVER_END_REQ; + if (status) + goto error; + } + + if (flags & STACK_SIZE_PARAM_IS_A_RESERVATION) stack_reserve = stack; + else stack_commit = stack; + + status = RtlCreateUserThread( hProcess, NULL, (flags & CREATE_SUSPENDED) != 0, + NULL, stack_reserve, stack_commit, + start_addr, info, &handle, &client_id ); + if (status) + goto error; + + if (id) *id = (DWORD)client_id.UniqueThread; + if (sa && (sa->nLength >= sizeof(*sa)) && sa->bInheritHandle) + SetHandleInformation( handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT ); + return handle;
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED); +error: + VirtualFreeEx( hProcess, info, 0, MEM_RELEASE ); + SetLastError( RtlNtStatusToDosError(status) ); return NULL; }
Index: dlls/ntdll/ntdll.spec =================================================================== RCS file: /home/wine/wine/dlls/ntdll/ntdll.spec,v retrieving revision 1.162 diff -u -r1.162 ntdll.spec --- dlls/ntdll/ntdll.spec 18 Aug 2004 00:04:58 -0000 1.162 +++ dlls/ntdll/ntdll.spec 23 Aug 2004 09:29:22 -0000 @@ -1135,6 +1135,9 @@ @ cdecl wine_nt_to_unix_file_name(ptr ptr long long) @ cdecl __wine_init_windows_dir(wstr wstr)
+# remote operations +@ cdecl __wine_is_current_process(long) + ################################################################ # Wine dll separation hacks, these will go away, don't use them # Index: dlls/ntdll/ntdll_misc.h =================================================================== RCS file: /home/wine/wine/dlls/ntdll/ntdll_misc.h,v retrieving revision 1.48 diff -u -r1.48 ntdll_misc.h --- dlls/ntdll/ntdll_misc.h 15 Jul 2004 22:07:21 -0000 1.48 +++ dlls/ntdll/ntdll_misc.h 23 Aug 2004 09:29:22 -0000 @@ -98,4 +98,9 @@ extern int ntdll_wcstoumbs(DWORD flags, const WCHAR* src, int srclen, char* dst, int dstlen, const char* defchar, int *used );
+/* remote operations */ +extern BOOL __wine_is_current_process( HANDLE handle ); +extern NTSTATUS remote_op( HANDLE process, int type, + void* params, int params_size, void* result, int* result_size ); + #endif Index: dlls/ntdll/thread.c =================================================================== RCS file: /home/wine/wine/dlls/ntdll/thread.c,v retrieving revision 1.20 diff -u -r1.20 thread.c --- dlls/ntdll/thread.c 13 Aug 2004 23:20:27 -0000 1.20 +++ dlls/ntdll/thread.c 23 Aug 2004 09:29:26 -0000 @@ -215,6 +215,29 @@ ULONG size; int request_pipe[2]; NTSTATUS status; + + if( ! __wine_is_current_process( process ) ) + { + struct remote_op_params_new_thread op_params; + struct remote_op_result_new_thread op_result; + int result_size = sizeof(struct remote_op_result_new_thread); + + op_params.suspend = suspended; + op_params.stack_addr = stack_addr; + op_params.stack_reserve = stack_reserve; + op_params.stack_commit = stack_commit; + op_params.start = start; + op_params.param = param; + status = remote_op( process, REMOTE_OP_NEW_THREAD, + &op_params, sizeof(op_params), + &op_result, &result_size ); + if (status) + return status; + + handle = op_result.handle; + tid = op_result.tid; + goto success; + }
if (pipe( request_pipe ) == -1) return STATUS_TOO_MANY_OPENED_FILES; fcntl( request_pipe[1], F_SETFD, 1 ); /* set close on exec flag */ @@ -284,6 +307,7 @@ goto error; }
+success: if (id) id->UniqueThread = (HANDLE)tid; if (handle_ptr) *handle_ptr = handle; else NtClose( handle ); Index: dlls/ntdll/virtual.c =================================================================== RCS file: /home/wine/wine/dlls/ntdll/virtual.c,v retrieving revision 1.38 diff -u -r1.38 virtual.c --- dlls/ntdll/virtual.c 18 Aug 2004 00:04:58 -0000 1.38 +++ dlls/ntdll/virtual.c 23 Aug 2004 09:29:36 -0000 @@ -1024,11 +1024,11 @@
/*********************************************************************** - * is_current_process + * __wine_is_current_process * * Check whether a process handle is for the current process. */ -static BOOL is_current_process( HANDLE handle ) +BOOL __wine_is_current_process( HANDLE handle ) { BOOL ret = FALSE;
@@ -1045,6 +1045,53 @@
/*********************************************************************** + * remote_op + * + */ +NTSTATUS remote_op( HANDLE process, int type, + void* params, int params_size, void* result, int* result_size ) +{ + HANDLE event; + NTSTATUS status, remote_status; + + /* send params */ + SERVER_START_REQ( remote_operation ) + { + req->handle = process; + req->type = type; + if (params) wine_server_add_data( req, params, params_size ); + if (!(status = wine_server_call( req ))) + event = reply->event; + } + SERVER_END_REQ; + if (status) + return status; + + /* wait for completion */ + status = NtWaitForMultipleObjects( 1, &event, FALSE, FALSE, NULL ); + NtClose( event ); + if (HIWORD(status)) + return status; + + /* get result */ + remote_status = 0; /* make compiler happy */ + SERVER_START_REQ( remote_operation_result ) + { + wine_server_set_reply( req, result, *result_size ); + if (!(status = wine_server_call( req ))) + { + remote_status = reply->status; + if (result) + *result_size = wine_server_reply_size( reply ); + } + } + SERVER_END_REQ; + + return status? status : remote_status; +} + + +/*********************************************************************** * virtual_init */ void virtual_init(void) @@ -1158,19 +1205,38 @@ { void *base; BYTE vprot; - DWORD size = *size_ptr; + DWORD size; NTSTATUS status = STATUS_SUCCESS; struct file_view *view;
- if (!is_current_process( process )) - { - ERR("Unsupported on other process\n"); - return STATUS_ACCESS_DENIED; - } + TRACE("%p %p %p %p %lx %08lx\n", process, ret, addr, size_ptr, type, protect ); + + /* sanity check */ + if (NULL == ret || NULL == size_ptr) return STATUS_INVALID_PARAMETER; + + TRACE("size=%08lx\n", *size_ptr ); + if (!(size = *size_ptr)) return STATUS_INVALID_PARAMETER;
- TRACE("%p %08lx %lx %08lx\n", addr, size, type, protect ); + if (!__wine_is_current_process( process )) + { + struct remote_op_params_vm_alloc alloc_params; + struct remote_op_result_vm_alloc alloc_result; + int result_size = sizeof(struct remote_op_result_vm_alloc);
- if (!size) return STATUS_INVALID_PARAMETER; + alloc_params.addr = addr; + alloc_params.size = size; + alloc_params.type = type; + alloc_params.protect = protect; + status = remote_op( process, REMOTE_OP_VM_ALLOC, + &alloc_params, sizeof(alloc_params), + &alloc_result, &result_size ); + if (!status) + { + *ret = alloc_result.base; + *size_ptr = alloc_result.size; + } + return status; + }
/* Round parameters to a page boundary */
@@ -1265,16 +1331,37 @@ FILE_VIEW *view; char *base; NTSTATUS status = STATUS_SUCCESS; - LPVOID addr = *addr_ptr; - DWORD size = *size_ptr; + LPVOID addr; + DWORD size;
- if (!is_current_process( process )) + TRACE("%p %p %p %lx\n", process, addr_ptr, size_ptr, type ); + + /* sanity check */ + if (NULL == addr_ptr || NULL == size_ptr) return ERROR_INVALID_PARAMETER; + + addr = *addr_ptr; + size = *size_ptr; + TRACE("addr=%p size=%08lx\n", addr, size ); + + if (!__wine_is_current_process( process )) { - ERR("Unsupported on other process\n"); - return STATUS_ACCESS_DENIED; - } + struct remote_op_params_vm_free free_params; + struct remote_op_result_vm_free free_result; + int result_size = sizeof(struct remote_op_result_vm_free);
- TRACE("%p %08lx %lx\n", addr, size, type ); + free_params.addr = addr; + free_params.size = size; + free_params.type = type; + status = remote_op( process, REMOTE_OP_VM_FREE, + &free_params, sizeof(free_params), + &free_result, &result_size ); + if (!status) + { + *addr_ptr = free_result.base; + *size_ptr = free_result.size; + } + return status; + }
/* Fix the parameters */
@@ -1341,16 +1428,38 @@ char *base; UINT i; BYTE vprot, *p; - DWORD prot, size = *size_ptr; - LPVOID addr = *addr_ptr; + DWORD prot, size; + LPVOID addr;
- if (!is_current_process( process )) + TRACE("%p %p %p %08lx %p\n", process, addr_ptr, size_ptr, new_prot, old_prot ); + + /* sanity check */ + if (NULL == addr_ptr || NULL == size_ptr) return ERROR_INVALID_PARAMETER; + + addr = *addr_ptr; + size = *size_ptr; + TRACE("addr=%p size=%08lx\n", addr, size ); + + if (!__wine_is_current_process( process )) { - ERR("Unsupported on other process\n"); - return STATUS_ACCESS_DENIED; - } + struct remote_op_params_vm_protect protect_params; + struct remote_op_result_vm_protect protect_result; + int result_size = sizeof(struct remote_op_result_vm_protect);
- TRACE("%p %08lx %08lx\n", addr, size, new_prot ); + protect_params.addr = addr; + protect_params.size = size; + protect_params.new_prot = new_prot; + status = remote_op( process, REMOTE_OP_VM_PROTECT, + &protect_params, sizeof(protect_params), + &protect_result, &result_size ); + if (!status) + { + *addr_ptr = protect_result.base; + *size_ptr = protect_result.size; + if (old_prot) *old_prot = protect_result.old_prot; + } + return status; + }
/* Fix the parameters */
@@ -1409,15 +1518,40 @@ UINT size = 0; MEMORY_BASIC_INFORMATION *info = buffer;
+ TRACE("%p %p %d %p %lu %p\n", process, addr, info_class, buffer, len, res_len ); + + /* sanity check */ + if (NULL == buffer) return ERROR_INVALID_PARAMETER; + + if (!__wine_is_current_process( process )) + { + struct remote_op_params_vm_query *query_params; + int result_size = len; + NTSTATUS status; + + query_params = (struct remote_op_params_vm_query*) + RtlAllocateHeap( GetProcessHeap(), 0, sizeof(struct remote_op_params_vm_query) + len ); + if (!query_params) + return STATUS_NO_MEMORY; + + query_params->addr = (void*) addr; + query_params->info_class = info_class; + if (len) memcpy( query_params + 1, buffer, len ); + status = remote_op( process, REMOTE_OP_VM_QUERY, + query_params, sizeof(struct remote_op_params_vm_query) + len, + buffer, &result_size ); + RtlFreeHeap( GetProcessHeap(), 0, query_params ); + if (!status) + if (res_len) *res_len = result_size; + return status; + } + if (info_class != MemoryBasicInformation) return STATUS_INVALID_INFO_CLASS; if (ADDRESS_SPACE_LIMIT && addr >= ADDRESS_SPACE_LIMIT) return STATUS_WORKING_SET_LIMIT_RANGE;
- if (!is_current_process( process )) - { - ERR("Unsupported on other process\n"); - return STATUS_ACCESS_DENIED; - } + /* sanity check */ + if (len < sizeof(MEMORY_BASIC_INFORMATION)) return ERROR_INVALID_PARAMETER;
base = ROUND_ADDR( addr, page_mask );
@@ -1497,10 +1631,10 @@ */ NTSTATUS WINAPI NtLockVirtualMemory( HANDLE process, PVOID *addr, ULONG *size, ULONG unknown ) { - if (!is_current_process( process )) + /* FIXME */ + if (!__wine_is_current_process( process )) { - ERR("Unsupported on other process\n"); - return STATUS_ACCESS_DENIED; + return STATUS_SUCCESS; } return STATUS_SUCCESS; } @@ -1512,10 +1646,10 @@ */ NTSTATUS WINAPI NtUnlockVirtualMemory( HANDLE process, PVOID *addr, ULONG *size, ULONG unknown ) { - if (!is_current_process( process )) + /* FIXME */ + if (!__wine_is_current_process( process )) { - ERR("Unsupported on other process\n"); - return STATUS_ACCESS_DENIED; + return STATUS_SUCCESS; } return STATUS_SUCCESS; } @@ -1608,14 +1742,39 @@ HANDLE shared_file; BOOL removable = FALSE;
- if (!is_current_process( process )) - { - ERR("Unsupported on other process\n"); - return STATUS_ACCESS_DENIED; - } + TRACE("%p %p %p %lu %lu %p %p %d %lu %lx\n", handle, process, addr_ptr, zero_bits, + commit_size, offset, size_ptr, inherit, alloc_type, protect );
- TRACE("handle=%p addr=%p off=%lx%08lx size=%x access=%lx\n", - handle, *addr_ptr, offset->u.HighPart, offset->u.LowPart, size, protect ); + /* sanity check */ + if (NULL == addr_ptr || NULL == size_ptr || NULL == offset) return ERROR_INVALID_PARAMETER; + + TRACE("addr=%p off=%lx%08lx\n", *addr_ptr, offset->u.HighPart, offset->u.LowPart ); + + if (!__wine_is_current_process( process )) + { + struct remote_op_params_vm_map map_params; + struct remote_op_result_vm_map map_result; + int result_size = sizeof(struct remote_op_result_vm_map); + + map_params.addr = *addr_ptr; + map_params.zero_bits = zero_bits; + map_params.commit_size = commit_size; + map_params.offset_low = offset->u.LowPart; + map_params.offset_high = offset->u.HighPart; + map_params.size = *size_ptr; + map_params.inherit = inherit; + map_params.alloc_type = alloc_type; + map_params.protect = protect; + res = remote_op( process, REMOTE_OP_VM_MAP, + &map_params, sizeof(map_params), + &map_result, &result_size ); + if (!res) + { + *addr_ptr = map_result.base; + *size_ptr = map_result.size; + } + return res; + }
/* Check parameters */
@@ -1764,11 +1923,14 @@ NTSTATUS status = STATUS_INVALID_PARAMETER; void *base = ROUND_ADDR( addr, page_mask );
- if (!is_current_process( process )) + TRACE("%p %p base=%p\n", process, addr, base ); + + if (!__wine_is_current_process( process )) { - ERR("Unsupported on other process\n"); - return STATUS_ACCESS_DENIED; + return remote_op( process, REMOTE_OP_VM_UNMAP, + &addr, sizeof(addr), NULL, NULL ); } + RtlEnterCriticalSection( &csVirtual ); if ((view = VIRTUAL_FindView( base )) && (base == view->base)) { @@ -1789,13 +1951,38 @@ { FILE_VIEW *view; NTSTATUS status = STATUS_SUCCESS; - void *addr = ROUND_ADDR( *addr_ptr, page_mask ); + void *addr; + ULONG size;
- if (!is_current_process( process )) + TRACE("%p %p %p %lx\n", process, addr_ptr, size_ptr, unknown ); + + /* sanity check */ + if (NULL == addr_ptr || NULL == size_ptr) return ERROR_INVALID_PARAMETER; + + size = *size_ptr; + TRACE("addr=%p size=%08lx\n", *addr_ptr, size ); + + if (!__wine_is_current_process( process )) { - ERR("Unsupported on other process\n"); - return STATUS_ACCESS_DENIED; + struct remote_op_params_vm_flush flush_params; + struct remote_op_result_vm_flush flush_result; + int result_size = sizeof(struct remote_op_result_vm_flush); + + flush_params.addr = (void*) *addr_ptr; + flush_params.size = size; + flush_params.unknown = unknown; + status = remote_op( process, REMOTE_OP_VM_FLUSH, + &flush_params, sizeof(flush_params), + &flush_result, &result_size ); + if (!status) + { + *addr_ptr = flush_result.base; + if (!*size_ptr) *size_ptr = flush_result.size; + } + return status; } + + addr = ROUND_ADDR( *addr_ptr, page_mask ); RtlEnterCriticalSection( &csVirtual ); if (!(view = VIRTUAL_FindView( addr ))) status = STATUS_INVALID_PARAMETER; else Index: server/process.c =================================================================== RCS file: /home/wine/wine/server/process.c,v retrieving revision 1.117 diff -u -r1.117 process.c --- server/process.c 14 Jun 2004 17:02:00 -0000 1.117 +++ server/process.c 23 Aug 2004 09:30:22 -0000 @@ -45,6 +45,7 @@ #include "request.h" #include "console.h" #include "user.h" +#include "object.h"
/* process structure */
@@ -972,6 +973,7 @@ reply->info_size = 0; current->process->peb = req->peb; current->process->ldt_copy = req->ldt_copy; + current->process->kernel_thread_start = req->kernel_thread_start; current->process->startup_info = init_process( reply ); }
@@ -1188,5 +1190,136 @@ reply->event = alloc_handle( current->process, process->idle_event, EVENT_ALL_ACCESS, 0 ); release_object( process ); + } +} + +/* return address of internal thread start function in kernel32 */ +DECL_HANDLER(get_kernel_thread_start) +{ + struct process *process; + + /* because this is used for CreateRemoteThread, + required access rights must be the same + */ + if ((process = get_process_from_handle( req->handle, + PROCESS_CREATE_THREAD + | PROCESS_QUERY_INFORMATION + | PROCESS_VM_OPERATION + | PROCESS_VM_READ | PROCESS_VM_WRITE ))) + { + reply->address = process->kernel_thread_start; + release_object( process ); + } +} + +/* Accept parameters for remote operation and start it */ +DECL_HANDLER(remote_operation) +{ + int access; + struct process *process; + struct event *event; + + /* define required access rights */ + switch( req->type ) + { + case REMOTE_OP_NEW_THREAD: + access = PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION + | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE; + break; + case REMOTE_OP_VM_ALLOC: access = PROCESS_VM_OPERATION; break; + case REMOTE_OP_VM_FREE: access = PROCESS_VM_OPERATION; break; + case REMOTE_OP_VM_PROTECT: access = PROCESS_VM_OPERATION; break; + case REMOTE_OP_VM_QUERY: access = PROCESS_QUERY_INFORMATION; break; + case REMOTE_OP_VM_MAP: access = PROCESS_VM_OPERATION; break; + case REMOTE_OP_VM_UNMAP: access = PROCESS_VM_OPERATION; break; + case REMOTE_OP_VM_FLUSH: access = PROCESS_VM_OPERATION; break; /* FIXME: is access right? */ + default: + set_error( STATUS_INVALID_PARAMETER ); + return; + } + + /* get process object */ + if (!(process = get_process_from_handle( req->handle, access ))) return; + + /* dispose result data buffer if allocated */ + if (current->result_data) + { + free( current->result_data ); + current->result_data = NULL; + } + + /* create event object */ + reply->event = NULL; + if (current->result_event) + { + release_object( current->result_event ); + current->result_event = NULL; + } + if (!(event = create_event( NULL, 0, 1, 0 ))) + goto error; + + if (!(reply->event = alloc_handle( current->process, event, EVENT_ALL_ACCESS, FALSE ))) + goto error; + + /* FIXME: pass somehow operation type, params and originator thread id + for some thread in the process and force execution of operation */ + set_error( STATUS_NOT_IMPLEMENTED ); + + /* save event object in thread structure for future set operation; + we do not release it here */ + current->result_event = event; + release_object( process ); + return; + +error: + if (reply->event) close_handle( current->process, reply->event, NULL ); + if (event) release_object( event ); + release_object( process ); +} + +/* save result of remote operation and wakeup originator */ +DECL_HANDLER(remote_operation_complete) +{ + struct thread *thread = get_thread_from_id( req->originator ); + + if (!thread) return; + + /* save status */ + thread->remote_status = req->status; + + /* allocate buffer for result data, if required */ + if (thread->result_data) + { + free( thread->result_data ); + thread->result_data = NULL; + } + if ((thread->result_size = get_req_data_size())) + { + if ((thread->result_data = mem_alloc( thread->result_size ))) + memcpy( thread->result_data, get_req_data(), thread->result_size ); + else + thread->remote_status = get_error(); + } + + /* set event */ + if (thread->result_event) + { + set_event( thread->result_event ); + release_object( thread->result_event ); + thread->result_event = NULL; + } + release_object( thread ); +} + +/* return status and result data from remote opertaion */ +DECL_HANDLER(remote_operation_result) +{ + reply->status = current->remote_status; + + if (current->result_data) + { + void *result = current->result_data; + current->result_data = NULL; + set_reply_data_ptr( result, current->result_size ); } } Index: server/process.h =================================================================== RCS file: /home/wine/wine/server/process.h,v retrieving revision 1.42 diff -u -r1.42 process.h --- server/process.h 10 Dec 2003 04:08:06 -0000 1.42 +++ server/process.h 23 Aug 2004 09:30:24 -0000 @@ -79,6 +79,8 @@ struct process_dll exe; /* main exe file */ void *peb; /* PEB address in client address space */ void *ldt_copy; /* pointer to LDT copy in client addr space */ + void *kernel_thread_start; /* addr of internal kernel32 thread routine */ + /* (in client address space) */ };
struct process_snapshot Index: server/protocol.def =================================================================== RCS file: /home/wine/wine/server/protocol.def,v retrieving revision 1.110 diff -u -r1.110 protocol.def --- server/protocol.def 18 Aug 2004 00:04:58 -0000 1.110 +++ server/protocol.def 23 Aug 2004 09:30:45 -0000 @@ -232,6 +232,7 @@ @REQ(init_process) void* peb; /* addr of PEB */ void* ldt_copy; /* addr of LDT copy */ + void* kernel_thread_start; /* addr of internal kernel32 thread routine */ @REPLY int create_flags; /* creation flags */ unsigned int server_start; /* server start time (GetTickCount) */ @@ -2166,3 +2167,133 @@ #define SET_GLOBAL_SHELL_WINDOWS 0x01 /* set both main shell and listview windows */ #define SET_GLOBAL_PROGMAN_WINDOW 0x02 #define SET_GLOBAL_TASKMAN_WINDOW 0x04 + +/* Get address of kernel32 internal THREAD_Start function */ +@REQ(get_kernel_thread_start) + obj_handle_t handle; /* process handle */ +@REPLY + void* address; +@END + +/* Accept parameters for remote operation and start it */ +@REQ(remote_operation) + obj_handle_t handle; /* process handle */ + int type; /* operation type (see below) */ + VARARG(data,bytes); /* operation parameters (see below) */ +@REPLY + obj_handle_t event; /* originator waits for it */ +@END +enum remote_op_type +{ + REMOTE_OP_NEW_THREAD, + REMOTE_OP_VM_ALLOC, + REMOTE_OP_VM_FREE, + REMOTE_OP_VM_PROTECT, + REMOTE_OP_VM_QUERY, + REMOTE_OP_VM_MAP, + REMOTE_OP_VM_UNMAP, + REMOTE_OP_VM_FLUSH +}; + +/* Notify that remote operation has been complete */ +@REQ(remote_operation_complete) + thread_id_t originator; /* originator thread id */ + unsigned int status; /* operation status */ + VARARG(data,bytes); /* operation result data (see below) */ +@END + +/* Get result of remote opertaion */ +@REQ(remote_operation_result) +@REPLY + unsigned int status; /* operation status */ + VARARG(data,bytes); /* operation result data (see below) */ +@END + +struct remote_op_params_new_thread +{ + int suspend; + void *stack_addr; + unsigned int stack_reserve; + unsigned int stack_commit; + void *start; + void *param; +}; +struct remote_op_result_new_thread +{ + obj_handle_t handle; + thread_id_t tid; +}; + +struct remote_op_params_vm_alloc +{ + void *addr; + unsigned long size; + unsigned long type; + unsigned long protect; +}; +struct remote_op_result_vm_alloc +{ + void *base; + unsigned long size; +}; + +struct remote_op_params_vm_free +{ + void *addr; + unsigned long size; + unsigned long type; +}; +struct remote_op_result_vm_free +{ + void *base; + unsigned long size; +}; + +struct remote_op_params_vm_protect +{ + void *addr; + unsigned long size; + unsigned long new_prot; +}; +struct remote_op_result_vm_protect +{ + void *base; + unsigned long size; + unsigned long old_prot; +}; + +struct remote_op_params_vm_query +{ + void *addr; + int info_class; +}; + +struct remote_op_params_vm_map +{ + void *addr; + unsigned long zero_bits; + unsigned long commit_size; + unsigned long offset_low; + long offset_high; + unsigned long size; + int inherit; + unsigned long alloc_type; + unsigned long protect; +}; +struct remote_op_result_vm_map +{ + void *base; + unsigned long size; +}; + +struct remote_op_params_vm_flush +{ + void *addr; + unsigned long size; + unsigned long unknown; +}; +struct remote_op_result_vm_flush +{ + void *base; + unsigned long size; +}; Index: server/thread.c =================================================================== RCS file: /home/wine/wine/server/thread.c,v retrieving revision 1.103 diff -u -r1.103 thread.c --- server/thread.c 27 Oct 2003 22:10:22 -0000 1.103 +++ server/thread.c 23 Aug 2004 09:30:54 -0000 @@ -142,6 +142,8 @@ thread->suspend = 0; thread->creation_time = time(NULL); thread->exit_time = 0; + thread->result_data = NULL; + thread->result_event = NULL;
for (i = 0; i < MAX_INFLIGHT_FDS; i++) thread->inflight[i].server = thread->inflight[i].client = -1; @@ -210,6 +212,8 @@ if (thread->request_fd) release_object( thread->request_fd ); if (thread->reply_fd) release_object( thread->reply_fd ); if (thread->wait_fd) release_object( thread->wait_fd ); + if (thread->result_data) free( thread->result_data ); + if (thread->result_event) release_object( thread->result_event ); free_msg_queue( thread ); cleanup_clipboard_thread(thread); destroy_thread_windows( thread ); @@ -226,6 +230,8 @@ thread->request_fd = NULL; thread->reply_fd = NULL; thread->wait_fd = NULL; + thread->result_data = NULL; + thread->result_event = NULL;
if (thread == booting_thread) /* killing booting thread */ { Index: server/thread.h =================================================================== RCS file: /home/wine/wine/server/thread.h,v retrieving revision 1.56 diff -u -r1.56 thread.h --- server/thread.h 10 Dec 2003 01:12:18 -0000 1.56 +++ server/thread.h 23 Aug 2004 09:30:56 -0000 @@ -94,6 +94,10 @@ time_t creation_time; /* Thread creation time */ time_t exit_time; /* Thread exit time */ struct token *token; /* security token associated with this thread */ + unsigned int remote_status; /* error code from remote operation */ + void *result_data; /* result data from remote operation */ + int result_size; /* size of result data */ + struct event *result_event; /* originator of remote operation waits for it */ };
struct thread_snapshot
Alexander Yaworsky wrote:
Hello
This is a basic framework. Not finished, not really tested -- just stable point. It does no harm for applications I'm using. I would be glad to get some comments and suggestions.
Looks OK to me but it's missing the most important part :) How are you intending to make the client do a remote op - signals? ptrace? worker thread? (I think Alexandre dislikes such worker threads, but it'd seem to be the simplest/most understandable solution)
cool! -mike
Alexander Yaworsky wrote:
Hello
This is a basic framework. Not finished, not really tested -- just stable point. It does no harm for applications I'm using. I would be glad to get some comments and suggestions.
Looks good so far, apart from a few comments below.
@@ -168,16 +137,68 @@
- Success: Handle to the new thread.
- Failure: NULL. Use GetLastError() to find the error cause.
- BUGS
- Unimplemented
*/ HANDLE WINAPI CreateRemoteThread( HANDLE hProcess, SECURITY_ATTRIBUTES *sa, SIZE_T stack, LPTHREAD_START_ROUTINE start, LPVOID param, DWORD flags, LPDWORD id ) {
- FIXME("(): stub, Write Me.\n");
- extern BOOL __wine_is_current_process( HANDLE hProcess );
- HANDLE handle;
- CLIENT_ID client_id;
- NTSTATUS status;
- SIZE_T stack_reserve = 0, stack_commit = 0;
- struct new_thread_info *info;
- PRTL_THREAD_START_ROUTINE start_addr;
- if (!(info = VirtualAllocEx( hProcess, NULL, sizeof(*info),
MEM_COMMIT, PAGE_READWRITE )))
return 0;
- if( __wine_is_current_process( hProcess ) )
There is no need to export __wine_is_current_process from ntdll and use it here when standard win32 apis will suffice.
- {
info->func = start;
info->arg = param;
start_addr = (void*) THREAD_Start;
- }
- else
- {
struct new_thread_info local_info;
DWORD written;
local_info.func = start;
local_info.arg = param;
if( ! WriteProcessMemory( hProcess, &info, &local_info,
This looks wrong. &info? Don't you mean just info?
/***********************************************************************
remote_op
- */
+NTSTATUS remote_op( HANDLE process, int type,
void* params, int params_size, void* result, int* result_size )
+{
- HANDLE event;
- NTSTATUS status, remote_status;
- /* send params */
- SERVER_START_REQ( remote_operation )
- {
req->handle = process;
req->type = type;
if (params) wine_server_add_data( req, params, params_size );
if (!(status = wine_server_call( req )))
event = reply->event;
- }
- SERVER_END_REQ;
- if (status)
return status;
- /* wait for completion */
- status = NtWaitForMultipleObjects( 1, &event, FALSE, FALSE, NULL );
- NtClose( event );
- if (HIWORD(status))
return status;
- /* get result */
- remote_status = 0; /* make compiler happy */
- SERVER_START_REQ( remote_operation_result )
- {
wine_server_set_reply( req, result, *result_size );
if (!(status = wine_server_call( req )))
{
remote_status = reply->status;
if (result)
*result_size = wine_server_reply_size( reply );
}
- }
- SERVER_END_REQ;
- return status? status : remote_status;
+}
I don't really like the idea of a multiplexer like this, but I guess otherwise we would end up with duplicated code.
+/* return address of internal thread start function in kernel32 */ +DECL_HANDLER(get_kernel_thread_start) +{
- struct process *process;
- /* because this is used for CreateRemoteThread,
required access rights must be the same
*/
- if ((process = get_process_from_handle( req->handle,
PROCESS_CREATE_THREAD
| PROCESS_QUERY_INFORMATION
| PROCESS_VM_OPERATION
| PROCESS_VM_READ | PROCESS_VM_WRITE )))
I would argue that it is perfectly alright for the access rights to be just PROCESS_QUERY_INFORMATION here.
- {
reply->address = process->kernel_thread_start;
release_object( process );
- }
+}
+/* Accept parameters for remote operation and start it */ +DECL_HANDLER(remote_operation) +{
- int access;
- struct process *process;
- struct event *event;
- /* define required access rights */
- switch( req->type )
- {
case REMOTE_OP_NEW_THREAD:
access = PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION
| PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE;
break;
case REMOTE_OP_VM_ALLOC: access = PROCESS_VM_OPERATION; break;
case REMOTE_OP_VM_FREE: access = PROCESS_VM_OPERATION; break;
case REMOTE_OP_VM_PROTECT: access = PROCESS_VM_OPERATION; break;
case REMOTE_OP_VM_QUERY: access = PROCESS_QUERY_INFORMATION; break;
case REMOTE_OP_VM_MAP: access = PROCESS_VM_OPERATION; break;
case REMOTE_OP_VM_UNMAP: access = PROCESS_VM_OPERATION; break;
case REMOTE_OP_VM_FLUSH: access = PROCESS_VM_OPERATION; break; /* FIXME: is access right? */
default:
set_error( STATUS_INVALID_PARAMETER );
return;
- }
- /* get process object */
- if (!(process = get_process_from_handle( req->handle, access ))) return;
- /* dispose result data buffer if allocated */
- if (current->result_data)
- {
free( current->result_data );
current->result_data = NULL;
- }
- /* create event object */
- reply->event = NULL;
- if (current->result_event)
- {
release_object( current->result_event );
current->result_event = NULL;
- }
- if (!(event = create_event( NULL, 0, 1, 0 )))
goto error;
- if (!(reply->event = alloc_handle( current->process, event, EVENT_ALL_ACCESS, FALSE )))
goto error;
- /* FIXME: pass somehow operation type, params and originator thread id
for some thread in the process and force execution of operation */
- set_error( STATUS_NOT_IMPLEMENTED );
- /* save event object in thread structure for future set operation;
we do not release it here */
- current->result_event = event;
- release_object( process );
- return;
+error:
- if (reply->event) close_handle( current->process, reply->event, NULL );
- if (event) release_object( event );
- release_object( process );
+}
Is it really necessary to perform the action required for remote invocation of some operation asynchronously? It seems to add a lot of unneeded complexity.
+/* save result of remote operation and wakeup originator */ +DECL_HANDLER(remote_operation_complete) +{
- struct thread *thread = get_thread_from_id( req->originator );
- if (!thread) return;
- /* save status */
- thread->remote_status = req->status;
- /* allocate buffer for result data, if required */
- if (thread->result_data)
- {
free( thread->result_data );
thread->result_data = NULL;
- }
- if ((thread->result_size = get_req_data_size()))
- {
if ((thread->result_data = mem_alloc( thread->result_size )))
memcpy( thread->result_data, get_req_data(), thread->result_size );
else
thread->remote_status = get_error();
- }
- /* set event */
- if (thread->result_event)
- {
set_event( thread->result_event );
release_object( thread->result_event );
thread->result_event = NULL;
- }
- release_object( thread );
+}
+/* return status and result data from remote opertaion */ +DECL_HANDLER(remote_operation_result) +{
- reply->status = current->remote_status;
- if (current->result_data)
- {
void *result = current->result_data;
current->result_data = NULL;
}set_reply_data_ptr( result, current->result_size );
}
Rob
There is no need to export __wine_is_current_process from ntdll and use it here when standard win32 apis will suffice.
Oh yes, I was wondering why this was bothering me. I think you can achieve the same effect like this:
if ((handle == (HANDLE)0xffffffff) || (handle == (HANDLE) NtCurrentTeb()->ClientId.UniqueProcess))
seeing as -1 is a magic handle value that means the current process so I guess either input could be valid.
On the multiplexing - I spent a few minutes kicking around ideas for others way to do it, but they all basically boiled down to variations on what you've done here ... the only way to make it race free is via the server, and that means marshalling the call into a structure and uploading it to the server so the other process can download it when it's been forced onto the right codepath.
That leaves the question of how to do the forcing.
The simplest is to have a worker thread which simply sits blocked on a semaphore or something, and then when signalled downloads a request from the server, dispatches it, and returns. That means spinning up a thread at startup though, and I seem to recall that due to the dubious history of the 'service thread' people are no longer keen on that.
So ... another way would be to send a signal and use a signal handler to invoke the RPC. I don't really know enough to comment on this approach: I think the way signals work differs in kthread/pthread systems, ie on NPTL boxes signals are sent to threads not processes which could cause wierd races and bugs inside apps. Maybe you can still send signals to processes in NPTL ... I don't know.
The final way I can think of is to suspend the entire app using ptrace, grab a thread (which?) and rewrite its register context and stack to point at some remote_op_dispatcher function the address of which is registered at startup with the wineserver. This downloads the struct using the host threads wineserver connection, deals with the request and returns. Then the stack is cleaned up, the register context restored, and the app resumed.
Ick. That sounds really really complicated. I'd be tempted to go for bringing back the service thread but I wasn't around when it was busy causing pain so maybe I sound a bit blase about that :)
Mike Hearn wrote:
There is no need to export __wine_is_current_process from ntdll and use it here when standard win32 apis will suffice.
Oh yes, I was wondering why this was bothering me. I think you can achieve the same effect like this:
if ((handle == (HANDLE)0xffffffff) || (handle == (HANDLE) NtCurrentTeb()->ClientId.UniqueProcess))
Well, not quite. You can open another handle to the process using DuplicateHandle or something else, so you need to go via the server. The simplest way to do this using Win32 is: if (GetProcessId(handle) == GetCurrentProcessId())
On the multiplexing - I spent a few minutes kicking around ideas for others way to do it, but they all basically boiled down to variations on what you've done here ... the only way to make it race free is via the server, and that means marshalling the call into a structure and uploading it to the server so the other process can download it when it's been forced onto the right codepath.
That leaves the question of how to do the forcing.
The simplest is to have a worker thread which simply sits blocked on a semaphore or something, and then when signalled downloads a request from the server, dispatches it, and returns. That means spinning up a thread at startup though, and I seem to recall that due to the dubious history of the 'service thread' people are no longer keen on that.
So ... another way would be to send a signal and use a signal handler to invoke the RPC. I don't really know enough to comment on this approach: I think the way signals work differs in kthread/pthread systems, ie on NPTL boxes signals are sent to threads not processes which could cause wierd races and bugs inside apps. Maybe you can still send signals to processes in NPTL ... I don't know.
The final way I can think of is to suspend the entire app using ptrace, grab a thread (which?) and rewrite its register context and stack to point at some remote_op_dispatcher function the address of which is registered at startup with the wineserver. This downloads the struct using the host threads wineserver connection, deals with the request and returns. Then the stack is cleaned up, the register context restored, and the app resumed.
This seems like the best idea if it is possible. The stack doesn't need to be anything special, just a rewritable piece of memory whose location is always known. The only trouble is that creating a thread is quite complicated and involves a lot of code, so there is a chance that it may not work using this method.
Ick. That sounds really really complicated. I'd be tempted to go for bringing back the service thread but I wasn't around when it was busy causing pain so maybe I sound a bit blase about that :)
Perhaps the old-timers can tell us a story around the camp fire about this...
Rob
Robert Shearman rob@codeweavers.com writes:
Mike Hearn wrote:
Ick. That sounds really really complicated. I'd be tempted to go for bringing back the service thread but I wasn't around when it was busy causing pain so maybe I sound a bit blase about that :)
Perhaps the old-timers can tell us a story around the camp fire about this...
The main problem with a service thread is that it basically turns all apps into multi-threaded apps whether they expect it or not, so it creates various synchronization issues (for instance apps suddenly start to receive DLL_THREAD_ATTACH notifications they don't expect, things like that). And of course it also uses more resources (stack space etc.); this is acceptable for apps that actually use the feature, but we don't want to penalize all apps to support such an uncommon case.
Hello
There is no need to export __wine_is_current_process from ntdll and use it here when standard win32 apis will suffice.
Well, not quite. You can open another handle to the process using DuplicateHandle or something else, so you need to go via the server. The simplest way to do this using Win32 is: if (GetProcessId(handle) == GetCurrentProcessId())
You miss. It seems we really need __wine_is_current_process.
if( ! WriteProcessMemory( hProcess, &info, &local_info,
This looks wrong. &info? Don't you mean just info?
Yes! Thanks.
- if ((process = get_process_from_handle( req->handle,
PROCESS_CREATE_THREAD
| PROCESS_QUERY_INFORMATION
| PROCESS_VM_OPERATION
| PROCESS_VM_READ | PROCESS_VM_WRITE )))
I would argue that it is perfectly alright for the access rights to be just PROCESS_QUERY_INFORMATION here.
Maybe. But with the full set of access rights CreateRemoteThread will return earlier in case of error.
Is it really necessary to perform the action required for remote invocation of some operation asynchronously? It seems to add a lot of unneeded complexity.
What shall we do with csVirtual critical section in ntdll/virtual.c?
Alexander Yaworsky wrote:
Hello
Well, not quite. You can open another handle to the process using DuplicateHandle or something else, so you need to go via the server. The simplest way to do this using Win32 is: if (GetProcessId(handle) == GetCurrentProcessId())
You miss. It seems we really need __wine_is_current_process.
Sorry, GetProcessId is not available in Wine yet. I'll submit a patch to add it.
Is it really necessary to perform the action required for remote invocation of some operation asynchronously? It seems to add a lot of unneeded complexity.
What shall we do with csVirtual critical section in ntdll/virtual.c?
Ah, I see now. We may need to make server calls while in the remote process and they will hang unless the "remote operation call" is done asynchronously.
Rob
Hi All,
I have been using spare time ro study a means to implement this api for several months in order to get my app (United Devices) to work but have been on the usual steep learning curve for Win32/Posix C and how Wine interacts. In looking at the wonderful work Alexander has submitted with this patch, I can see this was at least a mile over my head but am gratified to know I was at least exploring in the appropriate areas.
I have a question however, and by no means is this intended to be a critique but rather a newbie inquirey (I am too new to know what I don't know yet) and am very happy a developer was willing to take this on. :)
I am wondering why delete the CreateThread() code and refer all such requests to CreateRemoteThread()? It would appear to me that retaining the CreateThread() code in addition to and compliant with the patch changes would be more expeditious for apps that do not need a call for Remote Operations.
I ask this as I performed a cheap hack by copying the CreateThread() code to CreateRemoteThread(), sprinkled a bunch of Fixme's thru out the appropriate functions to see what info my app provided and what it may be looking for. The app actually worked quite well with the exception of spurious blocked threads upon closing, what a surprise :). Any help greatly appreciated and since I'm on the Left Coast, I can say "I'll be Back" to hopefully offer some production input.
Thanks, Roger
----- Original Message ----- From: "Alexander Yaworsky" yaworsky@migusoft.ru To: "Roger Olson" u60@comcast.net Cc: wine-devel@winehq.org Sent: Monday, August 23, 2004 4:15 AM Subject: CreateRemoteThread and related stuff (patch)
Hello
This is a basic framework. Not finished, not really tested -- just stable
point. It does no harm for applications I'm using. I would
be glad to get some comments and suggestions.
This patch does not contain files generated by tools/make_requests
Index: dlls/kernel/kernel_private.h
RCS file: /home/wine/wine/dlls/kernel/kernel_private.h,v retrieving revision 1.20 diff -u -r1.20 kernel_private.h --- dlls/kernel/kernel_private.h 14 May 2004 21:43:18 -0000 1.20 +++ dlls/kernel/kernel_private.h 23 Aug 2004 09:28:39 -0000 @@ -91,4 +91,6 @@ } INSTANCEDATA; #include "poppack.h"
+extern void CALLBACK THREAD_Start( void *ptr );
#endif Index: dlls/kernel/process.c =================================================================== RCS file: /home/wine/wine/dlls/kernel/process.c,v retrieving revision 1.71 diff -u -r1.71 process.c --- dlls/kernel/process.c 18 Aug 2004 21:03:32 -0000 1.71 +++ dlls/kernel/process.c 23 Aug 2004 09:29:03 -0000 @@ -894,6 +894,7 @@ { req->peb = peb; req->ldt_copy = &wine_ldt_copy;
req->kernel_thread_start = THREAD_Start; if ((ret = !wine_server_call_err( req ))) { main_exe_file = reply->exe_file;
Index: dlls/kernel/thread.c
RCS file: /home/wine/wine/dlls/kernel/thread.c,v retrieving revision 1.19 diff -u -r1.19 thread.c --- dlls/kernel/thread.c 7 Jul 2004 00:49:34 -0000 1.19 +++ dlls/kernel/thread.c 23 Aug 2004 09:29:08 -0000 @@ -90,13 +90,13 @@
- Start execution of a newly created thread. Does not return.
*/ -static void CALLBACK THREAD_Start( void *ptr ) +void CALLBACK THREAD_Start( void *ptr ) { struct new_thread_info *info = ptr; LPTHREAD_START_ROUTINE func = info->func; void *arg = info->arg;
- RtlFreeHeap( GetProcessHeap(), 0, info );
VirtualFree( info, 0, MEM_RELEASE );
if (TRACE_ON(relay)) DPRINTF("%04lx:Starting thread (entryproc=%p)\n",
GetCurrentThreadId(), func );
@@ -121,39 +121,8 @@ LPTHREAD_START_ROUTINE start, LPVOID param, DWORD flags, LPDWORD id ) {
- HANDLE handle;
- CLIENT_ID client_id;
- NTSTATUS status;
- SIZE_T stack_reserve = 0, stack_commit = 0;
- struct new_thread_info *info;
- if (!(info = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*info) )))
- {
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
return 0;
- }
- info->func = start;
- info->arg = param;
- if (flags & STACK_SIZE_PARAM_IS_A_RESERVATION) stack_reserve = stack;
- else stack_commit = stack;
- status = RtlCreateUserThread( GetCurrentProcess(), NULL, (flags &
CREATE_SUSPENDED) != 0,
NULL, stack_reserve, stack_commit,
THREAD_Start, info, &handle,
&client_id );
- if (status == STATUS_SUCCESS)
- {
if (id) *id = (DWORD)client_id.UniqueThread;
if (sa && (sa->nLength >= sizeof(*sa)) && sa->bInheritHandle)
SetHandleInformation( handle, HANDLE_FLAG_INHERIT,
HANDLE_FLAG_INHERIT );
- }
- else
- {
RtlFreeHeap( GetProcessHeap(), 0, info );
SetLastError( RtlNtStatusToDosError(status) );
handle = 0;
- }
- return handle;
return CreateRemoteThread( GetCurrentProcess(),
sa, stack, start, param, flags, id );
}
@@ -168,16 +137,68 @@
- Success: Handle to the new thread.
- Failure: NULL. Use GetLastError() to find the error cause.
- BUGS
*/
- Unimplemented
HANDLE WINAPI CreateRemoteThread( HANDLE hProcess, SECURITY_ATTRIBUTES
*sa, SIZE_T stack,
LPTHREAD_START_ROUTINE start, LPVOID
param,
DWORD flags, LPDWORD id )
{
- FIXME("(): stub, Write Me.\n");
- extern BOOL __wine_is_current_process( HANDLE hProcess );
- HANDLE handle;
- CLIENT_ID client_id;
- NTSTATUS status;
- SIZE_T stack_reserve = 0, stack_commit = 0;
- struct new_thread_info *info;
- PRTL_THREAD_START_ROUTINE start_addr;
- if (!(info = VirtualAllocEx( hProcess, NULL, sizeof(*info),
MEM_COMMIT, PAGE_READWRITE )))
return 0;
- if( __wine_is_current_process( hProcess ) )
- {
info->func = start;
info->arg = param;
start_addr = (void*) THREAD_Start;
- }
- else
- {
struct new_thread_info local_info;
DWORD written;
local_info.func = start;
local_info.arg = param;
if( ! WriteProcessMemory( hProcess, &info, &local_info,
sizeof(struct new_thread_info),
&written ) )
return NULL;
SERVER_START_REQ( get_kernel_thread_start )
{
req->handle = hProcess;
if (!(status = wine_server_call( req )))
start_addr = (PRTL_THREAD_START_ROUTINE) reply->address;
}
SERVER_END_REQ;
if (status)
goto error;
- }
- if (flags & STACK_SIZE_PARAM_IS_A_RESERVATION) stack_reserve = stack;
- else stack_commit = stack;
- status = RtlCreateUserThread( hProcess, NULL, (flags &
CREATE_SUSPENDED) != 0,
NULL, stack_reserve, stack_commit,
start_addr, info, &handle,
&client_id );
- if (status)
goto error;
- if (id) *id = (DWORD)client_id.UniqueThread;
- if (sa && (sa->nLength >= sizeof(*sa)) && sa->bInheritHandle)
SetHandleInformation( handle, HANDLE_FLAG_INHERIT,
HANDLE_FLAG_INHERIT );
- return handle;
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+error:
- VirtualFreeEx( hProcess, info, 0, MEM_RELEASE );
- SetLastError( RtlNtStatusToDosError(status) ); return NULL;
}
Index: dlls/ntdll/ntdll.spec
RCS file: /home/wine/wine/dlls/ntdll/ntdll.spec,v retrieving revision 1.162 diff -u -r1.162 ntdll.spec --- dlls/ntdll/ntdll.spec 18 Aug 2004 00:04:58 -0000 1.162 +++ dlls/ntdll/ntdll.spec 23 Aug 2004 09:29:22 -0000 @@ -1135,6 +1135,9 @@ @ cdecl wine_nt_to_unix_file_name(ptr ptr long long) @ cdecl __wine_init_windows_dir(wstr wstr)
+# remote operations +@ cdecl __wine_is_current_process(long)
################################################################ # Wine dll separation hacks, these will go away, don't use them # Index: dlls/ntdll/ntdll_misc.h =================================================================== RCS file: /home/wine/wine/dlls/ntdll/ntdll_misc.h,v retrieving revision 1.48 diff -u -r1.48 ntdll_misc.h --- dlls/ntdll/ntdll_misc.h 15 Jul 2004 22:07:21 -0000 1.48 +++ dlls/ntdll/ntdll_misc.h 23 Aug 2004 09:29:22 -0000 @@ -98,4 +98,9 @@ extern int ntdll_wcstoumbs(DWORD flags, const WCHAR* src, int srclen,
char* dst, int dstlen,
const char* defchar, int *used );
+/* remote operations */ +extern BOOL __wine_is_current_process( HANDLE handle ); +extern NTSTATUS remote_op( HANDLE process, int type,
void* params, int params_size, void* result,
int* result_size );
#endif Index: dlls/ntdll/thread.c =================================================================== RCS file: /home/wine/wine/dlls/ntdll/thread.c,v retrieving revision 1.20 diff -u -r1.20 thread.c --- dlls/ntdll/thread.c 13 Aug 2004 23:20:27 -0000 1.20 +++ dlls/ntdll/thread.c 23 Aug 2004 09:29:26 -0000 @@ -215,6 +215,29 @@ ULONG size; int request_pipe[2]; NTSTATUS status;
if( ! __wine_is_current_process( process ) )
{
struct remote_op_params_new_thread op_params;
struct remote_op_result_new_thread op_result;
int result_size = sizeof(struct remote_op_result_new_thread);
op_params.suspend = suspended;
op_params.stack_addr = stack_addr;
op_params.stack_reserve = stack_reserve;
op_params.stack_commit = stack_commit;
op_params.start = start;
op_params.param = param;
status = remote_op( process, REMOTE_OP_NEW_THREAD,
&op_params, sizeof(op_params),
&op_result, &result_size );
if (status)
return status;
handle = op_result.handle;
tid = op_result.tid;
goto success;
}
if (pipe( request_pipe ) == -1) return STATUS_TOO_MANY_OPENED_FILES; fcntl( request_pipe[1], F_SETFD, 1 ); /* set close on exec flag */
@@ -284,6 +307,7 @@ goto error; }
+success: if (id) id->UniqueThread = (HANDLE)tid; if (handle_ptr) *handle_ptr = handle; else NtClose( handle ); Index: dlls/ntdll/virtual.c =================================================================== RCS file: /home/wine/wine/dlls/ntdll/virtual.c,v retrieving revision 1.38 diff -u -r1.38 virtual.c --- dlls/ntdll/virtual.c 18 Aug 2004 00:04:58 -0000 1.38 +++ dlls/ntdll/virtual.c 23 Aug 2004 09:29:36 -0000 @@ -1024,11 +1024,11 @@
/***********************************************************************
is_current_process
*/
__wine_is_current_process
- Check whether a process handle is for the current process.
-static BOOL is_current_process( HANDLE handle ) +BOOL __wine_is_current_process( HANDLE handle ) { BOOL ret = FALSE;
@@ -1045,6 +1045,53 @@
/***********************************************************************
remote_op
- */
+NTSTATUS remote_op( HANDLE process, int type,
void* params, int params_size, void* result, int*
result_size )
+{
- HANDLE event;
- NTSTATUS status, remote_status;
- /* send params */
- SERVER_START_REQ( remote_operation )
- {
req->handle = process;
req->type = type;
if (params) wine_server_add_data( req, params, params_size );
if (!(status = wine_server_call( req )))
event = reply->event;
- }
- SERVER_END_REQ;
- if (status)
return status;
- /* wait for completion */
- status = NtWaitForMultipleObjects( 1, &event, FALSE, FALSE, NULL );
- NtClose( event );
- if (HIWORD(status))
return status;
- /* get result */
- remote_status = 0; /* make compiler happy */
- SERVER_START_REQ( remote_operation_result )
- {
wine_server_set_reply( req, result, *result_size );
if (!(status = wine_server_call( req )))
{
remote_status = reply->status;
if (result)
*result_size = wine_server_reply_size( reply );
}
- }
- SERVER_END_REQ;
- return status? status : remote_status;
+}
+/***********************************************************************
virtual_init
*/ void virtual_init(void) @@ -1158,19 +1205,38 @@ { void *base; BYTE vprot;
- DWORD size = *size_ptr;
- DWORD size; NTSTATUS status = STATUS_SUCCESS; struct file_view *view;
- if (!is_current_process( process ))
- {
ERR("Unsupported on other process\n");
return STATUS_ACCESS_DENIED;
- }
- TRACE("%p %p %p %p %lx %08lx\n", process, ret, addr, size_ptr, type,
protect );
- /* sanity check */
- if (NULL == ret || NULL == size_ptr) return STATUS_INVALID_PARAMETER;
- TRACE("size=%08lx\n", *size_ptr );
- if (!(size = *size_ptr)) return STATUS_INVALID_PARAMETER;
- TRACE("%p %08lx %lx %08lx\n", addr, size, type, protect );
- if (!__wine_is_current_process( process ))
- {
struct remote_op_params_vm_alloc alloc_params;
struct remote_op_result_vm_alloc alloc_result;
int result_size = sizeof(struct remote_op_result_vm_alloc);
- if (!size) return STATUS_INVALID_PARAMETER;
alloc_params.addr = addr;
alloc_params.size = size;
alloc_params.type = type;
alloc_params.protect = protect;
status = remote_op( process, REMOTE_OP_VM_ALLOC,
&alloc_params, sizeof(alloc_params),
&alloc_result, &result_size );
if (!status)
{
*ret = alloc_result.base;
*size_ptr = alloc_result.size;
}
return status;
}
/* Round parameters to a page boundary */
@@ -1265,16 +1331,37 @@ FILE_VIEW *view; char *base; NTSTATUS status = STATUS_SUCCESS;
- LPVOID addr = *addr_ptr;
- DWORD size = *size_ptr;
- LPVOID addr;
- DWORD size;
- if (!is_current_process( process ))
- TRACE("%p %p %p %lx\n", process, addr_ptr, size_ptr, type );
- /* sanity check */
- if (NULL == addr_ptr || NULL == size_ptr) return
ERROR_INVALID_PARAMETER;
- addr = *addr_ptr;
- size = *size_ptr;
- TRACE("addr=%p size=%08lx\n", addr, size );
- if (!__wine_is_current_process( process )) {
ERR("Unsupported on other process\n");
return STATUS_ACCESS_DENIED;
- }
struct remote_op_params_vm_free free_params;
struct remote_op_result_vm_free free_result;
int result_size = sizeof(struct remote_op_result_vm_free);
- TRACE("%p %08lx %lx\n", addr, size, type );
free_params.addr = addr;
free_params.size = size;
free_params.type = type;
status = remote_op( process, REMOTE_OP_VM_FREE,
&free_params, sizeof(free_params),
&free_result, &result_size );
if (!status)
{
*addr_ptr = free_result.base;
*size_ptr = free_result.size;
}
return status;
}
/* Fix the parameters */
@@ -1341,16 +1428,38 @@ char *base; UINT i; BYTE vprot, *p;
- DWORD prot, size = *size_ptr;
- LPVOID addr = *addr_ptr;
- DWORD prot, size;
- LPVOID addr;
- if (!is_current_process( process ))
- TRACE("%p %p %p %08lx %p\n", process, addr_ptr, size_ptr, new_prot,
old_prot );
- /* sanity check */
- if (NULL == addr_ptr || NULL == size_ptr) return
ERROR_INVALID_PARAMETER;
- addr = *addr_ptr;
- size = *size_ptr;
- TRACE("addr=%p size=%08lx\n", addr, size );
- if (!__wine_is_current_process( process )) {
ERR("Unsupported on other process\n");
return STATUS_ACCESS_DENIED;
- }
struct remote_op_params_vm_protect protect_params;
struct remote_op_result_vm_protect protect_result;
int result_size = sizeof(struct remote_op_result_vm_protect);
- TRACE("%p %08lx %08lx\n", addr, size, new_prot );
protect_params.addr = addr;
protect_params.size = size;
protect_params.new_prot = new_prot;
status = remote_op( process, REMOTE_OP_VM_PROTECT,
&protect_params, sizeof(protect_params),
&protect_result, &result_size );
if (!status)
{
*addr_ptr = protect_result.base;
*size_ptr = protect_result.size;
if (old_prot) *old_prot = protect_result.old_prot;
}
return status;
}
/* Fix the parameters */
@@ -1409,15 +1518,40 @@ UINT size = 0; MEMORY_BASIC_INFORMATION *info = buffer;
- TRACE("%p %p %d %p %lu %p\n", process, addr, info_class, buffer, len,
res_len );
- /* sanity check */
- if (NULL == buffer) return ERROR_INVALID_PARAMETER;
- if (!__wine_is_current_process( process ))
- {
struct remote_op_params_vm_query *query_params;
int result_size = len;
NTSTATUS status;
query_params = (struct remote_op_params_vm_query*)
RtlAllocateHeap( GetProcessHeap(), 0, sizeof(struct
remote_op_params_vm_query) + len );
if (!query_params)
return STATUS_NO_MEMORY;
query_params->addr = (void*) addr;
query_params->info_class = info_class;
if (len) memcpy( query_params + 1, buffer, len );
status = remote_op( process, REMOTE_OP_VM_QUERY,
query_params, sizeof(struct
remote_op_params_vm_query) + len,
buffer, &result_size );
RtlFreeHeap( GetProcessHeap(), 0, query_params );
if (!status)
if (res_len) *res_len = result_size;
return status;
- }
- if (info_class != MemoryBasicInformation) return
STATUS_INVALID_INFO_CLASS;
if (ADDRESS_SPACE_LIMIT && addr >= ADDRESS_SPACE_LIMIT) return STATUS_WORKING_SET_LIMIT_RANGE;
- if (!is_current_process( process ))
- {
ERR("Unsupported on other process\n");
return STATUS_ACCESS_DENIED;
- }
- /* sanity check */
- if (len < sizeof(MEMORY_BASIC_INFORMATION)) return
ERROR_INVALID_PARAMETER;
base = ROUND_ADDR( addr, page_mask );
@@ -1497,10 +1631,10 @@ */ NTSTATUS WINAPI NtLockVirtualMemory( HANDLE process, PVOID *addr, ULONG
*size, ULONG unknown )
{
- if (!is_current_process( process ))
- /* FIXME */
- if (!__wine_is_current_process( process )) {
ERR("Unsupported on other process\n");
return STATUS_ACCESS_DENIED;
} return STATUS_SUCCESS;return STATUS_SUCCESS;
} @@ -1512,10 +1646,10 @@ */ NTSTATUS WINAPI NtUnlockVirtualMemory( HANDLE process, PVOID *addr, ULONG
*size, ULONG unknown )
{
- if (!is_current_process( process ))
- /* FIXME */
- if (!__wine_is_current_process( process )) {
ERR("Unsupported on other process\n");
return STATUS_ACCESS_DENIED;
} return STATUS_SUCCESS;return STATUS_SUCCESS;
} @@ -1608,14 +1742,39 @@ HANDLE shared_file; BOOL removable = FALSE;
- if (!is_current_process( process ))
- {
ERR("Unsupported on other process\n");
return STATUS_ACCESS_DENIED;
- }
- TRACE("%p %p %p %lu %lu %p %p %d %lu %lx\n", handle, process, addr_pt
r, zero_bits,
commit_size, offset, size_ptr, inherit, alloc_type, protect );
- TRACE("handle=%p addr=%p off=%lx%08lx size=%x access=%lx\n",
handle, *addr_ptr, offset->u.HighPart, offset->u.LowPart, size,
protect );
- /* sanity check */
- if (NULL == addr_ptr || NULL == size_ptr || NULL == offset) return
ERROR_INVALID_PARAMETER;
- TRACE("addr=%p off=%lx%08lx\n", *addr_ptr, offset->u.HighPart,
offset->u.LowPart );
if (!__wine_is_current_process( process ))
{
struct remote_op_params_vm_map map_params;
struct remote_op_result_vm_map map_result;
int result_size = sizeof(struct remote_op_result_vm_map);
map_params.addr = *addr_ptr;
map_params.zero_bits = zero_bits;
map_params.commit_size = commit_size;
map_params.offset_low = offset->u.LowPart;
map_params.offset_high = offset->u.HighPart;
map_params.size = *size_ptr;
map_params.inherit = inherit;
map_params.alloc_type = alloc_type;
map_params.protect = protect;
res = remote_op( process, REMOTE_OP_VM_MAP,
&map_params, sizeof(map_params),
&map_result, &result_size );
if (!res)
{
*addr_ptr = map_result.base;
*size_ptr = map_result.size;
}
return res;
}
/* Check parameters */
@@ -1764,11 +1923,14 @@ NTSTATUS status = STATUS_INVALID_PARAMETER; void *base = ROUND_ADDR( addr, page_mask );
- if (!is_current_process( process ))
- TRACE("%p %p base=%p\n", process, addr, base );
- if (!__wine_is_current_process( process )) {
ERR("Unsupported on other process\n");
return STATUS_ACCESS_DENIED;
return remote_op( process, REMOTE_OP_VM_UNMAP,
}&addr, sizeof(addr), NULL, NULL );
- RtlEnterCriticalSection( &csVirtual ); if ((view = VIRTUAL_FindView( base )) && (base == view->base)) {
@@ -1789,13 +1951,38 @@ { FILE_VIEW *view; NTSTATUS status = STATUS_SUCCESS;
- void *addr = ROUND_ADDR( *addr_ptr, page_mask );
- void *addr;
- ULONG size;
- if (!is_current_process( process ))
- TRACE("%p %p %p %lx\n", process, addr_ptr, size_ptr, unknown );
- /* sanity check */
- if (NULL == addr_ptr || NULL == size_ptr) return
ERROR_INVALID_PARAMETER;
- size = *size_ptr;
- TRACE("addr=%p size=%08lx\n", *addr_ptr, size );
- if (!__wine_is_current_process( process )) {
ERR("Unsupported on other process\n");
return STATUS_ACCESS_DENIED;
struct remote_op_params_vm_flush flush_params;
struct remote_op_result_vm_flush flush_result;
int result_size = sizeof(struct remote_op_result_vm_flush);
flush_params.addr = (void*) *addr_ptr;
flush_params.size = size;
flush_params.unknown = unknown;
status = remote_op( process, REMOTE_OP_VM_FLUSH,
&flush_params, sizeof(flush_params),
&flush_result, &result_size );
if (!status)
{
*addr_ptr = flush_result.base;
if (!*size_ptr) *size_ptr = flush_result.size;
}
}return status;
- addr = ROUND_ADDR( *addr_ptr, page_mask ); RtlEnterCriticalSection( &csVirtual ); if (!(view = VIRTUAL_FindView( addr ))) status =
STATUS_INVALID_PARAMETER;
else
Index: server/process.c
RCS file: /home/wine/wine/server/process.c,v retrieving revision 1.117 diff -u -r1.117 process.c --- server/process.c 14 Jun 2004 17:02:00 -0000 1.117 +++ server/process.c 23 Aug 2004 09:30:22 -0000 @@ -45,6 +45,7 @@ #include "request.h" #include "console.h" #include "user.h" +#include "object.h"
/* process structure */
@@ -972,6 +973,7 @@ reply->info_size = 0; current->process->peb = req->peb; current->process->ldt_copy = req->ldt_copy;
- current->process->kernel_thread_start = req->kernel_thread_start; current->process->startup_info = init_process( reply );
}
@@ -1188,5 +1190,136 @@ reply->event = alloc_handle( current->process,
process->idle_event,
EVENT_ALL_ACCESS, 0 ); release_object( process );
- }
+}
+/* return address of internal thread start function in kernel32 */ +DECL_HANDLER(get_kernel_thread_start) +{
- struct process *process;
- /* because this is used for CreateRemoteThread,
required access rights must be the same
*/
- if ((process = get_process_from_handle( req->handle,
PROCESS_CREATE_THREAD
| PROCESS_QUERY_INFORMATION
| PROCESS_VM_OPERATION
| PROCESS_VM_READ |
PROCESS_VM_WRITE )))
- {
reply->address = process->kernel_thread_start;
release_object( process );
- }
+}
+/* Accept parameters for remote operation and start it */ +DECL_HANDLER(remote_operation) +{
- int access;
- struct process *process;
- struct event *event;
- /* define required access rights */
- switch( req->type )
- {
case REMOTE_OP_NEW_THREAD:
access = PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION
| PROCESS_VM_OPERATION | PROCESS_VM_READ |
PROCESS_VM_WRITE;
break;
case REMOTE_OP_VM_ALLOC: access = PROCESS_VM_OPERATION; break;
case REMOTE_OP_VM_FREE: access = PROCESS_VM_OPERATION; break;
case REMOTE_OP_VM_PROTECT: access = PROCESS_VM_OPERATION; break;
case REMOTE_OP_VM_QUERY: access = PROCESS_QUERY_INFORMATION;
break;
case REMOTE_OP_VM_MAP: access = PROCESS_VM_OPERATION; break;
case REMOTE_OP_VM_UNMAP: access = PROCESS_VM_OPERATION; break;
case REMOTE_OP_VM_FLUSH: access = PROCESS_VM_OPERATION; break;
/* FIXME: is access right? */
default:
set_error( STATUS_INVALID_PARAMETER );
return;
- }
- /* get process object */
- if (!(process = get_process_from_handle( req->handle, access )))
return;
- /* dispose result data buffer if allocated */
- if (current->result_data)
- {
free( current->result_data );
current->result_data = NULL;
- }
- /* create event object */
- reply->event = NULL;
- if (current->result_event)
- {
release_object( current->result_event );
current->result_event = NULL;
- }
- if (!(event = create_event( NULL, 0, 1, 0 )))
goto error;
- if (!(reply->event = alloc_handle( current->process, event,
EVENT_ALL_ACCESS, FALSE )))
goto error;
- /* FIXME: pass somehow operation type, params and originator thread
id
for some thread in the process and force execution of operation */
- set_error( STATUS_NOT_IMPLEMENTED );
- /* save event object in thread structure for future set operation;
we do not release it here */
- current->result_event = event;
- release_object( process );
- return;
+error:
- if (reply->event) close_handle( current->process, reply->event,
NULL );
- if (event) release_object( event );
- release_object( process );
+}
+/* save result of remote operation and wakeup originator */ +DECL_HANDLER(remote_operation_complete) +{
- struct thread *thread = get_thread_from_id( req->originator );
- if (!thread) return;
- /* save status */
- thread->remote_status = req->status;
- /* allocate buffer for result data, if required */
- if (thread->result_data)
- {
free( thread->result_data );
thread->result_data = NULL;
- }
- if ((thread->result_size = get_req_data_size()))
- {
if ((thread->result_data = mem_alloc( thread->result_size )))
memcpy( thread->result_data, get_req_data(),
thread->result_size );
else
thread->remote_status = get_error();
- }
- /* set event */
- if (thread->result_event)
- {
set_event( thread->result_event );
release_object( thread->result_event );
thread->result_event = NULL;
- }
- release_object( thread );
+}
+/* return status and result data from remote opertaion */ +DECL_HANDLER(remote_operation_result) +{
- reply->status = current->remote_status;
- if (current->result_data)
- {
void *result = current->result_data;
current->result_data = NULL;
}set_reply_data_ptr( result, current->result_size );
} Index: server/process.h =================================================================== RCS file: /home/wine/wine/server/process.h,v retrieving revision 1.42 diff -u -r1.42 process.h --- server/process.h 10 Dec 2003 04:08:06 -0000 1.42 +++ server/process.h 23 Aug 2004 09:30:24 -0000 @@ -79,6 +79,8 @@ struct process_dll exe; /* main exe file */ void *peb; /* PEB address in client
address space */
void *ldt_copy; /* pointer to LDT copy in
client addr space */
- void *kernel_thread_start; /* addr of internal
kernel32 thread routine */
/* (in client address
space) */
};
struct process_snapshot Index: server/protocol.def =================================================================== RCS file: /home/wine/wine/server/protocol.def,v retrieving revision 1.110 diff -u -r1.110 protocol.def --- server/protocol.def 18 Aug 2004 00:04:58 -0000 1.110 +++ server/protocol.def 23 Aug 2004 09:30:45 -0000 @@ -232,6 +232,7 @@ @REQ(init_process) void* peb; /* addr of PEB */ void* ldt_copy; /* addr of LDT copy */
- void* kernel_thread_start; /* addr of internal kernel32
thread routine */
@REPLY int create_flags; /* creation flags */ unsigned int server_start; /* server start time (GetTickCount) */ @@ -2166,3 +2167,133 @@ #define SET_GLOBAL_SHELL_WINDOWS 0x01 /* set both main shell and
listview windows */
#define SET_GLOBAL_PROGMAN_WINDOW 0x02 #define SET_GLOBAL_TASKMAN_WINDOW 0x04
+/* Get address of kernel32 internal THREAD_Start function */ +@REQ(get_kernel_thread_start)
- obj_handle_t handle; /* process handle */
+@REPLY
- void* address;
+@END
+/* Accept parameters for remote operation and start it */ +@REQ(remote_operation)
- obj_handle_t handle; /* process handle */
- int type; /* operation type (see below) */
- VARARG(data,bytes); /* operation parameters (see below) */
+@REPLY
- obj_handle_t event; /* originator waits for it */
+@END +enum remote_op_type +{
- REMOTE_OP_NEW_THREAD,
- REMOTE_OP_VM_ALLOC,
- REMOTE_OP_VM_FREE,
- REMOTE_OP_VM_PROTECT,
- REMOTE_OP_VM_QUERY,
- REMOTE_OP_VM_MAP,
- REMOTE_OP_VM_UNMAP,
- REMOTE_OP_VM_FLUSH
+};
+/* Notify that remote operation has been complete */ +@REQ(remote_operation_complete)
- thread_id_t originator; /* originator thread id */
- unsigned int status; /* operation status */
- VARARG(data,bytes); /* operation result data (see below) */
+@END
+/* Get result of remote opertaion */ +@REQ(remote_operation_result) +@REPLY
- unsigned int status; /* operation status */
- VARARG(data,bytes); /* operation result data (see below) */
+@END
+struct remote_op_params_new_thread +{
- int suspend;
- void *stack_addr;
- unsigned int stack_reserve;
- unsigned int stack_commit;
- void *start;
- void *param;
+}; +struct remote_op_result_new_thread +{
- obj_handle_t handle;
- thread_id_t tid;
+};
+struct remote_op_params_vm_alloc +{
- void *addr;
- unsigned long size;
- unsigned long type;
- unsigned long protect;
+}; +struct remote_op_result_vm_alloc +{
- void *base;
- unsigned long size;
+};
+struct remote_op_params_vm_free +{
- void *addr;
- unsigned long size;
- unsigned long type;
+}; +struct remote_op_result_vm_free +{
- void *base;
- unsigned long size;
+};
+struct remote_op_params_vm_protect +{
- void *addr;
- unsigned long size;
- unsigned long new_prot;
+}; +struct remote_op_result_vm_protect +{
- void *base;
- unsigned long size;
- unsigned long old_prot;
+};
+struct remote_op_params_vm_query +{
- void *addr;
- int info_class;
+};
+struct remote_op_params_vm_map +{
- void *addr;
- unsigned long zero_bits;
- unsigned long commit_size;
- unsigned long offset_low;
- long offset_high;
- unsigned long size;
- int inherit;
- unsigned long alloc_type;
- unsigned long protect;
+}; +struct remote_op_result_vm_map +{
- void *base;
- unsigned long size;
+};
+struct remote_op_params_vm_flush +{
- void *addr;
- unsigned long size;
- unsigned long unknown;
+}; +struct remote_op_result_vm_flush +{
- void *base;
- unsigned long size;
+}; Index: server/thread.c =================================================================== RCS file: /home/wine/wine/server/thread.c,v retrieving revision 1.103 diff -u -r1.103 thread.c --- server/thread.c 27 Oct 2003 22:10:22 -0000 1.103 +++ server/thread.c 23 Aug 2004 09:30:54 -0000 @@ -142,6 +142,8 @@ thread->suspend = 0; thread->creation_time = time(NULL); thread->exit_time = 0;
thread->result_data = NULL;
thread->result_event = NULL;
for (i = 0; i < MAX_INFLIGHT_FDS; i++) thread->inflight[i].server = thread->inflight[i].client = -1;
@@ -210,6 +212,8 @@ if (thread->request_fd) release_object( thread->request_fd ); if (thread->reply_fd) release_object( thread->reply_fd ); if (thread->wait_fd) release_object( thread->wait_fd );
- if (thread->result_data) free( thread->result_data );
- if (thread->result_event) release_object( thread->result_event ); free_msg_queue( thread ); cleanup_clipboard_thread(thread); destroy_thread_windows( thread );
@@ -226,6 +230,8 @@ thread->request_fd = NULL; thread->reply_fd = NULL; thread->wait_fd = NULL;
thread->result_data = NULL;
thread->result_event = NULL;
if (thread == booting_thread) /* killing booting thread */ {
Index: server/thread.h
RCS file: /home/wine/wine/server/thread.h,v retrieving revision 1.56 diff -u -r1.56 thread.h --- server/thread.h 10 Dec 2003 01:12:18 -0000 1.56 +++ server/thread.h 23 Aug 2004 09:30:56 -0000 @@ -94,6 +94,10 @@ time_t creation_time; /* Thread creation time */ time_t exit_time; /* Thread exit time */ struct token *token; /* security token associated
with this thread */
- unsigned int remote_status; /* error code from remote
operation */
- void *result_data; /* result data from remote
operation */
- int result_size; /* size of result data */
- struct event *result_event; /* originator of remote
operation waits for it */
};
struct thread_snapshot
"Roger Olson" u60@comcast.net wrote:
I am wondering why delete the CreateThread() code and refer all such requests to CreateRemoteThread()? It would appear to me that retaining the CreateThread() code in addition to and compliant with the patch changes would be more expeditious for apps that do not need a call for Remote Operations.
Because if an optimization is needed regarding taking into account current (local) process CreateRemoteThread can do it internally, and of course avoiding code duplication. That's how for instance VirtualAlloc is implemented via a call to VirtualAllocEx.