623 lines
18 KiB
C
623 lines
18 KiB
C
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
error.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the Win32 error APIs.
|
|
|
|
Author:
|
|
|
|
Mark Lucovsky (markl) 24-Sep-1990
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "basedll.h"
|
|
|
|
DWORD g_dwLastErrorToBreakOn = ERROR_SUCCESS;
|
|
|
|
UINT
|
|
GetErrorMode()
|
|
{
|
|
|
|
UINT PreviousMode;
|
|
NTSTATUS Status;
|
|
|
|
Status = NtQueryInformationProcess(
|
|
NtCurrentProcess(),
|
|
ProcessDefaultHardErrorMode,
|
|
(PVOID) &PreviousMode,
|
|
sizeof(PreviousMode),
|
|
NULL
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
return 0;
|
|
}
|
|
|
|
if (PreviousMode & 1) {
|
|
PreviousMode &= ~SEM_FAILCRITICALERRORS;
|
|
}
|
|
else {
|
|
PreviousMode |= SEM_FAILCRITICALERRORS;
|
|
}
|
|
return PreviousMode;
|
|
}
|
|
|
|
|
|
UINT
|
|
SetErrorMode(
|
|
UINT uMode
|
|
)
|
|
{
|
|
|
|
UINT PreviousMode;
|
|
UINT NewMode;
|
|
|
|
PreviousMode = GetErrorMode();
|
|
|
|
NewMode = uMode;
|
|
if (NewMode & SEM_FAILCRITICALERRORS ) {
|
|
NewMode &= ~SEM_FAILCRITICALERRORS;
|
|
}
|
|
else {
|
|
NewMode |= SEM_FAILCRITICALERRORS;
|
|
}
|
|
|
|
//
|
|
// Once SEM_NOALIGNMENTFAULTEXCEPT has been enabled for a given
|
|
// process, it cannot be disabled via this API.
|
|
//
|
|
|
|
NewMode |= (PreviousMode & SEM_NOALIGNMENTFAULTEXCEPT);
|
|
|
|
if ( NT_SUCCESS(NtSetInformationProcess(
|
|
NtCurrentProcess(),
|
|
ProcessDefaultHardErrorMode,
|
|
(PVOID) &NewMode,
|
|
sizeof(NewMode)
|
|
) ) ){
|
|
}
|
|
|
|
return( PreviousMode );
|
|
}
|
|
|
|
DWORD
|
|
GetLastError(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns the most recent error code set by a Win32 API
|
|
call. Applications should call this function immediately after a
|
|
Win32 API call returns a failure indications (e.g. FALSE, NULL or
|
|
-1) to determine the cause of the failure.
|
|
|
|
The last error code value is a per thread field, so that multiple
|
|
threads do not overwrite each other's last error code value.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
The return value is the most recent error code as set by a Win32 API
|
|
call.
|
|
|
|
--*/
|
|
|
|
{
|
|
return (DWORD)NtCurrentTeb()->LastErrorValue;
|
|
}
|
|
|
|
VOID
|
|
SetLastError(
|
|
DWORD dwErrCode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function set the most recent error code and error string in per
|
|
thread storage. Win32 API functions call this function whenever
|
|
they return a failure indication (e.g. FALSE, NULL or -1).
|
|
This function
|
|
is not called by Win32 API function calls that are successful, so
|
|
that if three Win32 API function calls are made, and the first one
|
|
fails and the second two succeed, the error code and string stored
|
|
by the first one are still available after the second two succeed.
|
|
|
|
Applications can retrieve the values saved by this function using
|
|
GetLastError. The use of this function is optional, as an
|
|
application need only call if it is interested in knowing the
|
|
specific reason for an API function failure.
|
|
|
|
The last error code value is kept in thread local storage so that
|
|
multiple threads do not overwrite each other's values.
|
|
|
|
Arguments:
|
|
|
|
dwErrCode - Specifies the error code to store in per thread storage
|
|
for the current thread.
|
|
|
|
Return Value:
|
|
|
|
return-value - Description of conditions needed to return value. - or -
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PTEB Teb = NtCurrentTeb();
|
|
|
|
if ((g_dwLastErrorToBreakOn != ERROR_SUCCESS) &&
|
|
(dwErrCode == g_dwLastErrorToBreakOn)) {
|
|
DbgBreakPoint();
|
|
}
|
|
|
|
// make write breakpoints to this field more meaningful by only writing to it when
|
|
// the value changes.
|
|
if (Teb->LastErrorValue != dwErrCode) {
|
|
Teb->LastErrorValue = dwErrCode;
|
|
}
|
|
}
|
|
|
|
ULONG
|
|
BaseSetLastNTError(
|
|
IN NTSTATUS Status
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API sets the "last error value" and the "last error string"
|
|
based on the value of Status. For status codes that don't have
|
|
a corresponding error string, the string is set to null.
|
|
|
|
Arguments:
|
|
|
|
Status - Supplies the status value to store as the last error value.
|
|
|
|
Return Value:
|
|
|
|
The corresponding Win32 error code that was stored in the
|
|
"last error value" thread variable.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG dwErrorCode;
|
|
|
|
dwErrorCode = RtlNtStatusToDosError( Status );
|
|
SetLastError( dwErrorCode );
|
|
return( dwErrorCode );
|
|
}
|
|
|
|
HANDLE
|
|
WINAPI
|
|
CreateIoCompletionPort(
|
|
HANDLE FileHandle,
|
|
HANDLE ExistingCompletionPort,
|
|
ULONG_PTR CompletionKey,
|
|
DWORD NumberOfConcurrentThreads
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function creates an I/O completion port. Completion ports
|
|
provide another mechanism that can be used to to recive I/O
|
|
completion notification.
|
|
|
|
Completion ports act as a queue. The Win32 I/O system can be
|
|
instructed to queue I/O completion notification packets to
|
|
completion ports. This API provides this mechanism. If a file
|
|
handle is created for overlapped I/O completion
|
|
(FILE_FLAG_OVERLAPPED) , a completion port can be associated with
|
|
the file handle. When I/O operations are done on a file handle that
|
|
has an associated completion port, the I/O system will queue a
|
|
completion packet when the I/O operation completes. The
|
|
GetQueuedCompletionStatus is used to pick up these queued I/O
|
|
completion packets.
|
|
|
|
This API can be used to create a completion port and associate it
|
|
with a file. If you supply a completion port, it can be used to
|
|
associate the specified file with the specified completion port.
|
|
|
|
Arguments:
|
|
|
|
FileHandle - Supplies a handle to a file opened for overlapped I/O
|
|
completion. This file is associated with either the specified
|
|
completion port, or a new completion port is created, and the
|
|
file is associated with that port. Once associated with a
|
|
completion port, the file handle may not be used in ReadFileEx
|
|
or WriteFileEx operations. It is not advisable to share an
|
|
associated file handle through either handle inheritence or
|
|
through DuplicateHandle. I/O operations done on these
|
|
duplicates will also generate a completion notification.
|
|
|
|
ExistingCompletionPort - If this parameter is specified, it supplies
|
|
an existing completion port that is to be associated with the
|
|
specified file handle. Otherwise, a new completion port is
|
|
created and associated with the specified file handle.
|
|
|
|
CompletionKey - Supplies a per-file completion key that is part of
|
|
every I/O completion packet for this file.
|
|
|
|
NumberOfConcurrentThreads - This is the number of threads that are
|
|
alowed to be concurrently active and can be used to avoid
|
|
spurious context switches, e.g., context switches that would
|
|
occur simply because of quantum end. Up to the number of
|
|
threads specified are allowed to execute concurrently. If one
|
|
of the threads enters a wait state, then another thread is
|
|
allowed to procede. There may be times when more then the
|
|
specified number of threads are active, but this will be quickly
|
|
throttled. A value of 0 tells the system to allow the same
|
|
number of threads as there are processors to run.
|
|
|
|
Return Value:
|
|
|
|
Not NULL - Returns the completion port handle associated with the file.
|
|
|
|
NULL - The operation failed. Extended error status is available
|
|
using GetLastError.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
HANDLE Port;
|
|
IO_STATUS_BLOCK IoSb;
|
|
FILE_COMPLETION_INFORMATION CompletionInfo;
|
|
|
|
Port = ExistingCompletionPort;
|
|
if ( !ARGUMENT_PRESENT(ExistingCompletionPort) ) {
|
|
Status = NtCreateIoCompletion (
|
|
&Port,
|
|
IO_COMPLETION_ALL_ACCESS,
|
|
NULL,
|
|
NumberOfConcurrentThreads
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if ( FileHandle != INVALID_HANDLE_VALUE ) {
|
|
CompletionInfo.Port = Port;
|
|
CompletionInfo.Key = (PVOID)CompletionKey;
|
|
|
|
Status = NtSetInformationFile(
|
|
FileHandle,
|
|
&IoSb,
|
|
&CompletionInfo,
|
|
sizeof(CompletionInfo),
|
|
FileCompletionInformation
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
if ( !ARGUMENT_PRESENT(ExistingCompletionPort) ) {
|
|
NtClose(Port);
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// file handle is INVALID_HANDLE_VALUE. Usually this is
|
|
// used to create a new unassociated completion port.
|
|
//
|
|
// Special case here to see if existing completion port was
|
|
// specified and fail if it is
|
|
//
|
|
|
|
if ( ARGUMENT_PRESENT(ExistingCompletionPort) ) {
|
|
Port = NULL;
|
|
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
|
|
}
|
|
}
|
|
|
|
return Port;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
PostQueuedCompletionStatus(
|
|
HANDLE CompletionPort,
|
|
DWORD dwNumberOfBytesTransferred,
|
|
ULONG_PTR dwCompletionKey,
|
|
LPOVERLAPPED lpOverlapped
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function allows the caller to post an I/O completion packet to
|
|
a completion port. This packet will satisfy an outstanding call to
|
|
GetQueuedCompletionStatus and will provide that caller with the three values
|
|
normally returned from that call.
|
|
|
|
Arguments:
|
|
|
|
CompletionPort - Supplies a handle to a completion port that the caller wants to
|
|
post a completion packet to.
|
|
|
|
dwNumberOfBytesTransferred - Supplies the value that is to be
|
|
returned through the lpNumberOfBytesTransfered parameter of the
|
|
GetQueuedCompletionStatus API.
|
|
|
|
dwCompletionKey - Supplies the value that is to be returned through
|
|
the lpCompletionKey parameter of the GetQueuedCompletionStatus
|
|
API.
|
|
|
|
lpOverlapped - Supplies the value that is to be returned through the
|
|
lpOverlapped parameter of the GetQueuedCompletionStatus API.
|
|
|
|
Return Value:
|
|
|
|
TRUE - The operation was successful
|
|
|
|
FALSE - The operation failed, use GetLastError to get detailed error information
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
BOOL rv;
|
|
|
|
rv = TRUE;
|
|
Status = NtSetIoCompletion(
|
|
CompletionPort,
|
|
(PVOID)dwCompletionKey,
|
|
(PVOID)lpOverlapped,
|
|
STATUS_SUCCESS,
|
|
dwNumberOfBytesTransferred
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
rv = FALSE;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
GetQueuedCompletionStatus(
|
|
HANDLE CompletionPort,
|
|
LPDWORD lpNumberOfBytesTransferred,
|
|
PULONG_PTR lpCompletionKey,
|
|
LPOVERLAPPED *lpOverlapped,
|
|
DWORD dwMilliseconds
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function waits for pending I/O operations associated with the
|
|
specified completion port to complete. Server applications may have
|
|
several threads issuing this call on the same completion port. As
|
|
I/O operations complete, they are queued to this port. If threads
|
|
are actively waiting in this call, queued requests complete their
|
|
call.
|
|
|
|
This API returns a boolean value.
|
|
|
|
A value of TRUE means that a pending I/O completed successfully.
|
|
The the number of bytes transfered during the I/O, the completion
|
|
key that indicates which file the I/O occured on, and the overlapped
|
|
structure address used in the original I/O are all returned.
|
|
|
|
A value of FALSE indicates one ow two things:
|
|
|
|
If *lpOverlapped is NULL, no I/O operation was dequeued. This
|
|
typically means that an error occured while processing the
|
|
parameters to this call, or that the CompletionPort handle has been
|
|
closed or is otherwise invalid. GetLastError() may be used to
|
|
further isolate this.
|
|
|
|
If *lpOverlapped is non-NULL, an I/O completion packet was dequeud,
|
|
but the I/O operation resulted in an error. GetLastError() can be
|
|
used to further isolate the I/O error. The the number of bytes
|
|
transfered during the I/O, the completion key that indicates which
|
|
file the I/O occured on, and the overlapped structure address used
|
|
in the original I/O are all returned.
|
|
|
|
Arguments:
|
|
|
|
CompletionPort - Supplies a handle to a completion port to wait on.
|
|
|
|
lpNumberOfBytesTransferred - Returns the number of bytes transfered during the
|
|
I/O operation whose completion is being reported.
|
|
|
|
lpCompletionKey - Returns a completion key value specified during
|
|
CreateIoCompletionPort. This is a per-file key that can be used
|
|
to tall the caller the file that an I/O operation completed on.
|
|
|
|
lpOverlapped - Returns the address of the overlapped structure that
|
|
was specified when the I/O was issued. The following APIs may
|
|
complete using completion ports. This ONLY occurs if the file
|
|
handle is associated with with a completion port AND an
|
|
overlapped structure was passed to the API.
|
|
|
|
LockFileEx
|
|
WriteFile
|
|
ReadFile
|
|
DeviceIoControl
|
|
WaitCommEvent
|
|
ConnectNamedPipe
|
|
TransactNamedPipe
|
|
|
|
dwMilliseconds - Supplies an optional timeout value that specifies
|
|
how long the caller is willing to wait for an I/O completion
|
|
packet.
|
|
|
|
Return Value:
|
|
|
|
TRUE - An I/O operation completed successfully.
|
|
lpNumberOfBytesTransferred, lpCompletionKey, and lpOverlapped
|
|
are all valid.
|
|
|
|
FALSE - If lpOverlapped is NULL, the operation failed and no I/O
|
|
completion data is retured. GetLastError() can be used to
|
|
further isolate the cause of the error (bad parameters, invalid
|
|
completion port handle). Otherwise, a pending I/O operation
|
|
completed, but it completed with an error. GetLastError() can
|
|
be used to further isolate the I/O error.
|
|
lpNumberOfBytesTransferred, lpCompletionKey, and lpOverlapped
|
|
are all valid.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
LARGE_INTEGER TimeOut;
|
|
PLARGE_INTEGER pTimeOut;
|
|
IO_STATUS_BLOCK IoSb;
|
|
NTSTATUS Status;
|
|
LPOVERLAPPED LocalOverlapped;
|
|
BOOL rv;
|
|
|
|
|
|
pTimeOut = BaseFormatTimeOut(&TimeOut,dwMilliseconds);
|
|
Status = NtRemoveIoCompletion(
|
|
CompletionPort,
|
|
(PVOID *)lpCompletionKey,
|
|
(PVOID *)&LocalOverlapped,
|
|
&IoSb,
|
|
pTimeOut
|
|
);
|
|
|
|
if ( !NT_SUCCESS(Status) || Status == STATUS_TIMEOUT ) {
|
|
*lpOverlapped = NULL;
|
|
if ( Status == STATUS_TIMEOUT ) {
|
|
SetLastError(WAIT_TIMEOUT);
|
|
}
|
|
else {
|
|
BaseSetLastNTError(Status);
|
|
}
|
|
rv = FALSE;
|
|
}
|
|
else {
|
|
*lpOverlapped = LocalOverlapped;
|
|
|
|
*lpNumberOfBytesTransferred = (DWORD)IoSb.Information;
|
|
|
|
if ( !NT_SUCCESS(IoSb.Status) ){
|
|
BaseSetLastNTError( IoSb.Status );
|
|
rv = FALSE;
|
|
}
|
|
else {
|
|
rv = TRUE;
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
GetOverlappedResult(
|
|
HANDLE hFile,
|
|
LPOVERLAPPED lpOverlapped,
|
|
LPDWORD lpNumberOfBytesTransferred,
|
|
BOOL bWait
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The GetOverlappedResult function returns the result of the last
|
|
operation that used lpOverlapped and returned ERROR_IO_PENDING.
|
|
|
|
Arguments:
|
|
|
|
hFile - Supplies the open handle to the file that the overlapped
|
|
structure lpOverlapped was supplied to ReadFile, WriteFile,
|
|
ConnectNamedPipe, WaitNamedPipe or TransactNamedPipe.
|
|
|
|
lpOverlapped - Points to an OVERLAPPED structure previously supplied to
|
|
ReadFile, WriteFile, ConnectNamedPipe, WaitNamedPipe or
|
|
TransactNamedPipe.
|
|
|
|
lpNumberOfBytesTransferred - Returns the number of bytes transferred
|
|
by the operation.
|
|
|
|
bWait - A boolean value that affects the behavior when the operation
|
|
is still in progress. If TRUE and the operation is still in progress,
|
|
GetOverlappedResult will wait for the operation to complete before
|
|
returning. If FALSE and the operation is incomplete,
|
|
GetOverlappedResult will return FALSE. In this case the extended
|
|
error information available from the GetLastError function will be
|
|
set to ERROR_IO_INCOMPLETE.
|
|
|
|
Return Value:
|
|
|
|
TRUE -- The operation was successful, the pipe is in the
|
|
connected state.
|
|
|
|
FALSE -- The operation failed. Extended error status is available using
|
|
GetLastError.
|
|
|
|
--*/
|
|
{
|
|
DWORD WaitReturn;
|
|
|
|
//
|
|
// Did caller specify an event to the original operation or was the
|
|
// default (file handle) used?
|
|
//
|
|
|
|
if (lpOverlapped->Internal == (DWORD)STATUS_PENDING ) {
|
|
if ( bWait ) {
|
|
WaitReturn = WaitForSingleObject(
|
|
( lpOverlapped->hEvent != NULL ) ?
|
|
lpOverlapped->hEvent : hFile,
|
|
INFINITE
|
|
);
|
|
}
|
|
else {
|
|
WaitReturn = WAIT_TIMEOUT;
|
|
}
|
|
|
|
if ( WaitReturn == WAIT_TIMEOUT ) {
|
|
// !bWait and event in not signalled state
|
|
SetLastError( ERROR_IO_INCOMPLETE );
|
|
return FALSE;
|
|
}
|
|
|
|
if ( WaitReturn != 0 ) {
|
|
return FALSE; // WaitForSingleObject calls BaseSetLastError
|
|
}
|
|
}
|
|
|
|
*lpNumberOfBytesTransferred = (DWORD)lpOverlapped->InternalHigh;
|
|
|
|
if ( NT_SUCCESS((NTSTATUS)lpOverlapped->Internal) ){
|
|
return TRUE;
|
|
}
|
|
else {
|
|
BaseSetLastNTError( (NTSTATUS)lpOverlapped->Internal );
|
|
return FALSE;
|
|
}
|
|
}
|