|
|
// ChatCtl.cpp : Implementation of DLL Exports.
// Note: Proxy/Stub Information
// To build a separate proxy/stub DLL,
// run nmake -f ChatCtlps.mk in the project directory.
#include "precomp.h"
#include "NmCtl1.h"
#include "Comboboxex.h"
#include <confguid.h>
BYTE szStr[MAX_PATH]; GCCRequestTag GccTag;
extern CChatObj *g_pChatObj; extern CNmChatCtl *g_pChatWindow; extern HANDLE g_hWorkThread;
GUID guidNM2Chat = { 0x340f3a60, 0x7067, 0x11d0, { 0xa0, 0x41, 0x44, 0x45, 0x53, 0x54, 0x0, 0x0 } }; #define cbKeyApp (4 + 1 + sizeof(GUID) + sizeof(DWORD))
static unsigned char H221IDGUID[5] = {H221GUIDKEY0, H221GUIDKEY1, H221GUIDKEY2, H221GUIDKEY3, H221GUIDKEY4};
static BYTE s_keyApp[cbKeyApp];
// Create an H.221 application key with a guid
VOID CreateH221AppKeyFromGuid(LPBYTE lpb, GUID * pguid) { CopyMemory(lpb, H221IDGUID, sizeof(H221IDGUID)); CopyMemory(lpb + sizeof(H221IDGUID), pguid, sizeof(GUID)); }
/* S E T A P P K E Y */ /*----------------------------------------------------------------------------
%%Function: SetAppKey
Set the two pieces of an OctetString (the length and the data.) Note that the length always includes the terminating null character. ----------------------------------------------------------------------------*/ VOID SetAppKey(LPOSTR pOct, LPBYTE lpb) { pOct->length = cbKeyApp; pOct->value = lpb; }
/* C R E A T E A P P K E Y */ /*----------------------------------------------------------------------------
%%Function: CreateAppKey
Given a guid and a userid, create the appropriate application key.
The key is formated as: 0xB5 0x00 0x53 0x4C - Microsoft Object Identifier 0x01 - guid identifier <binary guid> - guid data <dword node id> - user node id ----------------------------------------------------------------------------*/ VOID CreateAppKey(LPBYTE lpb, GUID * pguid, DWORD dwUserId) { CreateH221AppKeyFromGuid(lpb, pguid); CopyMemory(lpb + cbKeyApp - sizeof(DWORD), &dwUserId, sizeof(DWORD)); }
#define NODE_ID_ONLY 0x01
#define SEND_ID_ONLY 0x02
#define PRIVATE_SEND_ID_ONLY 0x04
#define WHISPER_ID_ONLY 0x08
#define ALL_IDS 0x10
/*
** Return the array index of the first duplicate copy */ int IsAlreadyInArray(MEMBER_CHANNEL_ID *aArray, MEMBER_CHANNEL_ID *pMember, int nSize, int nFlag) { int i;
for (i = 0; i < nSize; i++) { if (NODE_ID_ONLY == nFlag) { if (aArray[i].nNodeId == pMember->nNodeId) break; } else if (SEND_ID_ONLY == nFlag) { if (aArray[i].nSendId == pMember->nSendId) break; } else if (PRIVATE_SEND_ID_ONLY == nFlag) { if (aArray[i].nPrivateSendId == pMember->nPrivateSendId) break; } else if (WHISPER_ID_ONLY) { if (aArray[i].nWhisperId == pMember->nWhisperId) break; } else if (ALL_IDS == nFlag) { if ((aArray[i].nNodeId == pMember->nNodeId)&& (aArray[i].nSendId == pMember->nSendId)&& (aArray[i].nPrivateSendId == pMember->nPrivateSendId)&& (aArray[i].nWhisperId == pMember->nWhisperId)) break; } } return (i < nSize)?i:-1; }
void ChatTimerProc(HWND hWnd, UINT uMsg, UINT_PTR nTimerID, DWORD dwTime) { if (g_pChatObj) { g_pChatObj->SearchWhisperId(); } }
#include "NmCtlDbg.h"
HINSTANCE g_hInstance;
/////////////////////////////////////////////////////////////////////////////
// DLL Entry Point
extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/) { if (dwReason == DLL_PROCESS_ATTACH) { MyInitDebugModule(); DisableThreadLibraryCalls(hInstance); g_hInstance = hInstance; DBG_INIT_MEMORY_TRACKING(hInstance);
::T120_AppletStatus(APPLET_ID_CHAT, APPLET_LIBRARY_LOADED); } else if (dwReason == DLL_PROCESS_DETACH) { if (NULL != g_hWorkThread) { ::CloseHandle(g_hWorkThread); } ::T120_AppletStatus(APPLET_ID_CHAT, APPLET_LIBRARY_FREED);
DBG_CHECK_MEMORY_TRACKING(hDllInst); MyExitDebugModule(); } return TRUE; // ok
}
//
// T120 Applet Functions
//
void CALLBACK T120AppletCallbackProc ( T120AppletMsg *pMsg ) { CChatObj *pCHATOBJ = (CChatObj *) pMsg->pAppletContext; if (pCHATOBJ == g_pChatObj) { switch (pMsg->eMsgType) { case GCC_PERMIT_TO_ENROLL_INDICATION: pCHATOBJ->OnPermitToEnroll(pMsg->PermitToEnrollInd.nConfID, pMsg->PermitToEnrollInd.fPermissionGranted); break;
case T120_JOIN_SESSION_CONFIRM: default: break; } } }
void CALLBACK T120SessionCallbackProc ( T120AppletSessionMsg *pMsg ) { if(g_pChatObj == NULL) { return; }
CChatObj *pSession = (CChatObj *) pMsg->pSessionContext; ASSERT(pMsg->pAppletContext == pMsg->pSessionContext); if (pSession == g_pChatObj) { ASSERT(pMsg->nConfID == pSession->GetConfID()); switch (pMsg->eMsgType) { case MCS_UNIFORM_SEND_DATA_INDICATION: //
// Check if we are receiving a indication from owrself
//
if(pMsg->SendDataInd.initiator == GET_USER_ID_FROM_MEMBER_ID(g_pChatObj->m_MyMemberID)) { return; } case MCS_SEND_DATA_INDICATION: MCSSendDataIndication( pMsg->SendDataInd.user_data.length, pMsg->SendDataInd.user_data.value, pMsg->SendDataInd.channel_id, pMsg->SendDataInd.initiator); break;
case MCS_TRANSMIT_BUFFER_AVAILABLE_INDICATION: // m_fWaitingForBufferAvailable = FALSE;
break;
case GCC_APP_ROSTER_REPORT_INDICATION: pSession->OnRosterIndication((ULONG) pMsg->AppRosterReportInd.cRosters, pMsg->AppRosterReportInd.apAppRosters); break;
case T120_JOIN_SESSION_CONFIRM: pSession->OnJoinSessionConfirm(&pMsg->JoinSessionConfirm); break;
case GCC_RETRIEVE_ENTRY_CONFIRM: // Asynchronous Registry Retrieve Confirm message
pSession->OnRegistryEntryConfirm(&pMsg->RegistryConfirm); break;
default: break; } } }
CChatObj::CChatObj() : m_pApplet(NULL), m_aMembers(&g_aMembers[0]), m_nTimerID(0) { DBGENTRY(CChatObj::CChatObj);
// Construct GCCAppProtEntityList
::ZeroMemory(&m_ChatProtocolEnt, sizeof(m_ChatProtocolEnt)); m_ChatProtocolEnt.must_be_invoked = TRUE; m_ChatProtocolEnt.number_of_expected_capabilities = 0; m_ChatProtocolEnt.expected_capabilities_list = NULL; m_ChatProtocolEnt.startup_channel_type = MCS_DYNAMIC_MULTICAST_CHANNEL;
// construct the applet key
m_ChatProtocolEnt.session_key.application_protocol_key.key_type = APPLET_H221_NONSTD_KEY; SetAppKey(&m_ChatProtocolEnt.session_key.application_protocol_key.h221_non_standard_id, szStr); ::CreateH221AppKeyFromGuid(szStr, (GUID *)&guidNM2Chat );
// ape list
m_pChatProtocolEnt = &m_ChatProtocolEnt; m_AppProtoEntList.cApes = 1; m_AppProtoEntList.apApes = &m_pChatProtocolEnt;
// broadcast
::ZeroMemory(&m_NodeList, sizeof(m_NodeList));
// Cleanup per-conference T.120 info
CleanupPerConf();
// set the global pointer
g_pChatObj = this;
// T.120 Applet
T120Error rc = ::T120_CreateAppletSAP(&m_pApplet); if (T120_NO_ERROR != rc) { ERROR_OUT(("CChatObj::CChatObj: cannot create applet SAP")); return; }
ASSERT(NULL != m_pApplet); m_pApplet->Advise(T120AppletCallbackProc, this);
DBGEXIT(CChatObj::CChatObj); }
CChatObj::~CChatObj() {
DBGENTRY(CChatObj::~CChatObj);
ASSERT(NULL == m_pAppletSession); ASSERT(NULL == m_pApplet);
delete g_pChatWindow;
DBGEXIT(CChatObj::~CChatObj); }
void CChatObj::LeaveT120(void) { // no more T.120
if (NULL != m_pAppletSession) { m_pAppletSession->ReleaseInterface(); CleanupPerConf(); } if (NULL != m_pApplet) { m_pApplet->ReleaseInterface(); m_pApplet = NULL; } }
void CChatObj::OnPermitToEnroll ( T120ConfID nConfID, BOOL fPermissionGranted ) { if (fPermissionGranted) { // We are not in a conference, right?
ASSERT(NULL == m_pAppletSession);
// Create an applet session
T120Error rc = m_pApplet->CreateSession(&m_pAppletSession, nConfID); if (T120_NO_ERROR == rc) { ASSERT(NULL != m_pAppletSession); m_pAppletSession->Advise(T120SessionCallbackProc, this, this);
// Build join-sesion request
::ZeroMemory(&m_JoinSessionReq, sizeof(m_JoinSessionReq)); m_JoinSessionReq.dwAttachmentFlags = ATTACHMENT_DISCONNECT_IN_DATA_LOSS | ATTACHMENT_MCS_FREES_DATA_IND_BUFFER;
// Non standard key
CreateAppKey(s_keyApp, &guidNM2Chat, 0); GCCObjectKey FAR * pObjKey; pObjKey = &m_JoinSessionReq.SessionKey.application_protocol_key; pObjKey->key_type = GCC_H221_NONSTANDARD_KEY; SetAppKey(&(pObjKey->h221_non_standard_id), s_keyApp);
m_JoinSessionReq.SessionKey.session_id = m_sidMyself; m_JoinSessionReq.fConductingCapable = FALSE; m_JoinSessionReq.nStartupChannelType =MCS_DYNAMIC_MULTICAST_CHANNEL;
//
// Retrieve registry key
//
::ZeroMemory(&m_resourceRequest, sizeof(m_resourceRequest)); m_resourceRequest.eCommand = APPLET_JOIN_DYNAMIC_CHANNEL; m_resourceRequest.RegKey.session_key = m_JoinSessionReq.SessionKey; SetAppKey(&m_resourceRequest.RegKey.resource_id, s_keyApp); m_JoinSessionReq.cResourceReqs = 1; m_JoinSessionReq.aResourceReqs = &m_resourceRequest;
// Join now
rc = m_pAppletSession->Join(&m_JoinSessionReq); if (T120_NO_ERROR == rc) { m_nConfID = nConfID; } else { WARNING_OUT(("CChatObj::OnPermitToEnroll: cannot join conf=%u, rc=%u", nConfID, rc)); } } } else { if (NULL != m_pAppletSession) { T120RegistryRequest Req; ::ZeroMemory(&Req, sizeof(Req)); Req.eCommand = APPLET_DELETE_ENTRY; Req.pRegistryKey = &m_resourceRequest.RegKey; m_pAppletSession->RegistryRequest(&Req);
m_pAppletSession->ReleaseInterface(); CleanupPerConf(); } } }
void CChatObj::OnJoinSessionConfirm ( T120JoinSessionConfirm *pConfirm ) { if (NULL != m_pAppletSession) { ASSERT(m_pAppletSession == pConfirm->pIAppletSession); if (T120_RESULT_SUCCESSFUL == pConfirm->eResult) { m_uidMyself = pConfirm->uidMyself; m_sidMyself = pConfirm->sidMyself; m_eidMyself = pConfirm->eidMyself; m_nidMyself = pConfirm->nidMyself;
// get the broadcast channel
m_broadcastChannel = pConfirm->aResourceReqs[0].nChannelID;
// create member ID
m_MyMemberID = MAKE_MEMBER_ID(m_nidMyself, m_uidMyself);
// we are now in the conference
m_fInConference = TRUE;
if(g_pChatWindow) { g_pChatWindow->_UpdateContainerCaption(); g_pChatWindow->_AddEveryoneInChat(); }
// Invoke applet on other nodes (for interop with NM 2.x)
InvokeApplet();
// Register channel with GCC (for interop with NM 2.x)
T120RegistryRequest Req; GCCRegistryKey registry_key; BYTE SessionKey[cbKeyApp]; BYTE ResourceKey[cbKeyApp];
::ZeroMemory(&Req, sizeof(Req)); Req.eCommand = APPLET_REGISTER_CHANNEL; ::CopyMemory(®istry_key.session_key, &m_resourceRequest.RegKey.session_key, sizeof(GCCSessionKey)); CreateAppKey(SessionKey, &guidNM2Chat, 0); CreateAppKey(ResourceKey, &guidNM2Chat, m_nidMyself); SetAppKey(®istry_key.session_key.application_protocol_key.h221_non_standard_id, SessionKey); SetAppKey(®istry_key.resource_id, ResourceKey); Req.pRegistryKey = ®istry_key; Req.nChannelID = m_uidMyself;
m_pAppletSession->RegistryRequest(&Req); } else { WARNING_OUT(("CChatObj::OnJoinSessionConfirm: failed to join conference, result=%u. error=%u", pConfirm->eResult, pConfirm->eError)); m_pAppletSession->ReleaseInterface(); CleanupPerConf(); } } }
void CChatObj::InvokeApplet(void) { m_ChatProtocolEnt.session_key.session_id = m_sidMyself; if (m_pAppletSession) { m_pAppletSession->InvokeApplet(&m_AppProtoEntList, &m_NodeList, &GccTag); } }
void CChatObj::OnRosterIndication ( ULONG cRosters, GCCAppRoster *apRosters[] ) { if (IsInConference()) { BOOL fAdded = FALSE; BOOL fRemoved = FALSE; ULONG cOtherMembers = 0; ULONG i, j, k;
// Caculate how many members in this session
for (i = 0; i < cRosters; i++) { GCCAppRoster *pRoster = apRosters[i];
// bail out if this roster is not for this session
if (pRoster->session_key.session_id != m_sidMyself) { continue; }
// node added or removed?
fAdded |= pRoster->nodes_were_added; fRemoved |= pRoster->nodes_were_removed;
// parse the roster records
for (j = 0; j < pRoster->number_of_records; j++) { GCCAppRecord *pRecord = pRoster->application_record_list[j]; // Because the flag is_enrolled_actively is not set correctly in
// NM 2.11, we don't bother to check it.
// MEMBER_ID nMemberID = MAKE_MEMBER_ID(pRecord->node_id, pRecord->application_user_id);
if (pRecord->node_id != m_nidMyself) { cOtherMembers++; } } // for
} // for
// If there are changes, we then do the update
if (fAdded || fRemoved || cOtherMembers != g_pChatWindow->m_cOtherMembers) { MEMBER_CHANNEL_ID aTempMembers[MAX_MEMBERS]; // scratch copy
// make sure we are able to handle it
if (cOtherMembers >= MAX_MEMBERS) { ERROR_OUT(("CChatObj::OnRosterIndication: we hit the max members limit, cOtherMembers=%u, max-members=%u", cOtherMembers, MAX_MEMBERS)); cOtherMembers = MAX_MEMBERS; }
// reset the flags for members added and removed
fAdded = FALSE; fRemoved = FALSE;
// copy the members
ULONG idxTempMember = 0; for (i = 0; i < cRosters; i++) { GCCAppRoster *pRoster = apRosters[i];
// bail out if this roster is not for this session
if (pRoster->session_key.session_id != m_sidMyself) { continue; }
// parse the roster records
for (j = 0; j < pRoster->number_of_records; j++) { GCCAppRecord *pRecord = pRoster->application_record_list[j]; // Because of a bug in NM2.11, we don't check flag is_enrolled_actively
// MEMBER_ID nMemberID = MAKE_MEMBER_ID(pRecord->node_id, pRecord->application_user_id);
if (pRecord->node_id != m_nidMyself && idxTempMember < cOtherMembers) { aTempMembers[idxTempMember].nNodeId = pRecord->node_id; aTempMembers[idxTempMember].nSendId = aTempMembers[idxTempMember].nPrivateSendId = aTempMembers[idxTempMember].nWhisperId = pRecord->application_user_id; idxTempMember++;
// let's see if it is an 'add' or a 'delete'
for (k = 0; k < g_pChatWindow->m_cOtherMembers; k++) { if (m_aMembers[k].nNodeId == pRecord->node_id) { ::ZeroMemory(&m_aMembers[k], sizeof(MEMBER_CHANNEL_ID)); break; } } fAdded |= (k >= g_pChatWindow->m_cOtherMembers); // not found, must be new
} } // for
} // for
// sanity check
ASSERT(idxTempMember == cOtherMembers);
// see if there are ones that are not in the new roster.
// if so, they must be removed.
for (k = 0; k < g_pChatWindow->m_cOtherMembers; k++) { if (m_aMembers[k].nNodeId) { fRemoved = TRUE; g_pChatWindow->_RemoveMember(&m_aMembers[k]); } }
// now, update the member array
g_pChatWindow->m_cOtherMembers = cOtherMembers; if ( g_pChatWindow->m_cOtherMembers) { ASSERT(sizeof(m_aMembers[0]) == sizeof(aTempMembers[0])); ::CopyMemory(&m_aMembers[0], &aTempMembers[0], g_pChatWindow->m_cOtherMembers * sizeof(m_aMembers[0]));
// Setup Send Channel Id
int nDuplicates = 0; for (k = 0; k < g_pChatWindow->m_cOtherMembers; k++) { int nIndex = IsAlreadyInArray(m_aMembers, &m_aMembers[k], k, NODE_ID_ONLY); if (nIndex >= 0) { m_aMembers[nIndex].nSendId = m_aMembers[k].nSendId; nDuplicates++; m_aMembers[k].nNodeId = 0; } }
// Remove all zeroed out regions
if (nDuplicates) { k = 0; while (k < g_pChatWindow->m_cOtherMembers) { if (0 == m_aMembers[k].nNodeId) { for (i = k + 1; i < g_pChatWindow->m_cOtherMembers; i++) { if (m_aMembers[i].nNodeId) break; } if (i < g_pChatWindow->m_cOtherMembers) { m_aMembers[k] = m_aMembers[i]; m_aMembers[i].nNodeId = 0; } } k++; } } g_pChatWindow->m_cOtherMembers -= nDuplicates;
// Get the current selection
MEMBER_CHANNEL_ID *pMemberID = (MEMBER_CHANNEL_ID*)g_pChatWindow->_GetSelectedMember();
// Add the members to the list
g_pChatWindow->_DeleteAllListItems(); g_pChatWindow->_AddEveryoneInChat(); for (k = 0; k < g_pChatWindow->m_cOtherMembers; k++) { g_pChatWindow->_AddMember(&m_aMembers[k]); }
// Remove the bogus whisperId for Nm 2.x nodes
BOOL fHasNM2xNode = FALSE; for (k = 0; k < g_pChatWindow->m_cOtherMembers; k++) { if (T120_GetNodeVersion(m_nConfID, m_aMembers[k].nNodeId) < 0x0404) { // Version 2.x, give it a whisper id of 0
m_aMembers[k].nWhisperId = 0; fHasNM2xNode = TRUE; } }
if ((fHasNM2xNode)&&(!m_nTimerID)) { // time out every 1 sec
m_nTimerID = ::SetTimer(NULL, 0, 1000, ChatTimerProc); }
//
// Goto the current selection, if it is still there.
//
i = ComboBoxEx_FindMember(g_pChatWindow->GetMemberList(), 0, pMemberID); if(i == -1 ) { i = 0; } ComboBoxEx_SetCurSel( g_pChatWindow->GetMemberList(), i );
}
g_pChatWindow->_UpdateContainerCaption();
} // if any change
} // if is in conf
}
void CChatObj::OnRegistryEntryConfirm(GCCRegistryConfirm *pRegistryConfirm) { BOOL fAllFound = TRUE; // This is generated by "m_pAppletSession->RegistryRequest(&Req)" above to
// retrieve the channel id number of NM 2.x nodes
if (T120_RESULT_SUCCESSFUL == pRegistryConfirm->nResult) { // Update the m_aWhisperIds array.
T120NodeID nNodeId; ::CopyMemory(&nNodeId, pRegistryConfirm->pRegKey->resource_id.value + cbKeyApp - sizeof(DWORD), sizeof(T120NodeID)); T120ChannelID nChannelId = pRegistryConfirm->pRegItem->channel_id; WARNING_OUT(("Receive registry: node id 0x%x, channel id 0x%x.\n", nNodeId, nChannelId)); for (ULONG k = 0; k < g_pChatWindow->m_cOtherMembers; k++) { if (m_aMembers[k].nNodeId == nNodeId) { m_aMembers[k].nWhisperId = nChannelId; } if (fAllFound && (0 == m_aMembers[k].nWhisperId)) { fAllFound = FALSE; WARNING_OUT(("Node 0x%x is still not updated.\n", m_aMembers[k].nNodeId)); } } if (fAllFound) { ::KillTimer(NULL, m_nTimerID); m_nTimerID = 0; WARNING_OUT(("All updated. Kill timer.\n")); } } }
void CChatObj::CleanupPerConf(void) { m_fInConference = FALSE; m_pAppletSession = NULL; m_MyMemberID = 0; m_nConfID = 0; // Conf ID
m_uidMyself = 0; // User ID
m_sidMyself = 0; // Session ID
m_eidMyself = 0; // Entity ID
m_nidMyself = 0; // Node ID
if(g_pChatWindow) { g_pChatWindow->m_cOtherMembers = 0; g_pChatWindow->_UpdateContainerCaption(); g_pChatWindow->_DeleteAllListItems(); } }
T120Error CChatObj::SendData ( T120UserID userID, ULONG cbDataSize, PBYTE pbData ) { T120Error rc;
if (IsInConference()) { rc = m_pAppletSession->SendData( NORMAL_SEND_DATA, userID, APPLET_LOW_PRIORITY, pbData, cbDataSize, APP_ALLOCATION); } else { rc = GCC_CONFERENCE_NOT_ESTABLISHED; }
return rc; }
void CChatObj::SearchWhisperId(void) { T120RegistryRequest Req; GCCRegistryKey registry_key; BYTE SessionKey[cbKeyApp]; BYTE ResourceKey[cbKeyApp];
if (NULL != m_pAppletSession) { // Set up T120RegistryRequest
ZeroMemory(&Req, sizeof(Req)); Req.eCommand = APPLET_RETRIEVE_ENTRY; ::CopyMemory(®istry_key.session_key, &m_resourceRequest.RegKey.session_key, sizeof(GCCSessionKey)); CreateAppKey(SessionKey, &guidNM2Chat, 0); SetAppKey(®istry_key.session_key.application_protocol_key.h221_non_standard_id, SessionKey); SetAppKey(®istry_key.resource_id, ResourceKey); Req.pRegistryKey = ®istry_key;
for (ULONG i = 0; i < g_pChatWindow->m_cOtherMembers; i++) { if (m_aMembers[i].nWhisperId == 0) { CreateAppKey(ResourceKey, &guidNM2Chat, m_aMembers[i].nNodeId); m_pAppletSession->RegistryRequest(&Req); WARNING_OUT(("Send search registry for node 0x%x.\n", m_aMembers[i].nNodeId)); } } } }
void MCSSendDataIndication(ULONG uSize, LPBYTE pb, T120ChannelID destinationID, T120UserID senderID) { if(g_pChatWindow) { g_pChatWindow->_DataReceived(uSize, pb, destinationID, senderID); } }
|