1308 lines
26 KiB
C++
1308 lines
26 KiB
C++
|
// File: iconf.cpp
|
||
|
|
||
|
#include "precomp.h"
|
||
|
#include "version.h"
|
||
|
#include "imanager.h"
|
||
|
|
||
|
// BUGBUG:
|
||
|
// This is defined as 128 because the RNC_ROSTER structure has the
|
||
|
// same limitation. Investigate what the appropriate number is.
|
||
|
const int MAX_CALLER_NAME = 128;
|
||
|
|
||
|
static const WCHAR _szConferenceNameDefault[] = L"Personal Conference";
|
||
|
|
||
|
|
||
|
static HRESULT OnNotifyStateChanged(IUnknown *pConfNotify, PVOID pv, REFIID riid);
|
||
|
static HRESULT OnNotifyMemberAdded(IUnknown *pConfNotify, PVOID pv, REFIID riid);
|
||
|
static HRESULT OnNotifyMemberUpdated(IUnknown *pConfNotify, PVOID pv, REFIID riid);
|
||
|
static HRESULT OnNotifyMemberRemoved(IUnknown *pConfNotify, PVOID pv, REFIID riid);
|
||
|
static HRESULT OnNotifyNmUI(IUnknown *pConfNotify, PVOID pv, REFIID riid);
|
||
|
|
||
|
|
||
|
static const IID * g_apiidCP[] =
|
||
|
{
|
||
|
{&IID_INmConferenceNotify}
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
CConfObject::CConfObject() :
|
||
|
CConnectionPointContainer(g_apiidCP, ARRAY_ELEMENTS(g_apiidCP)),
|
||
|
m_hConf (NULL),
|
||
|
m_csState (CS_UNINITIALIZED),
|
||
|
m_fConferenceCreated(FALSE),
|
||
|
m_bstrConfName (NULL),
|
||
|
m_fServerMode (FALSE),
|
||
|
m_uMembers (0),
|
||
|
m_ourNodeID (0),
|
||
|
m_pMemberLocal (NULL),
|
||
|
m_uGCCConferenceID (0),
|
||
|
m_fSecure (FALSE),
|
||
|
m_cRef (1)
|
||
|
{
|
||
|
DebugEntry(CConfObject::CConfObject);
|
||
|
|
||
|
DebugExitVOID(CConfObject::CConfObject);
|
||
|
}
|
||
|
|
||
|
CConfObject::~CConfObject()
|
||
|
{
|
||
|
DebugEntry(CConfObject::~CConfObject);
|
||
|
|
||
|
// Empty the participant list:
|
||
|
while (!m_MemberList.IsEmpty())
|
||
|
{
|
||
|
CNmMember * pMember = (CNmMember *) m_MemberList.RemoveHead();
|
||
|
// Shouldn't have any NULL entries:
|
||
|
ASSERT(pMember);
|
||
|
pMember->Release();
|
||
|
}
|
||
|
|
||
|
SysFreeString(m_bstrConfName);
|
||
|
|
||
|
DebugExitVOID(CConfObject::~CConfObject);
|
||
|
}
|
||
|
|
||
|
VOID CConfObject::SetConfName(BSTR bstr)
|
||
|
{
|
||
|
SysFreeString(m_bstrConfName);
|
||
|
m_bstrConfName = SysAllocString(bstr);
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID CConfObject::SetConfSecurity(BOOL fSecure)
|
||
|
{
|
||
|
NM_CONFERENCE_STATE NmState;
|
||
|
|
||
|
m_fSecure = fSecure;
|
||
|
|
||
|
// Force update of the status icon to reflect security
|
||
|
GetState(&NmState);
|
||
|
NotifySink((PVOID) NmState, OnNotifyStateChanged);
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CConfObject::CreateConference(void)
|
||
|
{
|
||
|
DebugEntry(CConfObject::CreateConference);
|
||
|
HRESULT nsRet = E_FAIL;
|
||
|
|
||
|
switch (m_csState)
|
||
|
{
|
||
|
case CS_UNINITIALIZED:
|
||
|
case CS_TERMINATED:
|
||
|
{
|
||
|
if ((NULL == m_bstrConfName) || (0 == *m_bstrConfName))
|
||
|
{
|
||
|
m_bstrConfName = SysAllocString(_szConferenceNameDefault);
|
||
|
}
|
||
|
TRACE_OUT(("CConfObject:CreateConference [%ls]", m_bstrConfName));
|
||
|
|
||
|
ASSERT(g_pNodeController);
|
||
|
ASSERT(NULL == m_hConf);
|
||
|
nsRet = g_pNodeController->CreateConference(
|
||
|
m_bstrConfName,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
0,
|
||
|
m_fSecure,
|
||
|
&m_hConf);
|
||
|
|
||
|
if (0 == nsRet)
|
||
|
{
|
||
|
SetT120State(CS_CREATING);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_hConf = NULL;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
WARNING_OUT(("CConfObject: Can't create - bad state"));
|
||
|
nsRet = E_FAIL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DebugExitINT(CConfObject::CreateConference, nsRet);
|
||
|
return nsRet;
|
||
|
}
|
||
|
|
||
|
HRESULT CConfObject::JoinConference( LPCWSTR pcwszConferenceName,
|
||
|
LPCWSTR pcwszPassword,
|
||
|
LPCSTR pcszAddress,
|
||
|
BOOL fRetry)
|
||
|
{
|
||
|
DebugEntry(CConfObject::JoinConference);
|
||
|
HRESULT nsRet = E_FAIL;
|
||
|
|
||
|
|
||
|
|
||
|
switch (m_csState)
|
||
|
{
|
||
|
case CS_COMING_UP:
|
||
|
{
|
||
|
if (!fRetry)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
// fall through if this is another attempt to join
|
||
|
}
|
||
|
case CS_UNINITIALIZED:
|
||
|
case CS_TERMINATED:
|
||
|
{
|
||
|
TRACE_OUT(("CConfObject: Joining conference..."));
|
||
|
|
||
|
ASSERT(g_pNodeController);
|
||
|
nsRet = g_pNodeController->JoinConference(pcwszConferenceName,
|
||
|
pcwszPassword,
|
||
|
pcszAddress,
|
||
|
m_fSecure,
|
||
|
&m_hConf);
|
||
|
|
||
|
if (0 == nsRet)
|
||
|
{
|
||
|
SetT120State(CS_COMING_UP);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_hConf = NULL;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case CS_GOING_DOWN:
|
||
|
default:
|
||
|
{
|
||
|
WARNING_OUT(("CConfObject: Can't join - bad state"));
|
||
|
// BUGBUG: define return values
|
||
|
nsRet = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DebugExitINT(CConfObject::JoinConference, nsRet);
|
||
|
|
||
|
return nsRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
HRESULT CConfObject::InviteConference( LPCSTR Address,
|
||
|
REQUEST_HANDLE *phRequest )
|
||
|
{
|
||
|
DebugEntry(CConfObject::InviteConference);
|
||
|
HRESULT nsRet = E_FAIL;
|
||
|
ASSERT(phRequest);
|
||
|
|
||
|
switch (m_csState)
|
||
|
{
|
||
|
case CS_RUNNING:
|
||
|
{
|
||
|
TRACE_OUT(("CConfObject: Inviting conference..."));
|
||
|
|
||
|
ASSERT(g_pNodeController);
|
||
|
ASSERT(m_hConf);
|
||
|
m_hConf->SetSecurity(m_fSecure);
|
||
|
nsRet = m_hConf->Invite(Address,
|
||
|
phRequest);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
WARNING_OUT(("CConfObject: Can't invite - bad state"));
|
||
|
nsRet = E_FAIL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DebugExitINT(CConfObject::InviteConference, nsRet);
|
||
|
return nsRet;
|
||
|
}
|
||
|
|
||
|
HRESULT CConfObject::LeaveConference(BOOL fForceLeave)
|
||
|
{
|
||
|
DebugEntry(CConfObject::LeaveConference);
|
||
|
HRESULT nsRet = E_FAIL;
|
||
|
REQUEST_HANDLE hReq = NULL;
|
||
|
|
||
|
switch (m_csState)
|
||
|
{
|
||
|
case CS_GOING_DOWN:
|
||
|
{
|
||
|
// we're already going down
|
||
|
nsRet = S_OK;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case CS_COMING_UP:
|
||
|
case CS_RUNNING:
|
||
|
{
|
||
|
if (FALSE == fForceLeave)
|
||
|
{
|
||
|
COprahNCUI *pOprahNCUI = COprahNCUI::GetInstance();
|
||
|
if (NULL != pOprahNCUI)
|
||
|
{
|
||
|
int nNodes = pOprahNCUI->GetOutgoingCallCount();
|
||
|
|
||
|
if (m_fServerMode || (nNodes > 1) || (m_uMembers > 1))
|
||
|
{
|
||
|
// We are either in the process of calling another node
|
||
|
// or we have other people in our conference roster
|
||
|
TRACE_OUT(("CConfObject: Not leaving (there are other nodes)"));
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TRACE_OUT(("CConfObject: Leaving conference..."));
|
||
|
|
||
|
ASSERT(g_pNodeController);
|
||
|
ASSERT(m_hConf);
|
||
|
|
||
|
SetT120State(CS_GOING_DOWN);
|
||
|
nsRet = m_hConf->Leave();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
WARNING_OUT(("CConfObject: Can't leave - bad state"));
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DebugExitINT(CConfObject::LeaveConference, nsRet);
|
||
|
return nsRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL CConfObject::OnT120Invite(CONF_HANDLE hConference, BOOL fSecure)
|
||
|
{
|
||
|
DebugEntry(CConfObject::OnT120Invite);
|
||
|
|
||
|
BOOL bRet = FALSE;
|
||
|
|
||
|
switch (m_csState)
|
||
|
{
|
||
|
case CS_UNINITIALIZED:
|
||
|
case CS_TERMINATED:
|
||
|
{
|
||
|
TRACE_OUT(("CConfObject: Accepting a conference invitation..."));
|
||
|
|
||
|
ASSERT(g_pNodeController);
|
||
|
ASSERT(NULL == m_hConf);
|
||
|
m_hConf = hConference;
|
||
|
|
||
|
m_fSecure = fSecure;
|
||
|
hConference->SetSecurity(m_fSecure);
|
||
|
|
||
|
// WORKITEM need to issue INmManagerNotify::ConferenceCreated()
|
||
|
SetT120State(CS_COMING_UP);
|
||
|
|
||
|
bRet = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
WARNING_OUT(("CConfObject: Can't accept invite - bad state"));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DebugExitBOOL(CConfObject::OnT120Invite, bRet);
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
BOOL CConfObject::OnRosterChanged(PNC_ROSTER pRoster)
|
||
|
{
|
||
|
DebugEntry(CConfObject::OnRosterChanged);
|
||
|
|
||
|
BOOL bRet = TRUE;
|
||
|
int i;
|
||
|
|
||
|
// REVIEW: Could these be done more efficiently?
|
||
|
|
||
|
if (NULL != pRoster)
|
||
|
{
|
||
|
UINT nExistingParts = 0;
|
||
|
// Allocate an array of markers:
|
||
|
UINT uRosterNodes = pRoster->uNumNodes;
|
||
|
LPBOOL pMarkArray = new BOOL[uRosterNodes];
|
||
|
|
||
|
m_ourNodeID = pRoster->uLocalNodeID;
|
||
|
m_uGCCConferenceID = pRoster->uConferenceID;
|
||
|
|
||
|
if (NULL != pRoster->pwszConferenceName)
|
||
|
{
|
||
|
SysFreeString(m_bstrConfName);
|
||
|
m_bstrConfName = SysAllocString(pRoster->pwszConferenceName);
|
||
|
}
|
||
|
|
||
|
if (NULL != pMarkArray)
|
||
|
{
|
||
|
// Zero out the array:
|
||
|
for (UINT i = 0; i < uRosterNodes; i++)
|
||
|
{
|
||
|
pMarkArray[i] = FALSE;
|
||
|
}
|
||
|
|
||
|
// For all participants still in the roster,
|
||
|
// clear out the reserved flags and
|
||
|
// copy in new UserInfo
|
||
|
POSITION pos = m_MemberList.GetHeadPosition();
|
||
|
while (NULL != pos)
|
||
|
{
|
||
|
CNmMember * pMember = (CNmMember *) m_MemberList.GetNext(pos);
|
||
|
ASSERT(pMember);
|
||
|
pMember->RemovePf(PF_INCONFERENCE);
|
||
|
|
||
|
for (UINT uNode = 0; uNode < uRosterNodes; uNode++)
|
||
|
{
|
||
|
if (pMember->GetGCCID() == pRoster->nodes[uNode].uNodeID)
|
||
|
{
|
||
|
nExistingParts++;
|
||
|
pMarkArray[uNode] = TRUE; // mark this node as "existing member"
|
||
|
pMember->AddPf(PF_INCONFERENCE);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
RemoveOldMembers(m_uMembers - nExistingParts);
|
||
|
|
||
|
if (pRoster->uNumNodes > nExistingParts)
|
||
|
{
|
||
|
#ifdef _DEBUG
|
||
|
UINT nAdded = 0;
|
||
|
#endif // _DEBUG
|
||
|
// At least one participant joined:
|
||
|
// find the new participant(s)
|
||
|
for (UINT uNode = 0; uNode < uRosterNodes; uNode++)
|
||
|
{
|
||
|
if (FALSE == pMarkArray[uNode]) // a new participant?
|
||
|
{
|
||
|
BOOL fLocal = FALSE;
|
||
|
CNmMember * pMember = NULL;
|
||
|
|
||
|
|
||
|
if (m_ourNodeID == pRoster->nodes[uNode].uNodeID)
|
||
|
{
|
||
|
fLocal = TRUE;
|
||
|
}
|
||
|
|
||
|
if (fLocal)
|
||
|
{
|
||
|
pMember = GetLocalMember();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pMember = NULL;
|
||
|
}
|
||
|
|
||
|
if(pMember)
|
||
|
{
|
||
|
#ifdef _DEBUG
|
||
|
nAdded++; // a data participant was effectively added
|
||
|
#endif // _DEBUG
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pMember = CreateMember(
|
||
|
fLocal,
|
||
|
0,
|
||
|
&pRoster->nodes[uNode]);
|
||
|
#ifdef _DEBUG
|
||
|
if (NULL != pMember)
|
||
|
{
|
||
|
nAdded++;
|
||
|
}
|
||
|
#endif // _DEBUG
|
||
|
AddMember(pMember);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// Validate that we did the right thing:
|
||
|
ASSERT(nAdded == (uRosterNodes - nExistingParts));
|
||
|
}
|
||
|
delete pMarkArray;
|
||
|
pMarkArray = NULL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ERROR_OUT(("Couldn't allocate pMarkArray - no roster diff done"));
|
||
|
}
|
||
|
|
||
|
// Check to decide if we should auto-terminate here..
|
||
|
if ((1 == pRoster->uNumNodes) &&
|
||
|
(m_uMembers > 1))
|
||
|
{
|
||
|
if (!m_fServerMode)
|
||
|
{
|
||
|
LeaveConference(FALSE); // don't force (we could be inviting)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
WARNING_OUT(("NULL pRoster passed to CConfObject::OnRosterChanged!"));
|
||
|
}
|
||
|
|
||
|
DebugExitBOOL(CConfObject::OnRosterChanged, bRet);
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
VOID CConfObject::AddMember(CNmMember * pMember)
|
||
|
{
|
||
|
DebugEntry(CConfObject::AddMember);
|
||
|
|
||
|
if (NULL == pMember)
|
||
|
{
|
||
|
ERROR_OUT(("AddMember - null member!"));
|
||
|
goto Done;
|
||
|
}
|
||
|
|
||
|
NM_CONFERENCE_STATE oldNmState, newNmState;
|
||
|
GetState(&oldNmState);
|
||
|
|
||
|
m_MemberList.AddTail(pMember);
|
||
|
m_uMembers++;
|
||
|
|
||
|
CheckState(oldNmState);
|
||
|
|
||
|
NotifySink((INmMember *) pMember, OnNotifyMemberAdded);
|
||
|
|
||
|
Done:
|
||
|
DebugExitVOID(CConfObject::AddMember);
|
||
|
}
|
||
|
|
||
|
VOID CConfObject::RemoveMember(POSITION pos)
|
||
|
{
|
||
|
DebugEntry(CConfObject::RemoveMember);
|
||
|
|
||
|
NM_CONFERENCE_STATE oldNmState, newNmState;
|
||
|
|
||
|
GetState(&oldNmState);
|
||
|
|
||
|
CNmMember * pMember = (CNmMember *) m_MemberList.RemoveAt(pos);
|
||
|
--m_uMembers;
|
||
|
|
||
|
if (pMember->FLocal())
|
||
|
{
|
||
|
// this is the local node:
|
||
|
m_pMemberLocal = NULL;
|
||
|
}
|
||
|
|
||
|
NotifySink((INmMember *) pMember, OnNotifyMemberRemoved);
|
||
|
pMember->Release();
|
||
|
|
||
|
// Sanity check:
|
||
|
ASSERT((m_uMembers >= 0) &&
|
||
|
(m_uMembers < 10000));
|
||
|
|
||
|
CheckState(oldNmState);
|
||
|
|
||
|
DebugExitVOID(CConfObject::RemoveMember);
|
||
|
}
|
||
|
|
||
|
BOOL CConfObject::OnConferenceEnded()
|
||
|
{
|
||
|
DebugEntry(CConfObject::OnConferenceEnded);
|
||
|
BOOL bRet = TRUE;
|
||
|
|
||
|
switch (m_csState)
|
||
|
{
|
||
|
case CS_GOING_DOWN:
|
||
|
{
|
||
|
TRACE_OUT(("ConfEnded received (from CS_GOING_DOWN)"));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case CS_RUNNING:
|
||
|
{
|
||
|
TRACE_OUT(("ConfEnded received (from CS_RUNNING)"));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case CS_COMING_UP:
|
||
|
{
|
||
|
TRACE_OUT(("ConfEnded received (from CS_COMING_UP)"));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
WARNING_OUT(("ConfEnded received (UNEXPECTED)"));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (NULL != m_hConf)
|
||
|
{
|
||
|
m_hConf->ReleaseInterface();
|
||
|
m_hConf = NULL;
|
||
|
}
|
||
|
SetT120State(CS_TERMINATED);
|
||
|
|
||
|
TRACE_OUT(("OnConferenceEnded(), num participants is %d", m_uMembers));
|
||
|
|
||
|
// Empty the participant list:
|
||
|
NC_ROSTER FakeRoster;
|
||
|
ClearStruct(&FakeRoster);
|
||
|
FakeRoster.uConferenceID = m_uGCCConferenceID;
|
||
|
OnRosterChanged(&FakeRoster);
|
||
|
|
||
|
ASSERT(0 == m_ourNodeID);
|
||
|
ASSERT(0 == m_uMembers);
|
||
|
|
||
|
// Reset member variables that pertain to a conference
|
||
|
m_uGCCConferenceID = 0;
|
||
|
m_fServerMode = FALSE;
|
||
|
|
||
|
SysFreeString(m_bstrConfName);
|
||
|
m_bstrConfName = NULL;
|
||
|
|
||
|
DebugExitBOOL(CConfObject::OnConferenceEnded, bRet);
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
BOOL CConfObject::OnConferenceStarted(CONF_HANDLE hConf, HRESULT hResult)
|
||
|
{
|
||
|
DebugEntry(CConfObject::OnConferenceStarted);
|
||
|
BOOL bRet = TRUE;
|
||
|
|
||
|
ASSERT(hConf == m_hConf);
|
||
|
|
||
|
switch (m_csState)
|
||
|
{
|
||
|
case CS_CREATING:
|
||
|
case CS_COMING_UP:
|
||
|
{
|
||
|
switch(hResult)
|
||
|
{
|
||
|
case S_OK:
|
||
|
TRACE_OUT(("ConfStarted received -> now running"));
|
||
|
SetT120State(CS_RUNNING);
|
||
|
break;
|
||
|
case UI_RC_INVALID_PASSWORD:
|
||
|
// nop, don't mess with state
|
||
|
// the conference is still coming up
|
||
|
// the incoming call handler will deal with this
|
||
|
break;
|
||
|
default:
|
||
|
SetT120State(CS_GOING_DOWN);
|
||
|
TRACE_OUT(("ConfStarted failed"));
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
WARNING_OUT(("OnConferenceStarted received (UNEXPECTED)"));
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DebugExitBOOL(CConfObject::OnConferenceStarted, bRet);
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
VOID CConfObject::RemoveOldMembers(int nExpected)
|
||
|
{
|
||
|
DebugEntry(CConfObject::RemoveOldMembers);
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
int nRemoved = 0;
|
||
|
#endif // _DEBUG
|
||
|
ASSERT(nExpected >= 0);
|
||
|
|
||
|
if (nExpected > 0)
|
||
|
{
|
||
|
// At least one participant left:
|
||
|
POSITION pos = m_MemberList.GetHeadPosition();
|
||
|
while (NULL != pos)
|
||
|
{
|
||
|
POSITION oldpos = pos;
|
||
|
CNmMember * pMember = (CNmMember *) m_MemberList.GetNext(pos);
|
||
|
ASSERT(pMember);
|
||
|
DWORD dwFlags = pMember->GetDwFlags();
|
||
|
if (!(PF_INCONFERENCE & dwFlags))
|
||
|
{
|
||
|
// This one is not in the data call:
|
||
|
TRACE_OUT(("CConfObject Roster: %ls (%d) has left.",
|
||
|
pMember->GetName(), pMember->GetGCCID()));
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
nRemoved++;
|
||
|
#endif // _DEBUG
|
||
|
|
||
|
// If they were data only, then remove:
|
||
|
RemoveMember(oldpos);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Validate that we did the right thing:
|
||
|
ASSERT(nRemoved == nExpected);
|
||
|
}
|
||
|
|
||
|
DebugExitVOID(CConfObject::RemoveOldMembers);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
CNmMember * CConfObject::CreateMember(BOOL fLocal,
|
||
|
UINT uCaps,
|
||
|
NC_ROSTER_NODE_ENTRY* pRosterNode)
|
||
|
{
|
||
|
DebugEntry(CConfObject::CreateMember);
|
||
|
|
||
|
ASSERT(NULL != pRosterNode);
|
||
|
|
||
|
DWORD dwFlags = 0;
|
||
|
if (fLocal)
|
||
|
{
|
||
|
dwFlags |= PF_LOCAL_NODE;
|
||
|
}
|
||
|
if (pRosterNode->fMCU)
|
||
|
{
|
||
|
dwFlags |= PF_T120_MCU;
|
||
|
}
|
||
|
|
||
|
CNmMember * pMember = new CNmMember(pRosterNode->pwszNodeName,
|
||
|
pRosterNode->uNodeID,
|
||
|
dwFlags,
|
||
|
uCaps);
|
||
|
|
||
|
if (NULL != pMember)
|
||
|
{
|
||
|
pMember->SetGccIdParent(pRosterNode->uSuperiorNodeID);
|
||
|
|
||
|
if (fLocal)
|
||
|
{
|
||
|
ASSERT(NULL == m_pMemberLocal);
|
||
|
m_pMemberLocal = pMember;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
TRACE_OUT(("CConfObject Roster: %ls (%d) has joined.", pRosterNode->pwszNodeName, pRosterNode->uNodeID));
|
||
|
|
||
|
DebugExitPVOID(CConfObject::CreateMember, pMember);
|
||
|
return pMember;
|
||
|
}
|
||
|
|
||
|
VOID CConfObject::OnMemberUpdated(INmMember *pMember)
|
||
|
{
|
||
|
NotifySink(pMember, OnNotifyMemberUpdated);
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID CConfObject::SetT120State(CONFSTATE state)
|
||
|
{
|
||
|
NM_CONFERENCE_STATE oldNmState;
|
||
|
|
||
|
GetState(&oldNmState);
|
||
|
m_csState = state;
|
||
|
if ( state == CS_TERMINATED )
|
||
|
m_fSecure = FALSE; // Reset secure flag
|
||
|
CheckState(oldNmState);
|
||
|
}
|
||
|
|
||
|
VOID CConfObject::CheckState(NM_CONFERENCE_STATE oldNmState)
|
||
|
{
|
||
|
NM_CONFERENCE_STATE newNmState;
|
||
|
GetState(&newNmState);
|
||
|
if (oldNmState != newNmState)
|
||
|
{
|
||
|
NotifySink((PVOID) newNmState, OnNotifyStateChanged);
|
||
|
if (NM_CONFERENCE_IDLE == newNmState)
|
||
|
{
|
||
|
m_fConferenceCreated = FALSE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
ULONG CConfObject::AddRef(void)
|
||
|
{
|
||
|
return ++m_cRef;
|
||
|
}
|
||
|
|
||
|
ULONG CConfObject::Release(void)
|
||
|
{
|
||
|
ASSERT(m_cRef > 0);
|
||
|
|
||
|
if (m_cRef > 0)
|
||
|
{
|
||
|
m_cRef--;
|
||
|
}
|
||
|
|
||
|
ULONG cRef = m_cRef;
|
||
|
|
||
|
if (0 == cRef)
|
||
|
{
|
||
|
delete this;
|
||
|
}
|
||
|
|
||
|
return cRef;
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE CConfObject::QueryInterface(REFIID riid, PVOID *ppv)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
if((riid == IID_INmConference) || (riid == IID_IUnknown))
|
||
|
{
|
||
|
*ppv = (INmConference *)this;
|
||
|
TRACE_OUT(("CConfObject::QueryInterface()"));
|
||
|
}
|
||
|
else if (riid == IID_IConnectionPointContainer)
|
||
|
{
|
||
|
*ppv = (IConnectionPointContainer *) this;
|
||
|
TRACE_OUT(("CConfObject::QueryInterface(): Returning IConnectionPointContainer."));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = E_NOINTERFACE;
|
||
|
*ppv = NULL;
|
||
|
TRACE_OUT(("CConfObject::QueryInterface(): Called on unknown interface."));
|
||
|
}
|
||
|
|
||
|
if (S_OK == hr)
|
||
|
{
|
||
|
AddRef();
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CConfObject::GetName(BSTR *pbstrName)
|
||
|
{
|
||
|
HRESULT hr = E_POINTER;
|
||
|
|
||
|
if (NULL != pbstrName)
|
||
|
{
|
||
|
*pbstrName = SysAllocString(m_bstrConfName);
|
||
|
hr = *pbstrName ? S_OK : E_FAIL;
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CConfObject::GetID(ULONG *puID)
|
||
|
{
|
||
|
HRESULT hr = E_POINTER;
|
||
|
|
||
|
if (NULL != puID)
|
||
|
{
|
||
|
*puID = m_uGCCConferenceID;
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CConfObject::GetState(NM_CONFERENCE_STATE *pState)
|
||
|
{
|
||
|
HRESULT hr = E_POINTER;
|
||
|
|
||
|
if (NULL != pState)
|
||
|
{
|
||
|
hr = S_OK;
|
||
|
|
||
|
switch (m_csState)
|
||
|
{
|
||
|
// Note: All states are valid (at least, for now)
|
||
|
case CS_CREATING:
|
||
|
case CS_UNINITIALIZED:
|
||
|
case CS_TERMINATED:
|
||
|
*pState = NM_CONFERENCE_IDLE;
|
||
|
break;
|
||
|
|
||
|
case CS_COMING_UP:
|
||
|
case CS_GOING_DOWN:
|
||
|
case CS_RUNNING:
|
||
|
if (m_uMembers < 2)
|
||
|
{
|
||
|
if (m_fServerMode)
|
||
|
{
|
||
|
*pState = NM_CONFERENCE_WAITING;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*pState = NM_CONFERENCE_INITIALIZING;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*pState = NM_CONFERENCE_ACTIVE;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
hr = E_FAIL;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CConfObject::GetTopProvider(INmMember **ppMember)
|
||
|
{
|
||
|
CNmMember *pMemberRet = NULL;
|
||
|
HRESULT hr = E_POINTER;
|
||
|
|
||
|
if (NULL != ppMember)
|
||
|
{
|
||
|
POSITION pos = m_MemberList.GetHeadPosition();
|
||
|
while (NULL != pos)
|
||
|
{
|
||
|
CNmMember *pMember = (CNmMember *) m_MemberList.GetNext(pos);
|
||
|
ASSERT(pMember);
|
||
|
|
||
|
if (pMember->FTopProvider())
|
||
|
{
|
||
|
// We have found the top provider
|
||
|
pMemberRet = pMember;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*ppMember = pMemberRet;
|
||
|
hr = (NULL != pMemberRet) ? S_OK : S_FALSE;
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CConfObject::EnumMember(IEnumNmMember **ppEnum)
|
||
|
{
|
||
|
HRESULT hr = E_POINTER;
|
||
|
|
||
|
if (NULL != ppEnum)
|
||
|
{
|
||
|
*ppEnum = new CEnumNmMember(&m_MemberList, m_uMembers);
|
||
|
hr = (NULL != *ppEnum) ? S_OK : E_OUTOFMEMORY;
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CConfObject::GetMemberCount(ULONG *puCount)
|
||
|
{
|
||
|
HRESULT hr = E_POINTER;
|
||
|
|
||
|
if (NULL != puCount)
|
||
|
{
|
||
|
*puCount = m_uMembers;
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CConfObject::FindMember(ULONG gccID, INmMember ** ppMember)
|
||
|
{
|
||
|
CNmMember * pMember;
|
||
|
|
||
|
if (!ppMember)
|
||
|
return E_POINTER;
|
||
|
|
||
|
pMember = PMemberFromGCCID(gccID);
|
||
|
if (pMember)
|
||
|
{
|
||
|
pMember->AddRef();
|
||
|
}
|
||
|
|
||
|
*ppMember = pMember;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/* P M E M B E R L O C A L */
|
||
|
/*-------------------------------------------------------------------------
|
||
|
%%Function: PMemberLocal
|
||
|
|
||
|
-------------------------------------------------------------------------*/
|
||
|
CNmMember * PMemberLocal(COBLIST *pList)
|
||
|
{
|
||
|
if (NULL != pList)
|
||
|
{
|
||
|
POSITION posCurr;
|
||
|
POSITION pos = pList->GetHeadPosition();
|
||
|
while (NULL != pos)
|
||
|
{
|
||
|
posCurr = pos;
|
||
|
CNmMember * pMember = (CNmMember *) pList->GetNext(pos);
|
||
|
ASSERT(NULL != pMember);
|
||
|
if (pMember->FLocal())
|
||
|
return pMember;
|
||
|
}
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CConfObject::CreateDataChannel
|
||
|
(
|
||
|
INmChannelData ** ppChannel,
|
||
|
REFGUID rguid
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr = E_FAIL;
|
||
|
|
||
|
DebugEntry(CConfObject::CreateDataChannel);
|
||
|
|
||
|
if (NULL != ppChannel)
|
||
|
{
|
||
|
if (IsBadWritePtr(ppChannel, sizeof(LPVOID)))
|
||
|
return E_POINTER;
|
||
|
*ppChannel = NULL;
|
||
|
}
|
||
|
|
||
|
if (GUID_NULL == rguid)
|
||
|
{
|
||
|
WARNING_OUT(("CreateDataChannel: Null guid"));
|
||
|
return E_INVALIDARG;
|
||
|
}
|
||
|
|
||
|
// Make sure we're in a data conference
|
||
|
CNmMember * pMember = PMemberLocal(&m_MemberList);
|
||
|
if (NULL == pMember)
|
||
|
{
|
||
|
WARNING_OUT(("CreateDataChannel: No members yet"));
|
||
|
return E_FAIL;
|
||
|
}
|
||
|
|
||
|
CNmChannelData * pChannel = new CNmChannelData(this, rguid);
|
||
|
if (NULL == pChannel)
|
||
|
{
|
||
|
WARNING_OUT(("CreateDataChannel: Unable to create data channel"));
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
hr = pChannel->OpenConnection();
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
ERROR_OUT(("CreateDataChannel: Unable to set guid / create T.120 channels"));
|
||
|
// Failed to create T.120 data channels
|
||
|
delete pChannel;
|
||
|
*ppChannel = NULL;
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
if (NULL != ppChannel)
|
||
|
{
|
||
|
*ppChannel = (INmChannelData *)pChannel;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pChannel->Release(); // No one is watching this channel? - free it now
|
||
|
}
|
||
|
|
||
|
hr = S_OK;
|
||
|
|
||
|
DebugExitHRESULT(CConfObject::CreateDataChannel, hr);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
HRESULT CConfObject::IsHosting(void)
|
||
|
{
|
||
|
return m_fServerMode ? S_OK : S_FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CConfObject::Host(void)
|
||
|
{
|
||
|
HRESULT hr = E_FAIL;
|
||
|
|
||
|
if (m_fServerMode || IsConferenceActive())
|
||
|
{
|
||
|
WARNING_OUT(("Conference already exists!"));
|
||
|
// ncsRet = UI_RC_CONFERENCE_ALREADY_EXISTS;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
HRESULT ncsRet = CreateConference();
|
||
|
if (S_OK == ncsRet)
|
||
|
{
|
||
|
// The only success case:
|
||
|
TRACE_OUT(("Create local issued successfully"));
|
||
|
m_fServerMode = TRUE;
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// UI?
|
||
|
WARNING_OUT(("Create local failed!"));
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CConfObject::Leave(void)
|
||
|
{
|
||
|
DebugEntry(CConfObject::Leave);
|
||
|
|
||
|
COprahNCUI *pOprahNCUI = COprahNCUI::GetInstance();
|
||
|
if (NULL != pOprahNCUI)
|
||
|
{
|
||
|
pOprahNCUI->CancelCalls();
|
||
|
}
|
||
|
|
||
|
HRESULT hr = S_OK;
|
||
|
switch (m_csState)
|
||
|
{
|
||
|
case CS_GOING_DOWN:
|
||
|
// we are already exiting
|
||
|
break;
|
||
|
|
||
|
case CS_COMING_UP:
|
||
|
case CS_RUNNING:
|
||
|
{
|
||
|
SetT120State(CS_GOING_DOWN);
|
||
|
ASSERT(m_hConf);
|
||
|
TRACE_OUT(("Calling IDataConference::Leave"));
|
||
|
hr = m_hConf->Leave();
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
WARNING_OUT(("IDataConference::Leave failed"));
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
hr = E_FAIL;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
DebugExitHRESULT(CConfObject::Leave, hr);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
HRESULT CConfObject::LaunchRemote(REFGUID rguid, INmMember *pMember)
|
||
|
{
|
||
|
DWORD dwUserId = 0;
|
||
|
if(m_hConf)
|
||
|
{
|
||
|
if (NULL != pMember)
|
||
|
{
|
||
|
dwUserId = ((CNmMember*)pMember)->GetGCCID();
|
||
|
}
|
||
|
|
||
|
ASSERT(g_pNodeController);
|
||
|
ASSERT(m_hConf);
|
||
|
HRESULT nsRet = m_hConf->LaunchGuid(&rguid,
|
||
|
(PUINT) &dwUserId, (0 == dwUserId) ? 0 : 1);
|
||
|
|
||
|
return (S_OK == nsRet) ? S_OK : E_FAIL;
|
||
|
}
|
||
|
|
||
|
return NM_E_NO_T120_CONFERENCE;
|
||
|
}
|
||
|
|
||
|
|
||
|
/****************************************************************************
|
||
|
*
|
||
|
* CLASS: CConfObject
|
||
|
*
|
||
|
* FUNCTION: GetConferenceHandle(DWORD *)
|
||
|
*
|
||
|
* PURPOSE: Gets the T120 conference handle
|
||
|
*
|
||
|
****************************************************************************/
|
||
|
|
||
|
STDMETHODIMP CConfObject::GetConferenceHandle(DWORD_PTR *pdwHandle)
|
||
|
{
|
||
|
HRESULT hr = E_FAIL;
|
||
|
|
||
|
if (NULL != pdwHandle)
|
||
|
{
|
||
|
CONF_HANDLE hConf = GetConfHandle();
|
||
|
*pdwHandle = (DWORD_PTR)hConf;
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
return hr;
|
||
|
|
||
|
}
|
||
|
|
||
|
/* O N N O T I F Y S T A T E C H A N G E D */
|
||
|
/*-------------------------------------------------------------------------
|
||
|
%%Function: OnNotifyStateChanged
|
||
|
|
||
|
-------------------------------------------------------------------------*/
|
||
|
HRESULT OnNotifyStateChanged(IUnknown *pConfNotify, PVOID pv, REFIID riid)
|
||
|
{
|
||
|
ASSERT(NULL != pConfNotify);
|
||
|
|
||
|
((INmConferenceNotify*)pConfNotify)->StateChanged((NM_CONFERENCE_STATE)((DWORD_PTR)pv));
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
/* O N N O T I F Y M E M B E R A D D E D */
|
||
|
/*-------------------------------------------------------------------------
|
||
|
%%Function: OnNotifyMemberAdded
|
||
|
|
||
|
-------------------------------------------------------------------------*/
|
||
|
HRESULT OnNotifyMemberAdded(IUnknown *pConfNotify, PVOID pv, REFIID riid)
|
||
|
{
|
||
|
ASSERT(NULL != pConfNotify);
|
||
|
|
||
|
((INmConferenceNotify*)pConfNotify)->MemberChanged(NM_MEMBER_ADDED, (INmMember *) pv);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
/* O N N O T I F Y M E M B E R U P D A T E D */
|
||
|
/*-------------------------------------------------------------------------
|
||
|
%%Function: OnNotifyMemberUpdated
|
||
|
|
||
|
-------------------------------------------------------------------------*/
|
||
|
HRESULT OnNotifyMemberUpdated(IUnknown *pConfNotify, PVOID pv, REFIID riid)
|
||
|
{
|
||
|
ASSERT(NULL != pConfNotify);
|
||
|
|
||
|
((INmConferenceNotify*)pConfNotify)->MemberChanged(NM_MEMBER_UPDATED, (INmMember *) pv);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
/* O N N O T I F Y M E M B E R R E M O V E D */
|
||
|
/*-------------------------------------------------------------------------
|
||
|
%%Function: OnNotifyMemberRemoved
|
||
|
|
||
|
-------------------------------------------------------------------------*/
|
||
|
HRESULT OnNotifyMemberRemoved(IUnknown *pConfNotify, PVOID pv, REFIID riid)
|
||
|
{
|
||
|
ASSERT(NULL != pConfNotify);
|
||
|
|
||
|
((INmConferenceNotify*)pConfNotify)->MemberChanged(NM_MEMBER_REMOVED, (INmMember *) pv);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
/* O N N O T I F Y N M U I */
|
||
|
/*-------------------------------------------------------------------------
|
||
|
%%Function: OnNotifyNmUI
|
||
|
|
||
|
-------------------------------------------------------------------------*/
|
||
|
HRESULT OnNotifyNmUI(IUnknown *pConfNotify, PVOID pv, REFIID riid)
|
||
|
{
|
||
|
ASSERT(NULL != pConfNotify);
|
||
|
|
||
|
((INmConferenceNotify*)pConfNotify)->NmUI((CONFN)((DWORD_PTR)pv));
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
/* G E T C O N F O B J E C T */
|
||
|
/*-------------------------------------------------------------------------
|
||
|
%%Function: GetConfObject
|
||
|
|
||
|
Global function to get the conference object
|
||
|
-------------------------------------------------------------------------*/
|
||
|
CConfObject * GetConfObject(void)
|
||
|
{
|
||
|
COprahNCUI *pOprahNCUI = COprahNCUI::GetInstance();
|
||
|
if (NULL != pOprahNCUI)
|
||
|
{
|
||
|
return pOprahNCUI->GetConfObject();
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* G E T C O N F E R E N C E */
|
||
|
/*-------------------------------------------------------------------------
|
||
|
%%Function: GetConference
|
||
|
|
||
|
Global function to get the INmConference interface to the conf object
|
||
|
-------------------------------------------------------------------------*/
|
||
|
HRESULT GetConference(INmConference **ppConference)
|
||
|
{
|
||
|
HRESULT hr = E_POINTER;
|
||
|
if (NULL != ppConference)
|
||
|
{
|
||
|
hr = E_FAIL;
|
||
|
INmConference *pConference = GetConfObject();
|
||
|
if (NULL != pConference)
|
||
|
{
|
||
|
pConference->AddRef();
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
*ppConference = pConference;
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
/* G E T M E M B E R L I S T */
|
||
|
/*-------------------------------------------------------------------------
|
||
|
%%Function: GetMemberList
|
||
|
|
||
|
Global function to get the member list
|
||
|
-------------------------------------------------------------------------*/
|
||
|
COBLIST * GetMemberList(void)
|
||
|
{
|
||
|
CConfObject* pco = ::GetConfObject();
|
||
|
if (NULL == pco)
|
||
|
return NULL;
|
||
|
return pco->GetMemberList();
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
DWORD CConfObject::GetDwUserIdLocal(void)
|
||
|
{
|
||
|
CNmMember * pMemberLocal = GetLocalMember();
|
||
|
|
||
|
if (NULL != pMemberLocal)
|
||
|
{
|
||
|
return pMemberLocal->GetGCCID();
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
CNmMember * CConfObject::PMemberFromGCCID(UINT uNodeID)
|
||
|
{
|
||
|
COBLIST* pMemberList = ::GetMemberList();
|
||
|
if (NULL != pMemberList)
|
||
|
{
|
||
|
POSITION pos = pMemberList->GetHeadPosition();
|
||
|
while (pos)
|
||
|
{
|
||
|
CNmMember * pMember = (CNmMember *) pMemberList->GetNext(pos);
|
||
|
ASSERT(NULL != pMember);
|
||
|
if (uNodeID == pMember->GetGCCID())
|
||
|
{
|
||
|
return pMember;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
CNmMember * CConfObject::PMemberFromName(PCWSTR pwszName)
|
||
|
{
|
||
|
POSITION pos = m_MemberList.GetHeadPosition();
|
||
|
while (NULL != pos)
|
||
|
{
|
||
|
CNmMember * pMember = (CNmMember *) m_MemberList.GetNext(pos);
|
||
|
if (0 == UnicodeCompare(pwszName, pMember->GetName()))
|
||
|
{
|
||
|
return pMember;
|
||
|
}
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|