/*++ 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 (((DWORD)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 } } else { // // We have seen the status in the overlapped structure has changed // but we need to make sure that our read of other fields occur // after this point. // #if defined(_IA64_) __mf (); #else _ReadWriteBarrier(); #endif } *lpNumberOfBytesTransferred = (DWORD)lpOverlapped->InternalHigh; if ( NT_SUCCESS((NTSTATUS)lpOverlapped->Internal) ){ return TRUE; } else { BaseSetLastNTError( (NTSTATUS)lpOverlapped->Internal ); return FALSE; } }