646 lines
13 KiB
C++
646 lines
13 KiB
C++
|
/*++
|
||
|
|
||
|
Copyright (c) 1999-2000 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
DataChannelMgr.cpp
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
This module contains an implementation of the ISAFRemoteDesktopDataChannel
|
||
|
and ISAFRemoteDesktopChannelMgr interfaces. These interfaces are designed
|
||
|
to abstract out-of-band data channel access for the Salem project.
|
||
|
|
||
|
The classes implemented in this module achieve this objective by
|
||
|
multiplexing multiple data channels into a single data channel that is
|
||
|
implemented by the remote control-specific Salem layer.
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Tad Brockway 02/00
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#ifdef TRC_FILE
|
||
|
#undef TRC_FILE
|
||
|
#endif
|
||
|
|
||
|
#define TRC_FILE "_dcmpl"
|
||
|
|
||
|
#include "DataChannelMgr.h"
|
||
|
#include <RemoteDesktop.h>
|
||
|
#include <RemoteDesktopDBG.h>
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////
|
||
|
//
|
||
|
// Local Defines
|
||
|
//
|
||
|
|
||
|
#define OUTBUFRESIZEDELTA 100
|
||
|
|
||
|
|
||
|
///////////////////////////////////////////////////////
|
||
|
//
|
||
|
// CRemoteDesktopChannelMgr Members
|
||
|
//
|
||
|
|
||
|
CRemoteDesktopChannelMgr::CRemoteDesktopChannelMgr()
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Constructor
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
DC_BEGIN_FN("CRemoteDesktopChannelMgr::CRemoteDesktopChannelMgr");
|
||
|
|
||
|
#if DBG
|
||
|
m_LockCount = 0;
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// Initialize the critical section.
|
||
|
//
|
||
|
InitializeCriticalSection(&m_cs);
|
||
|
|
||
|
DC_END_FN();
|
||
|
}
|
||
|
|
||
|
CRemoteDesktopChannelMgr::~CRemoteDesktopChannelMgr()
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Destructor
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
DC_BEGIN_FN("CRemoteDesktopChannelMgr::~CRemoteDesktopChannelMgr");
|
||
|
|
||
|
ThreadLock();
|
||
|
|
||
|
CComBSTR name;
|
||
|
CRemoteDesktopDataChannel *chnl;
|
||
|
HRESULT hr;
|
||
|
|
||
|
//
|
||
|
// Remove each channel.
|
||
|
//
|
||
|
while (!m_ChannelMap.empty()) {
|
||
|
chnl = (*m_ChannelMap.begin()).second->channelObject;
|
||
|
RemoveChannel(chnl->m_ChannelName);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Clean up the critical section object.
|
||
|
//
|
||
|
ThreadUnlock();
|
||
|
DeleteCriticalSection(&m_cs);
|
||
|
|
||
|
DC_END_FN();
|
||
|
}
|
||
|
|
||
|
HRESULT
|
||
|
CRemoteDesktopChannelMgr::OpenDataChannel_(
|
||
|
BSTR name,
|
||
|
ISAFRemoteDesktopDataChannel **channel
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Open a data channel. Observe that this function doesn't keep
|
||
|
a reference of its own to the returned interface. The channel
|
||
|
notifies us when it goes away so we can remove it from our list.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
name - Channel name. Channel names are restricted to
|
||
|
16 bytes.
|
||
|
channel - Returned channe linterface.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
S_OK is returned on success. Otherwise, an error code
|
||
|
is returned.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
DC_BEGIN_FN("CRemoteDesktopChannelMgr::OpenDataChannel_");
|
||
|
|
||
|
PCHANNELMAPENTRY newChannel = NULL;
|
||
|
ChannelMap::iterator iter;
|
||
|
HRESULT hr = S_OK;
|
||
|
CComBSTR channelName;
|
||
|
|
||
|
ASSERT(IsValid());
|
||
|
|
||
|
ThreadLock();
|
||
|
|
||
|
//
|
||
|
// Check the parms.
|
||
|
//
|
||
|
if ((name == NULL) || !wcslen(name)) {
|
||
|
TRC_ERR((TB, TEXT("Invalid channel name")));
|
||
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
|
||
|
goto CLEANUPANDEXIT;
|
||
|
}
|
||
|
if (channel == NULL) {
|
||
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
|
||
|
goto CLEANUPANDEXIT;
|
||
|
}
|
||
|
channelName = name;
|
||
|
|
||
|
//
|
||
|
// AddRef an existing interface if the channel is already open.
|
||
|
//
|
||
|
iter = m_ChannelMap.find(channelName);
|
||
|
if (iter != m_ChannelMap.end()) {
|
||
|
|
||
|
TRC_NRM((TB, TEXT("Channel %s exists."), name));
|
||
|
|
||
|
CRemoteDesktopDataChannel *chnl = (*iter).second->channelObject;
|
||
|
hr = chnl->GetISAFRemoteDesktopDataChannel(channel);
|
||
|
|
||
|
if (hr != S_OK) {
|
||
|
TRC_ERR((TB, TEXT("GetISAFRemoteDesktopDataChannel failed: %08X"), hr));
|
||
|
}
|
||
|
goto CLEANUPANDEXIT;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Create the new channel with some help from the subclass.
|
||
|
//
|
||
|
newChannel = new CHANNELMAPENTRY;
|
||
|
if (newChannel == NULL) {
|
||
|
hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
|
||
|
goto CLEANUPANDEXIT;
|
||
|
}
|
||
|
newChannel->channelObject = OpenPlatformSpecificDataChannel(
|
||
|
name,
|
||
|
channel
|
||
|
);
|
||
|
if (newChannel->channelObject == NULL) {
|
||
|
TRC_ERR((TB, TEXT("Failed to allocate data channel.")));
|
||
|
hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
|
||
|
goto CLEANUPANDEXIT;
|
||
|
}
|
||
|
#if DBG
|
||
|
newChannel->bytesSent = 0;
|
||
|
newChannel->bytesRead = 0;
|
||
|
#endif
|
||
|
|
||
|
if (hr != S_OK) {
|
||
|
TRC_ERR((TB, TEXT("QI failed for ISAFRemoteDesktopDataChannel")));
|
||
|
goto CLEANUPANDEXIT;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Add the channel to the channel map.
|
||
|
//
|
||
|
try {
|
||
|
m_ChannelMap.insert(ChannelMap::value_type(channelName, newChannel));
|
||
|
}
|
||
|
catch(CRemoteDesktopException x) {
|
||
|
hr = HRESULT_FROM_WIN32(x.m_ErrorCode);
|
||
|
}
|
||
|
|
||
|
CLEANUPANDEXIT:
|
||
|
|
||
|
if (hr != S_OK) {
|
||
|
if (newChannel != NULL) {
|
||
|
(*channel)->Release();
|
||
|
delete newChannel;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ThreadUnlock();
|
||
|
|
||
|
DC_END_FN();
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
CRemoteDesktopChannelMgr::RemoveChannel(
|
||
|
BSTR channel
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Remove an existing data channel. This function is called from the
|
||
|
channel object when its ref count goes to 0.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
channel - Name of channel to remove.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
DC_BEGIN_FN("CRemoteDesktopChannelMgr::RemoveChannel");
|
||
|
|
||
|
ASSERT(IsValid());
|
||
|
|
||
|
ChannelMap::iterator iter;
|
||
|
PCHANNELMAPENTRY pChannel;
|
||
|
|
||
|
ThreadLock();
|
||
|
|
||
|
//
|
||
|
// Find the channel.
|
||
|
//
|
||
|
iter = m_ChannelMap.find(channel);
|
||
|
if (iter == m_ChannelMap.end()) {
|
||
|
ASSERT(FALSE);
|
||
|
TRC_ERR((TB, TEXT("Channel %ld does not exist."), channel));
|
||
|
goto CLEANUPANDEXIT;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Release the input buffer queue and its contents.
|
||
|
//
|
||
|
pChannel = (*iter).second;
|
||
|
while (!pChannel->inputBufferQueue.empty()) {
|
||
|
|
||
|
QUEUEDCHANNELBUFFER channelBuf = pChannel->inputBufferQueue.front();
|
||
|
SysFreeString(channelBuf.buf);
|
||
|
pChannel->inputBufferQueue.pop_front();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Erase the channel.
|
||
|
//
|
||
|
m_ChannelMap.erase(iter);
|
||
|
delete pChannel;
|
||
|
|
||
|
CLEANUPANDEXIT:
|
||
|
|
||
|
ThreadUnlock();
|
||
|
|
||
|
DC_END_FN();
|
||
|
}
|
||
|
|
||
|
HRESULT
|
||
|
CRemoteDesktopChannelMgr::SendChannelData(
|
||
|
BSTR channel,
|
||
|
BSTR outputBuf
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Send a buffer on the data channel.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
channel - Relevant channel.
|
||
|
outputBuf - Associated output data.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
ERROR_SUCCESS is returned on success. Otherwise, an error code
|
||
|
is returned.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
DC_BEGIN_FN("CRemoteDesktopChannelMgr::SendChannelData");
|
||
|
|
||
|
ASSERT(IsValid());
|
||
|
|
||
|
HRESULT result = S_OK;
|
||
|
PREMOTEDESKTOP_CHANNELBUFHEADER hdr;
|
||
|
DWORD bytesToSend;
|
||
|
PBYTE data;
|
||
|
BSTR fullOutputBuf;
|
||
|
DWORD bufLen = SysStringByteLen(outputBuf);
|
||
|
DWORD channelNameLen;
|
||
|
PBYTE ptr;
|
||
|
|
||
|
//
|
||
|
// Make sure this is a valid channel.
|
||
|
//
|
||
|
ChannelMap::iterator iter;
|
||
|
|
||
|
//
|
||
|
// ThreadLock
|
||
|
//
|
||
|
ThreadLock();
|
||
|
|
||
|
//
|
||
|
// Make sure the channel exists.
|
||
|
//
|
||
|
iter = m_ChannelMap.find(channel);
|
||
|
if (iter == m_ChannelMap.end()) {
|
||
|
ASSERT(FALSE);
|
||
|
goto CLEANUPANDEXIT;
|
||
|
}
|
||
|
|
||
|
#if DBG
|
||
|
(*iter).second->bytesSent += SysStringByteLen(outputBuf);
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// Allocate the outgoing buffer.
|
||
|
//
|
||
|
channelNameLen = SysStringByteLen(channel);
|
||
|
bytesToSend = sizeof(REMOTEDESKTOP_CHANNELBUFHEADER) + bufLen + channelNameLen;
|
||
|
fullOutputBuf = (BSTR)SysAllocStringByteLen(
|
||
|
NULL,
|
||
|
bytesToSend
|
||
|
);
|
||
|
if (fullOutputBuf == NULL) {
|
||
|
TRC_ERR((TB, TEXT("Can't allocate %ld bytes."),
|
||
|
bytesToSend + OUTBUFRESIZEDELTA));
|
||
|
result = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
|
||
|
goto CLEANUPANDEXIT;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Initialize the header.
|
||
|
//
|
||
|
hdr = (PREMOTEDESKTOP_CHANNELBUFHEADER)fullOutputBuf;
|
||
|
memset(hdr, 0, sizeof(REMOTEDESKTOP_CHANNELBUFHEADER));
|
||
|
|
||
|
#ifdef USE_MAGICNO
|
||
|
hdr->magicNo = CHANNELBUF_MAGICNO;
|
||
|
#endif
|
||
|
|
||
|
hdr->channelNameLen = channelNameLen;
|
||
|
hdr->dataLen = bufLen;
|
||
|
|
||
|
//
|
||
|
// Copy the channel name.
|
||
|
//
|
||
|
ptr = (PBYTE)(hdr + 1);
|
||
|
memcpy(ptr, channel, hdr->channelNameLen);
|
||
|
|
||
|
//
|
||
|
// Copy the data.
|
||
|
//
|
||
|
ptr += hdr->channelNameLen;
|
||
|
memcpy(ptr, outputBuf, bufLen);
|
||
|
|
||
|
//
|
||
|
// Send the data through the concrete subclass.
|
||
|
//
|
||
|
result = SendData(hdr);
|
||
|
|
||
|
//
|
||
|
// Release the send buffer that we allocated.
|
||
|
//
|
||
|
SysFreeString(fullOutputBuf);
|
||
|
|
||
|
CLEANUPANDEXIT:
|
||
|
|
||
|
ThreadUnlock();
|
||
|
|
||
|
DC_END_FN();
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
HRESULT
|
||
|
CRemoteDesktopChannelMgr::ReadChannelData(
|
||
|
IN BSTR channel,
|
||
|
OUT BSTR *msg
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Read the next message from a data channel.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
channel - Relevant data channel.
|
||
|
msg - The next message. The caller should release the
|
||
|
data buffer using SysFreeString.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
S_OK on success. ERROR_NO_MORE_ITEMS is returned if there
|
||
|
are no more messages. An error code otherwise.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
DC_BEGIN_FN("CRemoteDesktopChannelMgr::ReadChannelData");
|
||
|
HRESULT result = S_OK;
|
||
|
|
||
|
ChannelMap::iterator channelIterator;
|
||
|
PCHANNELMAPENTRY pChannel;
|
||
|
|
||
|
ASSERT(IsValid());
|
||
|
|
||
|
ThreadLock();
|
||
|
|
||
|
//
|
||
|
// Initialize the output buf to NULL.
|
||
|
//
|
||
|
*msg = NULL;
|
||
|
|
||
|
//
|
||
|
// Find the channel.
|
||
|
//
|
||
|
channelIterator = m_ChannelMap.find(channel);
|
||
|
if (channelIterator != m_ChannelMap.end()) {
|
||
|
pChannel = (*channelIterator).second;
|
||
|
}
|
||
|
else {
|
||
|
ASSERT(FALSE);
|
||
|
result = E_FAIL;
|
||
|
goto CLEANUPANDEXIT;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Make sure there is data in the queue.
|
||
|
//
|
||
|
if (pChannel->inputBufferQueue.empty()) {
|
||
|
result = HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS);
|
||
|
goto CLEANUPANDEXIT;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Return the buffer.
|
||
|
//
|
||
|
*msg = pChannel->inputBufferQueue.front().buf;
|
||
|
ASSERT(*msg != NULL);
|
||
|
|
||
|
//
|
||
|
// Delete it.
|
||
|
//
|
||
|
pChannel->inputBufferQueue.pop_front();
|
||
|
|
||
|
CLEANUPANDEXIT:
|
||
|
|
||
|
ThreadUnlock();
|
||
|
|
||
|
DC_END_FN();
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
CRemoteDesktopChannelMgr::DataReady(
|
||
|
BSTR msg
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Invoked by the subclass when the next message is ready. This
|
||
|
function copies the message buffer and returns.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
msg - Next message.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
DC_BEGIN_FN("CRemoteDesktopChannelMgr::DataReady");
|
||
|
|
||
|
ChannelMap::iterator channel;
|
||
|
QUEUEDCHANNELBUFFER channelBuf;
|
||
|
PREMOTEDESKTOP_CHANNELBUFHEADER hdr = NULL;
|
||
|
DWORD result = ERROR_SUCCESS;
|
||
|
PVOID data;
|
||
|
PBYTE ptr;
|
||
|
BSTR tmp;
|
||
|
CComBSTR channelName;
|
||
|
|
||
|
ASSERT(IsValid());
|
||
|
|
||
|
ASSERT(msg != NULL);
|
||
|
|
||
|
hdr = (PREMOTEDESKTOP_CHANNELBUFHEADER)msg;
|
||
|
|
||
|
#ifdef USE_MAGICNO
|
||
|
ASSERT(hdr->magicNo == CHANNELBUF_MAGICNO);
|
||
|
#endif
|
||
|
|
||
|
|
||
|
//
|
||
|
// Initialize the channel buf.
|
||
|
//
|
||
|
channelBuf.buf = NULL;
|
||
|
|
||
|
//
|
||
|
// Get the channel name.
|
||
|
//
|
||
|
tmp = SysAllocStringByteLen(NULL, hdr->channelNameLen);
|
||
|
if (tmp == NULL) {
|
||
|
TRC_ERR((TB, TEXT("Can't allocate channel name.")));
|
||
|
goto CLEANUPANDEXIT;
|
||
|
}
|
||
|
ptr = (PBYTE)(hdr + 1);
|
||
|
memcpy(tmp, ptr, hdr->channelNameLen);
|
||
|
channelName.Attach(tmp);
|
||
|
|
||
|
ThreadLock();
|
||
|
|
||
|
//
|
||
|
// Find the corresponding channel.
|
||
|
//
|
||
|
#ifdef USE_MAGICNO
|
||
|
ASSERT(hdr->magicNo == CHANNELBUF_MAGICNO);
|
||
|
#endif
|
||
|
|
||
|
channel = m_ChannelMap.find(channelName);
|
||
|
if (channel == m_ChannelMap.end()) {
|
||
|
TRC_ALT((TB, L"Data received for non-existent channel %s",
|
||
|
channelName.m_str));
|
||
|
result = E_FAIL;
|
||
|
ThreadUnlock();
|
||
|
goto CLEANUPANDEXIT;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Copy the incoming data buffer.
|
||
|
//
|
||
|
ptr += hdr->channelNameLen;
|
||
|
channelBuf.len = hdr->dataLen;
|
||
|
channelBuf.buf = SysAllocStringByteLen(NULL, channelBuf.len);
|
||
|
if (channelBuf.buf == NULL) {
|
||
|
TRC_ERR((TB, TEXT("Can't allocate %ld bytes for buf."), channelBuf.len));
|
||
|
result = E_FAIL;
|
||
|
ThreadUnlock();
|
||
|
goto CLEANUPANDEXIT;
|
||
|
}
|
||
|
memcpy(channelBuf.buf, ptr, hdr->dataLen);
|
||
|
|
||
|
//
|
||
|
// Add to the channel's input queue.
|
||
|
//
|
||
|
try {
|
||
|
(*channel).second->inputBufferQueue.push_back(channelBuf);
|
||
|
}
|
||
|
catch(CRemoteDesktopException x) {
|
||
|
result = x.m_ErrorCode;
|
||
|
ASSERT(result != ERROR_SUCCESS);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Notify the interface that data is ready.
|
||
|
//
|
||
|
if (result == ERROR_SUCCESS) {
|
||
|
(*channel).second->channelObject->DataReady();
|
||
|
|
||
|
#if DBG
|
||
|
(*channel).second->bytesRead += hdr->dataLen;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
ThreadUnlock();
|
||
|
|
||
|
CLEANUPANDEXIT:
|
||
|
|
||
|
if ((result != ERROR_SUCCESS) && (channelBuf.buf != NULL)) {
|
||
|
SysFreeString(channelBuf.buf);
|
||
|
}
|
||
|
|
||
|
DC_END_FN();
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|