/**MOD+**********************************************************************/ /* Module: vchannel.cpp */ /* */ /* Purpose: internal handling of the exposed virtual channel interfaces */ /* */ /* Copyright(C) Microsoft Corporation 1999 */ /* */ /****************************************************************************/ #include "stdafx.h" #include "atlwarn.h" //IDL generated header #include "mstsax.h" #include "mstscax.h" #include "vchannel.h" #include "cleanup.h" #define TRC_GROUP TRC_GROUP_UI #define TRC_FILE "vchannel" #include CVChannels::CVChannels() { DC_BEGIN_FN("~CVChannels"); _pChanInfo = NULL; _pEntryPoints = NULL; _dwConnectState = NOTHING; _phInitHandle = NULL; _ChanCount = NULL; _hwndControl = NULL; DC_END_FN(); } CVChannels::~CVChannels() { UINT i; DC_BEGIN_FN("~CVChannels"); if (_pChanInfo) { // // Free any incomplete channel receive buffers // for (i=0; i<_ChanCount; i++) { if (_pChanInfo[i].CurrentlyReceivingData.pData) { SysFreeString((BSTR)_pChanInfo[i].CurrentlyReceivingData.pData); _pChanInfo[i].CurrentlyReceivingData.pData = NULL; } } LocalFree(_pChanInfo); _pChanInfo = NULL; } DC_END_FN(); } /***************************************************************************** * * Routine Description: * Return the channel index for a given open channel handle * * Arguments: * dwHandle - handle to the channel * * Return Value: * index to the channel in the _pChanInfo array or -1 if not found * *****************************************************************************/ DCINT CVChannels::ChannelIndexFromOpenHandle(DWORD dwHandle) { DCUINT i; DC_BEGIN_FN("ChannelIndexFromOpenHandle"); TRC_ASSERT((_pChanInfo), (TB,_T("_pChanInfo is NULL"))); if (!_pChanInfo) { DC_QUIT; } for (i=0;i<_ChanCount;i++) { if (_pChanInfo[i].dwOpenHandle == dwHandle) { return i; } } DC_END_FN(); DC_EXIT_POINT: return -1; } /***************************************************************************** * * Routine Description: * Return the channel index for a given channel name * * Arguments: * szChanName - name of channel * * Return Value: * index to the channel in the _pChanInfo array or -1 if not found * *****************************************************************************/ DCINT CVChannels::ChannelIndexFromName(PDCACHAR szChanName) { DCUINT i; DC_BEGIN_FN("ChannelIndexFromName"); TRC_ASSERT((_pChanInfo), (TB,_T("_pChanInfo is NULL"))); TRC_ASSERT((szChanName), (TB,_T("szChanName is NULL"))); if (!_pChanInfo || !szChanName) { DC_QUIT; } for (i=0;i<_ChanCount;i++) { if (!DC_ASTRNCMP(_pChanInfo[i].chanName,szChanName, sizeof(_pChanInfo[i].chanName))) { return i; } } DC_END_FN(); DC_EXIT_POINT: return -1; } /***************************************************************************** * * Routine Description: * Sends data on a given virtual channel * * Arguments: * chanIndex : index of the channel to send on * pdata : pointer to the data * datalength : length of data * * Return Value: * nothing. write is asynchronous so no notification at this point * *****************************************************************************/ DCBOOL CVChannels::SendDataOnChannel(DCUINT chanIndex, LPVOID pdata, DWORD datalength) { DC_BEGIN_FN("SendDataOnNamedChannel"); DCBOOL bRetVal = TRUE; if (_dwConnectState != NON_V1_CONNECT) { TRC_DBG((TB,_T("MsTscAx Vchannel: Error: SendDataOnNamedChannel when not connected\n"))); return FALSE; } TRC_ASSERT((_pEntryPoints), (TB,_T("_pEntryPoints is NULL"))); if (!_pEntryPoints) { bRetVal = FALSE; DC_QUIT; } TRC_ASSERT((chanIndex < _ChanCount), (TB,_T("chanIndex out of range!!!"))); if (chanIndex >= _ChanCount) { TRC_DBG((TB,_T("MsTscAx Vchannel: chanIndex out of range\n"))); bRetVal = FALSE; DC_QUIT; } if (!_pChanInfo[chanIndex].fIsOpen || !_pChanInfo[chanIndex].fIsValidChannel) { TRC_DBG((TB,_T("MsTscAx Vchannel: channel not open or invalid channel\n"))); bRetVal = FALSE; DC_QUIT; } if (CHANNEL_RC_OK != _pEntryPoints->pVirtualChannelWriteEx(_phInitHandle, _pChanInfo[chanIndex].dwOpenHandle, pdata, datalength, pdata)) { bRetVal = FALSE; DC_QUIT; } // // pdata will be freed when a write complete notification is received // DC_EXIT_POINT: DC_END_FN(); return bRetVal; } /***************************************************************************** * * Routine Description: * Handle a data received notification * * Arguments: * chanIndex : index to the channel * pdata : if the event was data received, then this is the pointer * to data * datalength : length of data available * totalLength : totallength sent by server at a time. * dataFlags : Not Used * * Return Value: * TRUE if data was successfully received * *****************************************************************************/ DCBOOL CVChannels::HandleReceiveData(IN DCUINT chanIndex, IN LPVOID pdata, IN UINT32 dataLength, IN UINT32 totalLength, IN UINT32 dataFlags) { DCBOOL bRetVal = TRUE; DC_BEGIN_FN("HandleReceiveData"); TRC_ASSERT((chanIndex < _ChanCount), (TB,_T("chanIndex out of range!!!"))); if (chanIndex >= _ChanCount) { TRC_DBG((TB,_T("MsTscAx Vchannel: chanIndex out of range\n"))); DC_QUIT; } // // Server request has been received by DLL. Read it and store it // for later use. // if (dataFlags & CHANNEL_FLAG_FIRST) { TRC_DBG((TB,_T("MsTscAx Vchannel: Data Received first chunk\n"))); PCHANDATA pReceivedData = &_pChanInfo[chanIndex].CurrentlyReceivingData; pReceivedData->chanDataState = dataIncompleteAssemblingChunks; pReceivedData->dwDataLen = totalLength; // // The data buffer is stored in a BSTR // because it eventually gets handed out to the caller // in an out parameter (the caller frees) // TRC_ASSERT((NULL == _pChanInfo[chanIndex].CurrentlyReceivingData.pData), (TB,_T("_pChanInfo[chanIndex].CurrentlyReceivingData.pData is NOT NULL.") \ _T("Are we losing received data?"))); pReceivedData->pData = (LPVOID) SysAllocStringByteLen(NULL, totalLength); if(!pReceivedData->pData) { LocalFree(pReceivedData); TRC_ERR((TB,_T("Failed to allocate BSTR for received data in HandleReceiveData\n"))); DC_QUIT; } DC_MEMCPY( pReceivedData->pData, pdata, dataLength); pReceivedData->pCurWritePointer = (LPBYTE)pReceivedData->pData + dataLength; if (dataFlags & CHANNEL_FLAG_LAST) { // // chunk is both first and last, we're done // pReceivedData->chanDataState = dataReceivedComplete; } } else // middle or last block { TRC_ASSERT((_pChanInfo[chanIndex].CurrentlyReceivingData.pData), (TB,_T("_pChanInfo[chanIndex].CurrentlyReceivingData.pData is NULL.") \ _T("While receiving CHANNEL_FLAG_MIDDLE data!!!!"))); PCHANDATA pReceivedData = &_pChanInfo[chanIndex].CurrentlyReceivingData; TRC_ASSERT((pReceivedData->pData && pReceivedData->pCurWritePointer), (TB,_T("_pChanInfo[chanIndex].pCurrentlyReceivingData write pointer(s) are NULL."))); if (!pReceivedData->pData || !pReceivedData->pCurWritePointer) { bRetVal = FALSE; DC_QUIT; } // // Sanity check that the write pointer is within the data buffer // LPBYTE pEnd = (LPBYTE)pReceivedData->pData + pReceivedData->dwDataLen; if (pReceivedData->pCurWritePointer < (LPBYTE)pReceivedData->pData || pReceivedData->pCurWritePointer + dataLength > pEnd) { TRC_ASSERT(0,(TB,_T("pCurWritePointer is outside valid range"))); bRetVal = FALSE; DC_QUIT; } DC_MEMCPY( pReceivedData->pCurWritePointer, pdata, dataLength); pReceivedData->pCurWritePointer += dataLength; if (dataFlags & CHANNEL_FLAG_LAST) { // // chunk is both first and last, we're done // pReceivedData->chanDataState = dataReceivedComplete; } } // // If a complete chunk was received add it to the receive list // if (dataReceivedComplete == _pChanInfo[chanIndex].CurrentlyReceivingData.chanDataState ) { //Non blocking read, notify the window so it can //fire an event to the container if (_hwndControl) { PostMessage( _hwndControl, WM_VCHANNEL_DATARECEIVED, (WPARAM)chanIndex, (LPARAM)_pChanInfo[chanIndex].CurrentlyReceivingData.pData); } _pChanInfo[chanIndex].CurrentlyReceivingData.chanDataState = dataIncompleteAssemblingChunks; _pChanInfo[chanIndex].CurrentlyReceivingData.dwDataLen = 0; _pChanInfo[chanIndex].CurrentlyReceivingData.pData = NULL; } DC_EXIT_POINT: DC_END_FN(); return bRetVal; } VOID WINAPI CVChannels::IntVirtualChannelOpenEventEx( IN DWORD openHandle, IN UINT event, IN LPVOID pdata, IN UINT32 dataLength, IN UINT32 totalLength, IN UINT32 dataFlags) { DC_BEGIN_FN("IntVirtualChannelOpenEventEx"); DCUINT chanIndex = -1; TRC_ASSERT((_pChanInfo), (TB,_T("_pChanInfo is NULL"))); if (!_pChanInfo) { DC_QUIT; } chanIndex = ChannelIndexFromOpenHandle(openHandle); if (-1 == chanIndex) { TRC_DBG((TB,_T("MsTscAx Vchannel: openHandle does not map to any known channel structure\n"))); DC_QUIT; } TRC_ASSERT((chanIndex < _ChanCount), (TB,_T("chanIndex out of range!!!"))); if (chanIndex >= _ChanCount) { TRC_DBG((TB,_T("MsTscAx Vchannel: chanIndex out of range\n"))); DC_QUIT; } switch (event) { case CHANNEL_EVENT_DATA_RECEIVED: // // Receive and re-assemble data if necessary // HandleReceiveData(chanIndex, pdata, dataLength, totalLength, dataFlags); break; case CHANNEL_EVENT_WRITE_CANCELLED: TRC_DBG((TB,_T("MsTscAx Vchannel: Write cancelled\n"))); // No BREAK HERE. case CHANNEL_EVENT_WRITE_COMPLETE: // // A write has completed. // All we have to do is free the data buffer // pdata is the send buffer // TRC_ASSERT((pdata), (TB,_T("pdata is NULL on WRITE_COMPLETE/CANCELED"))); if (pdata) { LocalFree((HLOCAL) pdata); } break; default: TRC_DBG((TB,_T("MsTscAx Vchannel: unrecognized open event\n"))); break; } DC_EXIT_POINT: DC_END_FN(); } VOID VCAPITYPE CVChannels::IntVirtualChannelInitEventProcEx( IN LPVOID pInitHandle, IN UINT event, IN LPVOID pData, IN UINT dataLength) { UINT ui; UINT i; UNREFERENCED_PARAMETER(pInitHandle); UNREFERENCED_PARAMETER(pData); UNREFERENCED_PARAMETER(dataLength); DC_BEGIN_FN("IntVirtualChannelInitEventProc"); TRC_ASSERT((_pChanInfo), (TB,_T("_pChanInfo is NULL"))); if (!_pChanInfo) { DC_QUIT; } TRC_ASSERT((_pEntryPoints), (TB,_T("_pEntryPoints is NULL"))); if (!_pEntryPoints) { DC_QUIT; } switch (event) { case CHANNEL_EVENT_INITIALIZED: TRC_DBG((TB,_T("MsTscAx Vchannel: channel initialized\n"))); break; case CHANNEL_EVENT_CONNECTED: // // We have been connected to a server // _dwConnectState=NON_V1_CONNECT; TRC_DBG((TB,_T("MsTscAx Vchannel: channel connected\n"))); for (i=0; i< _ChanCount; i++) { // // open channel // if(_pChanInfo[i].fIsValidChannel) { ui = _pEntryPoints->pVirtualChannelOpenEx(_phInitHandle, &_pChanInfo[i].dwOpenHandle, _pChanInfo[i].chanName, (PCHANNEL_OPEN_EVENT_EX_FN) VirtualChannelOpenEventEx); if (ui != CHANNEL_RC_OK) { TRC_DBG((TB,_T("MsTscAx Vchannel: virtual channel open failed\n"))); continue; } _pChanInfo[i].fIsOpen = TRUE; } } break; case CHANNEL_EVENT_V1_CONNECTED: // // So nothing can be done in this case. // _dwConnectState=V1_CONNECT; TRC_DBG((TB,_T("MsTscAx Vchannel: v1 connected\n"))); break; case CHANNEL_EVENT_DISCONNECTED: // // Disconnected from the Server so cleanup // TRC_DBG((TB,_T("MsTscAx Vchannel: disconnected\n"))); if (_dwConnectState==NON_V1_CONNECT) { for (i=0; i< _ChanCount; i++) { // // Close the channel // if(_pChanInfo[i].fIsValidChannel) { _pEntryPoints->pVirtualChannelCloseEx(_phInitHandle, _pChanInfo[i].dwOpenHandle); _pChanInfo[i].fIsOpen = FALSE; } } } _dwConnectState=NOTHING; break; case CHANNEL_EVENT_TERMINATED: // // This means that process is exiting. So cleanup the memory // TRC_DBG((TB,_T("MsTscAx Vchannel: Terminated\n"))); if (_pEntryPoints!=NULL) { LocalFree((HLOCAL)_pEntryPoints); _pEntryPoints=NULL; } break; default: TRC_DBG((TB,_T("MsTscAx Vchannel: unrecognized init event\n"))); break; } DC_EXIT_POINT: DC_END_FN(); } BEGIN_EXTERN_C /***************************************************************************** * * Routine Description: * Virtual Channel Entry function. This is the first function called to * start a virtual channel * * Arguments: * pEntryPoDCINTs : pointer to a PCHANNEL_ENTRY_POINT which contains * information about this virtual channel * * Return Value: * TRUE/FALSE : Depending on success of function. * *****************************************************************************/ BOOL VCAPITYPE MSTSCAX_VirtualChannelEntryEx(IN PCHANNEL_ENTRY_POINTS_EX pEntryPoints, PVOID pInitHandle) { CHANNEL_DEF cd[CHANNEL_MAX_COUNT]; UINT uRet; UINT i = 0; HRESULT hr; DC_BEGIN_FN("MSTSCAX_virtualchannelentryEx"); if(!pInitHandle) { return FALSE; } PCHANNEL_INIT_HANDLE pChanInitHandle = (PCHANNEL_INIT_HANDLE)pInitHandle; CMsTscAx* pAxCtl = (CMsTscAx*)pChanInitHandle->lpInternalAddinParam; if(!pAxCtl) { return FALSE; } CVChannels* pVChans = &pAxCtl->_VChans; pVChans->_phInitHandle = pInitHandle; // // allocate memory // // // Check if the CHANINFO structures have been set up by the web control // if not then it means virtual channels are not requested // if (!pVChans->_pChanInfo || !pVChans->_ChanCount) { TRC_ALT((TB,_T("Returning FALSE. No channels requested\n"))); return FALSE; } pVChans->_pEntryPoints = (PCHANNEL_ENTRY_POINTS_EX) LocalAlloc(LPTR, pEntryPoints->cbSize); if (pVChans->_pEntryPoints == NULL) { TRC_ERR((TB,_T("MsTscAx: LocalAlloc failed\n"))); DC_END_FN(); return FALSE; } memcpy(pVChans->_pEntryPoints, pEntryPoints, pEntryPoints->cbSize); // // initialize CHANNEL_DEF structures // ZeroMemory(&cd, sizeof(cd)); // // Get comma separated channel names // for (i=0; i< pVChans->_ChanCount;i++) { hr = StringCchCopyA(cd[i].name, sizeof(cd[i].name), //ANSI buffer pVChans->_pChanInfo[i].chanName); if (SUCCEEDED(hr)) { cd[i].options = pVChans->_pChanInfo[i].channelOptions; } else { TRC_ERR((TB,_T("StringCchCopy error: 0x%x"), hr)); return FALSE; } } // // register channels // uRet = pVChans->_pEntryPoints->pVirtualChannelInitEx( (LPVOID) pVChans, pVChans->_phInitHandle, (PCHANNEL_DEF)&cd, pVChans->_ChanCount, VIRTUAL_CHANNEL_VERSION_WIN2000, (PCHANNEL_INIT_EVENT_EX_FN) VirtualChannelInitEventProcEx); // // make sure channel(s) were initialized // if (CHANNEL_RC_OK == uRet) { for(i=0;i_ChanCount;i++) { pVChans->_pChanInfo[i].fIsValidChannel = ((cd[i].options & CHANNEL_OPTION_INITIALIZED) ? TRUE : FALSE); //Update the vc options so they can be retreived from script pVChans->_pChanInfo[i].channelOptions = cd[i].options; } } else { LocalFree((HLOCAL)pVChans->_pEntryPoints); pVChans->_pEntryPoints=NULL; DC_END_FN(); return FALSE; } pVChans->_dwConnectState=NOTHING; DC_END_FN(); return TRUE; } /***************************************************************************** * * Routine Description: * Virtual Channel Open callback function. * * Arguments: * openHandle : specifies which of the channels was opened * event : Kind of event that has occured * pdata : if the event was data received, then this is the pointer * to data * datalength : length of data available * totalLength : totallength sent by server at a time. * dataFlags : Not Used * * Return Value: * None * *****************************************************************************/ VOID WINAPI VirtualChannelOpenEventEx(IN LPVOID lpParam, IN DWORD openHandle, IN UINT event, IN LPVOID pdata, IN UINT32 dataLength, IN UINT32 totalLength, IN UINT32 dataFlags) { DC_BEGIN_FN("IntVirtualChannelOpenEvent"); TRC_ASSERT((lpParam), (TB,_T("lpParam is NULL"))); if(lpParam) { CVChannels* pVChan = (CVChannels*)lpParam; pVChan->IntVirtualChannelOpenEventEx( openHandle, event ,pdata, dataLength, totalLength, dataFlags); } DC_END_FN(); } /***************************************************************************** * * Routine Description: * Virtual Channel Init callback function. * * Arguments: * pInitHandle : Not Used * event : Kind of event that has occured * pdata : Not Used * datalength : Not Used * * Return Value: * None * *****************************************************************************/ VOID VCAPITYPE VirtualChannelInitEventProcEx( IN LPVOID lpParam, IN LPVOID pInitHandle, IN UINT event, IN LPVOID pData, IN UINT dataLength) { UNREFERENCED_PARAMETER(pInitHandle); UNREFERENCED_PARAMETER(pData); UNREFERENCED_PARAMETER(dataLength); DC_BEGIN_FN("VirtualChannelInitEventProc"); TRC_ASSERT((lpParam), (TB,_T("lpParam is NULL"))); if(!lpParam) { return; } CVChannels* pVChan = (CVChannels*)lpParam; pVChan->IntVirtualChannelInitEventProcEx( pInitHandle, event, pData, dataLength); DC_END_FN(); } END_EXTERN_C