You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
752 lines
19 KiB
752 lines
19 KiB
/*++
|
|
|
|
Copyright (c) 1998-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Channel.cpp
|
|
|
|
Abstract:
|
|
|
|
This module implements a thin wrapper on the Read and Write routines so
|
|
we can issue Read/Write Irps to termdd.
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
--*/
|
|
#include "precomp.hxx"
|
|
#define TRC_FILE "channel"
|
|
#include "trc.h"
|
|
|
|
#include <winsta.h>
|
|
#include <ntddvdeo.h>
|
|
#include <icadd.h>
|
|
#include "TSQPublic.h"
|
|
|
|
//
|
|
// RDPDr.cpp : The TS Worker Queue pointer
|
|
//
|
|
extern PVOID RDPDR_TsQueue;
|
|
|
|
|
|
typedef struct tagCHANNELIOCONTEXT {
|
|
SmartPtr<VirtualChannel> Channel;
|
|
PIO_COMPLETION_ROUTINE CompletionRoutine;
|
|
PVOID Context;
|
|
PVOID Buffer;
|
|
ULONG Length;
|
|
ULONG IoOperation;
|
|
BOOL LowPrioSend;
|
|
} CHANNELIOCONTEXT, *PCHANNELIOCONTEXT;
|
|
|
|
typedef struct tagCHANNELCLOSECONTEXT {
|
|
SmartPtr<VirtualChannel> Channel;
|
|
} CHANNELCLOSECONTEXT, *PCHANNELCLOSECONTEXT;
|
|
|
|
VirtualChannel::VirtualChannel()
|
|
{
|
|
BEGIN_FN("VirtualChannel::VirtualChannel");
|
|
SetClassName("VirtualChannel");
|
|
_Channel = NULL;
|
|
_ChannelFileObject = NULL;
|
|
_ChannelDeviceObject = NULL;
|
|
_DeletionEvent = NULL;
|
|
_LowPrioChannelWriteFlags = 0;
|
|
_LowPrioChannelWriteFlags |= CHANNEL_WRITE_LOWPRIO;
|
|
}
|
|
|
|
VirtualChannel::~VirtualChannel()
|
|
{
|
|
BEGIN_FN("VirtualChannel::~VirtualChannel");
|
|
|
|
if (_DeletionEvent != NULL) {
|
|
KeSetEvent(_DeletionEvent, IO_NO_INCREMENT, FALSE);
|
|
}
|
|
}
|
|
|
|
BOOL VirtualChannel::Create(HANDLE hIca, ULONG SessionID, ULONG ChannelId,
|
|
PKEVENT DeletionEvent)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Opens the virtual channel and make it a kernel handle
|
|
|
|
Arguments:
|
|
|
|
Channel - A pointer to a location to store the Channel pointer
|
|
hIca - Required context for opening a channel
|
|
SessionId - The session for the channel
|
|
ChannelId - The Id of the RdpDr channel
|
|
|
|
Return Value:
|
|
|
|
a valid NTSTATUS code.
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
OBJECT_HANDLE_INFORMATION HandleInformation;
|
|
|
|
BEGIN_FN("VirtualChannel::Create");
|
|
|
|
ASSERT(_DeletionEvent == NULL);
|
|
_DeletionEvent = DeletionEvent;
|
|
|
|
//
|
|
// Get the channel open
|
|
//
|
|
Status = CreateTermDD(&_Channel, hIca, SessionID, ChannelId);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Get the file object from the handle
|
|
//
|
|
|
|
Status = ObReferenceObjectByHandle(_Channel,
|
|
STANDARD_RIGHTS_REQUIRED, NULL, KernelMode, (PVOID *)(&_ChannelFileObject),
|
|
&HandleInformation);
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
TRC_DBG((TB, "ObReferenced channel"));
|
|
|
|
_ChannelDeviceObject = IoGetRelatedDeviceObject((PFILE_OBJECT)_ChannelFileObject);
|
|
}
|
|
else {
|
|
TRC_ERR((TB, "Failed to open channel"));
|
|
|
|
if (_Channel != NULL) {
|
|
ZwClose(_Channel);
|
|
_Channel = NULL;
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
return TRUE;
|
|
} else {
|
|
if (_DeletionEvent) {
|
|
KeSetEvent(_DeletionEvent, IO_NO_INCREMENT, FALSE);
|
|
}
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
NTSTATUS VirtualChannel::Read(
|
|
IN PIO_COMPLETION_ROUTINE ReadRoutine OPTIONAL,
|
|
IN PVOID Context,
|
|
OUT PVOID Buffer,
|
|
IN ULONG Length,
|
|
IN BOOL bWorkerItem
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads data from the virtual channel for the specified Client
|
|
|
|
Arguments:
|
|
|
|
ReadRoutine - Completetion routine
|
|
Context - Data to pass to the completion routine
|
|
Buffer - Data to transfer
|
|
Length - Size of data to transfer
|
|
|
|
Return Value:
|
|
|
|
a valid NTSTATUS code.
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
BEGIN_FN("VirtualChannel::Read");
|
|
|
|
#if DBG
|
|
SmartPtr<DrSession> Session = (DrSession *)Context;
|
|
//ASSERT(InterlockedIncrement(&(Session->_ApcCount)) == 1);
|
|
InterlockedIncrement(&(Session->_ApcCount));
|
|
InterlockedIncrement(&(Session->_ApcChannelRef));
|
|
#endif
|
|
|
|
Status = SubmitIo(ReadRoutine, Context, Buffer, Length, IRP_MJ_READ, bWorkerItem, FALSE);
|
|
|
|
#if DBG
|
|
if (!NT_SUCCESS(Status)) {
|
|
//ASSERT(InterlockedDecrement(&(Session->_ApcCount)) == 0);
|
|
//ASSERT(InterlockedDecrement(&(Session->_ApcChannelRef)) == 0);
|
|
InterlockedDecrement(&(Session->_ApcCount));
|
|
InterlockedDecrement(&(Session->_ApcChannelRef));
|
|
}
|
|
#endif
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS VirtualChannel::Write(
|
|
IN PIO_COMPLETION_ROUTINE WriteRoutine OPTIONAL,
|
|
IN PVOID Context,
|
|
OUT PVOID Buffer,
|
|
IN ULONG Length,
|
|
IN BOOL bWorkerItem,
|
|
IN BOOL LowPrioSend
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Writes data to the virtual channel for the specified Client
|
|
|
|
Arguments:
|
|
|
|
WriteRoutine - Completetion routine
|
|
Context - Data to pass to the completion routine
|
|
Buffer - Data to transfer
|
|
Length - Size of data to transfer
|
|
LowPrioSend - Indicate that the channel write should be at
|
|
lower priority than other client destined data.
|
|
|
|
Return Value:
|
|
|
|
a valid NTSTATUS code.
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
BEGIN_FN("VirtualChannel::Write");
|
|
return SubmitIo(WriteRoutine, Context, Buffer, Length, IRP_MJ_WRITE,
|
|
bWorkerItem, LowPrioSend);
|
|
}
|
|
|
|
NTSTATUS VirtualChannel::SubmitIo(
|
|
IN PIO_COMPLETION_ROUTINE CompletionRoutine OPTIONAL,
|
|
IN PVOID Context,
|
|
OUT PVOID Buffer,
|
|
IN ULONG Length,
|
|
ULONG IoOperation,
|
|
BOOL bWorkerItem,
|
|
BOOL LowPrioSend
|
|
)
|
|
{
|
|
PCHANNELIOCONTEXT pChannelIoContext;
|
|
NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
BEGIN_FN("VirtualChannel::SubmitIo");
|
|
|
|
TRC_ASSERT((IoOperation == IRP_MJ_READ) ||
|
|
(IoOperation == IRP_MJ_WRITE), (TB, "Bad ChannelIo operation"));
|
|
|
|
if (bWorkerItem) {
|
|
//
|
|
// Move this operation to a system thread
|
|
//
|
|
|
|
TRC_NRM((TB, "DrChannelIo: queueing the I/O to a system thread"));
|
|
|
|
pChannelIoContext = new (NonPagedPool) CHANNELIOCONTEXT;
|
|
|
|
if (pChannelIoContext != NULL) {
|
|
pChannelIoContext->Channel = this;
|
|
pChannelIoContext->CompletionRoutine = CompletionRoutine;
|
|
pChannelIoContext->Context = Context;
|
|
pChannelIoContext->Buffer = Buffer;
|
|
pChannelIoContext->Length = Length;
|
|
pChannelIoContext->IoOperation = IoOperation;
|
|
pChannelIoContext->LowPrioSend = LowPrioSend;
|
|
//
|
|
// Use our own TS worker queue
|
|
//
|
|
Status = TSAddWorkItemToQueue(RDPDR_TsQueue,
|
|
pChannelIoContext,
|
|
IoWorker);
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
Status = STATUS_PENDING;
|
|
goto EXIT;
|
|
}
|
|
else {
|
|
//
|
|
// Ts Queue failed
|
|
//
|
|
TRC_ERR((TB, "RDPDR: FAILED Adding workitem to TS Queue 0x%8x", Status));
|
|
delete pChannelIoContext;
|
|
}
|
|
}
|
|
|
|
if (IoOperation == IRP_MJ_WRITE) {
|
|
PIO_STATUS_BLOCK pIoStatusBlock = (PIO_STATUS_BLOCK)Context;
|
|
pIoStatusBlock->Status = Status;
|
|
pIoStatusBlock->Information = 0;
|
|
|
|
CompletionRoutine(NULL, NULL, Context);
|
|
}
|
|
else {
|
|
// No read should go through here for now
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
}
|
|
else {
|
|
Status = Io(CompletionRoutine,
|
|
Context,
|
|
Buffer,
|
|
Length,
|
|
IoOperation,
|
|
LowPrioSend);
|
|
}
|
|
|
|
EXIT:
|
|
return Status;
|
|
}
|
|
|
|
VOID VirtualChannel::IoWorker(PDEVICE_OBJECT DeviceObject, PVOID Context)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads data from the virtual channel for the specified Client, and
|
|
signals the thread wanted it done
|
|
|
|
Arguments:
|
|
|
|
ClientEntry - The client with which to communicate
|
|
ApcRoutine - Completetion routine
|
|
ApcContext - Data to pass to the completion routine
|
|
IoStatusBlock - Place to store result code
|
|
Buffer - Data to transfer
|
|
Length - Size of data to transfer
|
|
ByteOffset - Offset into Buffer
|
|
IoOperation - Read or Write
|
|
Event - Event to signal when done
|
|
Status - Result code
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PCHANNELIOCONTEXT ChannelIoContext = (PCHANNELIOCONTEXT)Context;
|
|
|
|
BEGIN_FN_STATIC("VirtualChannel::IoWorker");
|
|
ASSERT(ChannelIoContext != NULL);
|
|
UNREFERENCED_PARAMETER(DeviceObject);
|
|
|
|
#if DBG
|
|
SmartPtr<DrSession> Session;
|
|
|
|
if (ChannelIoContext->IoOperation == IRP_MJ_READ) {
|
|
Session = (DrSession *)(ChannelIoContext->Context);
|
|
ASSERT(Session->GetBuffer() == ChannelIoContext->Buffer);
|
|
}
|
|
#endif
|
|
|
|
Status = ChannelIoContext->Channel->Io(
|
|
ChannelIoContext->CompletionRoutine,
|
|
ChannelIoContext->Context,
|
|
ChannelIoContext->Buffer,
|
|
ChannelIoContext->Length,
|
|
ChannelIoContext->IoOperation,
|
|
ChannelIoContext->LowPrioSend);
|
|
|
|
|
|
//
|
|
// Now delete the context
|
|
//
|
|
delete ChannelIoContext;
|
|
}
|
|
|
|
NTSTATUS VirtualChannel::Io(
|
|
IN PIO_COMPLETION_ROUTINE CompletionRoutine OPTIONAL,
|
|
IN PVOID Context,
|
|
OUT PVOID Buffer,
|
|
IN ULONG Length,
|
|
ULONG IoOperation,
|
|
BOOL LowPrioSend
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads/Writes data from/to the virtual channel for the specified Client
|
|
|
|
Arguments:
|
|
|
|
CompletionRoutine - Completetion routine
|
|
Context - Data to pass to the completion routine
|
|
Buffer - Data to transfer
|
|
Length - Size of data to transfer
|
|
IoOperation - Read or Write
|
|
|
|
Return Value:
|
|
|
|
a valid NTSTATUS code.
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PIRP Irp;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
LARGE_INTEGER StartOffset;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
|
|
BEGIN_FN("VirtualChannel::SubmitIo");
|
|
|
|
SharedLock sl(_HandleLock);
|
|
|
|
if (_Channel != NULL) {
|
|
//
|
|
// Build a read/write irp
|
|
//
|
|
StartOffset.QuadPart = 0;
|
|
Irp = IoBuildAsynchronousFsdRequest(IoOperation, _ChannelDeviceObject, Buffer, Length,
|
|
&StartOffset, &IoStatusBlock);
|
|
|
|
if (Irp) {
|
|
//
|
|
// Setup the fileobject parameter
|
|
//
|
|
IrpSp = IoGetNextIrpStackLocation(Irp);
|
|
IrpSp->FileObject = _ChannelFileObject;
|
|
|
|
Irp->Tail.Overlay.Thread = NULL;
|
|
|
|
//
|
|
// Set for low prio write, if specified.
|
|
//
|
|
if (!LowPrioSend) {
|
|
Irp->Tail.Overlay.DriverContext[0] = NULL;
|
|
}
|
|
else {
|
|
Irp->Tail.Overlay.DriverContext[0] = &_LowPrioChannelWriteFlags;
|
|
}
|
|
|
|
//
|
|
// Setup the completion routine
|
|
//
|
|
IoSetCompletionRoutine(Irp, CompletionRoutine, Context, TRUE, TRUE, TRUE);
|
|
|
|
//
|
|
// Send the Irp to Termdd
|
|
//
|
|
Status = IoCallDriver(_ChannelDeviceObject, Irp);
|
|
|
|
goto EXIT;
|
|
}
|
|
else {
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
else {
|
|
Status = STATUS_DEVICE_NOT_CONNECTED;
|
|
}
|
|
|
|
if (IoOperation == IRP_MJ_WRITE) {
|
|
PIO_STATUS_BLOCK pIoStatusBlock = (PIO_STATUS_BLOCK)Context;
|
|
pIoStatusBlock->Status = Status;
|
|
pIoStatusBlock->Information = 0;
|
|
CompletionRoutine(NULL, NULL, Context);
|
|
|
|
// read completion is not called this way.
|
|
}
|
|
|
|
EXIT:
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS VirtualChannel::CreateTermDD(HANDLE *Channel, HANDLE hIca,
|
|
ULONG SessionID, ULONG ChannelId)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Opens a virtual channel based on the supplied context
|
|
|
|
Arguments:
|
|
|
|
Channel - A pointer to a location to store the Channel pointer
|
|
hIca - Required context for opening a channel
|
|
SessionId - The session for the channel
|
|
ChannelId - The Id of the RdpDr channel
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE hChannel = NULL;
|
|
WCHAR ChannelNameBuffer[MAX_PATH];
|
|
UNICODE_STRING ChannelName;
|
|
UNICODE_STRING Number;
|
|
OBJECT_ATTRIBUTES ChannelAttributes;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
PFILE_FULL_EA_INFORMATION pEa = NULL;
|
|
ICA_OPEN_PACKET UNALIGNED * pIcaOpenPacket;
|
|
ULONG cbEa = sizeof( FILE_FULL_EA_INFORMATION )
|
|
+ ICA_OPEN_PACKET_NAME_LENGTH
|
|
+ sizeof( ICA_OPEN_PACKET );
|
|
|
|
BEGIN_FN("VirtualChannel::CreateTermDD");
|
|
|
|
//
|
|
// Kernel-mode applications open a virtual channel using ZwCreateFile on
|
|
// \Device\ICA\sss\Virtualvvv , where
|
|
//
|
|
// · sss is the logon session ID
|
|
// · vvv is the virtual channel number.
|
|
//
|
|
|
|
ChannelName.Buffer = ChannelNameBuffer;
|
|
ChannelName.Length = 0;
|
|
ChannelName.MaximumLength = sizeof(ChannelNameBuffer);
|
|
|
|
Status = RtlAppendUnicodeToString(&ChannelName, L"\\Device\\Termdd\\");
|
|
|
|
TRC_ASSERT(NT_SUCCESS(Status), (TB, "Creating channel path"));
|
|
|
|
//
|
|
// Create and append on the sessionID string
|
|
//
|
|
|
|
// Point another UNICODE_STRING to the next part of the buffer
|
|
Number.Buffer = (PWCHAR)(((PBYTE)ChannelName.Buffer) + ChannelName.Length);
|
|
Number.Length = 0;
|
|
Number.MaximumLength = ChannelName.MaximumLength - ChannelName.Length;
|
|
|
|
// Use that string to put the characters in the right place
|
|
Status = RtlIntegerToUnicodeString(SessionID, 10, &Number);
|
|
TRC_ASSERT(NT_SUCCESS(Status), (TB, "Creating channel path"));
|
|
|
|
// Add the length of that string to the real string
|
|
ChannelName.Length += Number.Length;
|
|
|
|
//
|
|
// Append the next part of the channel path
|
|
//
|
|
Status = RtlAppendUnicodeToString(&ChannelName, L"\\Virtual");
|
|
TRC_ASSERT(NT_SUCCESS(Status), (TB, "Creating channel path"));
|
|
|
|
//
|
|
// Create and append the channelID string
|
|
//
|
|
|
|
// Point another UNICODE_STRING to the next part of the buffer
|
|
Number.Buffer = (PWCHAR)(((PBYTE)ChannelName.Buffer) + ChannelName.Length);
|
|
Number.Length = 0;
|
|
Number.MaximumLength = ChannelName.MaximumLength - ChannelName.Length;
|
|
|
|
// Use that string to put the characters in the right place
|
|
Status = RtlIntegerToUnicodeString(ChannelId, 10, &Number);
|
|
TRC_ASSERT(NT_SUCCESS(Status), (TB, "Creating channel path"));
|
|
|
|
// Add the length of that string to the real string
|
|
ChannelName.Length += Number.Length;
|
|
|
|
//
|
|
// Actually open the channel
|
|
//
|
|
InitializeObjectAttributes(&ChannelAttributes,
|
|
&ChannelName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL);
|
|
|
|
//
|
|
// Pass in a cool EaBuffer thing so this will really work
|
|
// I basically lifted this code from private\tsext\icaapi\stack.c
|
|
// It's supposed to be a temporary measure
|
|
//
|
|
|
|
/*
|
|
* Allocate some memory for the EA buffer
|
|
*/
|
|
pEa = (PFILE_FULL_EA_INFORMATION)new BYTE[cbEa];
|
|
if (pEa != NULL) {
|
|
/*
|
|
* Initialize the EA buffer
|
|
*/
|
|
pEa->NextEntryOffset = 0;
|
|
pEa->Flags = 0;
|
|
pEa->EaNameLength = ICA_OPEN_PACKET_NAME_LENGTH;
|
|
RtlCopyMemory(pEa->EaName, ICAOPENPACKET, ICA_OPEN_PACKET_NAME_LENGTH + 1 );
|
|
pEa->EaValueLength = sizeof( ICA_OPEN_PACKET );
|
|
pIcaOpenPacket = (ICA_OPEN_PACKET UNALIGNED *)(pEa->EaName +
|
|
pEa->EaNameLength + 1);
|
|
|
|
/*
|
|
* Now put the open packet parameters into the EA buffer
|
|
*/
|
|
pIcaOpenPacket->IcaHandle = hIca;
|
|
pIcaOpenPacket->OpenType = IcaOpen_Channel;
|
|
pIcaOpenPacket->TypeInfo.ChannelClass = Channel_Virtual;
|
|
RtlCopyMemory(pIcaOpenPacket->TypeInfo.VirtualName, DR_CHANNEL_NAME,
|
|
sizeof(DR_CHANNEL_NAME));
|
|
|
|
//
|
|
// We keep this next line without "pEa, cbEa"
|
|
//
|
|
|
|
Status = ZwCreateFile(&hChannel, GENERIC_READ | GENERIC_WRITE,
|
|
&ChannelAttributes, &IoStatusBlock, 0, FILE_ATTRIBUTE_NORMAL, 0,
|
|
FILE_OPEN_IF, FILE_SEQUENTIAL_ONLY, pEa, cbEa);
|
|
|
|
delete pEa;
|
|
|
|
} else {
|
|
TRC_ERR((TB, "Unable to allocate EaBuffer for ZwCreateFile(channel)"));
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
*Channel = hChannel;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
void VirtualChannel::CloseWorker(PDEVICE_OBJECT DeviceObject, PVOID Context)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Closes the virtual channel in a work item
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code from ZwClose.
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
{
|
|
PCHANNELCLOSECONTEXT ChannelCloseContext = (PCHANNELCLOSECONTEXT)Context;
|
|
|
|
BEGIN_FN_STATIC("VirtualChannel::CloseWorker");
|
|
ASSERT(ChannelCloseContext != NULL);
|
|
UNREFERENCED_PARAMETER(DeviceObject);
|
|
|
|
ChannelCloseContext->Channel->Close();
|
|
|
|
|
|
//
|
|
// Now delete the context
|
|
//
|
|
delete ChannelCloseContext;
|
|
}
|
|
|
|
NTSTATUS VirtualChannel::Close()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Closes the virtual channel
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code from ZwClose.
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
BEGIN_FN("VirtualChannel::Close");
|
|
|
|
ExclusiveLock el(_HandleLock);
|
|
|
|
ASSERT(_Channel != NULL);
|
|
ASSERT(_ChannelFileObject != NULL);
|
|
|
|
TRC_NRM((TB, "DrChannelClose: Close the channel"));
|
|
_ChannelDeviceObject = NULL;
|
|
ObDereferenceObject(_ChannelFileObject);
|
|
_ChannelFileObject = NULL;
|
|
ZwClose(_Channel);
|
|
_Channel = NULL;
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS VirtualChannel::SubmitClose()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Post a close virtual channel request to a worker item
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code from ZwClose.
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
{
|
|
PCHANNELCLOSECONTEXT pChannelCloseContext;
|
|
NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
BEGIN_FN("VirtualChannel::SubmitClose");
|
|
|
|
//
|
|
// Move this operation to a system thread
|
|
//
|
|
|
|
TRC_NRM((TB, "DrChannelClose: queueing the I/O to a system thread"));
|
|
|
|
pChannelCloseContext = new (NonPagedPool) CHANNELCLOSECONTEXT;
|
|
|
|
if (pChannelCloseContext != NULL) {
|
|
pChannelCloseContext->Channel = this;
|
|
//
|
|
// Use our own TS worker queue
|
|
//
|
|
Status = TSAddWorkItemToQueue(RDPDR_TsQueue,
|
|
pChannelCloseContext,
|
|
CloseWorker);
|
|
|
|
if( Status == STATUS_SUCCESS) {
|
|
Status = STATUS_PENDING;
|
|
}
|
|
else {
|
|
//
|
|
// Ts Queue failed
|
|
//
|
|
TRC_ERR((TB, "RDPDR: FAILED Adding workitem to TS Queue 0x%8x", Status));
|
|
delete pChannelCloseContext;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|