|
|
/*++
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(); }
|