windows-nt/Source/XPSP1/NT/base/win32/client/synch.c
2020-09-26 16:20:57 +08:00

1984 lines
52 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
synch.c
Abstract:
This module implements all Win32 syncronization
objects.
Author:
Mark Lucovsky (markl) 19-Sep-1990
Revision History:
--*/
#include "basedll.h"
//
// Critical Section Services
//
VOID
InitializeCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
)
/*++
Routine Description:
A critical section object is initialized using
Win32InitializeCriticalSection.
Once a critical section object has been initialized threads within a
single process may enter and exit critical sections using the
critical section object. A critical section object may not me moved
or copied. The application must also not modify the insides of the
object, but must treat it as logically opaque.
description-of-function.
Arguments:
lpCriticalSection - Supplies the address of a critical section object
to be initialized. It is the callers resposibility to allocate
the storage used by a critical section object.
Return Value:
None.
--*/
{
NTSTATUS Status;
Status = RtlInitializeCriticalSection(lpCriticalSection);
if ( !NT_SUCCESS(Status) ){
RtlRaiseStatus(Status);
}
}
BOOL
InitializeCriticalSectionAndSpinCount(
LPCRITICAL_SECTION lpCriticalSection,
DWORD dwSpinCount
)
/*++
Routine Description:
A critical section object is initialized using
Win32InitializeCriticalSection.
Once a critical section object has been initialized threads within a
single process may enter and exit critical sections using the
critical section object. A critical section object may not me moved
or copied. The application must also not modify the insides of the
object, but must treat it as logically opaque.
description-of-function.
Arguments:
lpCriticalSection - Supplies the address of a critical section object
to be initialized. It is the callers resposibility to allocate
the storage used by a critical section object.
Return Value:
None.
--*/
{
NTSTATUS Status;
BOOL rv;
rv = TRUE;
Status = RtlInitializeCriticalSectionAndSpinCount(lpCriticalSection,dwSpinCount);
if ( !NT_SUCCESS(Status) ){
BaseSetLastNTError(Status);
rv = FALSE;
}
return rv;
}
//
// Event Services
//
HANDLE
APIENTRY
CreateEventA(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
LPCSTR lpName
)
/*++
Routine Description:
ANSI thunk to CreateEventW
--*/
{
PUNICODE_STRING Unicode;
ANSI_STRING AnsiString;
NTSTATUS Status;
LPCWSTR NameBuffer;
NameBuffer = NULL;
if ( ARGUMENT_PRESENT(lpName) ) {
Unicode = &NtCurrentTeb()->StaticUnicodeString;
RtlInitAnsiString(&AnsiString,lpName);
Status = RtlAnsiStringToUnicodeString(Unicode,&AnsiString,FALSE);
if ( !NT_SUCCESS(Status) ) {
if ( Status == STATUS_BUFFER_OVERFLOW ) {
SetLastError(ERROR_FILENAME_EXCED_RANGE);
}
else {
BaseSetLastNTError(Status);
}
return NULL;
}
NameBuffer = (LPCWSTR)Unicode->Buffer;
}
return CreateEventW(
lpEventAttributes,
bManualReset,
bInitialState,
NameBuffer
);
}
HANDLE
APIENTRY
CreateEventW(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
LPCWSTR lpName
)
/*++
Routine Description:
An event object is created and a handle opened for access to the
object with the CreateEvent function.
The CreateEvent function creates an event object with the specified
initial state. If an event is in the Signaled state (TRUE), a wait
operation on the event does not block. If the event is in the Not-
Signaled state (FALSE), a wait operation on the event blocks until
the specified event attains a state of Signaled, or the timeout
value is exceeded.
In addition to the STANDARD_RIGHTS_REQUIRED access flags, the following
object type specific access flags are valid for event objects:
- EVENT_MODIFY_STATE - Modify state access (set and reset) to
the event is desired.
- SYNCHRONIZE - Synchronization access (wait) to the event is
desired.
- EVENT_ALL_ACCESS - This set of access flags specifies all of
the possible access flags for an event object.
Arguments:
lpEventAttributes - An optional parameter that may be used to
specify the attributes of the new event. If the parameter is
not specified, then the event is created without a security
descriptor, and the resulting handle is not inherited on process
creation.
bManualReset - Supplies a flag which if TRUE specifies that the
event must be manually reset. If the value is FALSE, then after
releasing a single waiter, the system automaticaly resets the
event.
bInitialState - The initial state of the event object, one of TRUE
or FALSE. If the InitialState is specified as TRUE, the event's
current state value is set to one, otherwise it is set to zero.
lpName - Optional unicode name of event
Return Value:
NON-NULL - Returns a handle to the new event. The handle has full
access to the new event and may be used in any API that requires
a handle to an event object.
FALSE/NULL - The operation failed. Extended error status is available
using GetLastError.
--*/
{
NTSTATUS Status;
OBJECT_ATTRIBUTES Obja;
POBJECT_ATTRIBUTES pObja;
HANDLE Handle;
UNICODE_STRING ObjectName;
PWCHAR pstrNewObjName = NULL;
if ( ARGUMENT_PRESENT(lpName) ) {
if (gpTermsrvFormatObjectName &&
(pstrNewObjName = gpTermsrvFormatObjectName(lpName))) {
RtlInitUnicodeString(&ObjectName,pstrNewObjName);
} else {
RtlInitUnicodeString(&ObjectName,lpName);
}
pObja = BaseFormatObjectAttributes(&Obja,lpEventAttributes,&ObjectName);
}
else {
pObja = BaseFormatObjectAttributes(&Obja,lpEventAttributes,NULL);
}
Status = NtCreateEvent(
&Handle,
EVENT_ALL_ACCESS,
pObja,
bManualReset ? NotificationEvent : SynchronizationEvent,
(BOOLEAN)bInitialState
);
if (pstrNewObjName) {
RtlFreeHeap(RtlProcessHeap(), 0, pstrNewObjName);
}
if ( NT_SUCCESS(Status) ) {
if ( Status == STATUS_OBJECT_NAME_EXISTS ) {
SetLastError(ERROR_ALREADY_EXISTS);
}
else {
SetLastError(0);
}
return Handle;
}
else {
BaseSetLastNTError(Status);
return NULL;
}
}
HANDLE
APIENTRY
OpenEventA(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCSTR lpName
)
/*++
Routine Description:
ANSI thunk to OpenNamedEventW
--*/
{
PUNICODE_STRING Unicode;
ANSI_STRING AnsiString;
NTSTATUS Status;
if ( ARGUMENT_PRESENT(lpName) ) {
Unicode = &NtCurrentTeb()->StaticUnicodeString;
RtlInitAnsiString(&AnsiString,lpName);
Status = RtlAnsiStringToUnicodeString(Unicode,&AnsiString,FALSE);
if ( !NT_SUCCESS(Status) ) {
if ( Status == STATUS_BUFFER_OVERFLOW ) {
SetLastError(ERROR_FILENAME_EXCED_RANGE);
}
else {
BaseSetLastNTError(Status);
}
return NULL;
}
}
else {
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
return NULL;
}
return OpenEventW(
dwDesiredAccess,
bInheritHandle,
(LPCWSTR)Unicode->Buffer
);
}
HANDLE
APIENTRY
OpenEventW(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCWSTR lpName
)
{
OBJECT_ATTRIBUTES Obja;
UNICODE_STRING ObjectName;
NTSTATUS Status;
HANDLE Object;
PWCHAR pstrNewObjName = NULL;
if ( !lpName ) {
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
return NULL;
}
if (gpTermsrvFormatObjectName &&
(pstrNewObjName = gpTermsrvFormatObjectName(lpName))) {
RtlInitUnicodeString(&ObjectName,pstrNewObjName);
} else {
RtlInitUnicodeString(&ObjectName,lpName);
}
InitializeObjectAttributes(
&Obja,
&ObjectName,
(bInheritHandle ? OBJ_INHERIT : 0),
BaseGetNamedObjectDirectory(),
NULL
);
Status = NtOpenEvent(
&Object,
dwDesiredAccess,
&Obja
);
if (pstrNewObjName) {
RtlFreeHeap(RtlProcessHeap(), 0, pstrNewObjName);
}
if ( !NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status);
return NULL;
}
return Object;
}
BOOL
SetEvent(
HANDLE hEvent
)
/*++
Routine Description:
An event can be set to the signaled state (TRUE) with the SetEvent
function.
Setting the event causes the event to attain a state of Signaled,
which releases all currently waiting threads (for manual reset
events), or a single waiting thread (for automatic reset events).
Arguments:
hEvent - Supplies an open handle to an event object. The
handle must have EVENT_MODIFY_STATE access to the event.
Return Value:
TRUE - The operation was successful
FALSE/NULL - The operation failed. Extended error status is available
using GetLastError.
--*/
{
NTSTATUS Status;
Status = NtSetEvent(hEvent,NULL);
if ( NT_SUCCESS(Status) ) {
return TRUE;
}
else {
BaseSetLastNTError(Status);
return FALSE;
}
}
BOOL
ResetEvent(
HANDLE hEvent
)
/*++
Routine Description:
The state of an event is set to the Not-Signaled state (FALSE) using
the ClearEvent function.
Once the event attains a state of Not-Signaled, any threads which
wait on the event block, awaiting the event to become Signaled. The
reset event service sets the event count to zero for the state of
the event.
Arguments:
hEvent - Supplies an open handle to an event object. The
handle must have EVENT_MODIFY_STATE access to the event.
Return Value:
TRUE - The operation was successful
FALSE/NULL - The operation failed. Extended error status is available
using GetLastError.
--*/
{
NTSTATUS Status;
Status = NtClearEvent(hEvent);
if ( NT_SUCCESS(Status) ) {
return TRUE;
}
else {
BaseSetLastNTError(Status);
return FALSE;
}
}
BOOL
PulseEvent(
HANDLE hEvent
)
/*++
Routine Description:
An event can be set to the Signaled state and reset to the Not-
Signaled state atomically with the PulseEvent function.
Pulsing the event causes the event to attain a state of Signaled,
release appropriate threads, and then reset the event. When no
waiters are currently waiting on the event, pulsing an event causes
the event to release no threads and end up in the Not-Signaled
state. With waiters waiting on an event, pulsing an event has a
different effect for manual reset events that it does for automatic
reset events. For manual reset events, pulsing releases all waiters
and then leaves the event in the Not-Signaled state. For automatic
reset events, pulsing the event releases a single waiter and then
leaves the event in the Not-Signaled state.
Arguments:
hEvent - Supplies an open handle to an event object. The
handle must have EVENT_MODIFY_STATE access to the event.
Return Value:
TRUE - The operation was successful
FALSE/NULL - The operation failed. Extended error status is available
using GetLastError.
--*/
{
NTSTATUS Status;
Status = NtPulseEvent(hEvent,NULL);
if ( NT_SUCCESS(Status) ) {
return TRUE;
}
else {
BaseSetLastNTError(Status);
return FALSE;
}
}
//
// Semaphore Services
//
HANDLE
APIENTRY
CreateSemaphoreA(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
LONG lInitialCount,
LONG lMaximumCount,
LPCSTR lpName
)
/*++
Routine Description:
ANSI thunk to CreateSemaphoreW
--*/
{
PUNICODE_STRING Unicode;
ANSI_STRING AnsiString;
NTSTATUS Status;
LPCWSTR NameBuffer;
NameBuffer = NULL;
if ( ARGUMENT_PRESENT(lpName) ) {
Unicode = &NtCurrentTeb()->StaticUnicodeString;
RtlInitAnsiString(&AnsiString,lpName);
Status = RtlAnsiStringToUnicodeString(Unicode,&AnsiString,FALSE);
if ( !NT_SUCCESS(Status) ) {
if ( Status == STATUS_BUFFER_OVERFLOW ) {
SetLastError(ERROR_FILENAME_EXCED_RANGE);
}
else {
BaseSetLastNTError(Status);
}
return NULL;
}
NameBuffer = (LPCWSTR)Unicode->Buffer;
}
return CreateSemaphoreW(
lpSemaphoreAttributes,
lInitialCount,
lMaximumCount,
NameBuffer
);
}
HANDLE
APIENTRY
CreateSemaphoreW(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
LONG lInitialCount,
LONG lMaximumCount,
LPCWSTR lpName
)
/*++
Routine Description:
A semaphore object is created and a handle opened for access to the
object with the CreateSemaphore function.
The CreateSemaphore function causes a semaphore object to be created
which contains the specified initial and maximum counts.
In addition to the STANDARD_RIGHTS_REQUIRED access flags, the
following object type specific access flags are valid for semaphore
objects:
- SEMAPHORE_MODIFY_STATE - Modify state access (release) to the
semaphore is desired.
- SYNCHRONIZE - Synchronization access (wait) to the semaphore
is desired.
- SEMAPHORE_ALL_ACCESS - This set of access flags specifies all
of the possible access flags for a semaphore object.
Arguments:
lpSemaphoreAttributes - An optional parameter that may be used to
specify the attributes of the new semaphore. If the parameter
is not specified, then the semaphore is created without a
security descriptor, , and the resulting handle is not inherited
on process creation.
lInitialCount - The initial count for the semaphore, this value
must be positive and less than or equal to the maximum count.
lMaximumCount - The maximum count for the semaphore, this value
must be greater than zero..
lpName - Supplies an optional unicode name for the object.
Return Value:
NON-NULL - Returns a handle to the new semaphore. The handle has
full access to the new semaphore and may be used in any API that
requires a handle to a semaphore object.
FALSE/NULL - The operation failed. Extended error status is available
using GetLastError.
--*/
{
NTSTATUS Status;
OBJECT_ATTRIBUTES Obja;
POBJECT_ATTRIBUTES pObja;
HANDLE Handle;
UNICODE_STRING ObjectName;
PWCHAR pstrNewObjName = NULL;
if ( ARGUMENT_PRESENT(lpName) ) {
if (gpTermsrvFormatObjectName &&
(pstrNewObjName = gpTermsrvFormatObjectName(lpName))) {
RtlInitUnicodeString(&ObjectName,pstrNewObjName);
} else {
RtlInitUnicodeString(&ObjectName,lpName);
}
pObja = BaseFormatObjectAttributes(&Obja,lpSemaphoreAttributes,&ObjectName);
}
else {
pObja = BaseFormatObjectAttributes(&Obja,lpSemaphoreAttributes,NULL);
}
Status = NtCreateSemaphore(
&Handle,
SEMAPHORE_ALL_ACCESS,
pObja,
lInitialCount,
lMaximumCount
);
if (pstrNewObjName) {
RtlFreeHeap(RtlProcessHeap(), 0, pstrNewObjName);
}
if ( NT_SUCCESS(Status) ) {
if ( Status == STATUS_OBJECT_NAME_EXISTS ) {
SetLastError(ERROR_ALREADY_EXISTS);
}
else {
SetLastError(0);
}
return Handle;
}
else {
BaseSetLastNTError(Status);
return NULL;
}
}
HANDLE
APIENTRY
OpenSemaphoreA(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCSTR lpName
)
/*++
Routine Description:
ANSI thunk to OpenSemaphoreW
--*/
{
PUNICODE_STRING Unicode;
ANSI_STRING AnsiString;
NTSTATUS Status;
if ( ARGUMENT_PRESENT(lpName) ) {
Unicode = &NtCurrentTeb()->StaticUnicodeString;
RtlInitAnsiString(&AnsiString,lpName);
Status = RtlAnsiStringToUnicodeString(Unicode,&AnsiString,FALSE);
if ( !NT_SUCCESS(Status) ) {
if ( Status == STATUS_BUFFER_OVERFLOW ) {
SetLastError(ERROR_FILENAME_EXCED_RANGE);
}
else {
BaseSetLastNTError(Status);
}
return NULL;
}
}
else {
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
return NULL;
}
return OpenSemaphoreW(
dwDesiredAccess,
bInheritHandle,
(LPCWSTR)Unicode->Buffer
);
}
HANDLE
APIENTRY
OpenSemaphoreW(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCWSTR lpName
)
{
OBJECT_ATTRIBUTES Obja;
UNICODE_STRING ObjectName;
NTSTATUS Status;
HANDLE Object;
PWCHAR pstrNewObjName = NULL;
if ( !lpName ) {
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
return NULL;
}
if (gpTermsrvFormatObjectName &&
(pstrNewObjName = gpTermsrvFormatObjectName(lpName))) {
RtlInitUnicodeString(&ObjectName,pstrNewObjName);
} else {
RtlInitUnicodeString(&ObjectName,lpName);
}
InitializeObjectAttributes(
&Obja,
&ObjectName,
(bInheritHandle ? OBJ_INHERIT : 0),
BaseGetNamedObjectDirectory(),
NULL
);
Status = NtOpenSemaphore(
&Object,
dwDesiredAccess,
&Obja
);
if (pstrNewObjName) {
RtlFreeHeap(RtlProcessHeap(), 0, pstrNewObjName);
}
if ( !NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status);
return NULL;
}
return Object;
}
BOOL
ReleaseSemaphore(
HANDLE hSemaphore,
LONG lReleaseCount,
LPLONG lpPreviousCount
)
/*++
Routine Description:
A semaphore object can be released with the ReleaseSemaphore
function.
When the semaphore is released, the current count of the semaphore
is incremented by the ReleaseCount. Any threads that are waiting
for the semaphore are examined to see if the current semaphore value
is sufficient to satisfy their wait.
If the value specified by ReleaseCount would cause the maximum count
for the semaphore to be exceeded, then the count for the semaphore
is not affected and an error status is returned.
Arguments:
hSemaphore - Supplies an open handle to a semaphore object. The
handle must have SEMAPHORE_MODIFY_STATE access to the semaphore.
lReleaseCount - The release count for the semaphore. The count
must be greater than zero and less than the maximum value
specified for the semaphore.
lpPreviousCount - An optional pointer to a variable that receives
the previous count for the semaphore.
Return Value:
TRUE - The operation was successful
FALSE/NULL - The operation failed. Extended error status is available
using GetLastError.
--*/
{
NTSTATUS Status;
Status = NtReleaseSemaphore(hSemaphore,lReleaseCount,lpPreviousCount);
if ( NT_SUCCESS(Status) ) {
return TRUE;
}
else {
BaseSetLastNTError(Status);
return FALSE;
}
}
//
// Mutex Services
//
HANDLE
APIENTRY
CreateMutexA(
LPSECURITY_ATTRIBUTES lpMutexAttributes,
BOOL bInitialOwner,
LPCSTR lpName
)
/*++
Routine Description:
ANSI thunk to CreateMutexW
--*/
{
PUNICODE_STRING Unicode;
ANSI_STRING AnsiString;
NTSTATUS Status;
LPCWSTR NameBuffer;
NameBuffer = NULL;
if ( ARGUMENT_PRESENT(lpName) ) {
Unicode = &NtCurrentTeb()->StaticUnicodeString;
RtlInitAnsiString(&AnsiString,lpName);
Status = RtlAnsiStringToUnicodeString(Unicode,&AnsiString,FALSE);
if ( !NT_SUCCESS(Status) ) {
if ( Status == STATUS_BUFFER_OVERFLOW ) {
SetLastError(ERROR_FILENAME_EXCED_RANGE);
}
else {
BaseSetLastNTError(Status);
}
return NULL;
}
NameBuffer = (LPCWSTR)Unicode->Buffer;
}
return CreateMutexW(
lpMutexAttributes,
bInitialOwner,
NameBuffer
);
}
HANDLE
APIENTRY
CreateMutexW(
LPSECURITY_ATTRIBUTES lpMutexAttributes,
BOOL bInitialOwner,
LPCWSTR lpName
)
/*++
Routine Description:
A mutex object can be created and a handle opened for access to the
object with the CreateMutex function.
A new mutex object is created and a handle opened to the object with
ownership as determined by the InitialOwner parameter. The status
of the newly created mutex object is set to not abandoned.
In addition to the STANDARD_RIGHTS_REQUIRED access flags, the
following object type specific access flags are valid for mutex
objects:
- MUTEX_MODIFY_STATE - Modify access to the mutex is desired.
This allows a process to release a mutex.
- SYNCHRONIZE - Synchronization access (wait or release) to the
mutex object is desired.
- MUTEX_ALL_ACCESS - All possible types of access to the mutex
object are desired.
Arguments:
lpMutexAttributes - An optional parameter that may be used to specify
the attributes of the new mutex. If the parameter is not
specified, then the mutex is created without a security
descriptor, and the resulting handle is not inherited on process
creation.
bInitialOwner - A boolean value that determines whether the creator
of the object desires immediate ownership of the mutex object.
lpName - Supplies an optional unicode name for the mutex.
Return Value:
NON-NULL - Returns a handle to the new mutex. The handle has full
access to the new mutex and may be used in any API that
requires a handle to a mutex object.
FALSE/NULL - The operation failed. Extended error status is available
using GetLastError.
--*/
{
NTSTATUS Status;
OBJECT_ATTRIBUTES Obja;
POBJECT_ATTRIBUTES pObja;
HANDLE Handle;
UNICODE_STRING ObjectName;
PWCHAR pstrNewObjName = NULL;
if ( ARGUMENT_PRESENT(lpName) ) {
if (gpTermsrvFormatObjectName &&
(pstrNewObjName = gpTermsrvFormatObjectName(lpName))) {
RtlInitUnicodeString(&ObjectName,pstrNewObjName);
} else {
RtlInitUnicodeString(&ObjectName,lpName);
}
pObja = BaseFormatObjectAttributes(&Obja,lpMutexAttributes,&ObjectName);
}
else {
pObja = BaseFormatObjectAttributes(&Obja,lpMutexAttributes,NULL);
}
Status = NtCreateMutant(
&Handle,
MUTANT_ALL_ACCESS,
pObja,
(BOOLEAN)bInitialOwner
);
if (pstrNewObjName) {
RtlFreeHeap(RtlProcessHeap(), 0, pstrNewObjName);
}
if ( NT_SUCCESS(Status) ) {
if ( Status == STATUS_OBJECT_NAME_EXISTS ) {
SetLastError(ERROR_ALREADY_EXISTS);
}
else {
SetLastError(0);
}
return Handle;
}
else {
BaseSetLastNTError(Status);
return NULL;
}
}
HANDLE
APIENTRY
OpenMutexA(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCSTR lpName
)
/*++
Routine Description:
ANSI thunk to OpenMutexW
--*/
{
PUNICODE_STRING Unicode;
ANSI_STRING AnsiString;
NTSTATUS Status;
if ( ARGUMENT_PRESENT(lpName) ) {
Unicode = &NtCurrentTeb()->StaticUnicodeString;
RtlInitAnsiString(&AnsiString,lpName);
Status = RtlAnsiStringToUnicodeString(Unicode,&AnsiString,FALSE);
if ( !NT_SUCCESS(Status) ) {
if ( Status == STATUS_BUFFER_OVERFLOW ) {
SetLastError(ERROR_FILENAME_EXCED_RANGE);
}
else {
BaseSetLastNTError(Status);
}
return NULL;
}
}
else {
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
return NULL;
}
return OpenMutexW(
dwDesiredAccess,
bInheritHandle,
(LPCWSTR)Unicode->Buffer
);
}
HANDLE
APIENTRY
OpenMutexW(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCWSTR lpName
)
{
OBJECT_ATTRIBUTES Obja;
UNICODE_STRING ObjectName;
NTSTATUS Status;
HANDLE Object;
PWCHAR pstrNewObjName = NULL;
if ( !lpName ) {
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
return NULL;
}
if (gpTermsrvFormatObjectName &&
(pstrNewObjName = gpTermsrvFormatObjectName(lpName))) {
RtlInitUnicodeString(&ObjectName,pstrNewObjName);
} else {
RtlInitUnicodeString(&ObjectName,lpName);
}
InitializeObjectAttributes(
&Obja,
&ObjectName,
(bInheritHandle ? OBJ_INHERIT : 0),
BaseGetNamedObjectDirectory(),
NULL
);
Status = NtOpenMutant(
&Object,
dwDesiredAccess,
&Obja
);
if (pstrNewObjName) {
RtlFreeHeap(RtlProcessHeap(), 0, pstrNewObjName);
}
if ( !NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status);
return NULL;
}
return Object;
}
BOOL
ReleaseMutex(
HANDLE hMutex
)
/*++
Routine Description:
Ownership of a mutex object can be released with the ReleaseMutex
function.
A mutex object can only be released by a thread that currently owns
the mutex object. When the mutex is released, the current count of
the mutex object is incremented by one. If the resultant count is
one, then the mutex object is no longer owned. Any threads that are
waiting for the mutex object are examined to see if their wait can
be satisfied.
Arguments:
hMutex - An open handle to a mutex object. The handle must
have MUTEX_MODIFY_STATE access to the mutex.
Return Value:
TRUE - The operation was successful
FALSE/NULL - The operation failed. Extended error status is available
using GetLastError.
--*/
{
NTSTATUS Status;
Status = NtReleaseMutant(hMutex,NULL);
if ( NT_SUCCESS(Status) ) {
return TRUE;
}
else {
BaseSetLastNTError(Status);
return FALSE;
}
}
//
// Wait Services
//
DWORD
WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds
)
/*++
Routine Description:
A wait operation on a waitable object is accomplished with the
WaitForSingleObject function.
Waiting on an object checks the current state of the object. If the
current state of the object allows continued execution, any
adjustments to the object state are made (for example, decrementing
the semaphore count for a semaphore object) and the thread continues
execution. If the current state of the object does not allow
continued execution, the thread is placed into the wait state
pending the change of the object's state or time-out.
Arguments:
hHandle - An open handle to a waitable object. The handle must have
SYNCHRONIZE access to the object.
dwMilliseconds - A time-out value that specifies the relative time,
in milliseconds, over which the wait is to be completed. A
timeout value of 0 specified that the wait is to timeout
immediately. This allows an application to test an object to
determine if it is in the signaled state. A timeout value of -1
specifies an infinite timeout period.
Return Value:
WAIT_TIME_OUT - Indicates that the wait was terminated due to the
TimeOut conditions.
0 - indicates the specified object attained a Signaled
state thus completing the wait.
WAIT_ABANDONED - indicates the specified object attained a Signaled
state but was abandoned.
--*/
{
return WaitForSingleObjectEx(hHandle,dwMilliseconds,FALSE);
}
DWORD
APIENTRY
WaitForSingleObjectEx(
HANDLE hHandle,
DWORD dwMilliseconds,
BOOL bAlertable
)
/*++
Routine Description:
A wait operation on a waitable object is accomplished with the
WaitForSingleObjectEx function.
Waiting on an object checks the current state of the object. If the
current state of the object allows continued execution, any
adjustments to the object state are made (for example, decrementing
the semaphore count for a semaphore object) and the thread continues
execution. If the current state of the object does not allow
continued execution, the thread is placed into the wait state
pending the change of the object's state or time-out.
If the bAlertable parameter is FALSE, the only way the wait
terminates is because the specified timeout period expires, or
because the specified object entered the signaled state. If the
bAlertable parameter is TRUE, then the wait can return due to any
one of the above wait termination conditions, or because an I/O
completion callback terminated the wait early (return value of
WAIT_IO_COMPLETION).
Arguments:
hHandle - An open handle to a waitable object. The handle must have
SYNCHRONIZE access to the object.
dwMilliseconds - A time-out value that specifies the relative time,
in milliseconds, over which the wait is to be completed. A
timeout value of 0 specified that the wait is to timeout
immediately. This allows an application to test an object to
determine if it is in the signaled state. A timeout value of
0xffffffff specifies an infinite timeout period.
bAlertable - Supplies a flag that controls whether or not the
wait may terminate early due to an I/O completion callback.
A value of TRUE allows this API to complete early due to an I/O
completion callback. A value of FALSE will not allow I/O
completion callbacks to terminate this call early.
Return Value:
WAIT_TIME_OUT - Indicates that the wait was terminated due to the
TimeOut conditions.
0 - indicates the specified object attained a Signaled
state thus completing the wait.
0xffffffff - The wait terminated due to an error. GetLastError may be
used to get additional error information.
WAIT_ABANDONED - indicates the specified object attained a Signaled
state but was abandoned.
WAIT_IO_COMPLETION - The wait terminated due to one or more I/O
completion callbacks.
--*/
{
NTSTATUS Status;
LARGE_INTEGER TimeOut;
PLARGE_INTEGER pTimeOut;
PPEB Peb;
RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME Frame = { sizeof(Frame), RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER };
RtlActivateActivationContextUnsafeFast(&Frame, NULL); // make the process default activation context active so that APCs are delivered under it
__try {
Peb = NtCurrentPeb();
switch( HandleToUlong(hHandle) ) {
case STD_INPUT_HANDLE: hHandle = Peb->ProcessParameters->StandardInput;
break;
case STD_OUTPUT_HANDLE: hHandle = Peb->ProcessParameters->StandardOutput;
break;
case STD_ERROR_HANDLE: hHandle = Peb->ProcessParameters->StandardError;
break;
}
if (CONSOLE_HANDLE(hHandle) && VerifyConsoleIoHandle(hHandle)) {
hHandle = GetConsoleInputWaitHandle();
}
pTimeOut = BaseFormatTimeOut(&TimeOut,dwMilliseconds);
rewait:
Status = NtWaitForSingleObject(hHandle,(BOOLEAN)bAlertable,pTimeOut);
if ( !NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status);
Status = (NTSTATUS)0xffffffff;
}
else {
if ( bAlertable && Status == STATUS_ALERTED ) {
goto rewait;
}
}
} __finally {
RtlDeactivateActivationContextUnsafeFast(&Frame);
}
return (DWORD)Status;
}
DWORD
WINAPI
SignalObjectAndWait(
HANDLE hObjectToSignal,
HANDLE hObjectToWaitOn,
DWORD dwMilliseconds,
BOOL bAlertable
)
{
NTSTATUS Status;
LARGE_INTEGER TimeOut;
PLARGE_INTEGER pTimeOut;
PPEB Peb;
RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME Frame = { sizeof(Frame), RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER };
RtlActivateActivationContextUnsafeFast(&Frame, NULL); // make the process default activation context active so that APCs are delivered under it
__try {
Peb = NtCurrentPeb();
switch( HandleToUlong(hObjectToWaitOn) ) {
case STD_INPUT_HANDLE: hObjectToWaitOn = Peb->ProcessParameters->StandardInput;
break;
case STD_OUTPUT_HANDLE: hObjectToWaitOn = Peb->ProcessParameters->StandardOutput;
break;
case STD_ERROR_HANDLE: hObjectToWaitOn = Peb->ProcessParameters->StandardError;
break;
}
if (CONSOLE_HANDLE(hObjectToWaitOn) && VerifyConsoleIoHandle(hObjectToWaitOn)) {
hObjectToWaitOn = GetConsoleInputWaitHandle();
}
pTimeOut = BaseFormatTimeOut(&TimeOut,dwMilliseconds);
rewait:
Status = NtSignalAndWaitForSingleObject(
hObjectToSignal,
hObjectToWaitOn,
(BOOLEAN)bAlertable,
pTimeOut
);
if ( !NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status);
Status = (NTSTATUS)0xffffffff;
}
else {
if ( bAlertable && Status == STATUS_ALERTED ) {
goto rewait;
}
}
} __finally {
RtlDeactivateActivationContextUnsafeFast(&Frame);
}
return (DWORD)Status;
}
DWORD
WaitForMultipleObjects(
DWORD nCount,
CONST HANDLE *lpHandles,
BOOL bWaitAll,
DWORD dwMilliseconds
)
/*++
Routine Description:
A wait operation on multiple waitable objects (up to
MAXIMUM_WAIT_OBJECTS) is accomplished with the WaitForMultipleObjects
function.
Arguments:
nCount - A count of the number of objects that are to be waited on.
lpHandles - An array of object handles. Each handle must have
SYNCHRONIZE access to the associated object.
bWaitAll - A flag that supplies the wait type. A value of TRUE
indicates a "wait all". A value of false indicates a "wait
any".
dwMilliseconds - A time-out value that specifies the relative time,
in milliseconds, over which the wait is to be completed. A
timeout value of 0 specified that the wait is to timeout
immediately. This allows an application to test an object to
determine if it is in the signaled state. A timeout value of -1
specifies an infinite timeout period.
Return Value:
WAIT_TIME_OUT - indicates that the wait was terminated due to the
TimeOut conditions.
0 to MAXIMUM_WAIT_OBJECTS-1, indicates, in the case of wait for any
object, the object number which satisfied the wait. In the case
of wait for all objects, the value only indicates that the wait
was completed successfully.
WAIT_ABANDONED_0 to (WAIT_ABANDONED_0)+(MAXIMUM_WAIT_OBJECTS - 1),
indicates, in the case of wait for any object, the object number
which satisfied the event, and that the object which satisfied
the event was abandoned. In the case of wait for all objects,
the value indicates that the wait was completed successfully and
at least one of the objects was abandoned.
--*/
{
return WaitForMultipleObjectsEx(nCount,lpHandles,bWaitAll,dwMilliseconds,FALSE);
}
DWORD
APIENTRY
WaitForMultipleObjectsEx(
DWORD nCount,
CONST HANDLE *lpHandles,
BOOL bWaitAll,
DWORD dwMilliseconds,
BOOL bAlertable
)
/*++
Routine Description:
A wait operation on multiple waitable objects (up to
MAXIMUM_WAIT_OBJECTS) is accomplished with the
WaitForMultipleObjects function.
This API can be used to wait on any of the specified objects to
enter the signaled state, or all of the objects to enter the
signaled state.
If the bAlertable parameter is FALSE, the only way the wait
terminates is because the specified timeout period expires, or
because the specified objects entered the signaled state. If the
bAlertable parameter is TRUE, then the wait can return due to any one of
the above wait termination conditions, or because an I/O completion
callback terminated the wait early (return value of
WAIT_IO_COMPLETION).
Arguments:
nCount - A count of the number of objects that are to be waited on.
lpHandles - An array of object handles. Each handle must have
SYNCHRONIZE access to the associated object.
bWaitAll - A flag that supplies the wait type. A value of TRUE
indicates a "wait all". A value of false indicates a "wait
any".
dwMilliseconds - A time-out value that specifies the relative time,
in milliseconds, over which the wait is to be completed. A
timeout value of 0 specified that the wait is to timeout
immediately. This allows an application to test an object to
determine if it is in the signaled state. A timeout value of
0xffffffff specifies an infinite timeout period.
bAlertable - Supplies a flag that controls whether or not the
wait may terminate early due to an I/O completion callback.
A value of TRUE allows this API to complete early due to an I/O
completion callback. A value of FALSE will not allow I/O
completion callbacks to terminate this call early.
Return Value:
WAIT_TIME_OUT - indicates that the wait was terminated due to the
TimeOut conditions.
0 to MAXIMUM_WAIT_OBJECTS-1, indicates, in the case of wait for any
object, the object number which satisfied the wait. In the case
of wait for all objects, the value only indicates that the wait
was completed successfully.
0xffffffff - The wait terminated due to an error. GetLastError may be
used to get additional error information.
WAIT_ABANDONED_0 to (WAIT_ABANDONED_0)+(MAXIMUM_WAIT_OBJECTS - 1),
indicates, in the case of wait for any object, the object number
which satisfied the event, and that the object which satisfied
the event was abandoned. In the case of wait for all objects,
the value indicates that the wait was completed successfully and
at least one of the objects was abandoned.
WAIT_IO_COMPLETION - The wait terminated due to one or more I/O
completion callbacks.
--*/
{
NTSTATUS Status;
LARGE_INTEGER TimeOut;
PLARGE_INTEGER pTimeOut;
DWORD i;
LPHANDLE HandleArray;
HANDLE Handles[ 8 ];
PPEB Peb;
RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME Frame = { sizeof(Frame), RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER };
RtlActivateActivationContextUnsafeFast(&Frame, NULL); // make the process default activation context active so that APCs are delivered under it
__try {
if (nCount > 8) {
HandleArray = (LPHANDLE) RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), nCount*sizeof(HANDLE));
if (HandleArray == NULL) {
BaseSetLastNTError(STATUS_NO_MEMORY);
return 0xffffffff;
}
} else {
HandleArray = Handles;
}
RtlCopyMemory(HandleArray,(LPVOID)lpHandles,nCount*sizeof(HANDLE));
Peb = NtCurrentPeb();
for (i=0;i<nCount;i++) {
switch( HandleToUlong(HandleArray[i]) ) {
case STD_INPUT_HANDLE: HandleArray[i] = Peb->ProcessParameters->StandardInput;
break;
case STD_OUTPUT_HANDLE: HandleArray[i] = Peb->ProcessParameters->StandardOutput;
break;
case STD_ERROR_HANDLE: HandleArray[i] = Peb->ProcessParameters->StandardError;
break;
}
if (CONSOLE_HANDLE(HandleArray[i]) && VerifyConsoleIoHandle(HandleArray[i])) {
HandleArray[i] = GetConsoleInputWaitHandle();
}
}
pTimeOut = BaseFormatTimeOut(&TimeOut,dwMilliseconds);
rewait:
Status = NtWaitForMultipleObjects(
(CHAR)nCount,
HandleArray,
bWaitAll ? WaitAll : WaitAny,
(BOOLEAN)bAlertable,
pTimeOut
);
if ( !NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status);
Status = (NTSTATUS)0xffffffff;
}
else {
if ( bAlertable && Status == STATUS_ALERTED ) {
goto rewait;
}
}
if (HandleArray != Handles) {
RtlFreeHeap(RtlProcessHeap(), 0, HandleArray);
}
} __finally {
RtlDeactivateActivationContextUnsafeFast(&Frame);
}
return (DWORD)Status;
}
VOID
Sleep(
DWORD dwMilliseconds
)
/*++
Routine Description:
The execution of the current thread can be delayed for a specified
interval of time with the Sleep function.
The Sleep function causes the current thread to enter a
waiting state until the specified interval of time has passed.
Arguments:
dwMilliseconds - A time-out value that specifies the relative time,
in milliseconds, over which the wait is to be completed. A
timeout value of 0 specified that the wait is to timeout
immediately. This allows an application to test an object to
determine if it is in the signaled state. A timeout value of -1
specifies an infinite timeout period.
Return Value:
None.
--*/
{
SleepEx(dwMilliseconds,FALSE);
}
DWORD
APIENTRY
SleepEx(
DWORD dwMilliseconds,
BOOL bAlertable
)
/*++
Routine Description:
The execution of the current thread can be delayed for a specified
interval of time with the SleepEx function.
The SleepEx function causes the current thread to enter a waiting
state until the specified interval of time has passed.
If the bAlertable parameter is FALSE, the only way the SleepEx
returns is when the specified time interval has passed. If the
bAlertable parameter is TRUE, then the SleepEx can return due to the
expiration of the time interval (return value of 0), or because an
I/O completion callback terminated the SleepEx early (return value
of WAIT_IO_COMPLETION).
Arguments:
dwMilliseconds - A time-out value that specifies the relative time,
in milliseconds, over which the wait is to be completed. A
timeout value of 0 specified that the wait is to timeout
immediately. A timeout value of -1 specifies an infinite
timeout period.
bAlertable - Supplies a flag that controls whether or not the
SleepEx may terminate early due to an I/O completion callback.
A value of TRUE allows this API to complete early due to an I/O
completion callback. A value of FALSE will not allow I/O
completion callbacks to terminate this call early.
Return Value:
0 - The SleepEx terminated due to expiration of the time interval.
WAIT_IO_COMPLETION - The SleepEx terminated due to one or more I/O
completion callbacks.
--*/
{
LARGE_INTEGER TimeOut;
PLARGE_INTEGER pTimeOut;
NTSTATUS Status;
RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME Frame = { sizeof(Frame), RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER };
RtlActivateActivationContextUnsafeFast(&Frame, NULL); // make the process default activation context active so that APCs are delivered under it
__try {
pTimeOut = BaseFormatTimeOut(&TimeOut,dwMilliseconds);
if (pTimeOut == NULL) {
//
// If Sleep( -1 ) then delay for the longest possible integer
// relative to now.
//
TimeOut.LowPart = 0x0;
TimeOut.HighPart = 0x80000000;
pTimeOut = &TimeOut;
}
rewait:
Status = NtDelayExecution(
(BOOLEAN)bAlertable,
pTimeOut
);
if ( bAlertable && Status == STATUS_ALERTED ) {
goto rewait;
}
} __finally {
RtlDeactivateActivationContextUnsafeFast(&Frame);
}
return Status == STATUS_USER_APC ? WAIT_IO_COMPLETION : 0;
}
HANDLE
WINAPI
CreateWaitableTimerA(
LPSECURITY_ATTRIBUTES lpTimerAttributes,
BOOL bManualReset,
LPCSTR lpTimerName
)
/*++
Routine Description:
ANSI thunk to CreateWaitableTimerW
--*/
{
PUNICODE_STRING Unicode;
ANSI_STRING AnsiString;
NTSTATUS Status;
LPCWSTR NameBuffer;
NameBuffer = NULL;
if ( ARGUMENT_PRESENT(lpTimerName) ) {
Unicode = &NtCurrentTeb()->StaticUnicodeString;
RtlInitAnsiString(&AnsiString,lpTimerName);
Status = RtlAnsiStringToUnicodeString(Unicode,&AnsiString,FALSE);
if ( !NT_SUCCESS(Status) ) {
if ( Status == STATUS_BUFFER_OVERFLOW ) {
SetLastError(ERROR_FILENAME_EXCED_RANGE);
}
else {
BaseSetLastNTError(Status);
}
return NULL;
}
NameBuffer = (LPCWSTR)Unicode->Buffer;
}
return CreateWaitableTimerW(
lpTimerAttributes,
bManualReset,
NameBuffer
);
}
HANDLE
WINAPI
CreateWaitableTimerW(
LPSECURITY_ATTRIBUTES lpTimerAttributes,
BOOL bManualReset,
LPCWSTR lpTimerName
)
{
NTSTATUS Status;
OBJECT_ATTRIBUTES Obja;
POBJECT_ATTRIBUTES pObja;
HANDLE Handle;
UNICODE_STRING ObjectName;
if ( ARGUMENT_PRESENT(lpTimerName) ) {
RtlInitUnicodeString(&ObjectName,lpTimerName);
pObja = BaseFormatObjectAttributes(&Obja,lpTimerAttributes,&ObjectName);
}
else {
pObja = BaseFormatObjectAttributes(&Obja,lpTimerAttributes,NULL);
}
Status = NtCreateTimer(
&Handle,
TIMER_ALL_ACCESS,
pObja,
bManualReset ? NotificationTimer : SynchronizationTimer
);
if ( NT_SUCCESS(Status) ) {
if ( Status == STATUS_OBJECT_NAME_EXISTS ) {
SetLastError(ERROR_ALREADY_EXISTS);
}
else {
SetLastError(0);
}
return Handle;
}
else {
BaseSetLastNTError(Status);
return NULL;
}
}
HANDLE
WINAPI
OpenWaitableTimerA(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCSTR lpTimerName
)
{
PUNICODE_STRING Unicode;
ANSI_STRING AnsiString;
NTSTATUS Status;
if ( ARGUMENT_PRESENT(lpTimerName) ) {
Unicode = &NtCurrentTeb()->StaticUnicodeString;
RtlInitAnsiString(&AnsiString,lpTimerName);
Status = RtlAnsiStringToUnicodeString(Unicode,&AnsiString,FALSE);
if ( !NT_SUCCESS(Status) ) {
if ( Status == STATUS_BUFFER_OVERFLOW ) {
SetLastError(ERROR_FILENAME_EXCED_RANGE);
}
else {
BaseSetLastNTError(Status);
}
return NULL;
}
}
else {
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
return NULL;
}
return OpenWaitableTimerW(
dwDesiredAccess,
bInheritHandle,
(LPCWSTR)Unicode->Buffer
);
}
HANDLE
WINAPI
OpenWaitableTimerW(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCWSTR lpTimerName
)
{
OBJECT_ATTRIBUTES Obja;
UNICODE_STRING ObjectName;
NTSTATUS Status;
HANDLE Object;
if ( !lpTimerName ) {
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
return NULL;
}
RtlInitUnicodeString(&ObjectName,lpTimerName);
InitializeObjectAttributes(
&Obja,
&ObjectName,
(bInheritHandle ? OBJ_INHERIT : 0),
BaseGetNamedObjectDirectory(),
NULL
);
Status = NtOpenTimer(
&Object,
dwDesiredAccess,
&Obja
);
if ( !NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status);
return NULL;
}
return Object;
}
static
VOID
CALLBACK
BasepTimerAPCProc(
PVOID pvContext,
ULONG TimerLowValue,
LONG TimerHighValue
)
{
PBASE_ACTIVATION_CONTEXT_ACTIVATION_BLOCK ActivationBlock = (PBASE_ACTIVATION_CONTEXT_ACTIVATION_BLOCK) pvContext;
const PVOID CallbackContext = ActivationBlock->CallbackContext;
const PTIMERAPCROUTINE TimerAPCRoutine = (PTIMERAPCROUTINE) ActivationBlock->CallbackFunction;
RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME ActivationFrame = { sizeof(ActivationFrame), RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER };
const PACTIVATION_CONTEXT ActivationContext = ActivationBlock->ActivationContext;
if ((ActivationBlock->Flags & BASE_ACTIVATION_CONTEXT_ACTIVATION_BLOCK_FLAG_DO_NOT_FREE_AFTER_CALLBACK) == 0) {
BasepFreeActivationContextActivationBlock(ActivationBlock);
}
RtlActivateActivationContextUnsafeFast(&ActivationFrame, ActivationContext);
__try {
(*TimerAPCRoutine)(CallbackContext, TimerLowValue, TimerHighValue);
} __finally {
RtlDeactivateActivationContextUnsafeFast(&ActivationFrame);
}
}
BOOL
WINAPI
SetWaitableTimer(
HANDLE hTimer,
const LARGE_INTEGER *lpDueTime,
LONG lPeriod,
PTIMERAPCROUTINE pfnCompletionRoutine,
LPVOID lpArgToCompletionRoutine,
BOOL fResume
)
{
NTSTATUS Status;
PBASE_ACTIVATION_CONTEXT_ACTIVATION_BLOCK ActivationBlock = NULL;
PTIMER_APC_ROUTINE TimerApcRoutine = (PTIMER_APC_ROUTINE) pfnCompletionRoutine;
PVOID TimerApcContext = lpArgToCompletionRoutine;
// If there's an APC routine to call and we have a non-default activation
// context active for this thread, we need to allocate a little chunk of heap
// to pass to the APC callback.
if (pfnCompletionRoutine != NULL) {
DWORD dwActivationBlockAllocationFlags = BASEP_ALLOCATE_ACTIVATION_CONTEXT_ACTIVATION_BLOCK_FLAG_DO_NOT_ALLOCATE_IF_PROCESS_DEFAULT;
// If it's a periodic timer, don't free the block until the timer is cancelled.
if (lPeriod > 0)
dwActivationBlockAllocationFlags |= BASEP_ALLOCATE_ACTIVATION_CONTEXT_ACTIVATION_BLOCK_FLAG_DO_NOT_FREE_AFTER_CALLBACK;
Status = BasepAllocateActivationContextActivationBlock(dwActivationBlockAllocationFlags, pfnCompletionRoutine, lpArgToCompletionRoutine, &ActivationBlock);
if (!NT_SUCCESS(Status)) {
BaseSetLastNTError(Status);
return FALSE;
}
if (ActivationBlock != NULL) {
TimerApcRoutine = &BasepTimerAPCProc;
TimerApcContext = ActivationBlock;
}
}
Status = NtSetTimer(
hTimer,
(PLARGE_INTEGER) lpDueTime,
TimerApcRoutine, // will be NULL if pfnCompletionRoutine was null
TimerApcContext,
(BOOLEAN) fResume,
lPeriod,
NULL
);
if ( !NT_SUCCESS(Status) ) {
if (ActivationBlock != NULL)
BasepFreeActivationContextActivationBlock(ActivationBlock);
BaseSetLastNTError(Status);
return FALSE;
} else {
if ( Status == STATUS_TIMER_RESUME_IGNORED ) {
SetLastError(ERROR_NOT_SUPPORTED);
} else {
SetLastError(ERROR_SUCCESS);
}
return TRUE;
}
}
BOOL
WINAPI
CancelWaitableTimer(
HANDLE hTimer
)
{
NTSTATUS Status;
Status = NtCancelTimer(hTimer, NULL);
if ( !NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status);
return FALSE;
}
else {
return TRUE;
}
}