2011-07-20 15:54:21 +00:00
|
|
|
/*
|
|
|
|
* PROJECT: ReactOS Win32 Base API
|
|
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
|
|
* FILE: dll/win32/kernel32/synch/wait.c
|
|
|
|
* PURPOSE: Wrappers for the NT Wait Implementation
|
|
|
|
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include <k32.h>
|
|
|
|
|
|
|
|
#define NDEBUG
|
|
|
|
#include <debug.h>
|
|
|
|
|
2011-11-07 07:41:52 +00:00
|
|
|
#undef InterlockedIncrement
|
|
|
|
#undef InterlockedDecrement
|
|
|
|
#undef InterlockedExchange
|
|
|
|
#undef InterlockedExchangeAdd
|
|
|
|
#undef InterlockedCompareExchange
|
|
|
|
|
2011-07-20 15:54:21 +00:00
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
|
2011-11-07 07:41:52 +00:00
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
LONG
|
|
|
|
WINAPI
|
|
|
|
InterlockedIncrement(IN OUT LONG volatile *lpAddend)
|
|
|
|
{
|
|
|
|
return _InterlockedIncrement(lpAddend);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
LONG
|
|
|
|
WINAPI
|
|
|
|
InterlockedDecrement(IN OUT LONG volatile *lpAddend)
|
|
|
|
{
|
|
|
|
return _InterlockedDecrement(lpAddend);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
LONG
|
|
|
|
WINAPI
|
|
|
|
InterlockedExchange(IN OUT LONG volatile *Target,
|
|
|
|
IN LONG Value)
|
|
|
|
{
|
|
|
|
return _InterlockedExchange(Target, Value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
LONG
|
|
|
|
WINAPI
|
|
|
|
InterlockedExchangeAdd(IN OUT LONG volatile *Addend,
|
|
|
|
IN LONG Value)
|
|
|
|
{
|
|
|
|
return _InterlockedExchangeAdd(Addend, Value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
LONG
|
|
|
|
WINAPI
|
|
|
|
InterlockedCompareExchange(IN OUT LONG volatile *Destination,
|
|
|
|
IN LONG Exchange,
|
|
|
|
IN LONG Comperand)
|
|
|
|
{
|
|
|
|
return _InterlockedCompareExchange(Destination, Exchange, Comperand);
|
|
|
|
}
|
|
|
|
|
2011-07-20 15:54:21 +00:00
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
DWORD
|
|
|
|
WINAPI
|
|
|
|
WaitForSingleObject(IN HANDLE hHandle,
|
|
|
|
IN DWORD dwMilliseconds)
|
|
|
|
{
|
|
|
|
/* Call the extended API */
|
|
|
|
return WaitForSingleObjectEx(hHandle, dwMilliseconds, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
DWORD
|
|
|
|
WINAPI
|
|
|
|
WaitForSingleObjectEx(IN HANDLE hHandle,
|
|
|
|
IN DWORD dwMilliseconds,
|
|
|
|
IN BOOL bAlertable)
|
|
|
|
{
|
|
|
|
PLARGE_INTEGER TimePtr;
|
|
|
|
LARGE_INTEGER Time;
|
|
|
|
NTSTATUS Status;
|
2011-07-23 11:17:36 +00:00
|
|
|
RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME ActCtx;
|
2011-07-23 11:28:35 +00:00
|
|
|
|
2011-07-23 11:17:36 +00:00
|
|
|
/* APCs must execute with the default activation context */
|
|
|
|
if (bAlertable)
|
|
|
|
{
|
|
|
|
/* Setup the frame */
|
|
|
|
RtlZeroMemory(&ActCtx, sizeof(ActCtx));
|
|
|
|
ActCtx.Size = sizeof(ActCtx);
|
|
|
|
ActCtx.Format = RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER;
|
|
|
|
RtlActivateActivationContextUnsafeFast(&ActCtx, NULL);
|
|
|
|
}
|
2011-07-20 15:54:21 +00:00
|
|
|
|
|
|
|
/* Get real handle */
|
|
|
|
hHandle = TranslateStdHandle(hHandle);
|
|
|
|
|
|
|
|
/* Check for console handle */
|
|
|
|
if ((IsConsoleHandle(hHandle)) && (VerifyConsoleIoHandle(hHandle)))
|
|
|
|
{
|
|
|
|
/* Get the real wait handle */
|
|
|
|
hHandle = GetConsoleInputWaitHandle();
|
|
|
|
}
|
|
|
|
|
2011-07-23 11:05:00 +00:00
|
|
|
/* Convert the timeout */
|
|
|
|
TimePtr = BaseFormatTimeOut(&Time, dwMilliseconds);
|
2011-07-20 15:54:21 +00:00
|
|
|
|
|
|
|
/* Start wait loop */
|
|
|
|
do
|
|
|
|
{
|
|
|
|
/* Do the wait */
|
|
|
|
Status = NtWaitForSingleObject(hHandle, (BOOLEAN)bAlertable, TimePtr);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
/* The wait failed */
|
2011-07-23 18:54:29 +00:00
|
|
|
BaseSetLastNTError(Status);
|
2011-07-23 11:17:36 +00:00
|
|
|
Status = WAIT_FAILED;
|
2011-07-20 15:54:21 +00:00
|
|
|
}
|
|
|
|
} while ((Status == STATUS_ALERTED) && (bAlertable));
|
2011-07-23 11:28:35 +00:00
|
|
|
|
2011-07-23 11:17:36 +00:00
|
|
|
/* Cleanup the activation context */
|
|
|
|
if (bAlertable) RtlDeactivateActivationContextUnsafeFast(&ActCtx);
|
2011-07-20 15:54:21 +00:00
|
|
|
|
|
|
|
/* Return wait status */
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
DWORD
|
|
|
|
WINAPI
|
|
|
|
WaitForMultipleObjects(IN DWORD nCount,
|
|
|
|
IN CONST HANDLE *lpHandles,
|
|
|
|
IN BOOL bWaitAll,
|
|
|
|
IN DWORD dwMilliseconds)
|
|
|
|
{
|
|
|
|
/* Call the extended API */
|
|
|
|
return WaitForMultipleObjectsEx(nCount,
|
|
|
|
lpHandles,
|
|
|
|
bWaitAll,
|
|
|
|
dwMilliseconds,
|
|
|
|
FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
DWORD
|
|
|
|
WINAPI
|
|
|
|
WaitForMultipleObjectsEx(IN DWORD nCount,
|
|
|
|
IN CONST HANDLE *lpHandles,
|
|
|
|
IN BOOL bWaitAll,
|
|
|
|
IN DWORD dwMilliseconds,
|
|
|
|
IN BOOL bAlertable)
|
|
|
|
{
|
|
|
|
PLARGE_INTEGER TimePtr;
|
|
|
|
LARGE_INTEGER Time;
|
|
|
|
PHANDLE HandleBuffer;
|
|
|
|
HANDLE Handle[8];
|
|
|
|
DWORD i;
|
|
|
|
NTSTATUS Status;
|
2011-07-23 11:17:36 +00:00
|
|
|
RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME ActCtx;
|
2011-07-23 11:28:35 +00:00
|
|
|
|
2011-07-23 11:17:36 +00:00
|
|
|
/* APCs must execute with the default activation context */
|
|
|
|
if (bAlertable)
|
|
|
|
{
|
|
|
|
/* Setup the frame */
|
|
|
|
RtlZeroMemory(&ActCtx, sizeof(ActCtx));
|
|
|
|
ActCtx.Size = sizeof(ActCtx);
|
|
|
|
ActCtx.Format = RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER;
|
|
|
|
RtlActivateActivationContextUnsafeFast(&ActCtx, NULL);
|
|
|
|
}
|
2011-07-20 15:54:21 +00:00
|
|
|
|
|
|
|
/* Check if we have more handles then we locally optimize */
|
|
|
|
if (nCount > 8)
|
|
|
|
{
|
|
|
|
/* Allocate a buffer for them */
|
|
|
|
HandleBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
|
|
|
|
0,
|
|
|
|
nCount * sizeof(HANDLE));
|
|
|
|
if (!HandleBuffer)
|
|
|
|
{
|
|
|
|
/* No buffer, fail the wait */
|
|
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
2011-07-23 11:17:36 +00:00
|
|
|
if (bAlertable) RtlDeactivateActivationContextUnsafeFast(&ActCtx);
|
2011-07-20 15:54:21 +00:00
|
|
|
return WAIT_FAILED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Otherwise, use our local buffer */
|
|
|
|
HandleBuffer = Handle;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy the handles into our buffer and loop them all */
|
|
|
|
RtlCopyMemory(HandleBuffer, (LPVOID)lpHandles, nCount * sizeof(HANDLE));
|
|
|
|
for (i = 0; i < nCount; i++)
|
|
|
|
{
|
|
|
|
/* Check what kind of handle this is */
|
|
|
|
HandleBuffer[i] = TranslateStdHandle(HandleBuffer[i]);
|
|
|
|
|
|
|
|
/* Check for console handle */
|
|
|
|
if ((IsConsoleHandle(HandleBuffer[i])) &&
|
|
|
|
(VerifyConsoleIoHandle(HandleBuffer[i])))
|
|
|
|
{
|
|
|
|
/* Get the real wait handle */
|
|
|
|
HandleBuffer[i] = GetConsoleInputWaitHandle();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-23 11:05:00 +00:00
|
|
|
/* Convert the timeout */
|
|
|
|
TimePtr = BaseFormatTimeOut(&Time, dwMilliseconds);
|
2011-07-20 15:54:21 +00:00
|
|
|
|
|
|
|
/* Start wait loop */
|
|
|
|
do
|
|
|
|
{
|
|
|
|
/* Do the wait */
|
|
|
|
Status = NtWaitForMultipleObjects(nCount,
|
|
|
|
HandleBuffer,
|
|
|
|
bWaitAll ? WaitAll : WaitAny,
|
|
|
|
(BOOLEAN)bAlertable,
|
|
|
|
TimePtr);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
/* Wait failed */
|
2011-07-23 18:54:29 +00:00
|
|
|
BaseSetLastNTError(Status);
|
2011-07-23 11:17:36 +00:00
|
|
|
Status = WAIT_FAILED;
|
2011-07-20 15:54:21 +00:00
|
|
|
}
|
|
|
|
} while ((Status == STATUS_ALERTED) && (bAlertable));
|
|
|
|
|
|
|
|
/* Check if we didn't use our local buffer */
|
|
|
|
if (HandleBuffer != Handle)
|
|
|
|
{
|
|
|
|
/* Free the allocated one */
|
|
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, HandleBuffer);
|
|
|
|
}
|
|
|
|
|
2011-07-23 11:17:36 +00:00
|
|
|
/* Cleanup the activation context */
|
|
|
|
if (bAlertable) RtlDeactivateActivationContextUnsafeFast(&ActCtx);
|
2011-07-23 11:28:35 +00:00
|
|
|
|
2011-07-20 15:54:21 +00:00
|
|
|
/* Return wait status */
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
DWORD
|
|
|
|
WINAPI
|
|
|
|
SignalObjectAndWait(IN HANDLE hObjectToSignal,
|
|
|
|
IN HANDLE hObjectToWaitOn,
|
|
|
|
IN DWORD dwMilliseconds,
|
|
|
|
IN BOOL bAlertable)
|
|
|
|
{
|
|
|
|
PLARGE_INTEGER TimePtr;
|
|
|
|
LARGE_INTEGER Time;
|
|
|
|
NTSTATUS Status;
|
2011-07-23 11:17:36 +00:00
|
|
|
RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME ActCtx;
|
2011-07-23 11:28:35 +00:00
|
|
|
|
2011-07-23 11:17:36 +00:00
|
|
|
/* APCs must execute with the default activation context */
|
|
|
|
if (bAlertable)
|
|
|
|
{
|
|
|
|
/* Setup the frame */
|
|
|
|
RtlZeroMemory(&ActCtx, sizeof(ActCtx));
|
|
|
|
ActCtx.Size = sizeof(ActCtx);
|
|
|
|
ActCtx.Format = RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER;
|
|
|
|
RtlActivateActivationContextUnsafeFast(&ActCtx, NULL);
|
|
|
|
}
|
2011-07-23 11:28:35 +00:00
|
|
|
|
2011-07-20 15:54:21 +00:00
|
|
|
/* Get real handle */
|
|
|
|
hObjectToWaitOn = TranslateStdHandle(hObjectToWaitOn);
|
|
|
|
|
|
|
|
/* Check for console handle */
|
|
|
|
if ((IsConsoleHandle(hObjectToWaitOn)) &&
|
|
|
|
(VerifyConsoleIoHandle(hObjectToWaitOn)))
|
|
|
|
{
|
|
|
|
/* Get the real wait handle */
|
|
|
|
hObjectToWaitOn = GetConsoleInputWaitHandle();
|
|
|
|
}
|
|
|
|
|
2011-07-23 11:05:00 +00:00
|
|
|
/* Convert the timeout */
|
|
|
|
TimePtr = BaseFormatTimeOut(&Time, dwMilliseconds);
|
2011-07-20 15:54:21 +00:00
|
|
|
|
|
|
|
/* Start wait loop */
|
|
|
|
do
|
|
|
|
{
|
|
|
|
/* Do the wait */
|
|
|
|
Status = NtSignalAndWaitForSingleObject(hObjectToSignal,
|
|
|
|
hObjectToWaitOn,
|
|
|
|
(BOOLEAN)bAlertable,
|
|
|
|
TimePtr);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
/* The wait failed */
|
2011-07-23 18:54:29 +00:00
|
|
|
BaseSetLastNTError(Status);
|
2011-07-23 11:17:36 +00:00
|
|
|
Status = WAIT_FAILED;
|
2011-07-20 15:54:21 +00:00
|
|
|
}
|
|
|
|
} while ((Status == STATUS_ALERTED) && (bAlertable));
|
|
|
|
|
2011-07-23 11:17:36 +00:00
|
|
|
/* Cleanup the activation context */
|
|
|
|
if (bAlertable) RtlDeactivateActivationContextUnsafeFast(&ActCtx);
|
2011-07-23 11:28:35 +00:00
|
|
|
|
2011-07-20 15:54:21 +00:00
|
|
|
/* Return wait status */
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
HANDLE
|
|
|
|
WINAPI
|
|
|
|
CreateWaitableTimerW(IN LPSECURITY_ATTRIBUTES lpTimerAttributes OPTIONAL,
|
|
|
|
IN BOOL bManualReset,
|
|
|
|
IN LPCWSTR lpTimerName OPTIONAL)
|
|
|
|
{
|
2011-07-23 10:08:57 +00:00
|
|
|
CreateNtObjectFromWin32Api(WaitableTimer, Timer, TIMER,
|
|
|
|
lpTimerAttributes,
|
|
|
|
lpTimerName,
|
|
|
|
bManualReset ? NotificationTimer : SynchronizationTimer);
|
2011-07-20 15:54:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
HANDLE
|
|
|
|
WINAPI
|
|
|
|
CreateWaitableTimerA(IN LPSECURITY_ATTRIBUTES lpTimerAttributes OPTIONAL,
|
|
|
|
IN BOOL bManualReset,
|
|
|
|
IN LPCSTR lpTimerName OPTIONAL)
|
|
|
|
{
|
2011-07-23 10:08:57 +00:00
|
|
|
ConvertWin32AnsiObjectApiToUnicodeApi(WaitableTimer, lpTimerName, lpTimerAttributes, bManualReset);
|
2011-07-20 15:54:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
HANDLE
|
|
|
|
WINAPI
|
|
|
|
OpenWaitableTimerW(IN DWORD dwDesiredAccess,
|
|
|
|
IN BOOL bInheritHandle,
|
|
|
|
IN LPCWSTR lpTimerName)
|
|
|
|
{
|
2011-07-23 10:08:57 +00:00
|
|
|
OpenNtObjectFromWin32Api(Timer, dwDesiredAccess, bInheritHandle, lpTimerName);
|
2011-07-20 15:54:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
HANDLE
|
|
|
|
WINAPI
|
|
|
|
OpenWaitableTimerA(IN DWORD dwDesiredAccess,
|
|
|
|
IN BOOL bInheritHandle,
|
|
|
|
IN LPCSTR lpTimerName)
|
|
|
|
{
|
2011-07-23 10:08:57 +00:00
|
|
|
ConvertOpenWin32AnsiObjectApiToUnicodeApi(WaitableTimer, dwDesiredAccess, bInheritHandle, lpTimerName);
|
2011-07-20 15:54:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
BOOL
|
|
|
|
WINAPI
|
|
|
|
SetWaitableTimer(IN HANDLE hTimer,
|
|
|
|
IN const LARGE_INTEGER *pDueTime,
|
|
|
|
IN LONG lPeriod,
|
|
|
|
IN PTIMERAPCROUTINE pfnCompletionRoutine OPTIONAL,
|
|
|
|
IN OPTIONAL LPVOID lpArgToCompletionRoutine,
|
|
|
|
IN BOOL fResume)
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
|
|
|
|
|
|
/* Set the timer */
|
|
|
|
Status = NtSetTimer(hTimer,
|
|
|
|
(PLARGE_INTEGER)pDueTime,
|
|
|
|
(PTIMER_APC_ROUTINE)pfnCompletionRoutine,
|
|
|
|
lpArgToCompletionRoutine,
|
|
|
|
(BOOLEAN)fResume,
|
|
|
|
lPeriod,
|
|
|
|
NULL);
|
|
|
|
if (NT_SUCCESS(Status)) return TRUE;
|
|
|
|
|
|
|
|
/* If we got here, then we failed */
|
2011-07-23 18:54:29 +00:00
|
|
|
BaseSetLastNTError(Status);
|
2011-07-20 15:54:21 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
BOOL
|
|
|
|
WINAPI
|
|
|
|
CancelWaitableTimer(IN HANDLE hTimer)
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
|
|
|
|
|
|
/* Cancel the timer */
|
|
|
|
Status = NtCancelTimer(hTimer, NULL);
|
|
|
|
if (NT_SUCCESS(Status)) return TRUE;
|
|
|
|
|
|
|
|
/* If we got here, then we failed */
|
2011-07-23 18:54:29 +00:00
|
|
|
BaseSetLastNTError(Status);
|
2011-07-20 15:54:21 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
HANDLE
|
|
|
|
WINAPI
|
|
|
|
CreateSemaphoreA(IN LPSECURITY_ATTRIBUTES lpSemaphoreAttributes OPTIONAL,
|
|
|
|
IN LONG lInitialCount,
|
|
|
|
IN LONG lMaximumCount,
|
|
|
|
IN LPCSTR lpName OPTIONAL)
|
|
|
|
{
|
2011-07-23 10:05:02 +00:00
|
|
|
ConvertWin32AnsiObjectApiToUnicodeApi(Semaphore, lpName, lpSemaphoreAttributes, lInitialCount, lMaximumCount);
|
2011-07-20 15:54:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
HANDLE
|
|
|
|
WINAPI
|
|
|
|
CreateSemaphoreW(IN LPSECURITY_ATTRIBUTES lpSemaphoreAttributes OPTIONAL,
|
|
|
|
IN LONG lInitialCount,
|
|
|
|
IN LONG lMaximumCount,
|
|
|
|
IN LPCWSTR lpName OPTIONAL)
|
|
|
|
{
|
2011-07-23 10:05:02 +00:00
|
|
|
CreateNtObjectFromWin32Api(Semaphore, Semaphore, SEMAPHORE,
|
|
|
|
lpSemaphoreAttributes,
|
|
|
|
lpName,
|
|
|
|
lInitialCount,
|
|
|
|
lMaximumCount);
|
2011-07-20 15:54:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
HANDLE
|
|
|
|
WINAPI
|
|
|
|
OpenSemaphoreA(IN DWORD dwDesiredAccess,
|
|
|
|
IN BOOL bInheritHandle,
|
|
|
|
IN LPCSTR lpName)
|
|
|
|
{
|
2011-07-23 10:05:02 +00:00
|
|
|
ConvertOpenWin32AnsiObjectApiToUnicodeApi(Semaphore, dwDesiredAccess, bInheritHandle, lpName);
|
2011-07-20 15:54:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
HANDLE
|
|
|
|
WINAPI
|
|
|
|
OpenSemaphoreW(IN DWORD dwDesiredAccess,
|
|
|
|
IN BOOL bInheritHandle,
|
|
|
|
IN LPCWSTR lpName)
|
|
|
|
{
|
2011-07-23 10:05:02 +00:00
|
|
|
OpenNtObjectFromWin32Api(Semaphore, dwDesiredAccess, bInheritHandle, lpName);
|
2011-07-20 15:54:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
BOOL
|
|
|
|
WINAPI
|
|
|
|
ReleaseSemaphore(IN HANDLE hSemaphore,
|
|
|
|
IN LONG lReleaseCount,
|
|
|
|
IN LPLONG lpPreviousCount)
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
|
|
|
|
|
|
/* Release the semaphore */
|
|
|
|
Status = NtReleaseSemaphore(hSemaphore, lReleaseCount, lpPreviousCount);
|
|
|
|
if (NT_SUCCESS(Status)) return TRUE;
|
|
|
|
|
|
|
|
/* If we got here, then we failed */
|
2011-07-23 18:54:29 +00:00
|
|
|
BaseSetLastNTError(Status);
|
2011-07-20 15:54:21 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
HANDLE
|
|
|
|
WINAPI
|
|
|
|
CreateMutexA(IN LPSECURITY_ATTRIBUTES lpMutexAttributes OPTIONAL,
|
|
|
|
IN BOOL bInitialOwner,
|
|
|
|
IN LPCSTR lpName OPTIONAL)
|
|
|
|
{
|
2011-07-23 10:03:10 +00:00
|
|
|
ConvertWin32AnsiObjectApiToUnicodeApi(Mutex, lpName, lpMutexAttributes, bInitialOwner);
|
2011-07-20 15:54:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
HANDLE
|
|
|
|
WINAPI
|
|
|
|
CreateMutexW(IN LPSECURITY_ATTRIBUTES lpMutexAttributes OPTIONAL,
|
|
|
|
IN BOOL bInitialOwner,
|
|
|
|
IN LPCWSTR lpName OPTIONAL)
|
|
|
|
{
|
2011-07-23 10:03:10 +00:00
|
|
|
CreateNtObjectFromWin32Api(Mutex, Mutant, MUTEX,
|
|
|
|
lpMutexAttributes,
|
|
|
|
lpName,
|
|
|
|
bInitialOwner);
|
2011-07-20 15:54:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
HANDLE
|
|
|
|
WINAPI
|
|
|
|
OpenMutexA(IN DWORD dwDesiredAccess,
|
|
|
|
IN BOOL bInheritHandle,
|
|
|
|
IN LPCSTR lpName)
|
|
|
|
{
|
2011-07-23 10:03:10 +00:00
|
|
|
ConvertOpenWin32AnsiObjectApiToUnicodeApi(Mutex, dwDesiredAccess, bInheritHandle, lpName);
|
2011-07-20 15:54:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
HANDLE
|
|
|
|
WINAPI
|
|
|
|
OpenMutexW(IN DWORD dwDesiredAccess,
|
|
|
|
IN BOOL bInheritHandle,
|
|
|
|
IN LPCWSTR lpName)
|
|
|
|
{
|
2011-07-23 10:03:10 +00:00
|
|
|
OpenNtObjectFromWin32Api(Mutant, dwDesiredAccess, bInheritHandle, lpName);
|
2011-07-20 15:54:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
BOOL
|
|
|
|
WINAPI
|
|
|
|
ReleaseMutex(IN HANDLE hMutex)
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
|
|
|
|
|
|
/* Release the mutant */
|
|
|
|
Status = NtReleaseMutant(hMutex, NULL);
|
|
|
|
if (NT_SUCCESS(Status)) return TRUE;
|
|
|
|
|
|
|
|
/* If we got here, then we failed */
|
2011-07-23 18:54:29 +00:00
|
|
|
BaseSetLastNTError(Status);
|
2011-07-20 15:54:21 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2011-07-23 09:58:33 +00:00
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
2011-07-20 15:54:21 +00:00
|
|
|
HANDLE
|
|
|
|
WINAPI
|
|
|
|
CreateEventA(IN LPSECURITY_ATTRIBUTES lpEventAttributes OPTIONAL,
|
|
|
|
IN BOOL bManualReset,
|
|
|
|
IN BOOL bInitialState,
|
2011-07-23 09:58:33 +00:00
|
|
|
IN LPCSTR lpName OPTIONAL)
|
2011-07-20 15:54:21 +00:00
|
|
|
{
|
2011-07-23 09:58:33 +00:00
|
|
|
ConvertWin32AnsiObjectApiToUnicodeApi(Event, lpName, lpEventAttributes, bManualReset, bInitialState);
|
2011-07-20 15:54:21 +00:00
|
|
|
}
|
|
|
|
|
2011-07-23 09:58:33 +00:00
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
2011-07-20 15:54:21 +00:00
|
|
|
HANDLE
|
|
|
|
WINAPI
|
|
|
|
CreateEventW(IN LPSECURITY_ATTRIBUTES lpEventAttributes OPTIONAL,
|
|
|
|
IN BOOL bManualReset,
|
|
|
|
IN BOOL bInitialState,
|
|
|
|
IN LPCWSTR lpName OPTIONAL)
|
|
|
|
{
|
2011-07-23 09:58:33 +00:00
|
|
|
CreateNtObjectFromWin32Api(Event, Event, EVENT,
|
|
|
|
lpEventAttributes,
|
|
|
|
lpName,
|
2013-07-12 22:27:12 +00:00
|
|
|
bManualReset ? NotificationEvent : SynchronizationEvent,
|
2011-07-23 09:58:33 +00:00
|
|
|
bInitialState);
|
2011-07-20 15:54:21 +00:00
|
|
|
}
|
|
|
|
|
2011-07-23 09:58:33 +00:00
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
2011-07-20 15:54:21 +00:00
|
|
|
HANDLE
|
|
|
|
WINAPI
|
|
|
|
OpenEventA(IN DWORD dwDesiredAccess,
|
|
|
|
IN BOOL bInheritHandle,
|
|
|
|
IN LPCSTR lpName)
|
|
|
|
{
|
2011-07-23 09:58:33 +00:00
|
|
|
ConvertOpenWin32AnsiObjectApiToUnicodeApi(Event, dwDesiredAccess, bInheritHandle, lpName);
|
2011-07-20 15:54:21 +00:00
|
|
|
}
|
|
|
|
|
2011-07-23 09:58:33 +00:00
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
2011-07-20 15:54:21 +00:00
|
|
|
HANDLE
|
|
|
|
WINAPI
|
|
|
|
OpenEventW(IN DWORD dwDesiredAccess,
|
|
|
|
IN BOOL bInheritHandle,
|
|
|
|
IN LPCWSTR lpName)
|
|
|
|
{
|
2011-07-23 09:58:33 +00:00
|
|
|
OpenNtObjectFromWin32Api(Event, dwDesiredAccess, bInheritHandle, lpName);
|
2011-07-20 15:54:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
BOOL
|
|
|
|
WINAPI
|
|
|
|
PulseEvent(IN HANDLE hEvent)
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
|
|
|
|
|
|
/* Pulse the event */
|
|
|
|
Status = NtPulseEvent(hEvent, NULL);
|
|
|
|
if (NT_SUCCESS(Status)) return TRUE;
|
|
|
|
|
|
|
|
/* If we got here, then we failed */
|
2011-07-23 18:54:29 +00:00
|
|
|
BaseSetLastNTError(Status);
|
2011-07-20 15:54:21 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
BOOL
|
|
|
|
WINAPI
|
|
|
|
ResetEvent(IN HANDLE hEvent)
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
|
|
|
|
|
|
/* Clear the event */
|
|
|
|
Status = NtResetEvent(hEvent, NULL);
|
|
|
|
if (NT_SUCCESS(Status)) return TRUE;
|
|
|
|
|
|
|
|
/* If we got here, then we failed */
|
2011-07-23 18:54:29 +00:00
|
|
|
BaseSetLastNTError(Status);
|
2011-07-20 15:54:21 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
BOOL
|
|
|
|
WINAPI
|
|
|
|
SetEvent(IN HANDLE hEvent)
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
|
|
|
|
|
|
/* Set the event */
|
|
|
|
Status = NtSetEvent(hEvent, NULL);
|
|
|
|
if (NT_SUCCESS(Status)) return TRUE;
|
|
|
|
|
|
|
|
/* If we got here, then we failed */
|
2011-07-23 18:54:29 +00:00
|
|
|
BaseSetLastNTError(Status);
|
2011-07-20 15:54:21 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
VOID
|
|
|
|
WINAPI
|
|
|
|
InitializeCriticalSection(OUT LPCRITICAL_SECTION lpCriticalSection)
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
|
|
|
|
|
|
/* Initialize the critical section and raise an exception if we failed */
|
2011-07-23 00:29:00 +00:00
|
|
|
Status = RtlInitializeCriticalSection((PVOID)lpCriticalSection);
|
2011-07-20 15:54:21 +00:00
|
|
|
if (!NT_SUCCESS(Status)) RtlRaiseStatus(Status);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
BOOL
|
|
|
|
WINAPI
|
|
|
|
InitializeCriticalSectionAndSpinCount(OUT LPCRITICAL_SECTION lpCriticalSection,
|
|
|
|
IN DWORD dwSpinCount)
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
|
|
|
|
|
|
/* Initialize the critical section */
|
2011-07-23 00:29:00 +00:00
|
|
|
Status = RtlInitializeCriticalSectionAndSpinCount((PVOID)lpCriticalSection,
|
|
|
|
dwSpinCount);
|
2011-07-20 15:54:21 +00:00
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
|
|
|
/* Set failure code */
|
2011-07-23 18:54:29 +00:00
|
|
|
BaseSetLastNTError(Status);
|
2011-07-20 15:54:21 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Success */
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2011-07-22 02:13:57 +00:00
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
2011-07-23 11:05:00 +00:00
|
|
|
VOID
|
|
|
|
WINAPI
|
|
|
|
Sleep(IN DWORD dwMilliseconds)
|
2011-07-22 02:13:57 +00:00
|
|
|
{
|
2011-07-23 11:05:00 +00:00
|
|
|
/* Call the new API */
|
|
|
|
SleepEx(dwMilliseconds, FALSE);
|
2011-07-22 02:13:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
2011-07-23 11:05:00 +00:00
|
|
|
DWORD
|
|
|
|
WINAPI
|
|
|
|
SleepEx(IN DWORD dwMilliseconds,
|
|
|
|
IN BOOL bAlertable)
|
2011-07-22 02:13:57 +00:00
|
|
|
{
|
2011-07-23 11:05:00 +00:00
|
|
|
LARGE_INTEGER Time;
|
|
|
|
PLARGE_INTEGER TimePtr;
|
|
|
|
NTSTATUS errCode;
|
2011-07-23 11:17:36 +00:00
|
|
|
RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME ActCtx;
|
2011-07-23 11:28:35 +00:00
|
|
|
|
2011-07-23 11:17:36 +00:00
|
|
|
/* APCs must execute with the default activation context */
|
|
|
|
if (bAlertable)
|
|
|
|
{
|
|
|
|
/* Setup the frame */
|
|
|
|
RtlZeroMemory(&ActCtx, sizeof(ActCtx));
|
|
|
|
ActCtx.Size = sizeof(ActCtx);
|
|
|
|
ActCtx.Format = RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER;
|
|
|
|
RtlActivateActivationContextUnsafeFast(&ActCtx, NULL);
|
|
|
|
}
|
2011-07-22 02:13:57 +00:00
|
|
|
|
2011-07-23 11:05:00 +00:00
|
|
|
/* Convert the timeout */
|
|
|
|
TimePtr = BaseFormatTimeOut(&Time, dwMilliseconds);
|
|
|
|
if (!TimePtr)
|
2011-07-22 02:13:57 +00:00
|
|
|
{
|
2011-07-23 11:05:00 +00:00
|
|
|
/* Turn an infinite wait into a really long wait */
|
|
|
|
Time.LowPart = 0;
|
|
|
|
Time.HighPart = 0x80000000;
|
|
|
|
TimePtr = &Time;
|
2011-07-22 02:13:57 +00:00
|
|
|
}
|
2011-07-23 11:05:00 +00:00
|
|
|
|
|
|
|
/* Loop the delay while APCs are alerting us */
|
|
|
|
do
|
2011-07-22 02:13:57 +00:00
|
|
|
{
|
2011-07-23 11:05:00 +00:00
|
|
|
/* Do the delay */
|
|
|
|
errCode = NtDelayExecution((BOOLEAN)bAlertable, TimePtr);
|
2011-07-22 02:13:57 +00:00
|
|
|
}
|
2011-07-23 11:05:00 +00:00
|
|
|
while ((bAlertable) && (errCode == STATUS_ALERTED));
|
2011-07-23 17:17:06 +00:00
|
|
|
|
|
|
|
/* Cleanup the activation context */
|
|
|
|
if (bAlertable) RtlDeactivateActivationContextUnsafeFast(&ActCtx);
|
2011-07-23 11:28:35 +00:00
|
|
|
|
2011-07-23 11:05:00 +00:00
|
|
|
/* Return the correct code */
|
|
|
|
return (errCode == STATUS_USER_APC) ? WAIT_IO_COMPLETION : 0;
|
2011-07-22 02:13:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
BOOL
|
|
|
|
WINAPI
|
2011-07-23 10:16:10 +00:00
|
|
|
RegisterWaitForSingleObject(OUT PHANDLE phNewWaitObject,
|
|
|
|
IN HANDLE hObject,
|
|
|
|
IN WAITORTIMERCALLBACK Callback,
|
|
|
|
IN PVOID Context,
|
|
|
|
IN ULONG dwMilliseconds,
|
|
|
|
IN ULONG dwFlags)
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
|
|
|
|
|
|
/* Get real handle */
|
|
|
|
hObject = TranslateStdHandle(hObject);
|
|
|
|
|
|
|
|
/* Check for console handle */
|
|
|
|
if ((IsConsoleHandle(hObject)) && (VerifyConsoleIoHandle(hObject)))
|
|
|
|
{
|
|
|
|
/* Get the real wait handle */
|
|
|
|
hObject = GetConsoleInputWaitHandle();
|
|
|
|
}
|
2011-07-22 02:13:57 +00:00
|
|
|
|
2011-07-23 10:16:10 +00:00
|
|
|
/* Register the wait now */
|
|
|
|
Status = RtlRegisterWait(phNewWaitObject,
|
|
|
|
hObject,
|
|
|
|
Callback,
|
|
|
|
Context,
|
|
|
|
dwMilliseconds,
|
|
|
|
dwFlags);
|
2011-07-22 02:13:57 +00:00
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
2011-07-23 10:16:10 +00:00
|
|
|
/* Return failure */
|
2011-07-23 18:54:29 +00:00
|
|
|
BaseSetLastNTError(Status);
|
2011-07-22 02:13:57 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
2011-07-23 10:16:10 +00:00
|
|
|
|
|
|
|
/* All good */
|
2011-07-22 02:13:57 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
HANDLE
|
|
|
|
WINAPI
|
2011-07-23 10:16:10 +00:00
|
|
|
RegisterWaitForSingleObjectEx(IN HANDLE hObject,
|
|
|
|
IN WAITORTIMERCALLBACK Callback,
|
|
|
|
IN PVOID Context,
|
|
|
|
IN ULONG dwMilliseconds,
|
|
|
|
IN ULONG dwFlags)
|
2011-07-22 02:13:57 +00:00
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
|
|
HANDLE hNewWaitObject;
|
|
|
|
|
2011-07-23 10:16:10 +00:00
|
|
|
/* Get real handle */
|
|
|
|
hObject = TranslateStdHandle(hObject);
|
|
|
|
|
|
|
|
/* Check for console handle */
|
|
|
|
if ((IsConsoleHandle(hObject)) && (VerifyConsoleIoHandle(hObject)))
|
|
|
|
{
|
|
|
|
/* Get the real wait handle */
|
|
|
|
hObject = GetConsoleInputWaitHandle();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Register the wait */
|
2011-07-22 02:13:57 +00:00
|
|
|
Status = RtlRegisterWait(&hNewWaitObject,
|
|
|
|
hObject,
|
|
|
|
Callback,
|
|
|
|
Context,
|
|
|
|
dwMilliseconds,
|
|
|
|
dwFlags);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
|
|
{
|
2011-07-23 10:16:10 +00:00
|
|
|
/* Return failure */
|
2011-07-23 18:54:29 +00:00
|
|
|
BaseSetLastNTError(Status);
|
2011-07-22 02:13:57 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-07-23 10:16:10 +00:00
|
|
|
/* Return the object */
|
2011-07-22 02:13:57 +00:00
|
|
|
return hNewWaitObject;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
BOOL
|
|
|
|
WINAPI
|
2011-07-23 10:16:10 +00:00
|
|
|
UnregisterWait(IN HANDLE WaitHandle)
|
2011-07-22 02:13:57 +00:00
|
|
|
{
|
2011-07-23 10:16:10 +00:00
|
|
|
NTSTATUS Status;
|
2011-07-22 02:13:57 +00:00
|
|
|
|
2011-07-23 10:16:10 +00:00
|
|
|
/* Check for invalid handle */
|
|
|
|
if (!WaitHandle)
|
2011-07-22 02:13:57 +00:00
|
|
|
{
|
2011-07-23 10:16:10 +00:00
|
|
|
/* Fail */
|
|
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Deregister the wait and check status */
|
|
|
|
Status = RtlDeregisterWaitEx(WaitHandle, NULL);
|
|
|
|
if (!(NT_SUCCESS(Status)) || (Status == STATUS_PENDING))
|
|
|
|
{
|
|
|
|
/* Failure or non-blocking call */
|
2011-07-23 18:54:29 +00:00
|
|
|
BaseSetLastNTError(Status);
|
2011-07-22 02:13:57 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2011-07-23 10:16:10 +00:00
|
|
|
/* All good */
|
2011-07-22 02:13:57 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @implemented
|
|
|
|
*/
|
|
|
|
BOOL
|
|
|
|
WINAPI
|
2011-07-23 10:16:10 +00:00
|
|
|
UnregisterWaitEx(IN HANDLE WaitHandle,
|
|
|
|
IN HANDLE CompletionEvent)
|
2011-07-22 02:13:57 +00:00
|
|
|
{
|
2011-07-23 10:16:10 +00:00
|
|
|
NTSTATUS Status;
|
2011-07-22 02:13:57 +00:00
|
|
|
|
2011-07-23 10:16:10 +00:00
|
|
|
/* Check for invalid handle */
|
|
|
|
if (!WaitHandle)
|
|
|
|
{
|
|
|
|
/* Fail */
|
|
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Deregister the wait and check status */
|
|
|
|
Status = RtlDeregisterWaitEx(WaitHandle, CompletionEvent);
|
|
|
|
if (!(NT_SUCCESS(Status)) ||
|
|
|
|
((CompletionEvent != INVALID_HANDLE_VALUE) && (Status == STATUS_PENDING)))
|
2011-07-22 02:13:57 +00:00
|
|
|
{
|
2011-07-23 10:16:10 +00:00
|
|
|
/* Failure or non-blocking call */
|
2011-07-23 18:54:29 +00:00
|
|
|
BaseSetLastNTError(Status);
|
2011-07-22 02:13:57 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2011-07-23 10:16:10 +00:00
|
|
|
/* All good */
|
2011-07-22 02:13:57 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2011-07-20 15:54:21 +00:00
|
|
|
/* EOF */
|