Author: ion
Date: Tue Feb 21 23:27:45 2012
New Revision: 55801
URL:
http://svn.reactos.org/svn/reactos?rev=55801&view=rev
Log:
[KERNEL32]: Rewrite the File Change Notification APIs since they were pretty much wrong
(not surprising as we don't implement the functionality in the kernel/filesystems).
[KERNEL32]: Enable support for asynchronous I/O callbacks with SxS/Fusion activation
context support. Right now only added to the file Change notification APIs, but ReadFileEx
and WriteFileEx should use them too.
Modified:
trunk/reactos/dll/win32/kernel32/client/file/cnotify.c
trunk/reactos/dll/win32/kernel32/include/kernel32.h
trunk/reactos/dll/win32/kernel32/wine/actctx.c
Modified: trunk/reactos/dll/win32/kernel32/client/file/cnotify.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/kernel32/client/…
==============================================================================
--- trunk/reactos/dll/win32/kernel32/client/file/cnotify.c [iso-8859-1] (original)
+++ trunk/reactos/dll/win32/kernel32/client/file/cnotify.c [iso-8859-1] Tue Feb 21
23:27:45 2012
@@ -9,30 +9,106 @@
* Created 01/11/98
*/
-/* INCLUDES *****************************************************************/
+/* INCLUDES *******************************************************************/
#include <k32.h>
#define NDEBUG
#include <debug.h>
-/* FUNCTIONS ****************************************************************/
-
-/*
- * @implemented
- */
-BOOL WINAPI
-FindCloseChangeNotification (HANDLE hChangeHandle)
-{
- NTSTATUS Status = NtClose(hChangeHandle);
- if(!NT_SUCCESS(Status))
- {
- BaseSetLastNTError(Status);
- return FALSE;
- }
-
- return TRUE;
-}
-
+/* GLOBALS ********************************************************************/
+
+CHAR staticchangebuff[sizeof(FILE_NOTIFY_INFORMATION) + 16];
+IO_STATUS_BLOCK staticIoStatusBlock;
+
+/* PRIVATE FUNCTIONS **********************************************************/
+
+VOID
+WINAPI
+BasepIoCompletion(IN PVOID ApcContext,
+ IN PIO_STATUS_BLOCK IoStatusBlock,
+ IN DWORD Reserved)
+{
+ PBASEP_ACTCTX_BLOCK ActivationBlock = ApcContext;
+ LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine;
+ DWORD BytesTransfered, Result;
+ RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME ActCtx;
+ PVOID ActivationContext = NULL;
+
+ /* Setup the activation frame */
+ RtlZeroMemory(&ActCtx, sizeof(ActCtx));
+ ActCtx.Size = sizeof(ActCtx);
+ ActCtx.Format = RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER;
+
+ /* Check if the routine returned an error */
+ if (NT_ERROR(IoStatusBlock->Status))
+ {
+ /* Convert the error code and don't copy anything */
+ Result = RtlNtStatusToDosError(IoStatusBlock->Status);
+ BytesTransfered = 0;
+ }
+ else
+ {
+ /* Set success code and copy the bytes transferred */
+ Result = ERROR_SUCCESS;
+ BytesTransfered = IoStatusBlock->Information;
+ }
+
+ /* Read context and routine out from the activation block */
+ ActivationContext = ActivationBlock->ActivationContext;
+ CompletionRoutine = ActivationBlock->CompletionRoutine;
+
+ /* Check if the block should be freed */
+ if (!(ActivationBlock->Flags & 1))
+ {
+ /* Free it */
+ BasepFreeActivationContextActivationBlock(ActivationBlock);
+ }
+
+ /* Activate the context, call the routine, and then deactivate the context */
+ RtlActivateActivationContextUnsafeFast(&ActCtx, ActivationContext);
+ CompletionRoutine(Result, BytesTransfered, (LPOVERLAPPED)IoStatusBlock);
+ RtlDeactivateActivationContextUnsafeFast(&ActCtx);
+}
+
+VOID
+WINAPI
+BasepIoCompletionSimple(IN PVOID ApcContext,
+ IN PIO_STATUS_BLOCK IoStatusBlock,
+ IN DWORD Reserved)
+{
+ LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine = ApcContext;
+ DWORD Result, BytesTransfered;
+
+ /* Check if the routine returned an error */
+ if (NT_ERROR(IoStatusBlock->Status))
+ {
+ /* Convert the error code and don't copy anything */
+ Result = RtlNtStatusToDosError(IoStatusBlock->Status);
+ BytesTransfered = 0;
+ }
+ else
+ {
+ /* Set success code and copy the bytes transferred */
+ Result = ERROR_SUCCESS;
+ BytesTransfered = IoStatusBlock->Information;
+ }
+
+ /* Call the callback routine */
+ CompletionRoutine(Result, BytesTransfered, (LPOVERLAPPED)IoStatusBlock);
+}
+
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+FindCloseChangeNotification(IN HANDLE hChangeHandle)
+{
+ /* Just close the handle */
+ return CloseHandle(hChangeHandle);
+}
/*
* @implemented
@@ -50,206 +126,255 @@
dwNotifyFilter);
}
-
/*
* @implemented
*/
HANDLE
WINAPI
-FindFirstChangeNotificationW (
- LPCWSTR lpPathName,
- BOOL bWatchSubtree,
- DWORD dwNotifyFilter
- )
-{
- NTSTATUS Status;
- UNICODE_STRING NtPathU;
- IO_STATUS_BLOCK IoStatus;
- OBJECT_ATTRIBUTES ObjectAttributes;
- HANDLE hDir;
-
- if (!RtlDosPathNameToNtPathName_U (lpPathName,
- &NtPathU,
- NULL,
- NULL))
- {
- BaseSetLastNTError(STATUS_OBJECT_PATH_SYNTAX_BAD);
- return INVALID_HANDLE_VALUE;
- }
-
-
-
- InitializeObjectAttributes (&ObjectAttributes,
+FindFirstChangeNotificationW(IN LPCWSTR lpPathName,
+ IN BOOL bWatchSubtree,
+ IN DWORD dwNotifyFilter)
+{
+ NTSTATUS Status;
+ UNICODE_STRING NtPathU;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE hDir;
+ RTL_RELATIVE_NAME_U RelativeName;
+ PWCHAR PathBuffer;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ /* Convert to NT path and get the relative name too */
+ if (!RtlDosPathNameToNtPathName_U(lpPathName,
+ &NtPathU,
+ NULL,
+ &RelativeName))
+ {
+ /* Bail out if the path name makes no sense */
+ SetLastError(ERROR_PATH_NOT_FOUND);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ /* Save the path buffer in case we free it later */
+ PathBuffer = NtPathU.Buffer;
+
+ /* If we have a relative name... */
+ if (RelativeName.RelativeName.Length)
+ {
+ /* Do a relative open with only the relative path set */
+ NtPathU = RelativeName.RelativeName;
+ }
+ else
+ {
+ /* Do a full path open with no containing directory */
+ RelativeName.ContainingDirectory = NULL;
+ }
+
+ /* Now open the directory name that was passed in */
+ InitializeObjectAttributes(&ObjectAttributes,
&NtPathU,
OBJ_CASE_INSENSITIVE,
- NULL,
+ RelativeName.ContainingDirectory,
NULL);
-
- Status = NtOpenFile (&hDir,
- SYNCHRONIZE|FILE_LIST_DIRECTORY,
+ Status = NtOpenFile(&hDir,
+ SYNCHRONIZE | FILE_LIST_DIRECTORY,
&ObjectAttributes,
- &IoStatus,
- FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ &IoStatusBlock,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT);
- RtlFreeHeap(RtlGetProcessHeap(),
- 0,
- NtPathU.Buffer);
-
-
-
- if (!NT_SUCCESS(Status))
- {
- BaseSetLastNTError(Status);
- return INVALID_HANDLE_VALUE;
- }
-
- Status = NtNotifyChangeDirectoryFile(hDir,
- NULL,
- NULL,
- NULL,
- &IoStatus,
- NULL,//Buffer,
- 0,//BufferLength,
- dwNotifyFilter,
- (BOOLEAN)bWatchSubtree);
- if (!NT_SUCCESS(Status))
- {
- NtClose(hDir);
- BaseSetLastNTError(Status);
- return INVALID_HANDLE_VALUE;
- }
-
- return hDir;
-}
-
+ /* Release our buffer and relative name structure */
+ RtlReleaseRelativeName(&RelativeName);
+ RtlFreeHeap(RtlGetProcessHeap(), 0, PathBuffer);
+
+ /* Check if the open failed */
+ if (!NT_SUCCESS(Status))
+ {
+ /* Bail out in that case */
+ BaseSetLastNTError(Status);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ /* Now setup the notification on the directory as requested */
+ Status = NtNotifyChangeDirectoryFile(hDir,
+ NULL,
+ NULL,
+ NULL,
+ &staticIoStatusBlock,
+ staticchangebuff,
+ sizeof(staticchangebuff),
+ dwNotifyFilter,
+ (BOOLEAN)bWatchSubtree);
+ if (!NT_SUCCESS(Status))
+ {
+ /* We failed, close the handle and convert the error */
+ NtClose(hDir);
+ BaseSetLastNTError(Status);
+ hDir = INVALID_HANDLE_VALUE;
+ }
+
+ /* Return the directory handle on success, or invalid handle otherwise */
+ return hDir;
+}
/*
* @implemented
*/
BOOL
WINAPI
-FindNextChangeNotification (
- HANDLE hChangeHandle
- )
-{
- IO_STATUS_BLOCK IoStatus;
- NTSTATUS Status;
-
- Status = NtNotifyChangeDirectoryFile(hChangeHandle,
- NULL,
- NULL,
- NULL,
- &IoStatus,
- NULL,//Buffer,
- 0,//BufferLength,
- FILE_NOTIFY_CHANGE_SECURITY,//meaningless/ignored for subsequent calls, but must
contain a valid flag
- 0 //meaningless/ignored for subsequent calls
- );
-
- if (!NT_SUCCESS(Status))
- {
- BaseSetLastNTError(Status);
- return FALSE;
- }
-
- return TRUE;
-}
-
-
-extern VOID
-(WINAPI ApcRoutine)(PVOID ApcContext,
- struct _IO_STATUS_BLOCK* IoStatusBlock,
- ULONG Reserved);
-
+FindNextChangeNotification(IN HANDLE hChangeHandle)
+{
+ NTSTATUS Status;
+
+ /* Just call the native API directly, dealing with the non-optional parameters */
+ Status = NtNotifyChangeDirectoryFile(hChangeHandle,
+ NULL,
+ NULL,
+ NULL,
+ &staticIoStatusBlock,
+ staticchangebuff,
+ sizeof(staticchangebuff),
+ FILE_NOTIFY_CHANGE_SECURITY,
+ TRUE);
+ if (!NT_SUCCESS(Status))
+ {
+ /* Convert the error code and fail */
+ BaseSetLastNTError(Status);
+ return FALSE;
+ }
+
+ /* All went well */
+ return TRUE;
+}
/*
* @implemented
*/
BOOL
WINAPI
-ReadDirectoryChangesW(
- HANDLE hDirectory,
- LPVOID lpBuffer OPTIONAL,
- DWORD nBufferLength,
- BOOL bWatchSubtree,
- DWORD dwNotifyFilter,
- LPDWORD lpBytesReturned, /* undefined for asych. operations */
- LPOVERLAPPED lpOverlapped OPTIONAL,
- LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine /* OPTIONAL???????? */
- )
-{
- PVOID CompletionRoutine;
- NTSTATUS Status;
- IO_STATUS_BLOCK IoStatus;
- HANDLE EventHandle;
- PIO_APC_ROUTINE IoApcRoutine;
-
- if (lpOverlapped )
- {
- if (lpCompletionRoutine)
- {
- CompletionRoutine = (PVOID) lpCompletionRoutine;
- EventHandle = NULL;
- IoApcRoutine = ApcRoutine;
- }
- else
- {
- if (((ULONG_PTR) lpOverlapped->hEvent & 1) == 0)
- CompletionRoutine = (PVOID) lpOverlapped;
- else
- CompletionRoutine = NULL;
-
- EventHandle = lpOverlapped->hEvent;
- IoApcRoutine = NULL;
- }
-
- lpOverlapped->Internal = STATUS_PENDING;
- }
- else
- {
- EventHandle = NULL;
- IoApcRoutine = NULL;
- CompletionRoutine = NULL;
- }
-
- Status = NtNotifyChangeDirectoryFile(
- hDirectory,
- EventHandle,
- IoApcRoutine,
- CompletionRoutine, /* ApcContext */
- lpOverlapped ? (PIO_STATUS_BLOCK) lpOverlapped : &IoStatus,
- lpBuffer,
- nBufferLength,
- dwNotifyFilter,
- (BOOLEAN)bWatchSubtree
- );
-
- if ((Status == STATUS_PENDING) && (!lpOverlapped))
- {
- Status = NtWaitForSingleObject(hDirectory, FALSE, NULL);
-
- if (NT_SUCCESS(Status))
- {
- Status = IoStatus.Status;
- }
- }
-
- if (!NT_SUCCESS(Status))
- {
- BaseSetLastNTError(Status);
- return FALSE;
- }
-
-
- /* NOTE: lpBytesReturned is undefined for asynch. operations */
- *lpBytesReturned = IoStatus.Information;
-
- return TRUE;
-}
-
-
-
-
+ReadDirectoryChangesW(IN HANDLE hDirectory,
+ IN LPVOID lpBuffer OPTIONAL,
+ IN DWORD nBufferLength,
+ IN BOOL bWatchSubtree,
+ IN DWORD dwNotifyFilter,
+ OUT LPDWORD lpBytesReturned,
+ IN LPOVERLAPPED lpOverlapped OPTIONAL,
+ IN LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
+{
+
+ NTSTATUS Status;
+ HANDLE EventHandle;
+ PVOID ApcContext;
+ PIO_APC_ROUTINE ApcRoutine;
+ PBASEP_ACTCTX_BLOCK ActivationContext = NULL;
+ BOOL Result = TRUE;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ /* Is the caller doing this synchronously? */
+ if (!lpOverlapped)
+ {
+ /* Great, just pass in the parameters */
+ Status = NtNotifyChangeDirectoryFile(hDirectory,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ lpBuffer,
+ nBufferLength,
+ dwNotifyFilter,
+ bWatchSubtree);
+ if (Status == STATUS_PENDING)
+ {
+ /* Wait for completion since we are synchronous */
+ Status = NtWaitForSingleObject(hDirectory, FALSE, NULL);
+ if (!NT_SUCCESS(Status))
+ {
+ /* The wait failed, bail out */
+ BaseSetLastNTError(Status);
+ return FALSE;
+ }
+
+ /* Retrieve the final status code */
+ Status = IoStatusBlock.Status;
+ }
+
+ /* Did the operation succeed? */
+ if (NT_SUCCESS(Status))
+ {
+ /* Return the bytes transferd and success */
+ *lpBytesReturned = IoStatusBlock.Information;
+ return Result;
+ }
+
+ /* Convert error code and return failure */
+ BaseSetLastNTError(Status);
+ return FALSE;
+ }
+
+ /* Does the caller want an APC callbac? */
+ if (lpCompletionRoutine)
+ {
+ /* Don't use an event in this case */
+ EventHandle = NULL;
+
+ /* Allocate a Fusion/SxS activation context for the callback routine */
+ Status = BasepAllocateActivationContextActivationBlock(1 | 2,
+ lpCompletionRoutine,
+ lpOverlapped,
+ &ActivationContext);
+ if (!NT_SUCCESS(Status))
+ {
+ /* This failed, so abandon the call */
+ BaseSetLastNTError(Status);
+ return FALSE;
+ }
+
+ /* Use the SxS context as the APC context */
+ ApcContext = ActivationContext;
+ if (ActivationContext)
+ {
+ /* And use a special stub routine that deals with activation */
+ ApcRoutine = BasepIoCompletion;
+ }
+ else
+ {
+ /* If there was no context, however, use the simple stub routine */
+ ApcContext = lpCompletionRoutine;
+ ApcRoutine = BasepIoCompletionSimple;
+ }
+ }
+ else
+ {
+ /* Use the even with no APC routine */
+ EventHandle = lpOverlapped->hEvent;
+ ApcRoutine = 0;
+
+ /* LPOVERLAPPED should be ignored if event is ORed with 1 */
+ ApcContext = (ULONG_PTR)lpOverlapped->hEvent & 1 ? NULL : lpOverlapped;
+ }
+
+ /* Set the initial status to pending and call the native API */
+ lpOverlapped->Internal = STATUS_PENDING;
+ Status = NtNotifyChangeDirectoryFile(hDirectory,
+ EventHandle,
+ ApcRoutine,
+ ApcContext,
+ (PIO_STATUS_BLOCK)lpOverlapped,
+ lpBuffer,
+ nBufferLength,
+ dwNotifyFilter,
+ (BOOLEAN)bWatchSubtree);
+ if (NT_ERROR(Status))
+ {
+ /* Normally we cleanup the context in the completon routine, but we failed */
+ if (ActivationContext)
BasepFreeActivationContextActivationBlock(ActivationContext);
+
+ /* Convert the error and fail */
+ BaseSetLastNTError(Status);
+ return FALSE;
+ }
+
+ /* Return success */
+ return Result;
+}
/* EOF */
Modified: trunk/reactos/dll/win32/kernel32/include/kernel32.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/kernel32/include…
==============================================================================
--- trunk/reactos/dll/win32/kernel32/include/kernel32.h [iso-8859-1] (original)
+++ trunk/reactos/dll/win32/kernel32/include/kernel32.h [iso-8859-1] Tue Feb 21 23:27:45
2012
@@ -143,6 +143,14 @@
BaseCurrentDirPlacementMax
} BASE_CURRENT_DIR_PLACEMENT;
+typedef struct _BASEP_ACTCTX_BLOCK
+{
+ ULONG Flags;
+ PVOID ActivationContext;
+ PVOID CompletionContext;
+ LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine;
+} BASEP_ACTCTX_BLOCK, *PBASEP_ACTCTX_BLOCK;
+
#define BASEP_GET_MODULE_HANDLE_EX_PARAMETER_VALIDATION_ERROR 1
#define BASEP_GET_MODULE_HANDLE_EX_PARAMETER_VALIDATION_SUCCESS 2
#define BASEP_GET_MODULE_HANDLE_EX_PARAMETER_VALIDATION_CONTINUE 3
@@ -246,6 +254,21 @@
VOID
WINAPI
BaseProcessStartThunk(VOID);
+
+VOID
+NTAPI
+BasepFreeActivationContextActivationBlock(
+ IN PBASEP_ACTCTX_BLOCK ActivationBlock
+);
+
+NTSTATUS
+NTAPI
+BasepAllocateActivationContextActivationBlock(
+ IN DWORD Flags,
+ IN PVOID CompletionRoutine,
+ IN PVOID CompletionContext,
+ OUT PBASEP_ACTCTX_BLOCK *ActivationBlock
+);
__declspec(noreturn)
VOID
Modified: trunk/reactos/dll/win32/kernel32/wine/actctx.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/kernel32/wine/ac…
==============================================================================
--- trunk/reactos/dll/win32/kernel32/wine/actctx.c [iso-8859-1] (original)
+++ trunk/reactos/dll/win32/kernel32/wine/actctx.c [iso-8859-1] Tue Feb 21 23:27:45 2012
@@ -280,3 +280,117 @@
}
return TRUE;
}
+
+/* REACTOS PRIVATE ************************************************************/
+
+VOID
+NTAPI
+BasepFreeActivationContextActivationBlock(IN PBASEP_ACTCTX_BLOCK ActivationBlock)
+{
+ /* Exit if there was nothing passed in */
+ if (!ActivationBlock) return;
+
+ /* Do we have a context? */
+ if (ActivationBlock->ActivationContext)
+ {
+ /* Release and clear it */
+ RtlReleaseActivationContext(ActivationBlock->ActivationContext);
+ ActivationBlock->ActivationContext = NULL;
+ }
+
+ /* Free the block */
+ RtlFreeHeap(RtlGetProcessHeap(), 0, ActivationBlock);
+}
+
+NTSTATUS
+NTAPI
+BasepAllocateActivationContextActivationBlock(IN DWORD Flags,
+ IN PVOID CompletionRoutine,
+ IN PVOID CompletionContext,
+ OUT PBASEP_ACTCTX_BLOCK *ActivationBlock)
+{
+ NTSTATUS Status;
+ ACTIVATION_CONTEXT_BASIC_INFORMATION ContextInfo;
+
+ /* Clear the info structure */
+ ContextInfo.dwFlags = 0;
+ ContextInfo.hActCtx = NULL;
+
+ /* Assume failure */
+ if (ActivationBlock) *ActivationBlock = NULL;
+
+ /* Only support valid flags */
+ if (Flags & ~(1 | 2)) // FIXME: What are they? 2 looks like
BASEP_ACTCTX_FORCE_BLOCK
+ {
+ /* Fail if unknown flags are passed in */
+ Status = STATUS_INVALID_PARAMETER_1;
+ goto Quickie;
+ }
+
+ /* Caller should have passed in an activation block */
+ if (!ActivationBlock)
+ {
+ /* Fail otherwise */
+ Status = STATUS_INVALID_PARAMETER_4;
+ goto Quickie;
+ }
+
+ /* Query RTL for information on the current activation context */
+ Status = RtlQueryInformationActivationContext(1,
+ NULL,
+ 0,
+ ActivationContextBasicInformation,
+ &ContextInfo,
+ sizeof(ContextInfo),
+ NULL);
+ if (!NT_SUCCESS(Status))
+ {
+ /* Failed -- bail out */
+ DPRINT1("SXS: %s - Failure getting active activation context; ntstatus
%08lx\n",
+ __FUNCTION__, Status);
+ goto Quickie;
+ }
+
+ /* Check if the current one should be freed */
+ if (ContextInfo.dwFlags & 1)
+ {
+ /* Release and clear it */
+ RtlReleaseActivationContext(ContextInfo.hActCtx);
+ ContextInfo.hActCtx = NULL;
+ }
+
+ /* Check if there's an active context, or if the caller is forcing one */
+ if (!(Flags & 2) || (ContextInfo.hActCtx))
+ {
+ /* Allocate the block */
+ *ActivationBlock = RtlAllocateHeap(RtlGetProcessHeap(),
+ 0,
+ sizeof(BASEP_ACTCTX_BLOCK));
+ if (!(*ActivationBlock))
+ {
+ /* Ran out of memory, fail */
+ Status = STATUS_NO_MEMORY;
+ goto Quickie;
+ }
+
+ /* Fill it out */
+ (*ActivationBlock)->ActivationContext = ContextInfo.hActCtx;
+ (*ActivationBlock)->Flags = 0;
+ if (Flags & 1) (*ActivationBlock)->Flags |= 1; // Not sure about this
flag
+ (*ActivationBlock)->CompletionRoutine = CompletionRoutine;
+ (*ActivationBlock)->CompletionContext = CompletionContext;
+
+ /* Tell Quickie below not to free anything, since this is success */
+ ContextInfo.hActCtx = NULL;
+ }
+
+ /* Set success status */
+ Status = STATUS_SUCCESS;
+
+Quickie:
+ /* Failure or success path, return to caller and free on failure */
+ if (ContextInfo.hActCtx) RtlReleaseActivationContext(ContextInfo.hActCtx);
+ return Status;
+}
+
+/* EOF */