Sync with trunk.
[reactos.git] / ntoskrnl / ps / process.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ps/process.c
5 * PURPOSE: Process Manager: Process Management
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Thomas Weidenmueller (w3seek@reactos.org
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 /* GLOBALS *******************************************************************/
17
18 extern ULONG PsMinimumWorkingSet, PsMaximumWorkingSet;
19
20 POBJECT_TYPE PsProcessType = NULL;
21
22 LIST_ENTRY PsActiveProcessHead;
23 KGUARDED_MUTEX PspActiveProcessMutex;
24
25 LARGE_INTEGER ShortPsLockDelay;
26
27 ULONG PsRawPrioritySeparation = 0;
28 ULONG PsPrioritySeparation;
29 CHAR PspForegroundQuantum[3];
30
31 /* Fixed quantum table */
32 CHAR PspFixedQuantums[6] =
33 {
34 /* Short quantums */
35 3 * 6, /* Level 1 */
36 3 * 6, /* Level 2 */
37 3 * 6, /* Level 3 */
38
39 /* Long quantums */
40 6 * 6, /* Level 1 */
41 6 * 6, /* Level 2 */
42 6 * 6 /* Level 3 */
43 };
44
45 /* Variable quantum table */
46 CHAR PspVariableQuantums[6] =
47 {
48 /* Short quantums */
49 1 * 6, /* Level 1 */
50 2 * 6, /* Level 2 */
51 3 * 6, /* Level 3 */
52
53 /* Long quantums */
54 2 * 6, /* Level 1 */
55 4 * 6, /* Level 2 */
56 6 * 6 /* Level 3 */
57 };
58
59 /* Priority table */
60 KPRIORITY PspPriorityTable[PROCESS_PRIORITY_CLASS_ABOVE_NORMAL + 1] =
61 {
62 8,
63 4,
64 8,
65 13,
66 24,
67 6,
68 10
69 };
70
71 /* PRIVATE FUNCTIONS *********************************************************/
72
73 PETHREAD
74 NTAPI
75 PsGetNextProcessThread(IN PEPROCESS Process,
76 IN PETHREAD Thread OPTIONAL)
77 {
78 PETHREAD FoundThread = NULL;
79 PLIST_ENTRY ListHead, Entry;
80 PAGED_CODE();
81 PSTRACE(PS_PROCESS_DEBUG,
82 "Process: %p Thread: %p\n", Process, Thread);
83
84 /* Lock the process */
85 KeEnterCriticalRegion();
86 ExAcquirePushLockShared(&Process->ProcessLock);
87
88 /* Check if we're already starting somewhere */
89 if (Thread)
90 {
91 /* Start where we left off */
92 Entry = Thread->ThreadListEntry.Flink;
93 }
94 else
95 {
96 /* Start at the beginning */
97 Entry = Process->ThreadListHead.Flink;
98 }
99
100 /* Set the list head and start looping */
101 ListHead = &Process->ThreadListHead;
102 while (ListHead != Entry)
103 {
104 /* Get the Thread */
105 FoundThread = CONTAINING_RECORD(Entry, ETHREAD, ThreadListEntry);
106
107 /* Safe reference the thread */
108 if (ObReferenceObjectSafe(FoundThread)) break;
109
110 /* Nothing found, keep looping */
111 FoundThread = NULL;
112 Entry = Entry->Flink;
113 }
114
115 /* Unlock the process */
116 ExReleasePushLockShared(&Process->ProcessLock);
117 KeLeaveCriticalRegion();
118
119 /* Check if we had a starting thread, and dereference it */
120 if (Thread) ObDereferenceObject(Thread);
121
122 /* Return what we found */
123 return FoundThread;
124 }
125
126 PEPROCESS
127 NTAPI
128 PsGetNextProcess(IN PEPROCESS OldProcess)
129 {
130 PLIST_ENTRY Entry;
131 PEPROCESS FoundProcess = NULL;
132 PAGED_CODE();
133 PSTRACE(PS_PROCESS_DEBUG, "Process: %p\n", OldProcess);
134
135 /* Acquire the Active Process Lock */
136 KeAcquireGuardedMutex(&PspActiveProcessMutex);
137
138 /* Check if we're already starting somewhere */
139 if (OldProcess)
140 {
141 /* Start where we left off */
142 Entry = OldProcess->ActiveProcessLinks.Flink;
143 }
144 else
145 {
146 /* Start at the beginning */
147 Entry = PsActiveProcessHead.Flink;
148 }
149
150 /* Loop the process list */
151 while (Entry != &PsActiveProcessHead)
152 {
153 /* Get the process */
154 FoundProcess = CONTAINING_RECORD(Entry, EPROCESS, ActiveProcessLinks);
155
156 /* Reference the process */
157 if (ObReferenceObjectSafe(FoundProcess)) break;
158
159 /* Nothing found, keep trying */
160 FoundProcess = NULL;
161 Entry = Entry->Flink;
162 }
163
164 /* Release the lock */
165 KeReleaseGuardedMutex(&PspActiveProcessMutex);
166
167 /* Dereference the Process we had referenced earlier */
168 if (OldProcess) ObDereferenceObject(OldProcess);
169 return FoundProcess;
170 }
171
172 KPRIORITY
173 NTAPI
174 PspComputeQuantumAndPriority(IN PEPROCESS Process,
175 IN PSPROCESSPRIORITYMODE Mode,
176 OUT PUCHAR Quantum)
177 {
178 ULONG i;
179 UCHAR LocalQuantum, MemoryPriority;
180 PAGED_CODE();
181 PSTRACE(PS_PROCESS_DEBUG, "Process: %p Mode: %lx\n", Process, Mode);
182
183 /* Check if this is a foreground process */
184 if (Mode == PsProcessPriorityForeground)
185 {
186 /* Set the memory priority and use priority separation */
187 MemoryPriority = MEMORY_PRIORITY_FOREGROUND;
188 i = PsPrioritySeparation;
189 }
190 else
191 {
192 /* Set the background memory priority and no separation */
193 MemoryPriority = MEMORY_PRIORITY_BACKGROUND;
194 i = 0;
195 }
196
197 /* Make sure that the process mode isn't spinning */
198 if (Mode != PsProcessPrioritySpinning)
199 {
200 /* Set the priority */
201 MmSetMemoryPriorityProcess(Process, MemoryPriority);
202 }
203
204 /* Make sure that the process isn't idle */
205 if (Process->PriorityClass != PROCESS_PRIORITY_CLASS_IDLE)
206 {
207 /* Does the process have a job? */
208 if ((Process->Job) && (PspUseJobSchedulingClasses))
209 {
210 /* Use job quantum */
211 LocalQuantum = PspJobSchedulingClasses[Process->Job->
212 SchedulingClass];
213 }
214 else
215 {
216 /* Use calculated quantum */
217 LocalQuantum = PspForegroundQuantum[i];
218 }
219 }
220 else
221 {
222 /* Process is idle, use default quantum */
223 LocalQuantum = 6;
224 }
225
226 /* Return quantum to caller */
227 *Quantum = LocalQuantum;
228
229 /* Return priority */
230 return PspPriorityTable[Process->PriorityClass];
231 }
232
233 VOID
234 NTAPI
235 PsChangeQuantumTable(IN BOOLEAN Immediate,
236 IN ULONG PrioritySeparation)
237 {
238 PEPROCESS Process = NULL;
239 ULONG i;
240 UCHAR Quantum;
241 PCHAR QuantumTable;
242 PAGED_CODE();
243 PSTRACE(PS_PROCESS_DEBUG,
244 "%lx PrioritySeparation: %lx\n", Immediate, PrioritySeparation);
245
246 /* Write the current priority separation */
247 PsPrioritySeparation = PspPrioritySeparationFromMask(PrioritySeparation);
248
249 /* Normalize it if it was too high */
250 if (PsPrioritySeparation == 3) PsPrioritySeparation = 2;
251
252 /* Get the quantum table to use */
253 if (PspQuantumTypeFromMask(PrioritySeparation) == PSP_VARIABLE_QUANTUMS)
254 {
255 /* Use a variable table */
256 QuantumTable = PspVariableQuantums;
257 }
258 else
259 {
260 /* Use fixed table */
261 QuantumTable = PspFixedQuantums;
262 }
263
264 /* Now check if we should use long or short */
265 if (PspQuantumLengthFromMask(PrioritySeparation) == PSP_LONG_QUANTUMS)
266 {
267 /* Use long quantums */
268 QuantumTable += 3;
269 }
270
271 /* Check if we're using long fixed quantums */
272 if (QuantumTable == &PspFixedQuantums[3])
273 {
274 /* Use Job scheduling classes */
275 PspUseJobSchedulingClasses = TRUE;
276 }
277 else
278 {
279 /* Otherwise, we don't */
280 PspUseJobSchedulingClasses = FALSE;
281 }
282
283 /* Copy the selected table into the Foreground Quantum table */
284 RtlCopyMemory(PspForegroundQuantum,
285 QuantumTable,
286 sizeof(PspForegroundQuantum));
287
288 /* Check if we should apply these changes real-time */
289 if (Immediate)
290 {
291 /* We are...loop every process */
292 Process = PsGetNextProcess(Process);
293 while (Process)
294 {
295 /*
296 * Use the priority separation, unless the process has
297 * low memory priority
298 */
299 i = (Process->Vm.Flags.MemoryPriority == 1) ?
300 0: PsPrioritySeparation;
301
302 /* Make sure that the process isn't idle */
303 if (Process->PriorityClass != PROCESS_PRIORITY_CLASS_IDLE)
304 {
305 /* Does the process have a job? */
306 if ((Process->Job) && (PspUseJobSchedulingClasses))
307 {
308 /* Use job quantum */
309 Quantum = PspJobSchedulingClasses[Process->Job->
310 SchedulingClass];
311 }
312 else
313 {
314 /* Use calculated quantum */
315 Quantum = PspForegroundQuantum[i];
316 }
317 }
318 else
319 {
320 /* Process is idle, use default quantum */
321 Quantum = 6;
322 }
323
324 /* Now set the quantum */
325 KeSetQuantumProcess(&Process->Pcb, Quantum);
326
327 /* Get the next process */
328 Process = PsGetNextProcess(Process);
329 }
330 }
331 }
332
333 NTSTATUS
334 NTAPI
335 PspCreateProcess(OUT PHANDLE ProcessHandle,
336 IN ACCESS_MASK DesiredAccess,
337 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
338 IN HANDLE ParentProcess OPTIONAL,
339 IN ULONG Flags,
340 IN HANDLE SectionHandle OPTIONAL,
341 IN HANDLE DebugPort OPTIONAL,
342 IN HANDLE ExceptionPort OPTIONAL,
343 IN BOOLEAN InJob)
344 {
345 HANDLE hProcess;
346 PEPROCESS Process, Parent;
347 PVOID ExceptionPortObject;
348 PDEBUG_OBJECT DebugObject;
349 PSECTION_OBJECT SectionObject;
350 NTSTATUS Status, AccessStatus;
351 ULONG_PTR DirectoryTableBase[2] = {0,0};
352 KAFFINITY Affinity;
353 HANDLE_TABLE_ENTRY CidEntry;
354 PETHREAD CurrentThread = PsGetCurrentThread();
355 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
356 PEPROCESS CurrentProcess = PsGetCurrentProcess();
357 ULONG MinWs, MaxWs;
358 ACCESS_STATE LocalAccessState;
359 PACCESS_STATE AccessState = &LocalAccessState;
360 AUX_ACCESS_DATA AuxData;
361 UCHAR Quantum;
362 BOOLEAN Result, SdAllocated;
363 PSECURITY_DESCRIPTOR SecurityDescriptor;
364 SECURITY_SUBJECT_CONTEXT SubjectContext;
365 BOOLEAN NeedsPeb = FALSE;
366 INITIAL_PEB InitialPeb;
367 PAGED_CODE();
368 PSTRACE(PS_PROCESS_DEBUG,
369 "ProcessHandle: %p Parent: %p\n", ProcessHandle, ParentProcess);
370
371 /* Validate flags */
372 if (Flags & ~PROCESS_CREATE_FLAGS_LEGAL_MASK) return STATUS_INVALID_PARAMETER;
373
374 /* Check for parent */
375 if (ParentProcess)
376 {
377 /* Reference it */
378 Status = ObReferenceObjectByHandle(ParentProcess,
379 PROCESS_CREATE_PROCESS,
380 PsProcessType,
381 PreviousMode,
382 (PVOID*)&Parent,
383 NULL);
384 if (!NT_SUCCESS(Status)) return Status;
385
386 /* If this process should be in a job but the parent isn't */
387 if ((InJob) && (!Parent->Job))
388 {
389 /* This is illegal. Dereference the parent and fail */
390 ObDereferenceObject(Parent);
391 return STATUS_INVALID_PARAMETER;
392 }
393
394 /* Inherit Parent process's Affinity. */
395 Affinity = Parent->Pcb.Affinity;
396 }
397 else
398 {
399 /* We have no parent */
400 Parent = NULL;
401 Affinity = KeActiveProcessors;
402 }
403
404 /* Save working set data */
405 MinWs = PsMinimumWorkingSet;
406 MaxWs = PsMaximumWorkingSet;
407
408 /* Create the Object */
409 Status = ObCreateObject(PreviousMode,
410 PsProcessType,
411 ObjectAttributes,
412 PreviousMode,
413 NULL,
414 sizeof(EPROCESS),
415 0,
416 0,
417 (PVOID*)&Process);
418 if (!NT_SUCCESS(Status)) goto Cleanup;
419
420 /* Clean up the Object */
421 RtlZeroMemory(Process, sizeof(EPROCESS));
422
423 /* Initialize pushlock and rundown protection */
424 ExInitializeRundownProtection(&Process->RundownProtect);
425 Process->ProcessLock.Value = 0;
426
427 /* Setup the Thread List Head */
428 InitializeListHead(&Process->ThreadListHead);
429
430 /* Set up the Quota Block from the Parent */
431 PspInheritQuota(Process, Parent);
432
433 /* Set up Dos Device Map from the Parent */
434 ObInheritDeviceMap(Parent, Process);
435
436 /* Check if we have a parent */
437 if (Parent)
438 {
439 /* Inherit PID and Hard Error Processing */
440 Process->InheritedFromUniqueProcessId = Parent->UniqueProcessId;
441 Process->DefaultHardErrorProcessing = Parent->
442 DefaultHardErrorProcessing;
443 }
444 else
445 {
446 /* Use default hard error processing */
447 Process->DefaultHardErrorProcessing = TRUE;
448 }
449
450 /* Check for a section handle */
451 if (SectionHandle)
452 {
453 /* Get a pointer to it */
454 Status = ObReferenceObjectByHandle(SectionHandle,
455 SECTION_MAP_EXECUTE,
456 MmSectionObjectType,
457 PreviousMode,
458 (PVOID*)&SectionObject,
459 NULL);
460 if (!NT_SUCCESS(Status)) goto CleanupWithRef;
461 }
462 else
463 {
464 /* Assume no section object */
465 SectionObject = NULL;
466
467 /* Is the parent the initial process?
468 * Check for NULL also, as at initialization PsInitialSystemProcess is NULL */
469 if (Parent != PsInitialSystemProcess && (Parent != NULL))
470 {
471 /* It's not, so acquire the process rundown */
472 if (ExAcquireRundownProtection(&Parent->RundownProtect))
473 {
474 /* If the parent has a section, use it */
475 SectionObject = Parent->SectionObject;
476 if (SectionObject) ObReferenceObject(SectionObject);
477
478 /* Release process rundown */
479 ExReleaseRundownProtection(&Parent->RundownProtect);
480 }
481
482 /* If we don't have a section object */
483 if (!SectionObject)
484 {
485 /* Then the process is in termination, so fail */
486 Status = STATUS_PROCESS_IS_TERMINATING;
487 goto CleanupWithRef;
488 }
489 }
490 }
491
492 /* Save the pointer to the section object */
493 Process->SectionObject = SectionObject;
494
495 /* Check for the debug port */
496 if (DebugPort)
497 {
498 /* Reference it */
499 Status = ObReferenceObjectByHandle(DebugPort,
500 DEBUG_OBJECT_ADD_REMOVE_PROCESS,
501 DbgkDebugObjectType,
502 PreviousMode,
503 (PVOID*)&DebugObject,
504 NULL);
505 if (!NT_SUCCESS(Status)) goto CleanupWithRef;
506
507 /* Save the debug object */
508 Process->DebugPort = DebugObject;
509
510 /* Check if the caller doesn't want the debug stuff inherited */
511 if (Flags & PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT)
512 {
513 /* Set the process flag */
514 InterlockedOr((PLONG)&Process->Flags, PSF_NO_DEBUG_INHERIT_BIT);
515 }
516 }
517 else
518 {
519 /* Do we have a parent? Copy his debug port */
520 if (Parent) DbgkCopyProcessDebugPort(Process, Parent);
521 }
522
523 /* Now check for an exception port */
524 if (ExceptionPort)
525 {
526 /* Reference it */
527 Status = ObReferenceObjectByHandle(ExceptionPort,
528 PORT_ALL_ACCESS,
529 LpcPortObjectType,
530 PreviousMode,
531 (PVOID*)&ExceptionPortObject,
532 NULL);
533 if (!NT_SUCCESS(Status)) goto CleanupWithRef;
534
535 /* Save the exception port */
536 Process->ExceptionPort = ExceptionPortObject;
537 }
538
539 /* Save the pointer to the section object */
540 Process->SectionObject = SectionObject;
541
542 /* Set default exit code */
543 Process->ExitStatus = STATUS_PENDING;
544
545 /* Check if this is the initial process being built */
546 if (Parent)
547 {
548 /* Create the address space for the child */
549 if (!MmCreateProcessAddressSpace(MinWs,
550 Process,
551 DirectoryTableBase))
552 {
553 /* Failed */
554 Status = STATUS_INSUFFICIENT_RESOURCES;
555 goto CleanupWithRef;
556 }
557 }
558 else
559 {
560 /* Otherwise, we are the boot process, we're already semi-initialized */
561 Process->ObjectTable = CurrentProcess->ObjectTable;
562 Status = MmInitializeHandBuiltProcess(Process, DirectoryTableBase);
563 if (!NT_SUCCESS(Status)) goto CleanupWithRef;
564 }
565
566 /* We now have an address space */
567 InterlockedOr((PLONG)&Process->Flags, PSF_HAS_ADDRESS_SPACE_BIT);
568
569 /* Set the maximum WS */
570 Process->Vm.MaximumWorkingSetSize = MaxWs;
571
572 /* Now initialize the Kernel Process */
573 KeInitializeProcess(&Process->Pcb,
574 PROCESS_PRIORITY_NORMAL,
575 Affinity,
576 DirectoryTableBase,
577 (BOOLEAN)(Process->DefaultHardErrorProcessing & 4));
578
579 /* Duplicate Parent Token */
580 Status = PspInitializeProcessSecurity(Process, Parent);
581 if (!NT_SUCCESS(Status)) goto CleanupWithRef;
582
583 /* Set default priority class */
584 Process->PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
585
586 /* Check if we have a parent */
587 if (Parent)
588 {
589 /* Check our priority class */
590 if (Parent->PriorityClass == PROCESS_PRIORITY_CLASS_IDLE ||
591 Parent->PriorityClass == PROCESS_PRIORITY_CLASS_BELOW_NORMAL)
592 {
593 /* Normalize it */
594 Process->PriorityClass = Parent->PriorityClass;
595 }
596
597 /* Initialize object manager for the process */
598 Status = ObInitProcess(Flags & PROCESS_CREATE_FLAGS_INHERIT_HANDLES ?
599 Parent : NULL,
600 Process);
601 if (!NT_SUCCESS(Status)) goto CleanupWithRef;
602 }
603 else
604 {
605 /* Do the second part of the boot process memory setup */
606 Status = MmInitializeHandBuiltProcess2(Process);
607 if (!NT_SUCCESS(Status)) goto CleanupWithRef;
608 }
609
610 /* Set success for now */
611 Status = STATUS_SUCCESS;
612
613 /* Check if this is a real user-mode process */
614 if (SectionHandle)
615 {
616 /* Initialize the address space */
617 Status = MmInitializeProcessAddressSpace(Process,
618 NULL,
619 SectionObject,
620 &Flags,
621 &Process->
622 SeAuditProcessCreationInfo.
623 ImageFileName);
624 if (!NT_SUCCESS(Status)) goto CleanupWithRef;
625
626 //
627 // We need a PEB
628 //
629 NeedsPeb = TRUE;
630 }
631 else if (Parent)
632 {
633 /* Check if this is a child of the system process */
634 if (Parent != PsInitialSystemProcess)
635 {
636 //
637 // We need a PEB
638 //
639 NeedsPeb = TRUE;
640
641 /* This is a clone! */
642 ASSERTMSG("No support for cloning yet\n", FALSE);
643 }
644 else
645 {
646 /* This is the initial system process */
647 Flags &= ~PROCESS_CREATE_FLAGS_LARGE_PAGES;
648 Status = MmInitializeProcessAddressSpace(Process,
649 NULL,
650 NULL,
651 &Flags,
652 NULL);
653 if (!NT_SUCCESS(Status)) goto CleanupWithRef;
654
655 /* Create a dummy image file name */
656 Process->SeAuditProcessCreationInfo.ImageFileName =
657 ExAllocatePoolWithTag(PagedPool,
658 sizeof(OBJECT_NAME_INFORMATION),
659 TAG_SEPA);
660 if (!Process->SeAuditProcessCreationInfo.ImageFileName)
661 {
662 /* Fail */
663 Status = STATUS_INSUFFICIENT_RESOURCES;
664 goto CleanupWithRef;
665 }
666
667 /* Zero it out */
668 RtlZeroMemory(Process->SeAuditProcessCreationInfo.ImageFileName,
669 sizeof(OBJECT_NAME_INFORMATION));
670 }
671 }
672
673 #if MI_TRACE_PFNS
674 /* Copy the process name now that we have it */
675 memcpy(MiGetPfnEntry(Process->Pcb.DirectoryTableBase[0] >> PAGE_SHIFT)->ProcessName, Process->ImageFileName, 16);
676 if (Process->Pcb.DirectoryTableBase[1]) memcpy(MiGetPfnEntry(Process->Pcb.DirectoryTableBase[1] >> PAGE_SHIFT)->ProcessName, Process->ImageFileName, 16);
677 if (Process->WorkingSetPage) memcpy(MiGetPfnEntry(Process->WorkingSetPage)->ProcessName, Process->ImageFileName, 16);
678 #endif
679
680 /* Check if we have a section object and map the system DLL */
681 if (SectionObject) PspMapSystemDll(Process, NULL, FALSE);
682
683 /* Create a handle for the Process */
684 CidEntry.Object = Process;
685 CidEntry.GrantedAccess = 0;
686 Process->UniqueProcessId = ExCreateHandle(PspCidTable, &CidEntry);
687 if (!Process->UniqueProcessId)
688 {
689 /* Fail */
690 Status = STATUS_INSUFFICIENT_RESOURCES;
691 goto CleanupWithRef;
692 }
693
694 /* Set the handle table PID */
695 Process->ObjectTable->UniqueProcessId = Process->UniqueProcessId;
696
697 /* Check if we need to audit */
698 if (SeDetailedAuditingWithToken(NULL)) SeAuditProcessCreate(Process);
699
700 /* Check if the parent had a job */
701 if ((Parent) && (Parent->Job))
702 {
703 /* FIXME: We need to insert this process */
704 DPRINT1("Jobs not yet supported\n");
705 ASSERT(FALSE);
706 }
707
708 /* Create PEB only for User-Mode Processes */
709 if ((Parent) && (NeedsPeb))
710 {
711 //
712 // Set up the initial PEB
713 //
714 RtlZeroMemory(&InitialPeb, sizeof(INITIAL_PEB));
715 InitialPeb.Mutant = (HANDLE)-1;
716 InitialPeb.ImageUsesLargePages = 0; // FIXME: Not yet supported
717
718 //
719 // Create it only if we have an image section
720 //
721 if (SectionHandle)
722 {
723 //
724 // Create it
725 //
726 Status = MmCreatePeb(Process, &InitialPeb, &Process->Peb);
727 if (!NT_SUCCESS(Status)) goto CleanupWithRef;
728 }
729 else
730 {
731 //
732 // We have to clone it
733 //
734 ASSERTMSG("No support for cloning yet\n", FALSE);
735 }
736
737 }
738
739 /* The process can now be activated */
740 KeAcquireGuardedMutex(&PspActiveProcessMutex);
741 InsertTailList(&PsActiveProcessHead, &Process->ActiveProcessLinks);
742 KeReleaseGuardedMutex(&PspActiveProcessMutex);
743
744 /* Create an access state */
745 Status = SeCreateAccessStateEx(CurrentThread,
746 ((Parent) &&
747 (Parent == PsInitialSystemProcess)) ?
748 Parent : CurrentProcess,
749 &LocalAccessState,
750 &AuxData,
751 DesiredAccess,
752 &PsProcessType->TypeInfo.GenericMapping);
753 if (!NT_SUCCESS(Status)) goto CleanupWithRef;
754
755 /* Insert the Process into the Object Directory */
756 Status = ObInsertObject(Process,
757 AccessState,
758 DesiredAccess,
759 1,
760 NULL,
761 &hProcess);
762
763 /* Free the access state */
764 if (AccessState) SeDeleteAccessState(AccessState);
765
766 /* Cleanup on failure */
767 if (!NT_SUCCESS(Status)) goto Cleanup;
768
769 /* Compute Quantum and Priority */
770 ASSERT(IsListEmpty(&Process->ThreadListHead) == TRUE);
771 Process->Pcb.BasePriority =
772 (SCHAR)PspComputeQuantumAndPriority(Process,
773 PsProcessPriorityBackground,
774 &Quantum);
775 Process->Pcb.QuantumReset = Quantum;
776
777 /* Check if we have a parent other then the initial system process */
778 Process->GrantedAccess = PROCESS_TERMINATE;
779 if ((Parent) && (Parent != PsInitialSystemProcess))
780 {
781 /* Get the process's SD */
782 Status = ObGetObjectSecurity(Process,
783 &SecurityDescriptor,
784 &SdAllocated);
785 if (!NT_SUCCESS(Status))
786 {
787 /* We failed, close the handle and clean up */
788 ObCloseHandle(hProcess, PreviousMode);
789 goto CleanupWithRef;
790 }
791
792 /* Create the subject context */
793 SubjectContext.ProcessAuditId = Process;
794 SubjectContext.PrimaryToken = PsReferencePrimaryToken(Process);
795 SubjectContext.ClientToken = NULL;
796
797 /* Do the access check */
798 Result = SeAccessCheck(SecurityDescriptor,
799 &SubjectContext,
800 FALSE,
801 MAXIMUM_ALLOWED,
802 0,
803 NULL,
804 &PsProcessType->TypeInfo.GenericMapping,
805 PreviousMode,
806 &Process->GrantedAccess,
807 &AccessStatus);
808
809 /* Dereference the token and let go the SD */
810 ObFastDereferenceObject(&Process->Token,
811 SubjectContext.PrimaryToken);
812 ObReleaseObjectSecurity(SecurityDescriptor, SdAllocated);
813
814 /* Remove access if it failed */
815 if (!Result) Process->GrantedAccess = 0;
816
817 /* Give the process some basic access */
818 Process->GrantedAccess |= (PROCESS_VM_OPERATION |
819 PROCESS_VM_READ |
820 PROCESS_VM_WRITE |
821 PROCESS_QUERY_INFORMATION |
822 PROCESS_TERMINATE |
823 PROCESS_CREATE_THREAD |
824 PROCESS_DUP_HANDLE |
825 PROCESS_CREATE_PROCESS |
826 PROCESS_SET_INFORMATION |
827 STANDARD_RIGHTS_ALL |
828 PROCESS_SET_QUOTA);
829 }
830 else
831 {
832 /* Set full granted access */
833 Process->GrantedAccess = PROCESS_ALL_ACCESS;
834 }
835
836 /* Set the Creation Time */
837 KeQuerySystemTime(&Process->CreateTime);
838
839 /* Protect against bad user-mode pointer */
840 _SEH2_TRY
841 {
842 /* Hacky way of returning the PEB to the user-mode creator */
843 if ((Process->Peb) && (CurrentThread->Tcb.Teb))
844 {
845 CurrentThread->Tcb.Teb->NtTib.ArbitraryUserPointer = Process->Peb;
846 }
847
848 /* Save the process handle */
849 *ProcessHandle = hProcess;
850 }
851 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
852 {
853 /* Get the exception code */
854 Status = _SEH2_GetExceptionCode();
855 }
856 _SEH2_END;
857
858 /* Run the Notification Routines */
859 PspRunCreateProcessNotifyRoutines(Process, TRUE);
860
861 /* If 12 processes have been created, enough of user-mode is ready */
862 if (++ProcessCount == 12) Ki386PerfEnd();
863
864 CleanupWithRef:
865 /*
866 * Dereference the process. For failures, kills the process and does
867 * cleanup present in PspDeleteProcess. For success, kills the extra
868 * reference added by ObInsertObject.
869 */
870 ObDereferenceObject(Process);
871
872 Cleanup:
873 /* Dereference the parent */
874 if (Parent) ObDereferenceObject(Parent);
875
876 /* Return status to caller */
877 return Status;
878 }
879
880 /* PUBLIC FUNCTIONS **********************************************************/
881
882 /*
883 * @implemented
884 */
885 NTSTATUS
886 NTAPI
887 PsCreateSystemProcess(OUT PHANDLE ProcessHandle,
888 IN ACCESS_MASK DesiredAccess,
889 IN POBJECT_ATTRIBUTES ObjectAttributes)
890 {
891 /* Call the internal API */
892 return PspCreateProcess(ProcessHandle,
893 DesiredAccess,
894 ObjectAttributes,
895 NULL,
896 0,
897 NULL,
898 NULL,
899 NULL,
900 FALSE);
901 }
902
903 /*
904 * @implemented
905 */
906 NTSTATUS
907 NTAPI
908 PsLookupProcessByProcessId(IN HANDLE ProcessId,
909 OUT PEPROCESS *Process)
910 {
911 PHANDLE_TABLE_ENTRY CidEntry;
912 PEPROCESS FoundProcess;
913 NTSTATUS Status = STATUS_INVALID_PARAMETER;
914 PAGED_CODE();
915 PSTRACE(PS_PROCESS_DEBUG, "ProcessId: %p\n", ProcessId);
916 KeEnterCriticalRegion();
917
918 /* Get the CID Handle Entry */
919 CidEntry = ExMapHandleToPointer(PspCidTable, ProcessId);
920 if (CidEntry)
921 {
922 /* Get the Process */
923 FoundProcess = CidEntry->Object;
924
925 /* Make sure it's really a process */
926 if (FoundProcess->Pcb.Header.Type == ProcessObject)
927 {
928 /* Safe Reference and return it */
929 if (ObReferenceObjectSafe(FoundProcess))
930 {
931 *Process = FoundProcess;
932 Status = STATUS_SUCCESS;
933 }
934 }
935
936 /* Unlock the Entry */
937 ExUnlockHandleTableEntry(PspCidTable, CidEntry);
938 }
939
940 /* Return to caller */
941 KeLeaveCriticalRegion();
942 return Status;
943 }
944
945 /*
946 * @implemented
947 */
948 NTSTATUS
949 NTAPI
950 PsLookupProcessThreadByCid(IN PCLIENT_ID Cid,
951 OUT PEPROCESS *Process OPTIONAL,
952 OUT PETHREAD *Thread)
953 {
954 PHANDLE_TABLE_ENTRY CidEntry;
955 PETHREAD FoundThread;
956 NTSTATUS Status = STATUS_INVALID_CID;
957 PAGED_CODE();
958 PSTRACE(PS_PROCESS_DEBUG, "Cid: %p\n", Cid);
959 KeEnterCriticalRegion();
960
961 /* Get the CID Handle Entry */
962 CidEntry = ExMapHandleToPointer(PspCidTable, Cid->UniqueThread);
963 if (CidEntry)
964 {
965 /* Get the Process */
966 FoundThread = CidEntry->Object;
967
968 /* Make sure it's really a thread and this process' */
969 if ((FoundThread->Tcb.Header.Type == ThreadObject) &&
970 (FoundThread->Cid.UniqueProcess == Cid->UniqueProcess))
971 {
972 /* Safe Reference and return it */
973 if (ObReferenceObjectSafe(FoundThread))
974 {
975 *Thread = FoundThread;
976 Status = STATUS_SUCCESS;
977
978 /* Check if we should return the Process too */
979 if (Process)
980 {
981 /* Return it and reference it */
982 *Process = FoundThread->ThreadsProcess;
983 ObReferenceObject(*Process);
984 }
985 }
986 }
987
988 /* Unlock the Entry */
989 ExUnlockHandleTableEntry(PspCidTable, CidEntry);
990 }
991
992 /* Return to caller */
993 KeLeaveCriticalRegion();
994 return Status;
995 }
996
997 /*
998 * @implemented
999 */
1000 LARGE_INTEGER
1001 NTAPI
1002 PsGetProcessExitTime(VOID)
1003 {
1004 return PsGetCurrentProcess()->ExitTime;
1005 }
1006
1007 /*
1008 * @implemented
1009 */
1010 LONGLONG
1011 NTAPI
1012 PsGetProcessCreateTimeQuadPart(PEPROCESS Process)
1013 {
1014 return Process->CreateTime.QuadPart;
1015 }
1016
1017 /*
1018 * @implemented
1019 */
1020 PVOID
1021 NTAPI
1022 PsGetProcessDebugPort(PEPROCESS Process)
1023 {
1024 return Process->DebugPort;
1025 }
1026
1027 /*
1028 * @implemented
1029 */
1030 BOOLEAN
1031 NTAPI
1032 PsGetProcessExitProcessCalled(PEPROCESS Process)
1033 {
1034 return (BOOLEAN)Process->ProcessExiting;
1035 }
1036
1037 /*
1038 * @implemented
1039 */
1040 NTSTATUS
1041 NTAPI
1042 PsGetProcessExitStatus(PEPROCESS Process)
1043 {
1044 return Process->ExitStatus;
1045 }
1046
1047 /*
1048 * @implemented
1049 */
1050 HANDLE
1051 NTAPI
1052 PsGetProcessId(PEPROCESS Process)
1053 {
1054 return (HANDLE)Process->UniqueProcessId;
1055 }
1056
1057 /*
1058 * @implemented
1059 */
1060 LPSTR
1061 NTAPI
1062 PsGetProcessImageFileName(PEPROCESS Process)
1063 {
1064 return (LPSTR)Process->ImageFileName;
1065 }
1066
1067 /*
1068 * @implemented
1069 */
1070 HANDLE
1071 NTAPI
1072 PsGetProcessInheritedFromUniqueProcessId(PEPROCESS Process)
1073 {
1074 return Process->InheritedFromUniqueProcessId;
1075 }
1076
1077 /*
1078 * @implemented
1079 */
1080 PEJOB
1081 NTAPI
1082 PsGetProcessJob(PEPROCESS Process)
1083 {
1084 return Process->Job;
1085 }
1086
1087 /*
1088 * @implemented
1089 */
1090 PPEB
1091 NTAPI
1092 PsGetProcessPeb(PEPROCESS Process)
1093 {
1094 return Process->Peb;
1095 }
1096
1097 /*
1098 * @implemented
1099 */
1100 ULONG
1101 NTAPI
1102 PsGetProcessPriorityClass(PEPROCESS Process)
1103 {
1104 return Process->PriorityClass;
1105 }
1106
1107 /*
1108 * @implemented
1109 */
1110 HANDLE
1111 NTAPI
1112 PsGetCurrentProcessId(VOID)
1113 {
1114 return (HANDLE)PsGetCurrentProcess()->UniqueProcessId;
1115 }
1116
1117 /*
1118 * @implemented
1119 */
1120 ULONG
1121 NTAPI
1122 PsGetCurrentProcessSessionId(VOID)
1123 {
1124 return MmGetSessionId(PsGetCurrentProcess());
1125 }
1126
1127 /*
1128 * @implemented
1129 */
1130 PVOID
1131 NTAPI
1132 PsGetProcessSectionBaseAddress(PEPROCESS Process)
1133 {
1134 return Process->SectionBaseAddress;
1135 }
1136
1137 /*
1138 * @implemented
1139 */
1140 PVOID
1141 NTAPI
1142 PsGetProcessSecurityPort(PEPROCESS Process)
1143 {
1144 return Process->SecurityPort;
1145 }
1146
1147 /*
1148 * @implemented
1149 */
1150 HANDLE
1151 NTAPI
1152 PsGetProcessSessionId(PEPROCESS Process)
1153 {
1154 return (HANDLE)Process->Session;
1155 }
1156
1157 /*
1158 * @implemented
1159 */
1160 PVOID
1161 NTAPI
1162 PsGetCurrentProcessWin32Process(VOID)
1163 {
1164 return PsGetCurrentProcess()->Win32Process;
1165 }
1166
1167 /*
1168 * @implemented
1169 */
1170 PVOID
1171 NTAPI
1172 PsGetProcessWin32Process(PEPROCESS Process)
1173 {
1174 return Process->Win32Process;
1175 }
1176
1177 /*
1178 * @implemented
1179 */
1180 PVOID
1181 NTAPI
1182 PsGetProcessWin32WindowStation(PEPROCESS Process)
1183 {
1184 return Process->Win32WindowStation;
1185 }
1186
1187 /*
1188 * @implemented
1189 */
1190 BOOLEAN
1191 NTAPI
1192 PsIsProcessBeingDebugged(PEPROCESS Process)
1193 {
1194 return Process->DebugPort != NULL;
1195 }
1196
1197 /*
1198 * @implemented
1199 */
1200 BOOLEAN
1201 NTAPI
1202 PsIsSystemProcess(IN PEPROCESS Process)
1203 {
1204 /* Return if this is the System Process */
1205 return Process == PsInitialSystemProcess;
1206 }
1207
1208 /*
1209 * @implemented
1210 */
1211 VOID
1212 NTAPI
1213 PsSetProcessPriorityClass(PEPROCESS Process,
1214 ULONG PriorityClass)
1215 {
1216 Process->PriorityClass = (UCHAR)PriorityClass;
1217 }
1218
1219 /*
1220 * @implemented
1221 */
1222 VOID
1223 NTAPI
1224 PsSetProcessSecurityPort(PEPROCESS Process,
1225 PVOID SecurityPort)
1226 {
1227 Process->SecurityPort = SecurityPort;
1228 }
1229
1230 /*
1231 * @implemented
1232 */
1233 VOID
1234 NTAPI
1235 PsSetProcessWin32Process(PEPROCESS Process,
1236 PVOID Win32Process)
1237 {
1238 Process->Win32Process = Win32Process;
1239 }
1240
1241 /*
1242 * @implemented
1243 */
1244 VOID
1245 NTAPI
1246 PsSetProcessWindowStation(PEPROCESS Process,
1247 PVOID WindowStation)
1248 {
1249 Process->Win32WindowStation = WindowStation;
1250 }
1251
1252 /*
1253 * @implemented
1254 */
1255 VOID
1256 NTAPI
1257 PsSetProcessPriorityByClass(IN PEPROCESS Process,
1258 IN PSPROCESSPRIORITYMODE Type)
1259 {
1260 UCHAR Quantum;
1261 ULONG Priority;
1262 PSTRACE(PS_PROCESS_DEBUG, "Process: %p Type: %lx\n", Process, Type);
1263
1264 /* Compute quantum and priority */
1265 Priority = PspComputeQuantumAndPriority(Process, Type, &Quantum);
1266
1267 /* Set them */
1268 KeSetPriorityAndQuantumProcess(&Process->Pcb, Priority, Quantum);
1269 }
1270
1271 /*
1272 * @implemented
1273 */
1274 NTSTATUS
1275 NTAPI
1276 NtCreateProcessEx(OUT PHANDLE ProcessHandle,
1277 IN ACCESS_MASK DesiredAccess,
1278 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
1279 IN HANDLE ParentProcess,
1280 IN ULONG Flags,
1281 IN HANDLE SectionHandle OPTIONAL,
1282 IN HANDLE DebugPort OPTIONAL,
1283 IN HANDLE ExceptionPort OPTIONAL,
1284 IN BOOLEAN InJob)
1285 {
1286 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1287 NTSTATUS Status;
1288 PAGED_CODE();
1289 PSTRACE(PS_PROCESS_DEBUG,
1290 "ParentProcess: %p Flags: %lx\n", ParentProcess, Flags);
1291
1292 /* Check if we came from user mode */
1293 if (PreviousMode != KernelMode)
1294 {
1295 _SEH2_TRY
1296 {
1297 /* Probe process handle */
1298 ProbeForWriteHandle(ProcessHandle);
1299 }
1300 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1301 {
1302 /* Return the exception code */
1303 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1304 }
1305 _SEH2_END;
1306 }
1307
1308 /* Make sure there's a parent process */
1309 if (!ParentProcess)
1310 {
1311 /* Can't create System Processes like this */
1312 Status = STATUS_INVALID_PARAMETER;
1313 }
1314 else
1315 {
1316 /* Create a user Process */
1317 Status = PspCreateProcess(ProcessHandle,
1318 DesiredAccess,
1319 ObjectAttributes,
1320 ParentProcess,
1321 Flags,
1322 SectionHandle,
1323 DebugPort,
1324 ExceptionPort,
1325 InJob);
1326 }
1327
1328 /* Return Status */
1329 return Status;
1330 }
1331
1332 /*
1333 * @implemented
1334 */
1335 NTSTATUS
1336 NTAPI
1337 NtCreateProcess(OUT PHANDLE ProcessHandle,
1338 IN ACCESS_MASK DesiredAccess,
1339 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
1340 IN HANDLE ParentProcess,
1341 IN BOOLEAN InheritObjectTable,
1342 IN HANDLE SectionHandle OPTIONAL,
1343 IN HANDLE DebugPort OPTIONAL,
1344 IN HANDLE ExceptionPort OPTIONAL)
1345 {
1346 ULONG Flags = 0;
1347 PSTRACE(PS_PROCESS_DEBUG,
1348 "Parent: %p Attributes: %p\n", ParentProcess, ObjectAttributes);
1349
1350 /* Set new-style flags */
1351 if ((ULONG)SectionHandle & 1) Flags |= PROCESS_CREATE_FLAGS_BREAKAWAY;
1352 if ((ULONG)DebugPort & 1) Flags |= PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT;
1353 if (InheritObjectTable) Flags |= PROCESS_CREATE_FLAGS_INHERIT_HANDLES;
1354
1355 /* Call the new API */
1356 return NtCreateProcessEx(ProcessHandle,
1357 DesiredAccess,
1358 ObjectAttributes,
1359 ParentProcess,
1360 Flags,
1361 SectionHandle,
1362 DebugPort,
1363 ExceptionPort,
1364 FALSE);
1365 }
1366
1367 /*
1368 * @implemented
1369 */
1370 NTSTATUS
1371 NTAPI
1372 NtOpenProcess(OUT PHANDLE ProcessHandle,
1373 IN ACCESS_MASK DesiredAccess,
1374 IN POBJECT_ATTRIBUTES ObjectAttributes,
1375 IN PCLIENT_ID ClientId)
1376 {
1377 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
1378 CLIENT_ID SafeClientId;
1379 ULONG Attributes = 0;
1380 HANDLE hProcess;
1381 BOOLEAN HasObjectName = FALSE;
1382 PETHREAD Thread = NULL;
1383 PEPROCESS Process = NULL;
1384 NTSTATUS Status;
1385 ACCESS_STATE AccessState;
1386 AUX_ACCESS_DATA AuxData;
1387 PAGED_CODE();
1388 PSTRACE(PS_PROCESS_DEBUG,
1389 "ClientId: %p Attributes: %p\n", ClientId, ObjectAttributes);
1390
1391 /* Check if we were called from user mode */
1392 if (PreviousMode != KernelMode)
1393 {
1394 /* Enter SEH for probing */
1395 _SEH2_TRY
1396 {
1397 /* Probe the thread handle */
1398 ProbeForWriteHandle(ProcessHandle);
1399
1400 /* Check for a CID structure */
1401 if (ClientId)
1402 {
1403 /* Probe and capture it */
1404 ProbeForRead(ClientId, sizeof(CLIENT_ID), sizeof(ULONG));
1405 SafeClientId = *ClientId;
1406 ClientId = &SafeClientId;
1407 }
1408
1409 /*
1410 * Just probe the object attributes structure, don't capture it
1411 * completely. This is done later if necessary
1412 */
1413 ProbeForRead(ObjectAttributes,
1414 sizeof(OBJECT_ATTRIBUTES),
1415 sizeof(ULONG));
1416 HasObjectName = (ObjectAttributes->ObjectName != NULL);
1417 Attributes = ObjectAttributes->Attributes;
1418 }
1419 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1420 {
1421 /* Return the exception code */
1422 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1423 }
1424 _SEH2_END;
1425 }
1426 else
1427 {
1428 /* Otherwise just get the data directly */
1429 HasObjectName = (ObjectAttributes->ObjectName != NULL);
1430 Attributes = ObjectAttributes->Attributes;
1431 }
1432
1433 /* Can't pass both, fail */
1434 if ((HasObjectName) && (ClientId)) return STATUS_INVALID_PARAMETER_MIX;
1435
1436 /* Create an access state */
1437 Status = SeCreateAccessState(&AccessState,
1438 &AuxData,
1439 DesiredAccess,
1440 &PsProcessType->TypeInfo.GenericMapping);
1441 if (!NT_SUCCESS(Status)) return Status;
1442
1443 /* Check if this is a debugger */
1444 if (SeSinglePrivilegeCheck(SeDebugPrivilege, PreviousMode))
1445 {
1446 /* Did he want full access? */
1447 if (AccessState.RemainingDesiredAccess & MAXIMUM_ALLOWED)
1448 {
1449 /* Give it to him */
1450 AccessState.PreviouslyGrantedAccess |= PROCESS_ALL_ACCESS;
1451 }
1452 else
1453 {
1454 /* Otherwise just give every other access he could want */
1455 AccessState.PreviouslyGrantedAccess |=
1456 AccessState.RemainingDesiredAccess;
1457 }
1458
1459 /* The caller desires nothing else now */
1460 AccessState.RemainingDesiredAccess = 0;
1461 }
1462
1463 /* Open by name if one was given */
1464 if (HasObjectName)
1465 {
1466 /* Open it */
1467 Status = ObOpenObjectByName(ObjectAttributes,
1468 PsProcessType,
1469 PreviousMode,
1470 &AccessState,
1471 0,
1472 NULL,
1473 &hProcess);
1474
1475 /* Get rid of the access state */
1476 SeDeleteAccessState(&AccessState);
1477 }
1478 else if (ClientId)
1479 {
1480 /* Open by Thread ID */
1481 if (ClientId->UniqueThread)
1482 {
1483 /* Get the Process */
1484 Status = PsLookupProcessThreadByCid(ClientId, &Process, &Thread);
1485 }
1486 else
1487 {
1488 /* Get the Process */
1489 Status = PsLookupProcessByProcessId(ClientId->UniqueProcess,
1490 &Process);
1491 }
1492
1493 /* Check if we didn't find anything */
1494 if (!NT_SUCCESS(Status))
1495 {
1496 /* Get rid of the access state and return */
1497 SeDeleteAccessState(&AccessState);
1498 return Status;
1499 }
1500
1501 /* Open the Process Object */
1502 Status = ObOpenObjectByPointer(Process,
1503 Attributes,
1504 &AccessState,
1505 0,
1506 PsProcessType,
1507 PreviousMode,
1508 &hProcess);
1509
1510 /* Delete the access state */
1511 SeDeleteAccessState(&AccessState);
1512
1513 /* Dereference the thread if we used it */
1514 if (Thread) ObDereferenceObject(Thread);
1515
1516 /* Dereference the Process */
1517 ObDereferenceObject(Process);
1518 }
1519 else
1520 {
1521 /* neither an object name nor a client id was passed */
1522 return STATUS_INVALID_PARAMETER_MIX;
1523 }
1524
1525 /* Check for success */
1526 if (NT_SUCCESS(Status))
1527 {
1528 /* Use SEH for write back */
1529 _SEH2_TRY
1530 {
1531 /* Write back the handle */
1532 *ProcessHandle = hProcess;
1533 }
1534 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1535 {
1536 /* Get the exception code */
1537 Status = _SEH2_GetExceptionCode();
1538 }
1539 _SEH2_END;
1540 }
1541
1542 /* Return status */
1543 return Status;
1544 }
1545 /* EOF */