Synchronize with trunk revision 59636 (just before Alex's CreateProcess revamp).
[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 & ~PS_ALL_FLAGS) 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 & PS_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 & PS_INHERIT_HANDLES ? Parent : NULL,
599 Process);
600 if (!NT_SUCCESS(Status)) goto CleanupWithRef;
601 }
602 else
603 {
604 /* Do the second part of the boot process memory setup */
605 Status = MmInitializeHandBuiltProcess2(Process);
606 if (!NT_SUCCESS(Status)) goto CleanupWithRef;
607 }
608
609 /* Set success for now */
610 Status = STATUS_SUCCESS;
611
612 /* Check if this is a real user-mode process */
613 if (SectionHandle)
614 {
615 /* Initialize the address space */
616 Status = MmInitializeProcessAddressSpace(Process,
617 NULL,
618 SectionObject,
619 &Flags,
620 &Process->
621 SeAuditProcessCreationInfo.
622 ImageFileName);
623 if (!NT_SUCCESS(Status)) goto CleanupWithRef;
624
625 //
626 // We need a PEB
627 //
628 NeedsPeb = TRUE;
629 }
630 else if (Parent)
631 {
632 /* Check if this is a child of the system process */
633 if (Parent != PsInitialSystemProcess)
634 {
635 //
636 // We need a PEB
637 //
638 NeedsPeb = TRUE;
639
640 /* This is a clone! */
641 ASSERTMSG("No support for cloning yet\n", FALSE);
642 }
643 else
644 {
645 /* This is the initial system process */
646 Flags &= ~PS_LARGE_PAGES;
647 Status = MmInitializeProcessAddressSpace(Process,
648 NULL,
649 NULL,
650 &Flags,
651 NULL);
652 if (!NT_SUCCESS(Status)) goto CleanupWithRef;
653
654 /* Create a dummy image file name */
655 Process->SeAuditProcessCreationInfo.ImageFileName =
656 ExAllocatePoolWithTag(PagedPool,
657 sizeof(OBJECT_NAME_INFORMATION),
658 TAG_SEPA);
659 if (!Process->SeAuditProcessCreationInfo.ImageFileName)
660 {
661 /* Fail */
662 Status = STATUS_INSUFFICIENT_RESOURCES;
663 goto CleanupWithRef;
664 }
665
666 /* Zero it out */
667 RtlZeroMemory(Process->SeAuditProcessCreationInfo.ImageFileName,
668 sizeof(OBJECT_NAME_INFORMATION));
669 }
670 }
671
672 #if MI_TRACE_PFNS
673 /* Copy the process name now that we have it */
674 memcpy(MiGetPfnEntry(Process->Pcb.DirectoryTableBase[0] >> PAGE_SHIFT)->ProcessName, Process->ImageFileName, 16);
675 if (Process->Pcb.DirectoryTableBase[1]) memcpy(MiGetPfnEntry(Process->Pcb.DirectoryTableBase[1] >> PAGE_SHIFT)->ProcessName, Process->ImageFileName, 16);
676 if (Process->WorkingSetPage) memcpy(MiGetPfnEntry(Process->WorkingSetPage)->ProcessName, Process->ImageFileName, 16);
677 #endif
678
679 /* Check if we have a section object and map the system DLL */
680 if (SectionObject) PspMapSystemDll(Process, NULL, FALSE);
681
682 /* Create a handle for the Process */
683 CidEntry.Object = Process;
684 CidEntry.GrantedAccess = 0;
685 Process->UniqueProcessId = ExCreateHandle(PspCidTable, &CidEntry);
686 if (!Process->UniqueProcessId)
687 {
688 /* Fail */
689 Status = STATUS_INSUFFICIENT_RESOURCES;
690 goto CleanupWithRef;
691 }
692
693 /* Set the handle table PID */
694 Process->ObjectTable->UniqueProcessId = Process->UniqueProcessId;
695
696 /* Check if we need to audit */
697 if (SeDetailedAuditingWithToken(NULL)) SeAuditProcessCreate(Process);
698
699 /* Check if the parent had a job */
700 if ((Parent) && (Parent->Job))
701 {
702 /* FIXME: We need to insert this process */
703 DPRINT1("Jobs not yet supported\n");
704 ASSERT(FALSE);
705 }
706
707 /* Create PEB only for User-Mode Processes */
708 if ((Parent) && (NeedsPeb))
709 {
710 //
711 // Set up the initial PEB
712 //
713 RtlZeroMemory(&InitialPeb, sizeof(INITIAL_PEB));
714 InitialPeb.Mutant = (HANDLE)-1;
715 InitialPeb.ImageUsesLargePages = 0; // FIXME: Not yet supported
716
717 //
718 // Create it only if we have an image section
719 //
720 if (SectionHandle)
721 {
722 //
723 // Create it
724 //
725 Status = MmCreatePeb(Process, &InitialPeb, &Process->Peb);
726 if (!NT_SUCCESS(Status)) goto CleanupWithRef;
727 }
728 else
729 {
730 //
731 // We have to clone it
732 //
733 ASSERTMSG("No support for cloning yet\n", FALSE);
734 }
735
736 }
737
738 /* The process can now be activated */
739 KeAcquireGuardedMutex(&PspActiveProcessMutex);
740 InsertTailList(&PsActiveProcessHead, &Process->ActiveProcessLinks);
741 KeReleaseGuardedMutex(&PspActiveProcessMutex);
742
743 /* Create an access state */
744 Status = SeCreateAccessStateEx(CurrentThread,
745 ((Parent) &&
746 (Parent == PsInitialSystemProcess)) ?
747 Parent : CurrentProcess,
748 &LocalAccessState,
749 &AuxData,
750 DesiredAccess,
751 &PsProcessType->TypeInfo.GenericMapping);
752 if (!NT_SUCCESS(Status)) goto CleanupWithRef;
753
754 /* Insert the Process into the Object Directory */
755 Status = ObInsertObject(Process,
756 AccessState,
757 DesiredAccess,
758 1,
759 NULL,
760 &hProcess);
761
762 /* Free the access state */
763 if (AccessState) SeDeleteAccessState(AccessState);
764
765 /* Cleanup on failure */
766 if (!NT_SUCCESS(Status)) goto Cleanup;
767
768 /* Compute Quantum and Priority */
769 ASSERT(IsListEmpty(&Process->ThreadListHead) == TRUE);
770 Process->Pcb.BasePriority =
771 (SCHAR)PspComputeQuantumAndPriority(Process,
772 PsProcessPriorityBackground,
773 &Quantum);
774 Process->Pcb.QuantumReset = Quantum;
775
776 /* Check if we have a parent other then the initial system process */
777 Process->GrantedAccess = PROCESS_TERMINATE;
778 if ((Parent) && (Parent != PsInitialSystemProcess))
779 {
780 /* Get the process's SD */
781 Status = ObGetObjectSecurity(Process,
782 &SecurityDescriptor,
783 &SdAllocated);
784 if (!NT_SUCCESS(Status))
785 {
786 /* We failed, close the handle and clean up */
787 ObCloseHandle(hProcess, PreviousMode);
788 goto CleanupWithRef;
789 }
790
791 /* Create the subject context */
792 SubjectContext.ProcessAuditId = Process;
793 SubjectContext.PrimaryToken = PsReferencePrimaryToken(Process);
794 SubjectContext.ClientToken = NULL;
795
796 /* Do the access check */
797 Result = SeAccessCheck(SecurityDescriptor,
798 &SubjectContext,
799 FALSE,
800 MAXIMUM_ALLOWED,
801 0,
802 NULL,
803 &PsProcessType->TypeInfo.GenericMapping,
804 PreviousMode,
805 &Process->GrantedAccess,
806 &AccessStatus);
807
808 /* Dereference the token and let go the SD */
809 ObFastDereferenceObject(&Process->Token,
810 SubjectContext.PrimaryToken);
811 ObReleaseObjectSecurity(SecurityDescriptor, SdAllocated);
812
813 /* Remove access if it failed */
814 if (!Result) Process->GrantedAccess = 0;
815
816 /* Give the process some basic access */
817 Process->GrantedAccess |= (PROCESS_VM_OPERATION |
818 PROCESS_VM_READ |
819 PROCESS_VM_WRITE |
820 PROCESS_QUERY_INFORMATION |
821 PROCESS_TERMINATE |
822 PROCESS_CREATE_THREAD |
823 PROCESS_DUP_HANDLE |
824 PROCESS_CREATE_PROCESS |
825 PROCESS_SET_INFORMATION |
826 STANDARD_RIGHTS_ALL |
827 PROCESS_SET_QUOTA);
828 }
829 else
830 {
831 /* Set full granted access */
832 Process->GrantedAccess = PROCESS_ALL_ACCESS;
833 }
834
835 /* Set the Creation Time */
836 KeQuerySystemTime(&Process->CreateTime);
837
838 /* Protect against bad user-mode pointer */
839 _SEH2_TRY
840 {
841 /* Hacky way of returning the PEB to the user-mode creator */
842 if ((Process->Peb) && (CurrentThread->Tcb.Teb))
843 {
844 CurrentThread->Tcb.Teb->NtTib.ArbitraryUserPointer = Process->Peb;
845 }
846
847 /* Save the process handle */
848 *ProcessHandle = hProcess;
849 }
850 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
851 {
852 /* Get the exception code */
853 Status = _SEH2_GetExceptionCode();
854 }
855 _SEH2_END;
856
857 /* Run the Notification Routines */
858 PspRunCreateProcessNotifyRoutines(Process, TRUE);
859
860 /* If 12 processes have been created, enough of user-mode is ready */
861 if (++ProcessCount == 12) Ki386PerfEnd();
862
863 CleanupWithRef:
864 /*
865 * Dereference the process. For failures, kills the process and does
866 * cleanup present in PspDeleteProcess. For success, kills the extra
867 * reference added by ObInsertObject.
868 */
869 ObDereferenceObject(Process);
870
871 Cleanup:
872 /* Dereference the parent */
873 if (Parent) ObDereferenceObject(Parent);
874
875 /* Return status to caller */
876 return Status;
877 }
878
879 /* PUBLIC FUNCTIONS **********************************************************/
880
881 /*
882 * @implemented
883 */
884 NTSTATUS
885 NTAPI
886 PsCreateSystemProcess(OUT PHANDLE ProcessHandle,
887 IN ACCESS_MASK DesiredAccess,
888 IN POBJECT_ATTRIBUTES ObjectAttributes)
889 {
890 /* Call the internal API */
891 return PspCreateProcess(ProcessHandle,
892 DesiredAccess,
893 ObjectAttributes,
894 NULL,
895 0,
896 NULL,
897 NULL,
898 NULL,
899 FALSE);
900 }
901
902 /*
903 * @implemented
904 */
905 NTSTATUS
906 NTAPI
907 PsLookupProcessByProcessId(IN HANDLE ProcessId,
908 OUT PEPROCESS *Process)
909 {
910 PHANDLE_TABLE_ENTRY CidEntry;
911 PEPROCESS FoundProcess;
912 NTSTATUS Status = STATUS_INVALID_PARAMETER;
913 PAGED_CODE();
914 PSTRACE(PS_PROCESS_DEBUG, "ProcessId: %p\n", ProcessId);
915 KeEnterCriticalRegion();
916
917 /* Get the CID Handle Entry */
918 CidEntry = ExMapHandleToPointer(PspCidTable, ProcessId);
919 if (CidEntry)
920 {
921 /* Get the Process */
922 FoundProcess = CidEntry->Object;
923
924 /* Make sure it's really a process */
925 if (FoundProcess->Pcb.Header.Type == ProcessObject)
926 {
927 /* Safe Reference and return it */
928 if (ObReferenceObjectSafe(FoundProcess))
929 {
930 *Process = FoundProcess;
931 Status = STATUS_SUCCESS;
932 }
933 }
934
935 /* Unlock the Entry */
936 ExUnlockHandleTableEntry(PspCidTable, CidEntry);
937 }
938
939 /* Return to caller */
940 KeLeaveCriticalRegion();
941 return Status;
942 }
943
944 /*
945 * @implemented
946 */
947 NTSTATUS
948 NTAPI
949 PsLookupProcessThreadByCid(IN PCLIENT_ID Cid,
950 OUT PEPROCESS *Process OPTIONAL,
951 OUT PETHREAD *Thread)
952 {
953 PHANDLE_TABLE_ENTRY CidEntry;
954 PETHREAD FoundThread;
955 NTSTATUS Status = STATUS_INVALID_CID;
956 PAGED_CODE();
957 PSTRACE(PS_PROCESS_DEBUG, "Cid: %p\n", Cid);
958 KeEnterCriticalRegion();
959
960 /* Get the CID Handle Entry */
961 CidEntry = ExMapHandleToPointer(PspCidTable, Cid->UniqueThread);
962 if (CidEntry)
963 {
964 /* Get the Process */
965 FoundThread = CidEntry->Object;
966
967 /* Make sure it's really a thread and this process' */
968 if ((FoundThread->Tcb.Header.Type == ThreadObject) &&
969 (FoundThread->Cid.UniqueProcess == Cid->UniqueProcess))
970 {
971 /* Safe Reference and return it */
972 if (ObReferenceObjectSafe(FoundThread))
973 {
974 *Thread = FoundThread;
975 Status = STATUS_SUCCESS;
976
977 /* Check if we should return the Process too */
978 if (Process)
979 {
980 /* Return it and reference it */
981 *Process = FoundThread->ThreadsProcess;
982 ObReferenceObject(*Process);
983 }
984 }
985 }
986
987 /* Unlock the Entry */
988 ExUnlockHandleTableEntry(PspCidTable, CidEntry);
989 }
990
991 /* Return to caller */
992 KeLeaveCriticalRegion();
993 return Status;
994 }
995
996 /*
997 * @implemented
998 */
999 LARGE_INTEGER
1000 NTAPI
1001 PsGetProcessExitTime(VOID)
1002 {
1003 return PsGetCurrentProcess()->ExitTime;
1004 }
1005
1006 /*
1007 * @implemented
1008 */
1009 LONGLONG
1010 NTAPI
1011 PsGetProcessCreateTimeQuadPart(PEPROCESS Process)
1012 {
1013 return Process->CreateTime.QuadPart;
1014 }
1015
1016 /*
1017 * @implemented
1018 */
1019 PVOID
1020 NTAPI
1021 PsGetProcessDebugPort(PEPROCESS Process)
1022 {
1023 return Process->DebugPort;
1024 }
1025
1026 /*
1027 * @implemented
1028 */
1029 BOOLEAN
1030 NTAPI
1031 PsGetProcessExitProcessCalled(PEPROCESS Process)
1032 {
1033 return (BOOLEAN)Process->ProcessExiting;
1034 }
1035
1036 /*
1037 * @implemented
1038 */
1039 NTSTATUS
1040 NTAPI
1041 PsGetProcessExitStatus(PEPROCESS Process)
1042 {
1043 return Process->ExitStatus;
1044 }
1045
1046 /*
1047 * @implemented
1048 */
1049 HANDLE
1050 NTAPI
1051 PsGetProcessId(PEPROCESS Process)
1052 {
1053 return (HANDLE)Process->UniqueProcessId;
1054 }
1055
1056 /*
1057 * @implemented
1058 */
1059 LPSTR
1060 NTAPI
1061 PsGetProcessImageFileName(PEPROCESS Process)
1062 {
1063 return (LPSTR)Process->ImageFileName;
1064 }
1065
1066 /*
1067 * @implemented
1068 */
1069 HANDLE
1070 NTAPI
1071 PsGetProcessInheritedFromUniqueProcessId(PEPROCESS Process)
1072 {
1073 return Process->InheritedFromUniqueProcessId;
1074 }
1075
1076 /*
1077 * @implemented
1078 */
1079 PEJOB
1080 NTAPI
1081 PsGetProcessJob(PEPROCESS Process)
1082 {
1083 return Process->Job;
1084 }
1085
1086 /*
1087 * @implemented
1088 */
1089 PPEB
1090 NTAPI
1091 PsGetProcessPeb(PEPROCESS Process)
1092 {
1093 return Process->Peb;
1094 }
1095
1096 /*
1097 * @implemented
1098 */
1099 ULONG
1100 NTAPI
1101 PsGetProcessPriorityClass(PEPROCESS Process)
1102 {
1103 return Process->PriorityClass;
1104 }
1105
1106 /*
1107 * @implemented
1108 */
1109 HANDLE
1110 NTAPI
1111 PsGetCurrentProcessId(VOID)
1112 {
1113 return (HANDLE)PsGetCurrentProcess()->UniqueProcessId;
1114 }
1115
1116 /*
1117 * @implemented
1118 */
1119 ULONG
1120 NTAPI
1121 PsGetCurrentProcessSessionId(VOID)
1122 {
1123 return MmGetSessionId(PsGetCurrentProcess());
1124 }
1125
1126 /*
1127 * @implemented
1128 */
1129 PVOID
1130 NTAPI
1131 PsGetProcessSectionBaseAddress(PEPROCESS Process)
1132 {
1133 return Process->SectionBaseAddress;
1134 }
1135
1136 /*
1137 * @implemented
1138 */
1139 PVOID
1140 NTAPI
1141 PsGetProcessSecurityPort(PEPROCESS Process)
1142 {
1143 return Process->SecurityPort;
1144 }
1145
1146 /*
1147 * @implemented
1148 */
1149 HANDLE
1150 NTAPI
1151 PsGetProcessSessionId(PEPROCESS Process)
1152 {
1153 return (HANDLE)Process->Session;
1154 }
1155
1156 /*
1157 * @implemented
1158 */
1159 PVOID
1160 NTAPI
1161 PsGetCurrentProcessWin32Process(VOID)
1162 {
1163 return PsGetCurrentProcess()->Win32Process;
1164 }
1165
1166 /*
1167 * @implemented
1168 */
1169 PVOID
1170 NTAPI
1171 PsGetProcessWin32Process(PEPROCESS Process)
1172 {
1173 return Process->Win32Process;
1174 }
1175
1176 /*
1177 * @implemented
1178 */
1179 PVOID
1180 NTAPI
1181 PsGetProcessWin32WindowStation(PEPROCESS Process)
1182 {
1183 return Process->Win32WindowStation;
1184 }
1185
1186 /*
1187 * @implemented
1188 */
1189 BOOLEAN
1190 NTAPI
1191 PsIsProcessBeingDebugged(PEPROCESS Process)
1192 {
1193 return Process->DebugPort != NULL;
1194 }
1195
1196 /*
1197 * @implemented
1198 */
1199 BOOLEAN
1200 NTAPI
1201 PsIsSystemProcess(IN PEPROCESS Process)
1202 {
1203 /* Return if this is the System Process */
1204 return Process == PsInitialSystemProcess;
1205 }
1206
1207 /*
1208 * @implemented
1209 */
1210 VOID
1211 NTAPI
1212 PsSetProcessPriorityClass(PEPROCESS Process,
1213 ULONG PriorityClass)
1214 {
1215 Process->PriorityClass = (UCHAR)PriorityClass;
1216 }
1217
1218 /*
1219 * @implemented
1220 */
1221 VOID
1222 NTAPI
1223 PsSetProcessSecurityPort(PEPROCESS Process,
1224 PVOID SecurityPort)
1225 {
1226 Process->SecurityPort = SecurityPort;
1227 }
1228
1229 /*
1230 * @implemented
1231 */
1232 VOID
1233 NTAPI
1234 PsSetProcessWin32Process(PEPROCESS Process,
1235 PVOID Win32Process)
1236 {
1237 Process->Win32Process = Win32Process;
1238 }
1239
1240 /*
1241 * @implemented
1242 */
1243 VOID
1244 NTAPI
1245 PsSetProcessWindowStation(PEPROCESS Process,
1246 PVOID WindowStation)
1247 {
1248 Process->Win32WindowStation = WindowStation;
1249 }
1250
1251 /*
1252 * @implemented
1253 */
1254 VOID
1255 NTAPI
1256 PsSetProcessPriorityByClass(IN PEPROCESS Process,
1257 IN PSPROCESSPRIORITYMODE Type)
1258 {
1259 UCHAR Quantum;
1260 ULONG Priority;
1261 PSTRACE(PS_PROCESS_DEBUG, "Process: %p Type: %lx\n", Process, Type);
1262
1263 /* Compute quantum and priority */
1264 Priority = PspComputeQuantumAndPriority(Process, Type, &Quantum);
1265
1266 /* Set them */
1267 KeSetPriorityAndQuantumProcess(&Process->Pcb, Priority, Quantum);
1268 }
1269
1270 /*
1271 * @implemented
1272 */
1273 NTSTATUS
1274 NTAPI
1275 NtCreateProcessEx(OUT PHANDLE ProcessHandle,
1276 IN ACCESS_MASK DesiredAccess,
1277 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
1278 IN HANDLE ParentProcess,
1279 IN ULONG Flags,
1280 IN HANDLE SectionHandle OPTIONAL,
1281 IN HANDLE DebugPort OPTIONAL,
1282 IN HANDLE ExceptionPort OPTIONAL,
1283 IN BOOLEAN InJob)
1284 {
1285 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1286 NTSTATUS Status;
1287 PAGED_CODE();
1288 PSTRACE(PS_PROCESS_DEBUG,
1289 "ParentProcess: %p Flags: %lx\n", ParentProcess, Flags);
1290
1291 /* Check if we came from user mode */
1292 if (PreviousMode != KernelMode)
1293 {
1294 _SEH2_TRY
1295 {
1296 /* Probe process handle */
1297 ProbeForWriteHandle(ProcessHandle);
1298 }
1299 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1300 {
1301 /* Return the exception code */
1302 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1303 }
1304 _SEH2_END;
1305 }
1306
1307 /* Make sure there's a parent process */
1308 if (!ParentProcess)
1309 {
1310 /* Can't create System Processes like this */
1311 Status = STATUS_INVALID_PARAMETER;
1312 }
1313 else
1314 {
1315 /* Create a user Process */
1316 Status = PspCreateProcess(ProcessHandle,
1317 DesiredAccess,
1318 ObjectAttributes,
1319 ParentProcess,
1320 Flags,
1321 SectionHandle,
1322 DebugPort,
1323 ExceptionPort,
1324 InJob);
1325 }
1326
1327 /* Return Status */
1328 return Status;
1329 }
1330
1331 /*
1332 * @implemented
1333 */
1334 NTSTATUS
1335 NTAPI
1336 NtCreateProcess(OUT PHANDLE ProcessHandle,
1337 IN ACCESS_MASK DesiredAccess,
1338 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
1339 IN HANDLE ParentProcess,
1340 IN BOOLEAN InheritObjectTable,
1341 IN HANDLE SectionHandle OPTIONAL,
1342 IN HANDLE DebugPort OPTIONAL,
1343 IN HANDLE ExceptionPort OPTIONAL)
1344 {
1345 ULONG Flags = 0;
1346 PSTRACE(PS_PROCESS_DEBUG,
1347 "Parent: %p Attributes: %p\n", ParentProcess, ObjectAttributes);
1348
1349 /* Set new-style flags */
1350 if ((ULONG)SectionHandle & 1) Flags = PS_REQUEST_BREAKAWAY;
1351 if ((ULONG)DebugPort & 1) Flags |= PS_NO_DEBUG_INHERIT;
1352 if (InheritObjectTable) Flags |= PS_INHERIT_HANDLES;
1353
1354 /* Call the new API */
1355 return NtCreateProcessEx(ProcessHandle,
1356 DesiredAccess,
1357 ObjectAttributes,
1358 ParentProcess,
1359 Flags,
1360 SectionHandle,
1361 DebugPort,
1362 ExceptionPort,
1363 FALSE);
1364 }
1365
1366 /*
1367 * @implemented
1368 */
1369 NTSTATUS
1370 NTAPI
1371 NtOpenProcess(OUT PHANDLE ProcessHandle,
1372 IN ACCESS_MASK DesiredAccess,
1373 IN POBJECT_ATTRIBUTES ObjectAttributes,
1374 IN PCLIENT_ID ClientId)
1375 {
1376 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
1377 CLIENT_ID SafeClientId;
1378 ULONG Attributes = 0;
1379 HANDLE hProcess;
1380 BOOLEAN HasObjectName = FALSE;
1381 PETHREAD Thread = NULL;
1382 PEPROCESS Process = NULL;
1383 NTSTATUS Status;
1384 ACCESS_STATE AccessState;
1385 AUX_ACCESS_DATA AuxData;
1386 PAGED_CODE();
1387 PSTRACE(PS_PROCESS_DEBUG,
1388 "ClientId: %p Attributes: %p\n", ClientId, ObjectAttributes);
1389
1390 /* Check if we were called from user mode */
1391 if (PreviousMode != KernelMode)
1392 {
1393 /* Enter SEH for probing */
1394 _SEH2_TRY
1395 {
1396 /* Probe the thread handle */
1397 ProbeForWriteHandle(ProcessHandle);
1398
1399 /* Check for a CID structure */
1400 if (ClientId)
1401 {
1402 /* Probe and capture it */
1403 ProbeForRead(ClientId, sizeof(CLIENT_ID), sizeof(ULONG));
1404 SafeClientId = *ClientId;
1405 ClientId = &SafeClientId;
1406 }
1407
1408 /*
1409 * Just probe the object attributes structure, don't capture it
1410 * completely. This is done later if necessary
1411 */
1412 ProbeForRead(ObjectAttributes,
1413 sizeof(OBJECT_ATTRIBUTES),
1414 sizeof(ULONG));
1415 HasObjectName = (ObjectAttributes->ObjectName != NULL);
1416 Attributes = ObjectAttributes->Attributes;
1417 }
1418 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1419 {
1420 /* Return the exception code */
1421 _SEH2_YIELD(return _SEH2_GetExceptionCode());
1422 }
1423 _SEH2_END;
1424 }
1425 else
1426 {
1427 /* Otherwise just get the data directly */
1428 HasObjectName = (ObjectAttributes->ObjectName != NULL);
1429 Attributes = ObjectAttributes->Attributes;
1430 }
1431
1432 /* Can't pass both, fail */
1433 if ((HasObjectName) && (ClientId)) return STATUS_INVALID_PARAMETER_MIX;
1434
1435 /* Create an access state */
1436 Status = SeCreateAccessState(&AccessState,
1437 &AuxData,
1438 DesiredAccess,
1439 &PsProcessType->TypeInfo.GenericMapping);
1440 if (!NT_SUCCESS(Status)) return Status;
1441
1442 /* Check if this is a debugger */
1443 if (SeSinglePrivilegeCheck(SeDebugPrivilege, PreviousMode))
1444 {
1445 /* Did he want full access? */
1446 if (AccessState.RemainingDesiredAccess & MAXIMUM_ALLOWED)
1447 {
1448 /* Give it to him */
1449 AccessState.PreviouslyGrantedAccess |= PROCESS_ALL_ACCESS;
1450 }
1451 else
1452 {
1453 /* Otherwise just give every other access he could want */
1454 AccessState.PreviouslyGrantedAccess |=
1455 AccessState.RemainingDesiredAccess;
1456 }
1457
1458 /* The caller desires nothing else now */
1459 AccessState.RemainingDesiredAccess = 0;
1460 }
1461
1462 /* Open by name if one was given */
1463 if (HasObjectName)
1464 {
1465 /* Open it */
1466 Status = ObOpenObjectByName(ObjectAttributes,
1467 PsProcessType,
1468 PreviousMode,
1469 &AccessState,
1470 0,
1471 NULL,
1472 &hProcess);
1473
1474 /* Get rid of the access state */
1475 SeDeleteAccessState(&AccessState);
1476 }
1477 else if (ClientId)
1478 {
1479 /* Open by Thread ID */
1480 if (ClientId->UniqueThread)
1481 {
1482 /* Get the Process */
1483 Status = PsLookupProcessThreadByCid(ClientId, &Process, &Thread);
1484 }
1485 else
1486 {
1487 /* Get the Process */
1488 Status = PsLookupProcessByProcessId(ClientId->UniqueProcess,
1489 &Process);
1490 }
1491
1492 /* Check if we didn't find anything */
1493 if (!NT_SUCCESS(Status))
1494 {
1495 /* Get rid of the access state and return */
1496 SeDeleteAccessState(&AccessState);
1497 return Status;
1498 }
1499
1500 /* Open the Process Object */
1501 Status = ObOpenObjectByPointer(Process,
1502 Attributes,
1503 &AccessState,
1504 0,
1505 PsProcessType,
1506 PreviousMode,
1507 &hProcess);
1508
1509 /* Delete the access state */
1510 SeDeleteAccessState(&AccessState);
1511
1512 /* Dereference the thread if we used it */
1513 if (Thread) ObDereferenceObject(Thread);
1514
1515 /* Dereference the Process */
1516 ObDereferenceObject(Process);
1517 }
1518 else
1519 {
1520 /* neither an object name nor a client id was passed */
1521 return STATUS_INVALID_PARAMETER_MIX;
1522 }
1523
1524 /* Check for success */
1525 if (NT_SUCCESS(Status))
1526 {
1527 /* Use SEH for write back */
1528 _SEH2_TRY
1529 {
1530 /* Write back the handle */
1531 *ProcessHandle = hProcess;
1532 }
1533 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1534 {
1535 /* Get the exception code */
1536 Status = _SEH2_GetExceptionCode();
1537 }
1538 _SEH2_END;
1539 }
1540
1541 /* Return status */
1542 return Status;
1543 }
1544 /* EOF */