Pass new arguments (suspend/inherit) to the server.
[wine] / scheduler / process.c
1 /*
2  * Win32 processes
3  *
4  * Copyright 1996, 1998 Alexandre Julliard
5  */
6
7 #include <assert.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include "process.h"
12 #include "module.h"
13 #include "neexe.h"
14 #include "file.h"
15 #include "global.h"
16 #include "heap.h"
17 #include "task.h"
18 #include "ldt.h"
19 #include "syslevel.h"
20 #include "thread.h"
21 #include "winerror.h"
22 #include "pe_image.h"
23 #include "task.h"
24 #include "server.h"
25 #include "debug.h"
26
27
28 /* The initial process PDB */
29 static PDB initial_pdb;
30
31 static PDB *PROCESS_First = &initial_pdb;
32
33 /***********************************************************************
34  *           PROCESS_Current
35  */
36 PDB *PROCESS_Current(void)
37 {
38     return THREAD_Current()->process;
39 }
40
41 /***********************************************************************
42  *           PROCESS_Initial
43  *
44  * FIXME: This works only while running all processes in the same
45  *        address space (or, at least, the initial process is mapped
46  *        into all address spaces as is KERNEL32 in Windows 95)
47  *
48  */
49 PDB *PROCESS_Initial(void)
50 {
51     return &initial_pdb;
52 }
53
54 /***********************************************************************
55  *           PROCESS_QueryInfo
56  *
57  * Retrieve information about a process
58  */
59 static BOOL PROCESS_QueryInfo( HANDLE handle,
60                                  struct get_process_info_reply *reply )
61 {
62     struct get_process_info_request req;
63     req.handle = handle;
64     CLIENT_SendRequest( REQ_GET_PROCESS_INFO, -1, 1, &req, sizeof(req) );
65     return !CLIENT_WaitSimpleReply( reply, sizeof(*reply), NULL );
66 }
67
68 /***********************************************************************
69  *           PROCESS_IsCurrent
70  *
71  * Check if a handle is to the current process
72  */
73 BOOL PROCESS_IsCurrent( HANDLE handle )
74 {
75     struct get_process_info_reply reply;
76     return (PROCESS_QueryInfo( handle, &reply ) &&
77             (reply.pid == PROCESS_Current()->server_pid));
78 }
79
80
81 /***********************************************************************
82  *           PROCESS_IdToPDB
83  *
84  * Convert a process id to a PDB, making sure it is valid.
85  */
86 PDB *PROCESS_IdToPDB( DWORD id )
87 {
88     PDB *pdb;
89
90     if (!id) return PROCESS_Current();
91     pdb = PROCESS_First;
92     while (pdb)
93     {
94         if ((DWORD)pdb->server_pid == id) return pdb;
95         pdb = pdb->next;
96     }
97     SetLastError( ERROR_INVALID_PARAMETER );
98     return NULL;
99 }
100
101
102
103 /***********************************************************************
104  *           PROCESS_BuildEnvDB
105  *
106  * Build the env DB for the initial process
107  */
108 static BOOL PROCESS_BuildEnvDB( PDB *pdb )
109 {
110     /* Allocate the env DB (FIXME: should not be on the system heap) */
111
112     if (!(pdb->env_db = HeapAlloc(SystemHeap,HEAP_ZERO_MEMORY,sizeof(ENVDB))))
113         return FALSE;
114     InitializeCriticalSection( &pdb->env_db->section );
115
116     /* Allocate startup info */
117     if (!(pdb->env_db->startup_info = 
118           HeapAlloc( SystemHeap, HEAP_ZERO_MEMORY, sizeof(STARTUPINFOA) )))
119         return FALSE;
120
121     /* Allocate the standard handles */
122
123     pdb->env_db->hStdin  = FILE_DupUnixHandle( 0, GENERIC_READ );
124     pdb->env_db->hStdout = FILE_DupUnixHandle( 1, GENERIC_WRITE );
125     pdb->env_db->hStderr = FILE_DupUnixHandle( 2, GENERIC_WRITE );
126
127     /* Build the command-line */
128
129     pdb->env_db->cmd_line = HEAP_strdupA( SystemHeap, 0, "kernel32" );
130
131     /* Build the environment strings */
132
133     return ENV_BuildEnvironment( pdb );
134 }
135
136
137 /***********************************************************************
138  *           PROCESS_InheritEnvDB
139  */
140 static BOOL PROCESS_InheritEnvDB( PDB *pdb, LPCSTR cmd_line, LPCSTR env,
141                                     BOOL inherit_handles, STARTUPINFOA *startup )
142 {
143     if (!(pdb->env_db = HeapAlloc(pdb->heap, HEAP_ZERO_MEMORY, sizeof(ENVDB))))
144         return FALSE;
145     InitializeCriticalSection( &pdb->env_db->section );
146
147     /* Copy the parent environment */
148
149     if (!ENV_InheritEnvironment( pdb, env )) return FALSE;
150
151     /* Copy the command line */
152
153     if (!(pdb->env_db->cmd_line = HEAP_strdupA( pdb->heap, 0, cmd_line )))
154         return FALSE;
155
156     /* Remember startup info */
157     if (!(pdb->env_db->startup_info = 
158           HeapAlloc( pdb->heap, HEAP_ZERO_MEMORY, sizeof(STARTUPINFOA) )))
159         return FALSE;
160     *pdb->env_db->startup_info = *startup;
161
162     /* Inherit the standard handles */
163     if (pdb->env_db->startup_info->dwFlags & STARTF_USESTDHANDLES)
164     {
165         pdb->env_db->hStdin  = pdb->env_db->startup_info->hStdInput;
166         pdb->env_db->hStdout = pdb->env_db->startup_info->hStdOutput;
167         pdb->env_db->hStderr = pdb->env_db->startup_info->hStdError;
168     }
169     else if (inherit_handles)
170     {
171         pdb->env_db->hStdin  = pdb->parent->env_db->hStdin;
172         pdb->env_db->hStdout = pdb->parent->env_db->hStdout;
173         pdb->env_db->hStderr = pdb->parent->env_db->hStderr;
174     }
175     /* else will be done later on in PROCESS_Create */
176
177     return TRUE;
178 }
179
180
181 /***********************************************************************
182  *           PROCESS_FreePDB
183  *
184  * Free a PDB and all associated storage.
185  */
186 void PROCESS_FreePDB( PDB *pdb )
187 {
188     PDB **pptr = &PROCESS_First;
189
190     ENV_FreeEnvironment( pdb );
191     while (*pptr && (*pptr != pdb)) pptr = &(*pptr)->next;
192     if (*pptr) *pptr = pdb->next;
193     if (pdb->heap && (pdb->heap != pdb->system_heap)) HeapDestroy( pdb->heap );
194     DeleteCriticalSection( &pdb->crit_section );
195     HeapFree( SystemHeap, 0, pdb );
196 }
197
198
199 /***********************************************************************
200  *           PROCESS_CreatePDB
201  *
202  * Allocate and fill a PDB structure.
203  * Runs in the context of the parent process.
204  */
205 static PDB *PROCESS_CreatePDB( PDB *parent, BOOL inherit )
206 {
207     PDB *pdb = HeapAlloc( SystemHeap, HEAP_ZERO_MEMORY, sizeof(PDB) );
208
209     if (!pdb) return NULL;
210     pdb->exit_code       = 0x103; /* STILL_ACTIVE */
211     pdb->threads         = 1;
212     pdb->running_threads = 1;
213     pdb->ring0_threads   = 1;
214     pdb->system_heap     = SystemHeap;
215     pdb->parent          = parent;
216     pdb->group           = pdb;
217     pdb->priority        = 8;  /* Normal */
218     pdb->heap            = pdb->system_heap;  /* will be changed later on */
219     pdb->next            = PROCESS_First;
220     PROCESS_First = pdb;
221     return pdb;
222 }
223
224
225 /***********************************************************************
226  *           PROCESS_FinishCreatePDB
227  *
228  * Second part of CreatePDB
229  */
230 static BOOL PROCESS_FinishCreatePDB( PDB *pdb )
231 {
232     InitializeCriticalSection( &pdb->crit_section );
233     /* Allocate the event */
234     if (!(pdb->load_done_evt = CreateEventA( NULL, TRUE, FALSE, NULL )))
235         return FALSE;
236     return TRUE;
237 }
238
239
240 /***********************************************************************
241  *           PROCESS_Init
242  */
243 BOOL PROCESS_Init(void)
244 {
245     THDB *thdb;
246
247     /* Fill the initial process structure */
248     initial_pdb.exit_code       = 0x103; /* STILL_ACTIVE */
249     initial_pdb.threads         = 1;
250     initial_pdb.running_threads = 1;
251     initial_pdb.ring0_threads   = 1;
252     initial_pdb.group           = &initial_pdb;
253     initial_pdb.priority        = 8;  /* Normal */
254
255     /* Initialize virtual memory management */
256     if (!VIRTUAL_Init()) return FALSE;
257
258     /* Create the initial thread structure */
259     if (!(thdb = THREAD_CreateInitialThread( &initial_pdb ))) return FALSE;
260
261     /* Remember TEB selector of initial process for emergency use */
262     SYSLEVEL_EmergencyTeb = thdb->teb_sel;
263
264     /* Create the system heap */
265     if (!(SystemHeap = HeapCreate( HEAP_GROWABLE, 0x10000, 0 ))) return FALSE;
266     initial_pdb.system_heap = initial_pdb.heap = SystemHeap;
267
268     /* Create the environment DB of the first process */
269     if (!PROCESS_BuildEnvDB( &initial_pdb )) return FALSE;
270
271     /* Initialize the first thread */
272     if (CLIENT_InitThread()) return FALSE;
273     if (!PROCESS_FinishCreatePDB( &initial_pdb )) return FALSE;
274
275     /* Create the SEGPTR heap */
276     if (!(SegptrHeap = HeapCreate( HEAP_WINE_SEGPTR, 0, 0 ))) return FALSE;
277
278     return TRUE;
279 }
280
281
282 /***********************************************************************
283  *           PROCESS_Create
284  *
285  * Create a new process database and associated info.
286  */
287 PDB *PROCESS_Create( NE_MODULE *pModule, LPCSTR cmd_line, LPCSTR env,
288                      HINSTANCE16 hInstance, HINSTANCE16 hPrevInstance,
289                      LPSECURITY_ATTRIBUTES psa, LPSECURITY_ATTRIBUTES tsa,
290                      BOOL inherit, STARTUPINFOA *startup,
291                      PROCESS_INFORMATION *info )
292 {
293     DWORD size, commit;
294     int server_thandle, server_phandle;
295     UINT cmdShow = 0;
296     THDB *thdb = NULL;
297     PDB *parent = PROCESS_Current();
298     PDB *pdb = PROCESS_CreatePDB( parent, inherit );
299
300     if (!pdb) return NULL;
301     info->hThread = info->hProcess = INVALID_HANDLE_VALUE;
302     if (!PROCESS_FinishCreatePDB( pdb )) goto error;
303
304     /* Create the heap */
305
306     if (pModule->module32)
307     {
308         size  = PE_HEADER(pModule->module32)->OptionalHeader.SizeOfHeapReserve;
309         commit = PE_HEADER(pModule->module32)->OptionalHeader.SizeOfHeapCommit;
310     }
311     else
312     {
313         size = 0x10000;
314         commit = 0;
315         pdb->flags |= PDB32_WIN16_PROC;  /* This is a Win16 process */
316     }
317     if (!(pdb->heap = HeapCreate( HEAP_GROWABLE, size, commit ))) goto error;
318     pdb->heap_list = pdb->heap;
319
320     /* Inherit the env DB from the parent */
321
322     if (!PROCESS_InheritEnvDB( pdb, cmd_line, env, inherit, startup )) goto error;
323
324     /* Create the main thread */
325
326     if (pModule->module32)
327         size = PE_HEADER(pModule->module32)->OptionalHeader.SizeOfStackReserve;
328     else
329         size = 0;
330     if (!(thdb = THREAD_Create( pdb, 0L, size, hInstance == 0, 
331                                 tsa, psa, &server_thandle, &server_phandle, 
332                                 NULL, NULL ))) 
333         goto error;
334     info->hThread     = server_thandle;
335     info->hProcess    = server_phandle;
336     info->dwProcessId = (DWORD)pdb->server_pid;
337     info->dwThreadId  = (DWORD)thdb->server_tid;
338
339     /* Duplicate the standard handles */
340
341     if ((!(pdb->env_db->startup_info->dwFlags & STARTF_USESTDHANDLES)) && !inherit)
342     {
343         DuplicateHandle( GetCurrentProcess(), pdb->parent->env_db->hStdin,
344                          info->hProcess, &pdb->env_db->hStdin, 0, TRUE, DUPLICATE_SAME_ACCESS );
345         DuplicateHandle( GetCurrentProcess(), pdb->parent->env_db->hStdout,
346                          info->hProcess, &pdb->env_db->hStdout, 0, TRUE, DUPLICATE_SAME_ACCESS );
347         DuplicateHandle( GetCurrentProcess(), pdb->parent->env_db->hStderr,
348                          info->hProcess, &pdb->env_db->hStderr, 0, TRUE, DUPLICATE_SAME_ACCESS );
349     }
350
351     /* Create a Win16 task for this process */
352
353     if (startup->dwFlags & STARTF_USESHOWWINDOW)
354         cmdShow = startup->wShowWindow;
355
356     if ( !TASK_Create( thdb, pModule, hInstance, hPrevInstance, cmdShow) )
357         goto error;
358
359
360     /* Map system DLLs into this process (from initial process) */
361     /* FIXME: this is a hack */
362     pdb->modref_list = PROCESS_Initial()->modref_list;
363     
364
365     return pdb;
366
367 error:
368     if (info->hThread != INVALID_HANDLE_VALUE) CloseHandle( info->hThread );
369     if (info->hProcess != INVALID_HANDLE_VALUE) CloseHandle( info->hProcess );
370     PROCESS_FreePDB( pdb );
371     return NULL;
372 }
373
374
375 /***********************************************************************
376  *           ExitProcess   (KERNEL32.100)
377  */
378 void WINAPI ExitProcess( DWORD status )
379 {
380     PDB *pdb = PROCESS_Current();
381     TDB *pTask = (TDB *)GlobalLock16( pdb->task );
382     if ( pTask ) pTask->nEvents++;
383
384     if ( pTask && pTask->thdb != THREAD_Current() )
385         ExitThread( status );
386
387     /* FIXME: should kill all running threads of this process */
388     pdb->exit_code = status;
389     FreeConsole();
390
391     __RESTORE_ES;  /* Necessary for Pietrek's showseh example program */
392     TASK_KillCurrentTask( status );
393 }
394
395
396 /******************************************************************************
397  *           TerminateProcess   (KERNEL32.684)
398  */
399 BOOL WINAPI TerminateProcess( HANDLE handle, DWORD exit_code )
400 {
401     struct terminate_process_request req;
402     req.handle    = handle;
403     req.exit_code = exit_code;
404     CLIENT_SendRequest( REQ_TERMINATE_PROCESS, -1, 1, &req, sizeof(req) );
405     return !CLIENT_WaitReply( NULL, NULL, 0 );
406 }
407
408 /***********************************************************************
409  *           GetCurrentProcess   (KERNEL32.198)
410  */
411 HANDLE WINAPI GetCurrentProcess(void)
412 {
413     return CURRENT_PROCESS_PSEUDOHANDLE;
414 }
415
416
417 /*********************************************************************
418  *           OpenProcess   (KERNEL32.543)
419  */
420 HANDLE WINAPI OpenProcess( DWORD access, BOOL inherit, DWORD id )
421 {
422     struct open_process_request req;
423     struct open_process_reply reply;
424
425     req.pid     = (void *)id;
426     req.access  = access;
427     req.inherit = inherit;
428     CLIENT_SendRequest( REQ_OPEN_PROCESS, -1, 1, &req, sizeof(req) );
429     if (CLIENT_WaitSimpleReply( &reply, sizeof(reply), NULL )) return 0;
430     return reply.handle;
431 }                             
432
433
434 /***********************************************************************
435  *           GetCurrentProcessId   (KERNEL32.199)
436  */
437 DWORD WINAPI GetCurrentProcessId(void)
438 {
439     return (DWORD)PROCESS_Current()->server_pid;
440 }
441
442
443 /***********************************************************************
444  *           GetProcessHeap    (KERNEL32.259)
445  */
446 HANDLE WINAPI GetProcessHeap(void)
447 {
448     PDB *pdb = PROCESS_Current();
449     return pdb->heap ? pdb->heap : SystemHeap;
450 }
451
452
453 /***********************************************************************
454  *           GetThreadLocale    (KERNEL32.295)
455  */
456 LCID WINAPI GetThreadLocale(void)
457 {
458     return PROCESS_Current()->locale;
459 }
460
461
462 /***********************************************************************
463  *           SetPriorityClass   (KERNEL32.503)
464  */
465 BOOL WINAPI SetPriorityClass( HANDLE hprocess, DWORD priorityclass )
466 {
467     struct set_process_info_request req;
468     req.handle   = hprocess;
469     req.priority = priorityclass;
470     req.mask     = SET_PROCESS_INFO_PRIORITY;
471     CLIENT_SendRequest( REQ_SET_PROCESS_INFO, -1, 1, &req, sizeof(req) );
472     return !CLIENT_WaitReply( NULL, NULL, 0 );
473 }
474
475
476 /***********************************************************************
477  *           GetPriorityClass   (KERNEL32.250)
478  */
479 DWORD WINAPI GetPriorityClass(HANDLE hprocess)
480 {
481     struct get_process_info_reply reply;
482     if (!PROCESS_QueryInfo( hprocess, &reply )) return 0;
483     return reply.priority;
484 }
485
486
487 /***********************************************************************
488  *          SetProcessAffinityMask   (KERNEL32.662)
489  */
490 BOOL WINAPI SetProcessAffinityMask( HANDLE hProcess, DWORD affmask )
491 {
492     struct set_process_info_request req;
493     req.handle   = hProcess;
494     req.affinity = affmask;
495     req.mask     = SET_PROCESS_INFO_AFFINITY;
496     CLIENT_SendRequest( REQ_SET_PROCESS_INFO, -1, 1, &req, sizeof(req) );
497     return !CLIENT_WaitReply( NULL, NULL, 0 );
498 }
499
500 /**********************************************************************
501  *          GetProcessAffinityMask    (KERNEL32.373)
502  */
503 BOOL WINAPI GetProcessAffinityMask( HANDLE hProcess,
504                                       LPDWORD lpProcessAffinityMask,
505                                       LPDWORD lpSystemAffinityMask )
506 {
507     struct get_process_info_reply reply;
508     if (!PROCESS_QueryInfo( hProcess, &reply )) return FALSE;
509     if (lpProcessAffinityMask) *lpProcessAffinityMask = reply.process_affinity;
510     if (lpSystemAffinityMask) *lpSystemAffinityMask = reply.system_affinity;
511     return TRUE;
512 }
513
514
515 /***********************************************************************
516  *           GetStdHandle    (KERNEL32.276)
517  */
518 HANDLE WINAPI GetStdHandle( DWORD std_handle )
519 {
520     PDB *pdb = PROCESS_Current();
521
522     switch(std_handle)
523     {
524     case STD_INPUT_HANDLE:  return pdb->env_db->hStdin;
525     case STD_OUTPUT_HANDLE: return pdb->env_db->hStdout;
526     case STD_ERROR_HANDLE:  return pdb->env_db->hStderr;
527     }
528     SetLastError( ERROR_INVALID_PARAMETER );
529     return INVALID_HANDLE_VALUE;
530 }
531
532
533 /***********************************************************************
534  *           SetStdHandle    (KERNEL32.506)
535  */
536 BOOL WINAPI SetStdHandle( DWORD std_handle, HANDLE handle )
537 {
538     PDB *pdb = PROCESS_Current();
539     /* FIXME: should we close the previous handle? */
540     switch(std_handle)
541     {
542     case STD_INPUT_HANDLE:
543         pdb->env_db->hStdin = handle;
544         return TRUE;
545     case STD_OUTPUT_HANDLE:
546         pdb->env_db->hStdout = handle;
547         return TRUE;
548     case STD_ERROR_HANDLE:
549         pdb->env_db->hStderr = handle;
550         return TRUE;
551     }
552     SetLastError( ERROR_INVALID_PARAMETER );
553     return FALSE;
554 }
555
556 /***********************************************************************
557  *           GetProcessVersion    (KERNEL32)
558  */
559 DWORD WINAPI GetProcessVersion( DWORD processid )
560 {
561     TDB *pTask;
562     PDB *pdb = PROCESS_IdToPDB( processid );
563
564     if (!pdb) return 0;
565     if (!(pTask = (TDB *)GlobalLock16( pdb->task ))) return 0;
566     return (pTask->version&0xff) | (((pTask->version >>8) & 0xff)<<16);
567 }
568
569 /***********************************************************************
570  *           GetProcessFlags    (KERNEL32)
571  */
572 DWORD WINAPI GetProcessFlags( DWORD processid )
573 {
574     PDB *pdb = PROCESS_IdToPDB( processid );
575     if (!pdb) return 0;
576     return pdb->flags;
577 }
578
579 /***********************************************************************
580  *              SetProcessWorkingSetSize        [KERNEL32.662]
581  * Sets the min/max working set sizes for a specified process.
582  *
583  * PARAMS
584  *    hProcess [I] Handle to the process of interest
585  *    minset   [I] Specifies minimum working set size
586  *    maxset   [I] Specifies maximum working set size
587  *
588  * RETURNS  STD
589  */
590 BOOL WINAPI SetProcessWorkingSetSize(HANDLE hProcess,DWORD minset,
591                                        DWORD maxset)
592 {
593     FIXME(process,"(0x%08x,%ld,%ld): stub - harmless\n",hProcess,minset,maxset);
594     if(( minset == -1) && (maxset == -1)) {
595         /* Trim the working set to zero */
596         /* Swap the process out of physical RAM */
597     }
598     return TRUE;
599 }
600
601 /***********************************************************************
602  *           GetProcessWorkingSetSize    (KERNEL32)
603  */
604 BOOL WINAPI GetProcessWorkingSetSize(HANDLE hProcess,LPDWORD minset,
605                                        LPDWORD maxset)
606 {
607         FIXME(process,"(0x%08x,%p,%p): stub\n",hProcess,minset,maxset);
608         /* 32 MB working set size */
609         if (minset) *minset = 32*1024*1024;
610         if (maxset) *maxset = 32*1024*1024;
611         return TRUE;
612 }
613
614 /***********************************************************************
615  *           SetProcessShutdownParameters    (KERNEL32)
616  *
617  * CHANGED - James Sutherland (JamesSutherland@gmx.de)
618  * Now tracks changes made (but does not act on these changes)
619  * NOTE: the definition for SHUTDOWN_NORETRY was done on guesswork.
620  * It really shouldn't be here, but I'll move it when it's been checked!
621  */
622 #define SHUTDOWN_NORETRY 1
623 static unsigned int shutdown_noretry = 0;
624 static unsigned int shutdown_priority = 0x280L;
625 BOOL WINAPI SetProcessShutdownParameters(DWORD level,DWORD flags)
626 {
627     if (flags & SHUTDOWN_NORETRY)
628       shutdown_noretry = 1;
629     else
630       shutdown_noretry = 0;
631     if (level > 0x100L && level < 0x3FFL)
632       shutdown_priority = level;
633     else
634       {
635         ERR(process,"invalid priority level 0x%08lx\n", level);
636         return FALSE;
637       }
638     return TRUE;
639 }
640
641
642 /***********************************************************************
643  * GetProcessShutdownParameters                 (KERNEL32)
644  *
645  */
646 BOOL WINAPI GetProcessShutdownParameters( LPDWORD lpdwLevel,
647                                             LPDWORD lpdwFlags )
648 {
649   (*lpdwLevel) = shutdown_priority;
650   (*lpdwFlags) = (shutdown_noretry * SHUTDOWN_NORETRY);
651   return TRUE;
652 }
653 /***********************************************************************
654  *           SetProcessPriorityBoost    (KERNEL32)
655  */
656 BOOL WINAPI SetProcessPriorityBoost(HANDLE hprocess,BOOL disableboost)
657 {
658     FIXME(process,"(%d,%d): stub\n",hprocess,disableboost);
659     /* Say we can do it. I doubt the program will notice that we don't. */
660     return TRUE;
661 }
662
663 /***********************************************************************
664  *           ReadProcessMemory                  (KERNEL32)
665  * FIXME: check this, if we ever run win32 binaries in different addressspaces
666  *        ... and add a sizecheck
667  */
668 BOOL WINAPI ReadProcessMemory( HANDLE hProcess, LPCVOID lpBaseAddress,
669                                  LPVOID lpBuffer, DWORD nSize,
670                                  LPDWORD lpNumberOfBytesRead )
671 {
672         memcpy(lpBuffer,lpBaseAddress,nSize);
673         if (lpNumberOfBytesRead) *lpNumberOfBytesRead = nSize;
674         return TRUE;
675 }
676
677 /***********************************************************************
678  *           WriteProcessMemory                 (KERNEL32)
679  * FIXME: check this, if we ever run win32 binaries in different addressspaces
680  *        ... and add a sizecheck
681  */
682 BOOL WINAPI WriteProcessMemory(HANDLE hProcess, LPVOID lpBaseAddress,
683                                  LPVOID lpBuffer, DWORD nSize,
684                                  LPDWORD lpNumberOfBytesWritten )
685 {
686         memcpy(lpBaseAddress,lpBuffer,nSize);
687         if (lpNumberOfBytesWritten) *lpNumberOfBytesWritten = nSize;
688         return TRUE;
689 }
690
691 /***********************************************************************
692  *           RegisterServiceProcess             (KERNEL, KERNEL32)
693  *
694  * A service process calls this function to ensure that it continues to run
695  * even after a user logged off.
696  */
697 DWORD WINAPI RegisterServiceProcess(DWORD dwProcessId, DWORD dwType)
698 {
699         /* I don't think that Wine needs to do anything in that function */
700         return 1; /* success */
701 }
702
703 /***********************************************************************
704  * GetExitCodeProcess [KERNEL32.325]
705  *
706  * Gets termination status of specified process
707  * 
708  * RETURNS
709  *   Success: TRUE
710  *   Failure: FALSE
711  */
712 BOOL WINAPI GetExitCodeProcess(
713     HANDLE hProcess,  /* [I] handle to the process */
714     LPDWORD lpExitCode) /* [O] address to receive termination status */
715 {
716     struct get_process_info_reply reply;
717     if (!PROCESS_QueryInfo( hProcess, &reply )) return FALSE;
718     if (lpExitCode) *lpExitCode = reply.exit_code;
719     return TRUE;
720 }
721
722
723 /***********************************************************************
724  * GetProcessHeaps [KERNEL32.376]
725  */
726 DWORD WINAPI GetProcessHeaps(DWORD nrofheaps,HANDLE *heaps) {
727         FIXME(win32,"(%ld,%p), incomplete implementation.\n",nrofheaps,heaps);
728
729         if (nrofheaps) {
730                 heaps[0] = GetProcessHeap();
731                 /* ... probably SystemHeap too ? */
732                 return 1;
733         }
734         /* number of available heaps */
735         return 1;
736 }
737
738 /***********************************************************************
739  * PROCESS_SuspendOtherThreads
740  */
741
742 void PROCESS_SuspendOtherThreads(void)
743 {
744 #if 0
745     PDB *pdb;
746     THREAD_ENTRY *entry;
747
748     SYSTEM_LOCK();
749
750     pdb = PROCESS_Current();
751     entry = pdb->thread_list->next;
752     for (;;)
753     {
754          if (entry->thread != THREAD_Current() && !THREAD_IsWin16(entry->thread))
755          {
756              HANDLE handle = HANDLE_Alloc( PROCESS_Current(), 
757                                              &entry->thread->header,
758                                              THREAD_ALL_ACCESS, FALSE, -1 );
759              SuspendThread(handle);
760              CloseHandle(handle);
761          }
762          if (entry == pdb->thread_list) break;
763          entry = entry->next;
764     }
765
766     SYSTEM_UNLOCK();
767 #endif
768 }
769
770 /***********************************************************************
771  * PROCESS_ResumeOtherThreads
772  */
773
774 void PROCESS_ResumeOtherThreads(void)
775 {
776 #if 0
777     PDB *pdb;
778     THREAD_ENTRY *entry;
779
780     SYSTEM_LOCK();
781
782     pdb = PROCESS_Current();
783     entry = pdb->thread_list->next;
784     for (;;)
785     {
786          if (entry->thread != THREAD_Current() && !THREAD_IsWin16(entry->thread))
787          {
788              HANDLE handle = HANDLE_Alloc( PROCESS_Current(), 
789                                              &entry->thread->header,
790                                              THREAD_ALL_ACCESS, FALSE, -1 );
791              ResumeThread(handle);
792              CloseHandle(handle);
793          }
794          if (entry == pdb->thread_list) break;
795          entry = entry->next;
796     }
797
798     SYSTEM_UNLOCK();
799 #endif
800 }
801