4 * Copyright 1996, 1998 Alexandre Julliard
26 static BOOL32 PROCESS_Signaled( K32OBJ *obj, DWORD thread_id );
27 static BOOL32 PROCESS_Satisfied( K32OBJ *obj, DWORD thread_id );
28 static void PROCESS_AddWait( K32OBJ *obj, DWORD thread_id );
29 static void PROCESS_RemoveWait( K32OBJ *obj, DWORD thread_id );
30 static void PROCESS_Destroy( K32OBJ *obj );
32 const K32OBJ_OPS PROCESS_Ops =
34 PROCESS_Signaled, /* signaled */
35 PROCESS_Satisfied, /* satisfied */
36 PROCESS_AddWait, /* add_wait */
37 PROCESS_RemoveWait, /* remove_wait */
40 PROCESS_Destroy /* destroy */
43 static DWORD PROCESS_InitialProcessID = 0;
46 /***********************************************************************
49 PDB32 *PROCESS_Current(void)
51 return THREAD_Current()->process;
54 /***********************************************************************
57 * FIXME: This works only while running all processes in the same
58 * address space (or, at least, the initial process is mapped
59 * into all address spaces as is KERNEL32 in Windows 95)
62 PDB32 *PROCESS_Initial(void)
64 return PROCESS_IdToPDB( PROCESS_InitialProcessID );
67 /***********************************************************************
70 * Get a process from a handle, incrementing the PDB refcount.
72 PDB32 *PROCESS_GetPtr( HANDLE32 handle, DWORD access, int *server_handle )
74 return (PDB32 *)HANDLE_GetObjPtr( PROCESS_Current(), handle,
75 K32OBJ_PROCESS, access, server_handle );
79 /***********************************************************************
82 * Convert a process id to a PDB, making sure it is valid.
84 PDB32 *PROCESS_IdToPDB( DWORD id )
88 if (!id) return PROCESS_Current();
89 pdb = PROCESS_ID_TO_PDB( id );
90 if (!K32OBJ_IsValid( &pdb->header, K32OBJ_PROCESS ))
92 SetLastError( ERROR_INVALID_PARAMETER );
100 /***********************************************************************
103 * Build the env DB for the initial process
105 static BOOL32 PROCESS_BuildEnvDB( PDB32 *pdb )
107 /* Allocate the env DB (FIXME: should not be on the system heap) */
109 if (!(pdb->env_db = HeapAlloc(SystemHeap,HEAP_ZERO_MEMORY,sizeof(ENVDB))))
111 InitializeCriticalSection( &pdb->env_db->section );
113 /* Allocate startup info */
114 if (!(pdb->env_db->startup_info =
115 HeapAlloc( SystemHeap, HEAP_ZERO_MEMORY, sizeof(STARTUPINFO32A) )))
118 /* Allocate the standard handles */
120 pdb->env_db->hStdin = FILE_DupUnixHandle( 0 );
121 pdb->env_db->hStdout = FILE_DupUnixHandle( 1 );
122 pdb->env_db->hStderr = FILE_DupUnixHandle( 2 );
123 FILE_SetFileType( pdb->env_db->hStdin, FILE_TYPE_CHAR );
124 FILE_SetFileType( pdb->env_db->hStdout, FILE_TYPE_CHAR );
125 FILE_SetFileType( pdb->env_db->hStderr, FILE_TYPE_CHAR );
127 /* Build the command-line */
129 pdb->env_db->cmd_line = HEAP_strdupA( SystemHeap, 0, "kernel32" );
131 /* Build the environment strings */
133 return ENV_BuildEnvironment( pdb );
137 /***********************************************************************
138 * PROCESS_InheritEnvDB
140 static BOOL32 PROCESS_InheritEnvDB( PDB32 *pdb, LPCSTR cmd_line, LPCSTR env,
141 STARTUPINFO32A *startup )
143 if (!(pdb->env_db = HeapAlloc(pdb->heap, HEAP_ZERO_MEMORY, sizeof(ENVDB))))
145 InitializeCriticalSection( &pdb->env_db->section );
147 /* Copy the parent environment */
149 if (!ENV_InheritEnvironment( pdb, env )) return FALSE;
151 /* Copy the command line */
153 if (!(pdb->env_db->cmd_line = HEAP_strdupA( pdb->heap, 0, cmd_line )))
156 /* Remember startup info */
157 if (!(pdb->env_db->startup_info =
158 HeapAlloc( pdb->heap, HEAP_ZERO_MEMORY, sizeof(STARTUPINFO32A) )))
160 *pdb->env_db->startup_info = *startup;
162 /* Inherit the standard handles */
163 if (pdb->env_db->startup_info->dwFlags & STARTF_USESTDHANDLES)
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;
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;
180 /***********************************************************************
183 * Free a PDB and all associated storage.
185 static void PROCESS_FreePDB( PDB32 *pdb )
187 pdb->header.type = K32OBJ_UNKNOWN;
188 if (pdb->handle_table) HANDLE_CloseAll( pdb, NULL );
189 ENV_FreeEnvironment( pdb );
190 if (pdb->heap && (pdb->heap != pdb->system_heap)) HeapDestroy( pdb->heap );
191 if (pdb->load_done_evt) K32OBJ_DecCount( pdb->load_done_evt );
192 if (pdb->event) K32OBJ_DecCount( pdb->event );
193 DeleteCriticalSection( &pdb->crit_section );
194 HeapFree( SystemHeap, 0, pdb );
198 /***********************************************************************
201 * Allocate and fill a PDB structure.
202 * Runs in the context of the parent process.
204 static PDB32 *PROCESS_CreatePDB( PDB32 *parent )
206 PDB32 *pdb = HeapAlloc( SystemHeap, HEAP_ZERO_MEMORY, sizeof(PDB32) );
208 if (!pdb) return NULL;
209 pdb->header.type = K32OBJ_PROCESS;
210 pdb->header.refcount = 1;
211 pdb->exit_code = 0x103; /* STILL_ACTIVE */
213 pdb->running_threads = 1;
214 pdb->ring0_threads = 1;
215 pdb->system_heap = SystemHeap;
216 pdb->parent = parent;
218 pdb->priority = 8; /* Normal */
219 pdb->heap = pdb->system_heap; /* will be changed later on */
221 InitializeCriticalSection( &pdb->crit_section );
223 /* Allocate the events */
225 if (!(pdb->event = EVENT_Create( TRUE, FALSE ))) goto error;
226 if (!(pdb->load_done_evt = EVENT_Create( TRUE, FALSE ))) goto error;
228 /* Create the handle table */
230 if (!HANDLE_CreateTable( pdb, TRUE )) goto error;
235 PROCESS_FreePDB( pdb );
240 /***********************************************************************
243 BOOL32 PROCESS_Init(void)
248 /* Initialize virtual memory management */
249 if (!VIRTUAL_Init()) return FALSE;
251 /* Create the system and SEGPTR heaps */
252 if (!(SystemHeap = HeapCreate( HEAP_GROWABLE, 0x10000, 0 ))) return FALSE;
253 if (!(SegptrHeap = HeapCreate( HEAP_WINE_SEGPTR, 0, 0 ))) return FALSE;
255 /* Create the initial process and thread structures */
256 if (!(pdb = PROCESS_CreatePDB( NULL ))) return FALSE;
257 if (!(thdb = THREAD_Create( pdb, 0, FALSE, NULL, NULL, NULL, NULL ))) return FALSE;
258 thdb->unix_pid = getpid();
260 PROCESS_InitialProcessID = PDB_TO_PROCESS_ID(pdb);
262 /* Remember TEB selector of initial process for emergency use */
263 SYSLEVEL_EmergencyTeb = thdb->teb_sel;
265 /* Create the environment DB of the first process */
266 if (!PROCESS_BuildEnvDB( pdb )) return FALSE;
268 /* Initialize the first thread */
269 if (CLIENT_InitThread()) return FALSE;
275 /***********************************************************************
278 * Create a new process database and associated info.
280 PDB32 *PROCESS_Create( NE_MODULE *pModule, LPCSTR cmd_line, LPCSTR env,
281 HINSTANCE16 hInstance, HINSTANCE16 hPrevInstance,
282 STARTUPINFO32A *startup, PROCESS_INFORMATION *info )
285 int server_thandle, server_phandle;
288 PDB32 *parent = PROCESS_Current();
289 PDB32 *pdb = PROCESS_CreatePDB( parent );
292 if (!pdb) return NULL;
293 info->hThread = info->hProcess = INVALID_HANDLE_VALUE32;
295 /* Create the heap */
297 if (pModule->module32)
299 size = PE_HEADER(pModule->module32)->OptionalHeader.SizeOfHeapReserve;
300 commit = PE_HEADER(pModule->module32)->OptionalHeader.SizeOfHeapCommit;
306 pdb->flags |= PDB32_WIN16_PROC; /* This is a Win16 process */
308 if (!(pdb->heap = HeapCreate( HEAP_GROWABLE, size, commit ))) goto error;
309 pdb->heap_list = pdb->heap;
311 /* Inherit the env DB from the parent */
313 if (!PROCESS_InheritEnvDB( pdb, cmd_line, env, startup )) goto error;
315 /* Create the main thread */
317 if (pModule->module32)
318 size = PE_HEADER(pModule->module32)->OptionalHeader.SizeOfStackReserve;
321 if (!(thdb = THREAD_Create( pdb, size, FALSE, &server_thandle, &server_phandle,
322 NULL, NULL ))) goto error;
323 if ((info->hThread = HANDLE_Alloc( parent, &thdb->header, THREAD_ALL_ACCESS,
324 FALSE, server_thandle )) == INVALID_HANDLE_VALUE32)
326 if ((info->hProcess = HANDLE_Alloc( parent, &pdb->header, PROCESS_ALL_ACCESS,
327 FALSE, server_phandle )) == INVALID_HANDLE_VALUE32)
329 info->dwProcessId = PDB_TO_PROCESS_ID(pdb);
330 info->dwThreadId = THDB_TO_THREAD_ID(thdb);
333 thdb->unix_pid = getpid(); /* FIXME: wrong here ... */
335 /* All Win16 'threads' have the same unix_pid, no matter by which thread
336 they were created ! */
337 pTask = (TDB *)GlobalLock16( parent->task );
338 thdb->unix_pid = pTask? pTask->thdb->unix_pid : THREAD_Current()->unix_pid;
341 /* Create a Win16 task for this process */
343 if (startup->dwFlags & STARTF_USESHOWWINDOW)
344 cmdShow = startup->wShowWindow;
346 pdb->task = TASK_Create( thdb, pModule, hInstance, hPrevInstance, cmdShow);
347 if (!pdb->task) goto error;
350 /* Map system DLLs into this process (from initial process) */
351 /* FIXME: this is a hack */
352 pdb->modref_list = PROCESS_Initial()->modref_list;
358 if (info->hThread != INVALID_HANDLE_VALUE32) CloseHandle( info->hThread );
359 if (info->hProcess != INVALID_HANDLE_VALUE32) CloseHandle( info->hProcess );
360 if (thdb) K32OBJ_DecCount( &thdb->header );
361 PROCESS_FreePDB( pdb );
366 /***********************************************************************
369 static BOOL32 PROCESS_Signaled( K32OBJ *obj, DWORD thread_id )
371 PDB32 *pdb = (PDB32 *)obj;
372 assert( obj->type == K32OBJ_PROCESS );
373 return K32OBJ_OPS( pdb->event )->signaled( pdb->event, thread_id );
377 /***********************************************************************
380 * Wait on this object has been satisfied.
382 static BOOL32 PROCESS_Satisfied( K32OBJ *obj, DWORD thread_id )
384 PDB32 *pdb = (PDB32 *)obj;
385 assert( obj->type == K32OBJ_PROCESS );
386 return K32OBJ_OPS( pdb->event )->satisfied( pdb->event, thread_id );
390 /***********************************************************************
393 * Add thread to object wait queue.
395 static void PROCESS_AddWait( K32OBJ *obj, DWORD thread_id )
397 PDB32 *pdb = (PDB32 *)obj;
398 assert( obj->type == K32OBJ_PROCESS );
399 return K32OBJ_OPS( pdb->event )->add_wait( pdb->event, thread_id );
403 /***********************************************************************
406 * Remove thread from object wait queue.
408 static void PROCESS_RemoveWait( K32OBJ *obj, DWORD thread_id )
410 PDB32 *pdb = (PDB32 *)obj;
411 assert( obj->type == K32OBJ_PROCESS );
412 return K32OBJ_OPS( pdb->event )->remove_wait( pdb->event, thread_id );
416 /***********************************************************************
419 static void PROCESS_Destroy( K32OBJ *ptr )
421 PDB32 *pdb = (PDB32 *)ptr;
422 assert( ptr->type == K32OBJ_PROCESS );
424 /* Free everything */
426 ptr->type = K32OBJ_UNKNOWN;
427 PROCESS_FreePDB( pdb );
431 /***********************************************************************
432 * ExitProcess (KERNEL32.100)
434 void WINAPI ExitProcess( DWORD status )
436 PDB32 *pdb = PROCESS_Current();
437 TDB *pTask = (TDB *)GlobalLock16( pdb->task );
438 if ( pTask ) pTask->nEvents++;
440 if ( pTask && pTask->thdb != THREAD_Current() )
441 ExitThread( status );
444 /* FIXME: should kill all running threads of this process */
445 pdb->exit_code = status;
446 EVENT_Set( pdb->event );
447 if (pdb->console) FreeConsole();
450 __RESTORE_ES; /* Necessary for Pietrek's showseh example program */
451 TASK_KillCurrentTask( status );
455 /******************************************************************************
456 * TerminateProcess (KERNEL32.684)
458 BOOL32 WINAPI TerminateProcess( HANDLE32 handle, DWORD exit_code )
462 PDB32 *pdb = PROCESS_GetPtr( handle, PROCESS_TERMINATE, &server_handle );
463 if (!pdb) return FALSE;
464 ret = !CLIENT_TerminateProcess( server_handle, exit_code );
465 K32OBJ_DecCount( &pdb->header );
469 /***********************************************************************
470 * GetCurrentProcess (KERNEL32.198)
472 HANDLE32 WINAPI GetCurrentProcess(void)
474 return CURRENT_PROCESS_PSEUDOHANDLE;
478 /*********************************************************************
479 * OpenProcess (KERNEL32.543)
481 HANDLE32 WINAPI OpenProcess( DWORD access, BOOL32 inherit, DWORD id )
484 PDB32 *pdb = PROCESS_ID_TO_PDB(id);
485 if (!K32OBJ_IsValid( &pdb->header, K32OBJ_PROCESS ))
487 SetLastError( ERROR_INVALID_HANDLE );
490 if ((server_handle = CLIENT_OpenProcess( pdb->server_pid, access, inherit )) == -1)
492 SetLastError( ERROR_INVALID_HANDLE );
495 return HANDLE_Alloc( PROCESS_Current(), &pdb->header, access,
496 inherit, server_handle );
500 /***********************************************************************
501 * GetCurrentProcessId (KERNEL32.199)
503 DWORD WINAPI GetCurrentProcessId(void)
505 PDB32 *pdb = PROCESS_Current();
506 return PDB_TO_PROCESS_ID( pdb );
510 /***********************************************************************
511 * GetProcessHeap (KERNEL32.259)
513 HANDLE32 WINAPI GetProcessHeap(void)
515 PDB32 *pdb = PROCESS_Current();
516 return pdb->heap ? pdb->heap : SystemHeap;
520 /***********************************************************************
521 * GetThreadLocale (KERNEL32.295)
523 LCID WINAPI GetThreadLocale(void)
525 return PROCESS_Current()->locale;
529 /***********************************************************************
530 * SetPriorityClass (KERNEL32.503)
532 BOOL32 WINAPI SetPriorityClass( HANDLE32 hprocess, DWORD priorityclass )
534 PDB32 *pdb = PROCESS_GetPtr( hprocess, PROCESS_SET_INFORMATION, NULL );
535 if (!pdb) return FALSE;
536 switch (priorityclass)
538 case NORMAL_PRIORITY_CLASS:
539 pdb->priority = 0x00000008;
541 case IDLE_PRIORITY_CLASS:
542 pdb->priority = 0x00000004;
544 case HIGH_PRIORITY_CLASS:
545 pdb->priority = 0x0000000d;
547 case REALTIME_PRIORITY_CLASS:
548 pdb->priority = 0x00000018;
551 WARN(process,"Unknown priority class %ld\n",priorityclass);
554 K32OBJ_DecCount( &pdb->header );
559 /***********************************************************************
560 * GetPriorityClass (KERNEL32.250)
562 DWORD WINAPI GetPriorityClass(HANDLE32 hprocess)
564 PDB32 *pdb = PROCESS_GetPtr( hprocess, PROCESS_QUERY_INFORMATION, NULL );
568 switch (pdb->priority)
571 ret = NORMAL_PRIORITY_CLASS;
574 ret = IDLE_PRIORITY_CLASS;
577 ret = HIGH_PRIORITY_CLASS;
580 ret = REALTIME_PRIORITY_CLASS;
583 WARN(process,"Unknown priority %ld\n",pdb->priority);
585 K32OBJ_DecCount( &pdb->header );
591 /***********************************************************************
592 * GetStdHandle (KERNEL32.276)
594 * FIXME: These should be allocated when a console is created, or inherited
597 HANDLE32 WINAPI GetStdHandle( DWORD std_handle )
601 PDB32 *pdb = PROCESS_Current();
605 case STD_INPUT_HANDLE:
606 if (pdb->env_db->hStdin) return pdb->env_db->hStdin;
609 case STD_OUTPUT_HANDLE:
610 if (pdb->env_db->hStdout) return pdb->env_db->hStdout;
613 case STD_ERROR_HANDLE:
614 if (pdb->env_db->hStderr) return pdb->env_db->hStderr;
618 SetLastError( ERROR_INVALID_PARAMETER );
619 return INVALID_HANDLE_VALUE32;
621 hFile = FILE_DupUnixHandle( fd );
622 if (hFile != HFILE_ERROR32)
624 FILE_SetFileType( hFile, FILE_TYPE_CHAR );
627 case STD_INPUT_HANDLE: pdb->env_db->hStdin = hFile; break;
628 case STD_OUTPUT_HANDLE: pdb->env_db->hStdout = hFile; break;
629 case STD_ERROR_HANDLE: pdb->env_db->hStderr = hFile; break;
636 /***********************************************************************
637 * SetStdHandle (KERNEL32.506)
639 BOOL32 WINAPI SetStdHandle( DWORD std_handle, HANDLE32 handle )
641 PDB32 *pdb = PROCESS_Current();
642 /* FIXME: should we close the previous handle? */
645 case STD_INPUT_HANDLE:
646 pdb->env_db->hStdin = handle;
648 case STD_OUTPUT_HANDLE:
649 pdb->env_db->hStdout = handle;
651 case STD_ERROR_HANDLE:
652 pdb->env_db->hStderr = handle;
655 SetLastError( ERROR_INVALID_PARAMETER );
659 /***********************************************************************
660 * GetProcessVersion (KERNEL32)
662 DWORD WINAPI GetProcessVersion( DWORD processid )
665 PDB32 *pdb = PROCESS_IdToPDB( processid );
668 if (!(pTask = (TDB *)GlobalLock16( pdb->task ))) return 0;
669 return (pTask->version&0xff) | (((pTask->version >>8) & 0xff)<<16);
672 /***********************************************************************
673 * GetProcessFlags (KERNEL32)
675 DWORD WINAPI GetProcessFlags( DWORD processid )
677 PDB32 *pdb = PROCESS_IdToPDB( processid );
682 /***********************************************************************
683 * SetProcessWorkingSetSize [KERNEL32.662]
684 * Sets the min/max working set sizes for a specified process.
687 * hProcess [I] Handle to the process of interest
688 * minset [I] Specifies minimum working set size
689 * maxset [I] Specifies maximum working set size
693 BOOL32 WINAPI SetProcessWorkingSetSize(HANDLE32 hProcess,DWORD minset,
696 FIXME(process,"(0x%08x,%ld,%ld): stub - harmless\n",hProcess,minset,maxset);
697 if(( minset == -1) && (maxset == -1)) {
698 /* Trim the working set to zero */
699 /* Swap the process out of physical RAM */
704 /***********************************************************************
705 * GetProcessWorkingSetSize (KERNEL32)
707 BOOL32 WINAPI GetProcessWorkingSetSize(HANDLE32 hProcess,LPDWORD minset,
710 FIXME(process,"(0x%08x,%p,%p): stub\n",hProcess,minset,maxset);
711 /* 32 MB working set size */
712 if (minset) *minset = 32*1024*1024;
713 if (maxset) *maxset = 32*1024*1024;
717 /***********************************************************************
718 * SetProcessShutdownParameters (KERNEL32)
720 BOOL32 WINAPI SetProcessShutdownParameters(DWORD level,DWORD flags)
722 FIXME(process,"(%ld,0x%08lx): stub\n",level,flags);
726 /***********************************************************************
727 * SetProcessPriorityBoost (KERNEL32)
729 BOOL32 WINAPI SetProcessPriorityBoost(HANDLE32 hprocess,BOOL32 disableboost)
731 FIXME(process,"(%d,%d): stub\n",hprocess,disableboost);
732 /* Say we can do it. I doubt the program will notice that we don't. */
736 /***********************************************************************
737 * ReadProcessMemory (KERNEL32)
738 * FIXME: check this, if we ever run win32 binaries in different addressspaces
739 * ... and add a sizecheck
741 BOOL32 WINAPI ReadProcessMemory( HANDLE32 hProcess, LPCVOID lpBaseAddress,
742 LPVOID lpBuffer, DWORD nSize,
743 LPDWORD lpNumberOfBytesRead )
745 memcpy(lpBuffer,lpBaseAddress,nSize);
746 if (lpNumberOfBytesRead) *lpNumberOfBytesRead = nSize;
750 /***********************************************************************
751 * WriteProcessMemory (KERNEL32)
752 * FIXME: check this, if we ever run win32 binaries in different addressspaces
753 * ... and add a sizecheck
755 BOOL32 WINAPI WriteProcessMemory(HANDLE32 hProcess, LPVOID lpBaseAddress,
756 LPVOID lpBuffer, DWORD nSize,
757 LPDWORD lpNumberOfBytesWritten )
759 memcpy(lpBaseAddress,lpBuffer,nSize);
760 if (lpNumberOfBytesWritten) *lpNumberOfBytesWritten = nSize;
764 /***********************************************************************
765 * ConvertToGlobalHandle (KERNEL32)
767 HANDLE32 WINAPI ConvertToGlobalHandle(HANDLE32 hSrc)
769 HANDLE32 hProcessInit, hDest;
771 /* Get a handle to the initial process */
772 hProcessInit = OpenProcess( PROCESS_ALL_ACCESS, FALSE, PROCESS_InitialProcessID );
774 /* Duplicate the handle into the initial process */
775 if ( !DuplicateHandle( GetCurrentProcess(), hSrc, hProcessInit, &hDest,
776 0, FALSE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE ) )
779 /* Close initial process handle */
780 CloseHandle( hProcessInit );
782 /* Return obfuscated global handle */
783 return hDest? HANDLE_LOCAL_TO_GLOBAL( hDest ) : 0;
786 /***********************************************************************
787 * RegisterServiceProcess (KERNEL, KERNEL32)
789 * A service process calls this function to ensure that it continues to run
790 * even after a user logged off.
792 DWORD WINAPI RegisterServiceProcess(DWORD dwProcessId, DWORD dwType)
794 /* I don't think that Wine needs to do anything in that function */
795 return 1; /* success */
798 /***********************************************************************
799 * GetExitCodeProcess [KERNEL32.325]
801 * Gets termination status of specified process
808 * Should call SetLastError (but doesn't).
810 BOOL32 WINAPI GetExitCodeProcess(
811 HANDLE32 hProcess, /* [I] handle to the process */
812 LPDWORD lpExitCode) /* [O] address to receive termination status */
816 struct get_process_info_reply info;
818 if (!(process = PROCESS_GetPtr( hProcess, PROCESS_QUERY_INFORMATION,
821 if (server_handle != -1)
823 CLIENT_GetProcessInfo( server_handle, &info );
824 if (lpExitCode) *lpExitCode = info.exit_code;
826 else if (lpExitCode) *lpExitCode = process->exit_code;
827 K32OBJ_DecCount( &process->header );
831 /***********************************************************************
832 * GetProcessHeaps [KERNEL32.376]
834 DWORD WINAPI GetProcessHeaps(DWORD nrofheaps,HANDLE32 *heaps) {
835 FIXME(win32,"(%ld,%p), incomplete implementation.\n",nrofheaps,heaps);
838 heaps[0] = GetProcessHeap();
839 /* ... probably SystemHeap too ? */
842 /* number of available heaps */
846 /***********************************************************************
847 * PROCESS_SuspendOtherThreads
850 void PROCESS_SuspendOtherThreads(void)
857 pdb = PROCESS_Current();
858 entry = pdb->thread_list->next;
861 if (entry->thread != THREAD_Current() && !THREAD_IsWin16(entry->thread))
863 HANDLE32 handle = HANDLE_Alloc( PROCESS_Current(),
864 &entry->thread->header,
865 THREAD_ALL_ACCESS, FALSE, -1 );
866 SuspendThread(handle);
869 if (entry == pdb->thread_list) break;
876 /***********************************************************************
877 * PROCESS_ResumeOtherThreads
880 void PROCESS_ResumeOtherThreads(void)
887 pdb = PROCESS_Current();
888 entry = pdb->thread_list->next;
891 if (entry->thread != THREAD_Current() && !THREAD_IsWin16(entry->thread))
893 HANDLE32 handle = HANDLE_Alloc( PROCESS_Current(),
894 &entry->thread->header,
895 THREAD_ALL_ACCESS, FALSE, -1 );
896 ResumeThread(handle);
899 if (entry == pdb->thread_list) break;
906 BOOL32 WINAPI Process32First(HANDLE32 hSnapshot, LPPROCESSENTRY32 lppe)
908 FIXME (process, "(0x%08lx,0x%08lx), stub!\n", hSnapshot, lppe);
909 SetLastError (ERROR_NO_MORE_FILES);
913 BOOL32 WINAPI Process32Next(HANDLE32 hSnapshot, LPPROCESSENTRY32 lppe)
915 FIXME (process, "(0x%08lx,0x%08lx), stub!\n", hSnapshot, lppe);
916 SetLastError (ERROR_NO_MORE_FILES);