c97ab652757762a33322e496ecb5ee4edd26a76a
[reactos.git] / reactos / dll / win32 / kernel32 / client / except.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: lib/kernel32/misc/except.c
5 * PURPOSE: Exception functions
6 * PROGRAMMER: Ariadne ( ariadne@xs4all.nl)
7 * modified from WINE [ Onno Hovers, (onno@stack.urc.tue.nl) ]
8 * UPDATE HISTORY:
9 * Created 01/11/98
10 */
11
12 /* INCLUDES *******************************************************************/
13
14 #include <k32.h>
15
16 #define NDEBUG
17 #include <debug.h>
18
19 /*
20 * Private helper function to lookup the module name from a given address.
21 * The address can point to anywhere within the module.
22 */
23 static const char*
24 _module_name_from_addr(const void* addr, void **module_start_addr,
25 char* psz, size_t nChars)
26 {
27 MEMORY_BASIC_INFORMATION mbi;
28 if (VirtualQuery(addr, &mbi, sizeof(mbi)) != sizeof(mbi) ||
29 !GetModuleFileNameA((HMODULE)mbi.AllocationBase, psz, nChars))
30 {
31 psz[0] = '\0';
32 *module_start_addr = 0;
33 }
34 else
35 {
36 *module_start_addr = (void *)mbi.AllocationBase;
37 }
38 return psz;
39 }
40
41
42 static VOID
43 _dump_context(PCONTEXT pc)
44 {
45 #ifdef _M_IX86
46 /*
47 * Print out the CPU registers
48 */
49 DbgPrint("CS:EIP %x:%x\n", pc->SegCs&0xffff, pc->Eip );
50 DbgPrint("DS %x ES %x FS %x GS %x\n", pc->SegDs&0xffff, pc->SegEs&0xffff,
51 pc->SegFs&0xffff, pc->SegGs&0xfff);
52 DbgPrint("EAX: %.8x EBX: %.8x ECX: %.8x\n", pc->Eax, pc->Ebx, pc->Ecx);
53 DbgPrint("EDX: %.8x EBP: %.8x ESI: %.8x ESP: %.8x\n", pc->Edx,
54 pc->Ebp, pc->Esi, pc->Esp);
55 DbgPrint("EDI: %.8x EFLAGS: %.8x\n", pc->Edi, pc->EFlags);
56 #elif defined(_M_AMD64)
57 DbgPrint("CS:RIP %x:%I64x\n", pc->SegCs&0xffff, pc->Rip );
58 DbgPrint("DS %x ES %x FS %x GS %x\n", pc->SegDs&0xffff, pc->SegEs&0xffff,
59 pc->SegFs&0xffff, pc->SegGs&0xfff);
60 DbgPrint("RAX: %I64x RBX: %I64x RCX: %I64x RDI: %I64x\n", pc->Rax, pc->Rbx, pc->Rcx, pc->Rdi);
61 DbgPrint("RDX: %I64x RBP: %I64x RSI: %I64x RSP: %I64x\n", pc->Rdx, pc->Rbp, pc->Rsi, pc->Rsp);
62 DbgPrint("R8: %I64x R9: %I64x R10: %I64x R11: %I64x\n", pc->R8, pc->R9, pc->R10, pc->R11);
63 DbgPrint("R12: %I64x R13: %I64x R14: %I64x R15: %I64x\n", pc->R12, pc->R13, pc->R14, pc->R15);
64 DbgPrint("EFLAGS: %.8x\n", pc->EFlags);
65 #elif defined(_M_ARM)
66 DbgPrint("PC: %08lx LR: %08lx SP: %08lx\n", pc->Pc);
67 DbgPrint("R0: %08lx R1: %08lx R2: %08lx R3: %08lx\n", pc->R0, pc->R1, pc->R2, pc->R3);
68 DbgPrint("R4: %08lx R5: %08lx R6: %08lx R7: %08lx\n", pc->R4, pc->R5, pc->R6, pc->R7);
69 DbgPrint("R8: %08lx R9: %08lx R10: %08lx R11: %08lx\n", pc->R8, pc->R9, pc->R10, pc->R11);
70 DbgPrint("R12: %08lx CPSR: %08lx FPSCR: %08lx\n", pc->R12, pc->Cpsr, pc->R1, pc->Fpscr, pc->R3);
71 #else
72 #error "Unknown architecture"
73 #endif
74 }
75
76 static VOID
77 PrintStackTrace(struct _EXCEPTION_POINTERS *ExceptionInfo)
78 {
79 PVOID StartAddr;
80 CHAR szMod[128] = "";
81 PEXCEPTION_RECORD ExceptionRecord = ExceptionInfo->ExceptionRecord;
82 PCONTEXT ContextRecord = ExceptionInfo->ContextRecord;
83
84 /* Print a stack trace. */
85 DbgPrint("Unhandled exception\n");
86 DbgPrint("ExceptionCode: %8x\n", ExceptionRecord->ExceptionCode);
87
88 if ((NTSTATUS)ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION &&
89 ExceptionRecord->NumberParameters == 2)
90 {
91 DbgPrint("Faulting Address: %8x\n", ExceptionRecord->ExceptionInformation[1]);
92 }
93
94 _dump_context (ContextRecord);
95 _module_name_from_addr(ExceptionRecord->ExceptionAddress, &StartAddr, szMod, sizeof(szMod));
96 DbgPrint("Address:\n %8x+%-8x %s\n",
97 (PVOID)StartAddr,
98 (ULONG_PTR)ExceptionRecord->ExceptionAddress - (ULONG_PTR)StartAddr,
99 szMod);
100 #ifdef _M_IX86
101 DbgPrint("Frames:\n");
102
103 _SEH2_TRY
104 {
105 UINT i;
106 PULONG Frame = (PULONG)ContextRecord->Ebp;
107
108 for (i = 0; Frame[1] != 0 && Frame[1] != 0xdeadbeef && i < 128; i++)
109 {
110 if (IsBadReadPtr((PVOID)Frame[1], 4))
111 {
112 DbgPrint(" %8x%9s %s\n", Frame[1], "<invalid address>"," ");
113 }
114 else
115 {
116 _module_name_from_addr((const void*)Frame[1], &StartAddr,
117 szMod, sizeof(szMod));
118 DbgPrint(" %8x+%-8x %s\n",
119 (PVOID)StartAddr,
120 (ULONG_PTR)Frame[1] - (ULONG_PTR)StartAddr,
121 szMod);
122 }
123
124 if (IsBadReadPtr((PVOID)Frame[0], sizeof(*Frame) * 2))
125 break;
126
127 Frame = (PULONG)Frame[0];
128 }
129 }
130 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
131 {
132 DbgPrint("<error dumping stack trace: 0x%x>\n", _SEH2_GetExceptionCode());
133 }
134 _SEH2_END;
135 #endif
136 }
137
138 /* GLOBALS ********************************************************************/
139
140 LPTOP_LEVEL_EXCEPTION_FILTER GlobalTopLevelExceptionFilter;
141 DWORD g_dwLastErrorToBreakOn;
142
143 /* FUNCTIONS ******************************************************************/
144
145 LONG
146 WINAPI
147 BasepCheckForReadOnlyResource(IN PVOID Ptr)
148 {
149 PVOID Data;
150 ULONG Size, OldProtect;
151 MEMORY_BASIC_INFORMATION mbi;
152 NTSTATUS Status;
153 LONG Ret = EXCEPTION_CONTINUE_SEARCH;
154
155 /* Check if it was an attempt to write to a read-only image section! */
156 Status = NtQueryVirtualMemory(NtCurrentProcess(),
157 Ptr,
158 MemoryBasicInformation,
159 &mbi,
160 sizeof(mbi),
161 NULL);
162 if (NT_SUCCESS(Status) &&
163 mbi.Protect == PAGE_READONLY && mbi.Type == MEM_IMAGE)
164 {
165 /* Attempt to treat it as a resource section. We need to
166 use SEH here because we don't know if it's actually a
167 resource mapping */
168 _SEH2_TRY
169 {
170 Data = RtlImageDirectoryEntryToData(mbi.AllocationBase,
171 TRUE,
172 IMAGE_DIRECTORY_ENTRY_RESOURCE,
173 &Size);
174
175 if (Data != NULL &&
176 (ULONG_PTR)Ptr >= (ULONG_PTR)Data &&
177 (ULONG_PTR)Ptr < (ULONG_PTR)Data + Size)
178 {
179 /* The user tried to write into the resources. Make the page
180 writable... */
181 Size = 1;
182 Status = NtProtectVirtualMemory(NtCurrentProcess(),
183 &Ptr,
184 &Size,
185 PAGE_READWRITE,
186 &OldProtect);
187 if (NT_SUCCESS(Status))
188 {
189 Ret = EXCEPTION_CONTINUE_EXECUTION;
190 }
191 }
192 }
193 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
194 {
195 }
196 _SEH2_END;
197 }
198
199 return Ret;
200 }
201
202 UINT
203 WINAPI
204 GetErrorMode(VOID)
205 {
206 NTSTATUS Status;
207 UINT ErrMode;
208
209 /* Query the current setting */
210 Status = NtQueryInformationProcess(NtCurrentProcess(),
211 ProcessDefaultHardErrorMode,
212 (PVOID)&ErrMode,
213 sizeof(ErrMode),
214 NULL);
215 if (!NT_SUCCESS(Status))
216 {
217 /* Fail if we couldn't query */
218 BaseSetLastNTError(Status);
219 return 0;
220 }
221
222 /* Check if NOT failing critical errors was requested */
223 if (ErrMode & SEM_FAILCRITICALERRORS)
224 {
225 /* Mask it out, since the native API works differently */
226 ErrMode &= ~SEM_FAILCRITICALERRORS;
227 }
228 else
229 {
230 /* OR it if the caller didn't, due to different native semantics */
231 ErrMode |= SEM_FAILCRITICALERRORS;
232 }
233
234 /* Return the mode */
235 return ErrMode;
236 }
237
238 /*
239 * @implemented
240 */
241 LONG WINAPI
242 UnhandledExceptionFilter(struct _EXCEPTION_POINTERS *ExceptionInfo)
243 {
244 LONG RetValue;
245 HANDLE DebugPort = NULL;
246 NTSTATUS ErrCode;
247 ULONG_PTR ErrorParameters[4];
248 ULONG ErrorResponse;
249 PEXCEPTION_RECORD ExceptionRecord = ExceptionInfo->ExceptionRecord;
250 LPTOP_LEVEL_EXCEPTION_FILTER RealFilter;
251
252 if ((NTSTATUS)ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION &&
253 ExceptionRecord->NumberParameters >= 2)
254 {
255 switch(ExceptionRecord->ExceptionInformation[0])
256 {
257 case EXCEPTION_WRITE_FAULT:
258 /* Change the protection on some write attempts, some InstallShield setups
259 have this bug */
260 RetValue = BasepCheckForReadOnlyResource(
261 (PVOID)ExceptionRecord->ExceptionInformation[1]);
262 if (RetValue == EXCEPTION_CONTINUE_EXECUTION)
263 return EXCEPTION_CONTINUE_EXECUTION;
264 break;
265 case EXCEPTION_EXECUTE_FAULT:
266 /* FIXME */
267 break;
268 }
269 }
270
271 /* Is there a debugger running ? */
272 ErrCode = NtQueryInformationProcess(NtCurrentProcess(), ProcessDebugPort,
273 &DebugPort, sizeof(HANDLE), NULL);
274 if (!NT_SUCCESS(ErrCode) && ErrCode != STATUS_NOT_IMPLEMENTED)
275 {
276 BaseSetLastNTError(ErrCode);
277 return EXCEPTION_EXECUTE_HANDLER;
278 }
279
280 if (DebugPort)
281 {
282 /* Pass the exception to debugger. */
283 DPRINT("Passing exception to debugger\n");
284 return EXCEPTION_CONTINUE_SEARCH;
285 }
286
287 RealFilter = RtlDecodePointer(GlobalTopLevelExceptionFilter);
288 if (RealFilter)
289 {
290 LONG ret = RealFilter(ExceptionInfo);
291 if (ret != EXCEPTION_CONTINUE_SEARCH)
292 return ret;
293 }
294
295 PrintStackTrace(ExceptionInfo);
296
297 /* Save exception code and address */
298 ErrorParameters[0] = (ULONG)ExceptionRecord->ExceptionCode;
299 ErrorParameters[1] = (ULONG_PTR)ExceptionRecord->ExceptionAddress;
300
301 if ((NTSTATUS)ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION)
302 {
303 /* get the type of operation that caused the access violation */
304 ErrorParameters[2] = ExceptionRecord->ExceptionInformation[0];
305 }
306 else
307 {
308 ErrorParameters[2] = ExceptionRecord->ExceptionInformation[2];
309 }
310
311 /* Save faulting address */
312 ErrorParameters[3] = ExceptionRecord->ExceptionInformation[1];
313
314 /* Raise the harderror */
315 ErrCode = NtRaiseHardError(STATUS_UNHANDLED_EXCEPTION,
316 4, 0, ErrorParameters, OptionOkCancel, &ErrorResponse);
317
318 if (NT_SUCCESS(ErrCode) && (ErrorResponse == ResponseCancel))
319 {
320 /* FIXME: Check the result, if the "Cancel" button was
321 clicked run a debugger */
322 DPRINT1("Debugging is not implemented yet\n");
323 }
324
325 /*
326 * Returning EXCEPTION_EXECUTE_HANDLER means that the code in
327 * the __except block will be executed. Normally this will end up in a
328 * Terminate process.
329 */
330
331 return EXCEPTION_EXECUTE_HANDLER;
332 }
333
334 /*
335 * @implemented
336 */
337 VOID
338 WINAPI
339 RaiseException(IN DWORD dwExceptionCode,
340 IN DWORD dwExceptionFlags,
341 IN DWORD nNumberOfArguments,
342 IN CONST ULONG_PTR *lpArguments OPTIONAL)
343 {
344 EXCEPTION_RECORD ExceptionRecord;
345
346 /* Setup the exception record */
347 ExceptionRecord.ExceptionCode = dwExceptionCode;
348 ExceptionRecord.ExceptionRecord = NULL;
349 ExceptionRecord.ExceptionAddress = (PVOID)RaiseException;
350 ExceptionRecord.ExceptionFlags = dwExceptionFlags & EXCEPTION_NONCONTINUABLE;
351
352 /* Check if we have arguments */
353 if (!lpArguments)
354 {
355 /* We don't */
356 ExceptionRecord.NumberParameters = 0;
357 }
358 else
359 {
360 /* We do, normalize the count */
361 if (nNumberOfArguments > EXCEPTION_MAXIMUM_PARAMETERS)
362 {
363 nNumberOfArguments = EXCEPTION_MAXIMUM_PARAMETERS;
364 }
365
366 /* Set the count of parameters and copy them */
367 ExceptionRecord.NumberParameters = nNumberOfArguments;
368 RtlCopyMemory(ExceptionRecord.ExceptionInformation,
369 lpArguments,
370 nNumberOfArguments * sizeof(ULONG));
371 }
372
373 /* Better handling of Delphi Exceptions... a ReactOS Hack */
374 if (dwExceptionCode == 0xeedface || dwExceptionCode == 0xeedfade)
375 {
376 DPRINT1("Delphi Exception at address: %p\n", ExceptionRecord.ExceptionInformation[0]);
377 DPRINT1("Exception-Object: %p\n", ExceptionRecord.ExceptionInformation[1]);
378 DPRINT1("Exception text: %lx\n", ExceptionRecord.ExceptionInformation[2]);
379 }
380
381 /* Trace the wine special error and show the modulename and functionname */
382 if (dwExceptionCode == 0x80000100 /*EXCEPTION_WINE_STUB*/)
383 {
384 /* Numbers of parameter must be equal to two */
385 if (ExceptionRecord.NumberParameters == 2)
386 {
387 DPRINT1("Missing function in : %s\n", ExceptionRecord.ExceptionInformation[0]);
388 DPRINT1("with the functionname : %s\n", ExceptionRecord.ExceptionInformation[1]);
389 }
390 }
391
392 /* Raise the exception */
393 RtlRaiseException(&ExceptionRecord);
394 }
395
396 /*
397 * @implemented
398 */
399 UINT
400 WINAPI
401 SetErrorMode(IN UINT uMode)
402 {
403 UINT PrevErrMode, NewMode;
404
405 /* Get the previous mode */
406 PrevErrMode = GetErrorMode();
407 NewMode = uMode;
408
409 /* Check if failing critical errors was requested */
410 if (NewMode & SEM_FAILCRITICALERRORS)
411 {
412 /* Mask it out, since the native API works differently */
413 NewMode &= ~SEM_FAILCRITICALERRORS;
414 }
415 else
416 {
417 /* OR it if the caller didn't, due to different native semantics */
418 NewMode |= SEM_FAILCRITICALERRORS;
419 }
420
421 /* Always keep no alignment faults if they were set */
422 NewMode |= (PrevErrMode & SEM_NOALIGNMENTFAULTEXCEPT);
423
424 /* Set the new mode */
425 NtSetInformationProcess(NtCurrentProcess(),
426 ProcessDefaultHardErrorMode,
427 (PVOID)&NewMode,
428 sizeof(NewMode));
429
430 /* Return the previous mode */
431 return PrevErrMode;
432 }
433
434 /*
435 * @implemented
436 */
437 LPTOP_LEVEL_EXCEPTION_FILTER
438 WINAPI
439 DECLSPEC_HOTPATCH
440 SetUnhandledExceptionFilter(IN LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
441 {
442 PVOID EncodedPointer, EncodedOldPointer;
443
444 EncodedPointer = RtlEncodePointer(lpTopLevelExceptionFilter);
445 EncodedOldPointer = InterlockedExchangePointer((PVOID*)&GlobalTopLevelExceptionFilter,
446 EncodedPointer);
447 return RtlDecodePointer(EncodedOldPointer);
448 }
449
450 /*
451 * @implemented
452 */
453 BOOL
454 WINAPI
455 IsBadReadPtr(IN LPCVOID lp,
456 IN UINT_PTR ucb)
457 {
458 ULONG PageSize;
459 BOOLEAN Result = FALSE;
460 volatile CHAR *Current;
461 PCHAR Last;
462
463 /* Quick cases */
464 if (!ucb) return FALSE;
465 if (!lp) return TRUE;
466
467 /* Get the page size */
468 PageSize = BaseStaticServerData->SysInfo.PageSize;
469
470 /* Calculate start and end */
471 Current = (volatile CHAR*)lp;
472 Last = (PCHAR)((ULONG_PTR)lp + ucb - 1);
473
474 /* Another quick failure case */
475 if (Last < Current) return TRUE;
476
477 /* Enter SEH */
478 _SEH2_TRY
479 {
480 /* Do an initial probe */
481 *Current;
482
483 /* Align the addresses */
484 Current = (volatile CHAR *)ROUND_DOWN(Current, PageSize);
485 Last = (PCHAR)ROUND_DOWN(Last, PageSize);
486
487 /* Probe the entire range */
488 while (Current != Last)
489 {
490 Current += PageSize;
491 *Current;
492 }
493 }
494 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
495 {
496 /* We hit an exception, so return true */
497 Result = TRUE;
498 }
499 _SEH2_END
500
501 /* Return exception status */
502 return Result;
503 }
504
505 /*
506 * @implemented
507 */
508 BOOL
509 NTAPI
510 IsBadHugeReadPtr(LPCVOID lp,
511 UINT_PTR ucb)
512 {
513 /* Implementation is the same on 32-bit */
514 return IsBadReadPtr(lp, ucb);
515 }
516
517 /*
518 * @implemented
519 */
520 BOOL
521 NTAPI
522 IsBadCodePtr(FARPROC lpfn)
523 {
524 /* Executing has the same privileges as reading */
525 return IsBadReadPtr((LPVOID)lpfn, 1);
526 }
527
528 /*
529 * @implemented
530 */
531 BOOL
532 NTAPI
533 IsBadWritePtr(IN LPVOID lp,
534 IN UINT_PTR ucb)
535 {
536 ULONG PageSize;
537 BOOLEAN Result = FALSE;
538 volatile CHAR *Current;
539 PCHAR Last;
540
541 /* Quick cases */
542 if (!ucb) return FALSE;
543 if (!lp) return TRUE;
544
545 /* Get the page size */
546 PageSize = BaseStaticServerData->SysInfo.PageSize;
547
548 /* Calculate start and end */
549 Current = (volatile CHAR*)lp;
550 Last = (PCHAR)((ULONG_PTR)lp + ucb - 1);
551
552 /* Another quick failure case */
553 if (Last < Current) return TRUE;
554
555 /* Enter SEH */
556 _SEH2_TRY
557 {
558 /* Do an initial probe */
559 *Current = *Current;
560
561 /* Align the addresses */
562 Current = (volatile CHAR *)ROUND_DOWN(Current, PageSize);
563 Last = (PCHAR)ROUND_DOWN(Last, PageSize);
564
565 /* Probe the entire range */
566 while (Current != Last)
567 {
568 Current += PageSize;
569 *Current = *Current;
570 }
571 }
572 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
573 {
574 /* We hit an exception, so return true */
575 Result = TRUE;
576 }
577 _SEH2_END
578
579 /* Return exception status */
580 return Result;
581 }
582
583 /*
584 * @implemented
585 */
586 BOOL
587 NTAPI
588 IsBadHugeWritePtr(IN LPVOID lp,
589 IN UINT_PTR ucb)
590 {
591 /* Implementation is the same on 32-bit */
592 return IsBadWritePtr(lp, ucb);
593 }
594
595 /*
596 * @implemented
597 */
598 BOOL
599 NTAPI
600 IsBadStringPtrW(IN LPCWSTR lpsz,
601 IN UINT_PTR ucchMax)
602 {
603 BOOLEAN Result = FALSE;
604 volatile WCHAR *Current;
605 PWCHAR Last;
606 WCHAR Char;
607
608 /* Quick cases */
609 if (!ucchMax) return FALSE;
610 if (!lpsz) return TRUE;
611
612 /* Calculate start and end */
613 Current = (volatile WCHAR*)lpsz;
614 Last = (PWCHAR)((ULONG_PTR)lpsz + (ucchMax * 2) - 2);
615
616 /* Enter SEH */
617 _SEH2_TRY
618 {
619 /* Probe the entire range */
620 Char = *Current++;
621 while ((Char) && (Current != Last)) Char = *Current++;
622 }
623 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
624 {
625 /* We hit an exception, so return true */
626 Result = TRUE;
627 }
628 _SEH2_END
629
630 /* Return exception status */
631 return Result;
632 }
633
634 /*
635 * @implemented
636 */
637 BOOL
638 NTAPI
639 IsBadStringPtrA(IN LPCSTR lpsz,
640 IN UINT_PTR ucchMax)
641 {
642 BOOLEAN Result = FALSE;
643 volatile CHAR *Current;
644 PCHAR Last;
645 CHAR Char;
646
647 /* Quick cases */
648 if (!ucchMax) return FALSE;
649 if (!lpsz) return TRUE;
650
651 /* Calculate start and end */
652 Current = (volatile CHAR*)lpsz;
653 Last = (PCHAR)((ULONG_PTR)lpsz + ucchMax - 1);
654
655 /* Enter SEH */
656 _SEH2_TRY
657 {
658 /* Probe the entire range */
659 Char = *Current++;
660 while ((Char) && (Current != Last)) Char = *Current++;
661 }
662 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
663 {
664 /* We hit an exception, so return true */
665 Result = TRUE;
666 }
667 _SEH2_END
668
669 /* Return exception status */
670 return Result;
671 }
672
673 /*
674 * @implemented
675 */
676 VOID
677 WINAPI
678 SetLastError(IN DWORD dwErrCode)
679 {
680 /* Break if a debugger requested checking for this error code */
681 if ((g_dwLastErrorToBreakOn) && (g_dwLastErrorToBreakOn == dwErrCode)) DbgBreakPoint();
682
683 /* Set last error if it's a new error */
684 if (NtCurrentTeb()->LastErrorValue != dwErrCode) NtCurrentTeb()->LastErrorValue = dwErrCode;
685 }
686
687 /*
688 * @implemented
689 */
690 DWORD
691 WINAPI
692 BaseSetLastNTError(IN NTSTATUS Status)
693 {
694 DWORD dwErrCode;
695
696 /* Convert from NT to Win32, then set */
697 dwErrCode = RtlNtStatusToDosError(Status);
698 SetLastError(dwErrCode);
699 return dwErrCode;
700 }
701
702 /*
703 * @implemented
704 */
705 DWORD
706 WINAPI
707 GetLastError(VOID)
708 {
709 /* Return the current value */
710 return NtCurrentTeb()->LastErrorValue;
711 }
712
713 /* EOF */