1613 lines
30 KiB
C++
1613 lines
30 KiB
C++
/* ----------------------------------------------------------------------
|
||
|
||
Module: ULS.DLL (Service Provider)
|
||
File: sppqueue.cpp
|
||
Content: This file contains the pending item/queue objects.
|
||
History:
|
||
10/15/96 Chu, Lon-Chan [lonchanc]
|
||
Created.
|
||
|
||
Copyright (c) Microsoft Corporation 1996-1997
|
||
|
||
---------------------------------------------------------------------- */
|
||
|
||
#include "ulsp.h"
|
||
#include "spinc.h"
|
||
|
||
// #define MEASURE_ENUM_USER_INFO 1
|
||
|
||
ULONG g_uResponseTimeout = ILS_DEF_RESP_TIMEOUT;
|
||
ULONG g_uResponsePollPeriod = ILS_DEF_RESP_POLL_PERIOD;
|
||
SP_CResponseQueue *g_pRespQueue = NULL;
|
||
SP_CRequestQueue *g_pReqQueue = NULL;
|
||
SP_CRefreshScheduler *g_pRefreshScheduler = NULL;
|
||
|
||
|
||
typedef BOOL (RESPONSE_HANDLER) ( HRESULT, SP_CResponse * );
|
||
typedef LPARAM (REQUEST_HANDLER) ( MARSHAL_REQ * );
|
||
|
||
extern RESPONSE_HANDLER *GetResponseHandler ( ULONG uNotifyMsg );
|
||
extern REQUEST_HANDLER *GetRequestHandler ( ULONG uNotifyMsg );
|
||
|
||
|
||
/* ---------- REQUEST QUEUE ----------- */
|
||
|
||
|
||
MARSHAL_REQ *
|
||
MarshalReq_Alloc (
|
||
ULONG uNotifyMsg,
|
||
ULONG cbSize,
|
||
ULONG cParams )
|
||
{
|
||
// Align the chunk of data for each parameter on 4-byte boundary
|
||
//
|
||
cbSize += cParams * sizeof (DWORD);
|
||
|
||
// Calculate the total size of marshal buffer
|
||
//
|
||
ULONG cbTotalSize = sizeof (MARSHAL_REQ) +
|
||
cParams * sizeof (DWORD) +
|
||
cbSize;
|
||
|
||
// Allocate the marshal buffer
|
||
//
|
||
MARSHAL_REQ *p = (MARSHAL_REQ *) MemAlloc (cbTotalSize);
|
||
if (p != NULL)
|
||
{
|
||
// p->next = NULL;
|
||
p->cbTotalSize = cbTotalSize;
|
||
p->pb = (BYTE *) ((ULONG_PTR) p + (cbTotalSize - cbSize));
|
||
|
||
p->uRespID = GetUniqueNotifyID ();
|
||
|
||
p->uNotifyMsg = uNotifyMsg;
|
||
p->cParams = cParams;
|
||
}
|
||
|
||
return p;
|
||
}
|
||
|
||
|
||
HRESULT
|
||
MarshalReq_SetParam (
|
||
MARSHAL_REQ *p,
|
||
ULONG nIndex,
|
||
DWORD_PTR dwParam,
|
||
ULONG cbParamSize )
|
||
{
|
||
if (p != NULL && nIndex < p->cParams)
|
||
{
|
||
MyAssert (p->aParams[nIndex] == 0); // not used before
|
||
|
||
// If cbParamSize > 0, then
|
||
// this means uParam is a pointer to a structure or
|
||
// a pointer to a string
|
||
//
|
||
if (cbParamSize > 0)
|
||
{
|
||
// The pointer is now the one pointing to the new location
|
||
//
|
||
p->aParams[nIndex] = (DWORD_PTR) p->pb;
|
||
|
||
// Copy the data chunk
|
||
//
|
||
CopyMemory (p->pb, (VOID *) dwParam, cbParamSize);
|
||
|
||
// Make sure the data chunk is aligned on 4-byte boundary
|
||
//
|
||
if (cbParamSize & 0x3)
|
||
{
|
||
// Round it up
|
||
//
|
||
cbParamSize = (cbParamSize & (~0x3)) + 4;
|
||
}
|
||
|
||
// Adjust the running pointer
|
||
//
|
||
p->pb += cbParamSize;
|
||
}
|
||
else
|
||
{
|
||
// uParam can be an signed/unsigned integer,
|
||
//
|
||
p->aParams[nIndex] = dwParam;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
MyAssert (FALSE);
|
||
}
|
||
|
||
return S_OK;
|
||
}
|
||
|
||
|
||
DWORD_PTR
|
||
MarshalReq_GetParam (
|
||
MARSHAL_REQ *p,
|
||
ULONG nIndex )
|
||
{
|
||
DWORD_PTR dwParam = 0;
|
||
|
||
if (p != NULL && nIndex < p->cParams)
|
||
{
|
||
dwParam = p->aParams[nIndex];
|
||
}
|
||
else
|
||
{
|
||
MyAssert (FALSE);
|
||
}
|
||
|
||
return dwParam;
|
||
}
|
||
|
||
|
||
HRESULT
|
||
MarshalReq_SetParamServer (
|
||
MARSHAL_REQ *p,
|
||
ULONG nIndex,
|
||
SERVER_INFO *pServer,
|
||
ULONG cbServer )
|
||
{
|
||
if (p != NULL && nIndex < p->cParams)
|
||
{
|
||
MyAssert (p->aParams[nIndex] == 0); // not used before
|
||
MyAssert (cbServer > sizeof (SERVER_INFO));
|
||
|
||
// The pointer is now the one pointing to the new location
|
||
//
|
||
p->aParams[nIndex] = (DWORD_PTR) p->pb;
|
||
|
||
// Linearize the server info
|
||
//
|
||
IlsLinearizeServerInfo (p->pb, pServer);
|
||
|
||
// Make sure the data chunk is aligned on 4-byte boundary
|
||
//
|
||
if (cbServer & 0x3)
|
||
{
|
||
// Round it up
|
||
//
|
||
cbServer = (cbServer & (~0x3)) + 4;
|
||
}
|
||
|
||
// Adjust the running pointer
|
||
//
|
||
p->pb += cbServer;
|
||
}
|
||
|
||
return S_OK;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
SP_CRequestQueue::
|
||
SP_CRequestQueue ( VOID )
|
||
:
|
||
m_ItemList (NULL),
|
||
m_uCurrOpRespID (INVALID_RESP_ID)
|
||
{
|
||
// Create critical sections for thread safe access
|
||
//
|
||
::MyInitializeCriticalSection (&m_csReqQ);
|
||
::MyInitializeCriticalSection (&m_csCurrOp);
|
||
}
|
||
|
||
|
||
SP_CRequestQueue::
|
||
~SP_CRequestQueue ( VOID )
|
||
{
|
||
// when this is called, the hidden window thread exited already.
|
||
// this is assured in UlsLdap_Deinitialize().
|
||
//
|
||
|
||
WriteLock ();
|
||
|
||
// Free all the items in this list
|
||
//
|
||
MARSHAL_REQ *p, *next;
|
||
for (p = m_ItemList; p != NULL; p = next)
|
||
{
|
||
next = p->next;
|
||
MemFree (p);
|
||
}
|
||
m_ItemList = NULL;
|
||
|
||
WriteUnlock ();
|
||
|
||
// Delete critical sections
|
||
//
|
||
::MyDeleteCriticalSection (&m_csReqQ);
|
||
::MyDeleteCriticalSection (&m_csCurrOp);
|
||
}
|
||
|
||
|
||
HRESULT SP_CRequestQueue::
|
||
Enter ( MARSHAL_REQ *p )
|
||
{
|
||
// Make sure we have valid pointers
|
||
//
|
||
if (p == NULL)
|
||
{
|
||
MyAssert (FALSE);
|
||
return ILS_E_POINTER;
|
||
}
|
||
MyAssert (! MyIsBadWritePtr (p, p->cbTotalSize));
|
||
MyAssert (p->next == NULL);
|
||
MyAssert (p->uRespID != 0);
|
||
|
||
WriteLock ();
|
||
|
||
// Append the new request
|
||
//
|
||
p->next = NULL;
|
||
if (m_ItemList == NULL)
|
||
{
|
||
m_ItemList = p;
|
||
}
|
||
else
|
||
{
|
||
for ( MARSHAL_REQ *prev = m_ItemList;
|
||
prev->next != NULL;
|
||
prev = prev->next)
|
||
;
|
||
|
||
MyAssert (prev != NULL);
|
||
prev->next = p;
|
||
}
|
||
|
||
WriteUnlock ();
|
||
|
||
// Signal the internal request thread to pick up this request
|
||
//
|
||
SetEvent (g_hevNewRequest);
|
||
|
||
return S_OK;
|
||
}
|
||
|
||
|
||
VOID SP_CRequestQueue::
|
||
Schedule ( VOID )
|
||
{
|
||
MARSHAL_REQ *p;
|
||
|
||
while (IsAnyReqInQueue () && ! g_fExitNow)
|
||
{
|
||
// Reset to null, we will use this as an indicator
|
||
// to see if we need to process the request
|
||
//
|
||
p = NULL;
|
||
|
||
// Lock request queue
|
||
//
|
||
WriteLock ();
|
||
|
||
// Get a request to process
|
||
//
|
||
if (IsAnyReqInQueue ())
|
||
{
|
||
p = m_ItemList;
|
||
m_ItemList = m_ItemList->next;
|
||
}
|
||
|
||
// We want to lock both request queue and CurrOp at the same time
|
||
// because we cannot have a temporal window that either one can change.
|
||
|
||
// Set CurrOp
|
||
//
|
||
if (p != NULL)
|
||
{
|
||
// Lock CurrOp
|
||
//
|
||
LockCurrOp ();
|
||
|
||
// Set CurrOp
|
||
//
|
||
m_uCurrOpRespID = p->uRespID;
|
||
|
||
// Unlock CurrOp
|
||
//
|
||
UnlockCurrOp ();
|
||
}
|
||
|
||
// Unlock request queue
|
||
//
|
||
WriteUnlock ();
|
||
|
||
// Make sure we have something to process
|
||
//
|
||
if (p == NULL)
|
||
{
|
||
// Nothing to do any more
|
||
//
|
||
MyAssert (FALSE);
|
||
break;
|
||
}
|
||
|
||
// Let's process the request
|
||
//
|
||
Dispatch (p);
|
||
|
||
MemFree(p);
|
||
}
|
||
}
|
||
|
||
|
||
HRESULT SP_CRequestQueue::
|
||
Cancel ( ULONG uRespID )
|
||
{
|
||
HRESULT hr;
|
||
MARSHAL_REQ *p, *next, *prev;
|
||
|
||
// The locking order is always in
|
||
// Lock(PendingOpQueue), Lock(RequestQueue), Lock (CurrOp)
|
||
//
|
||
WriteLock ();
|
||
LockCurrOp ();
|
||
|
||
if (m_uCurrOpRespID == uRespID)
|
||
{
|
||
// Invalidate the curr op.
|
||
// When the curr op is done, then the request thread will remove it
|
||
// from the pending op queue.
|
||
//
|
||
m_uCurrOpRespID = INVALID_RESP_ID;
|
||
hr = S_OK;
|
||
}
|
||
else
|
||
{
|
||
// Look for the item with a matching response id
|
||
//
|
||
for (prev = NULL, p = m_ItemList; p != NULL; prev = p, p = next)
|
||
{
|
||
// Cache the next pointer
|
||
//
|
||
next = p->next;
|
||
|
||
// See if the response id matches
|
||
//
|
||
if (p->uRespID == uRespID)
|
||
{
|
||
// It is a match
|
||
//
|
||
MyDebugMsg ((ZONE_REQ, "ULS: cancelled request(0x%lX) in ReqQ\r\n", p->uNotifyMsg));
|
||
|
||
// Let's destroy this item
|
||
//
|
||
if (p == m_ItemList)
|
||
{
|
||
m_ItemList = next;
|
||
}
|
||
else
|
||
{
|
||
MyAssert (prev != NULL);
|
||
prev->next = next;
|
||
}
|
||
|
||
// Free this structure
|
||
//
|
||
MemFree (p);
|
||
|
||
// Get out of the loop
|
||
//
|
||
break;
|
||
}
|
||
} // for
|
||
|
||
hr = (p == NULL) ? ILS_E_NOTIFY_ID : S_OK;
|
||
} // else
|
||
|
||
UnlockCurrOp ();
|
||
WriteUnlock ();
|
||
|
||
return hr;
|
||
}
|
||
|
||
|
||
VOID SP_CRequestQueue::
|
||
Dispatch ( MARSHAL_REQ *p )
|
||
{
|
||
// Make sure we have a valid pointer
|
||
//
|
||
if (p == NULL)
|
||
{
|
||
MyAssert (FALSE);
|
||
return;
|
||
}
|
||
|
||
// If it is keep alive, then do it
|
||
//
|
||
HRESULT hr;
|
||
if (p->uNotifyMsg == WM_ILS_REFRESH)
|
||
{
|
||
// Keep alive handler
|
||
//
|
||
if (g_pRefreshScheduler != NULL)
|
||
{
|
||
ULONG uTTL = (ULONG) MarshalReq_GetParam (p, 0);
|
||
hr = g_pRefreshScheduler->SendRefreshMessages (uTTL);
|
||
}
|
||
else
|
||
{
|
||
MyAssert (FALSE);
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
// Locate the appropriate handler
|
||
//
|
||
REQUEST_HANDLER *pfn = ::GetRequestHandler (p->uNotifyMsg);
|
||
if (pfn == NULL)
|
||
{
|
||
MyAssert (FALSE);
|
||
return;
|
||
}
|
||
|
||
// Send the request to the server
|
||
//
|
||
MyDebugMsg ((ZONE_REQ, "ULS: sending request(0x%lX)\r\n", p->uNotifyMsg));
|
||
ULONG uRespID = p->uRespID;
|
||
LPARAM lParam = (*pfn) (p);
|
||
MyDebugMsg ((ZONE_REQ, "ULS: sent request(0x%lX), lParam=0x%lX\r\n", p->uNotifyMsg, lParam));
|
||
if (lParam != 0)
|
||
{
|
||
::PostMessage (g_hWndNotify, p->uNotifyMsg, p->uRespID, lParam);
|
||
return;
|
||
}
|
||
// BUGBUG: this is a workaround for a server bug which results in lost requests if several
|
||
// are sent very quickly. Remove this Sleep() as soon as the bug is fixed!!!
|
||
// Sleep(100);
|
||
|
||
// Lock CurrOp again
|
||
//
|
||
LockCurrOp ();
|
||
|
||
// Is this request cancelled
|
||
//
|
||
BOOL fCancelled = (m_uCurrOpRespID == INVALID_RESP_ID) ? TRUE : FALSE;
|
||
|
||
// Clean up CurrOp
|
||
//
|
||
m_uCurrOpRespID = INVALID_RESP_ID;
|
||
|
||
// Unlock CurrOp
|
||
//
|
||
UnlockCurrOp ();
|
||
|
||
// If this request was cancelled, then remove it from the pending op queue
|
||
//
|
||
if (fCancelled)
|
||
{
|
||
// Redirect the call to the pending op queue object
|
||
//
|
||
if (g_pRespQueue != NULL)
|
||
{
|
||
g_pRespQueue->Cancel (uRespID);
|
||
}
|
||
else
|
||
{
|
||
MyAssert (FALSE);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/* ---------- RESPONSE ITEM ----------- */
|
||
|
||
/* ---------- public methods ----------- */
|
||
|
||
|
||
SP_CResponse::
|
||
SP_CResponse ( VOID )
|
||
:
|
||
m_pSession (NULL), // Clean up session pointer
|
||
m_pLdapMsg (NULL), // Clean up ldap msg pointer
|
||
m_next (NULL) // Clean up the pointer to the next pending item
|
||
{
|
||
// Clean up pending info structure
|
||
//
|
||
::ZeroMemory (&m_ri, sizeof (m_ri));
|
||
|
||
// Fill in creation time
|
||
//
|
||
UpdateLastModifiedTime ();
|
||
m_tcTimeout = g_uResponseTimeout;
|
||
}
|
||
|
||
|
||
SP_CResponse::
|
||
~SP_CResponse ( VOID )
|
||
{
|
||
// Release the session if needed
|
||
//
|
||
if (m_pSession != NULL)
|
||
m_pSession->Disconnect ();
|
||
|
||
// Free the ldap msg if needed
|
||
//
|
||
if (m_pLdapMsg != NULL)
|
||
::ldap_msgfree (m_pLdapMsg);
|
||
|
||
// Free extended attribute name list
|
||
//
|
||
::MemFree (m_ri.pszAnyAttrNameList);
|
||
|
||
// Free protocol names to resolve
|
||
//
|
||
::MemFree (m_ri.pszProtNameToResolve);
|
||
}
|
||
|
||
|
||
/* ---------- protected methods ----------- */
|
||
|
||
|
||
VOID SP_CResponse::
|
||
EnterResult ( LDAPMessage *pLdapMsg )
|
||
{
|
||
// Free the old ldap msg if needed
|
||
//
|
||
if (m_pLdapMsg != NULL)
|
||
::ldap_msgfree (m_pLdapMsg);
|
||
|
||
// Keep the new ldap msg
|
||
//
|
||
m_pLdapMsg = pLdapMsg;
|
||
}
|
||
|
||
|
||
/* ---------- private methods ----------- */
|
||
|
||
|
||
|
||
|
||
/* ---------- RESPONSE QUEUE ----------- */
|
||
|
||
|
||
/* ---------- public methods ----------- */
|
||
|
||
|
||
SP_CResponseQueue::
|
||
SP_CResponseQueue ( VOID )
|
||
:
|
||
m_ItemList (NULL) // Clean up the item list
|
||
{
|
||
// Create a critical section for thread safe access
|
||
//
|
||
::MyInitializeCriticalSection (&m_csRespQ);
|
||
}
|
||
|
||
|
||
SP_CResponseQueue::
|
||
~SP_CResponseQueue ( VOID )
|
||
{
|
||
// when this is called, the hidden window thread exited already.
|
||
// this is assured in UlsLdap_Deinitialize().
|
||
//
|
||
|
||
WriteLock ();
|
||
|
||
// Free all the items in this list
|
||
//
|
||
SP_CResponse *pItem, *next;
|
||
for (pItem = m_ItemList; pItem != NULL; pItem = next)
|
||
{
|
||
next = pItem->GetNext ();
|
||
delete pItem;
|
||
}
|
||
m_ItemList = NULL;
|
||
|
||
WriteUnlock ();
|
||
|
||
// Delete the critical section
|
||
//
|
||
::MyDeleteCriticalSection (&m_csRespQ);
|
||
}
|
||
|
||
|
||
HRESULT SP_CResponseQueue::
|
||
EnterRequest (
|
||
SP_CSession *pSession,
|
||
RESP_INFO *pInfo )
|
||
{
|
||
// Make sure we have valid pointers
|
||
//
|
||
if (pSession == NULL || pInfo == NULL)
|
||
{
|
||
MyAssert (FALSE);
|
||
return ILS_E_POINTER;
|
||
}
|
||
|
||
// Sanity checks
|
||
//
|
||
MyAssert (! MyIsBadWritePtr (pInfo, sizeof (*pInfo)));
|
||
MyAssert (! MyIsBadWritePtr (pSession, sizeof (*pSession)));
|
||
MyAssert (pInfo->ld != NULL && pInfo->uMsgID[0] != INVALID_MSG_ID);
|
||
MyAssert (pInfo->uRespID != 0);
|
||
|
||
// Create a new pending item
|
||
//
|
||
SP_CResponse *pItem = new SP_CResponse;
|
||
if (pItem == NULL)
|
||
return ILS_E_MEMORY;
|
||
|
||
// Remember the contents of pending info
|
||
//
|
||
pItem->EnterRequest (pSession, pInfo);
|
||
|
||
WriteLock ();
|
||
|
||
// If this is the first item on the list, then
|
||
// let's start the timer
|
||
//
|
||
if (m_ItemList == NULL)
|
||
::SetTimer (g_hWndHidden, ID_TIMER_POLL_RESULT, g_uResponsePollPeriod, NULL);
|
||
|
||
// Append the new pending op
|
||
//
|
||
pItem->SetNext (NULL);
|
||
if (m_ItemList == NULL)
|
||
{
|
||
m_ItemList = pItem;
|
||
}
|
||
else
|
||
{
|
||
for ( SP_CResponse *prev = m_ItemList;
|
||
prev->GetNext () != NULL;
|
||
prev = prev->GetNext ())
|
||
;
|
||
|
||
MyAssert (prev != NULL);
|
||
prev->SetNext (pItem);
|
||
}
|
||
|
||
WriteUnlock ();
|
||
|
||
return S_OK;
|
||
}
|
||
|
||
|
||
HRESULT SP_CResponseQueue::
|
||
PollLdapResults ( LDAP_TIMEVAL *pTimeout )
|
||
{
|
||
MyAssert (pTimeout != NULL);
|
||
|
||
SP_CResponse *pItem, *next, *prev;
|
||
INT RetCode;
|
||
RESP_INFO *pInfo;
|
||
LDAPMessage *pLdapMsg;
|
||
HRESULT hr;
|
||
RESPONSE_HANDLER *pfn;
|
||
ULONG uResultSetType;
|
||
|
||
::KillTimer (g_hWndHidden, ID_TIMER_POLL_RESULT); // avoid overrun
|
||
|
||
WriteLock ();
|
||
|
||
// Enumerate all the items to get available results for them
|
||
//
|
||
for (prev = NULL, pItem = m_ItemList; pItem != NULL; pItem = next)
|
||
{
|
||
// Cache the next pointer
|
||
//
|
||
next = pItem->GetNext ();
|
||
|
||
// Get the pinding info structure
|
||
//
|
||
pInfo = pItem->GetRespInfo ();
|
||
|
||
// Clean up ldap msg pointer
|
||
//
|
||
pLdapMsg = NULL;
|
||
|
||
// Make sure ew have valid ld and msg id
|
||
//
|
||
MyAssert (pInfo->ld != NULL);
|
||
MyAssert (pInfo->uMsgID[0] != INVALID_MSG_ID ||
|
||
pInfo->uMsgID[1] != INVALID_MSG_ID);
|
||
|
||
// Check integrity in pending info
|
||
//
|
||
MyAssert (pInfo->uRespID != 0);
|
||
|
||
// Set the result set type
|
||
//
|
||
switch (pInfo->uNotifyMsg)
|
||
{
|
||
case WM_ILS_ENUM_CLIENTS:
|
||
case WM_ILS_ENUM_CLIENTINFOS:
|
||
#ifdef ENABLE_MEETING_PLACE
|
||
case WM_ILS_ENUM_MEETINGS:
|
||
case WM_ILS_ENUM_MEETINGINFOS:
|
||
#endif
|
||
uResultSetType = LDAP_MSG_RECEIVED; // partial result set
|
||
break;
|
||
default:
|
||
uResultSetType = LDAP_MSG_ALL; // complete result set
|
||
break;
|
||
}
|
||
|
||
#ifdef _DEBUG
|
||
if (MyIsBadWritePtr (pInfo->ld, sizeof (*(pInfo->ld))))
|
||
{
|
||
MyDebugMsg ((ZONE_CONN, "ILS:: poll result, bad ld=0x%p\r\n", pInfo->ld));
|
||
MyAssert (FALSE);
|
||
}
|
||
if (pInfo->ld != pItem->GetSession()->GetLd())
|
||
{
|
||
MyDebugMsg ((ZONE_CONN, "ILS:: poll result, inconsistent pInfo->ld=0x%p, pItem->pSession->ld=0x%p\r\n", pInfo->ld, pItem->GetSession()->GetLd()));
|
||
MyAssert (FALSE);
|
||
}
|
||
#endif // _DEBUG
|
||
|
||
// If primary msg id is valid
|
||
//
|
||
if (pInfo->uMsgID[0] != INVALID_MSG_ID)
|
||
RetCode = ::ldap_result (pInfo->ld,
|
||
pInfo->uMsgID[0],
|
||
uResultSetType,
|
||
pTimeout,
|
||
&pLdapMsg);
|
||
else
|
||
// If secondary msg id is valid
|
||
//
|
||
if (pInfo->uMsgID[1] != INVALID_MSG_ID)
|
||
RetCode = ::ldap_result (pInfo->ld,
|
||
pInfo->uMsgID[1],
|
||
uResultSetType,
|
||
pTimeout,
|
||
&pLdapMsg);
|
||
|
||
// If timeout, ignore this item
|
||
//
|
||
if (RetCode == 0)
|
||
{
|
||
// Let's see if this item is expired
|
||
//
|
||
if (! pItem->IsExpired ())
|
||
{
|
||
// Not timed out, next please!
|
||
//
|
||
prev = pItem;
|
||
continue;
|
||
}
|
||
|
||
// Timed out
|
||
//
|
||
hr = ILS_E_TIMEOUT;
|
||
}
|
||
|
||
// If error, delete this request item
|
||
//
|
||
if (RetCode == -1)
|
||
{
|
||
// Convert the error
|
||
//
|
||
hr = ::LdapError2Hresult (pInfo->ld->ld_errno);
|
||
}
|
||
else
|
||
// If not timed out
|
||
//
|
||
if (RetCode != 0)
|
||
{
|
||
// It appears to be successful!
|
||
//
|
||
MyAssert (pLdapMsg != NULL);
|
||
|
||
// Cache the ldap msg pointer
|
||
pItem->EnterResult (pLdapMsg);
|
||
|
||
// Get the ldap error code
|
||
//
|
||
hr = (pLdapMsg != NULL) ? ::LdapError2Hresult (pLdapMsg->lm_returncode) :
|
||
S_OK;
|
||
}
|
||
|
||
// Get the result handler based on uNotifyMsg
|
||
//
|
||
pfn = ::GetResponseHandler (pInfo->uNotifyMsg);
|
||
if (pfn == NULL)
|
||
{
|
||
prev = pItem;
|
||
continue;
|
||
}
|
||
|
||
// Check integrity in pending info
|
||
//
|
||
MyAssert (pInfo->uRespID != 0);
|
||
|
||
// Deal with the result or error
|
||
//
|
||
MyDebugMsg ((ZONE_RESP, "ULS: response(0x%lX), hr=0x%lX\r\n", pInfo->uNotifyMsg, hr));
|
||
if ((*pfn) (hr, pItem))
|
||
{
|
||
// Let's destroy this item
|
||
//
|
||
if (pItem == m_ItemList)
|
||
{
|
||
m_ItemList = next;
|
||
}
|
||
else
|
||
{
|
||
MyAssert (prev != NULL);
|
||
prev->SetNext (next);
|
||
}
|
||
delete pItem; // SP_CSession::Disconnect() and ldap_msgfree() will be called in destructor
|
||
}
|
||
else
|
||
{
|
||
// Let's keep this item around.
|
||
// There are pending results coming in.
|
||
//
|
||
pItem->UpdateLastModifiedTime ();
|
||
|
||
// Update the pointer
|
||
//
|
||
prev = pItem;
|
||
}
|
||
} // for
|
||
|
||
// If there is no more items on the list, then stop the timer
|
||
//
|
||
if (m_ItemList != NULL)
|
||
::SetTimer (g_hWndHidden, ID_TIMER_POLL_RESULT, g_uResponsePollPeriod, NULL);
|
||
|
||
WriteUnlock ();
|
||
|
||
return S_OK;
|
||
}
|
||
|
||
|
||
HRESULT SP_CResponseQueue::
|
||
Cancel ( ULONG uRespID )
|
||
{
|
||
SP_CResponse *pItem, *next, *prev;
|
||
RESP_INFO *pInfo;
|
||
BOOL fNeedCleanup = FALSE;
|
||
|
||
WriteLock ();
|
||
|
||
// Look for the item with a matching response id
|
||
//
|
||
for (prev = NULL, pItem = m_ItemList; pItem != NULL; prev = pItem, pItem = next)
|
||
{
|
||
// Cache the next pointer
|
||
//
|
||
next = pItem->GetNext ();
|
||
|
||
// Get the pinding info structure
|
||
//
|
||
pInfo = pItem->GetRespInfo ();
|
||
MyAssert (pInfo != NULL);
|
||
|
||
// See if the response id matches
|
||
//
|
||
if (pInfo->uRespID == uRespID)
|
||
{
|
||
// It is a match
|
||
//
|
||
SP_CSession *pSession = pItem->GetSession ();
|
||
MyAssert (pSession != NULL);
|
||
|
||
// Make sure we have a valid ldap session
|
||
//
|
||
MyAssert (pInfo->ld != NULL);
|
||
|
||
// If we are NOT in the request thread, then we need to marshal it
|
||
// to the request thread!!! Exit and report success!!!
|
||
//
|
||
if (GetCurrentThreadId () != g_dwReqThreadID)
|
||
{
|
||
MyDebugMsg ((ZONE_RESP, "ULS: marshalling request(0x%lX) in RespQ\r\n", pInfo->uNotifyMsg));
|
||
MARSHAL_REQ *pReq = MarshalReq_Alloc (WM_ILS_CANCEL, 0, 1);
|
||
if (pReq != NULL)
|
||
{
|
||
MarshalReq_SetParam (pReq, 0, (DWORD) uRespID, 0);
|
||
if (g_pReqQueue != NULL)
|
||
{
|
||
// This means that the locking order is
|
||
// Lock(PendingOpQueue), Lock(RequestQueue)
|
||
//
|
||
g_pReqQueue->Enter (pReq);
|
||
}
|
||
else
|
||
{
|
||
MyAssert (FALSE);
|
||
}
|
||
}
|
||
|
||
// Exit this loop
|
||
//
|
||
break;
|
||
}
|
||
|
||
// Indicate that we need to clean up item. Why?
|
||
// because we should not have any network operation inside critical section.
|
||
// this is to avoid any possible network blocking.
|
||
//
|
||
fNeedCleanup = TRUE;
|
||
|
||
// Let's destroy this item
|
||
//
|
||
if (pItem == m_ItemList)
|
||
{
|
||
m_ItemList = next;
|
||
}
|
||
else
|
||
{
|
||
MyAssert (prev != NULL);
|
||
prev->SetNext (next);
|
||
}
|
||
|
||
// Get out of the loop
|
||
//
|
||
break;
|
||
} // if matched
|
||
} // for
|
||
|
||
// If there is no more items on the list, then stop the timer
|
||
//
|
||
if (m_ItemList == NULL)
|
||
::KillTimer (g_hWndHidden, ID_TIMER_POLL_RESULT);
|
||
|
||
WriteUnlock ();
|
||
|
||
if (fNeedCleanup && pItem != NULL)
|
||
{
|
||
MyDebugMsg ((ZONE_RESP, "ULS: cancelled request(0x%lX) in RespQ\r\n", pInfo->uNotifyMsg));
|
||
|
||
// Get resp info pointer
|
||
//
|
||
pInfo = pItem->GetRespInfo ();
|
||
MyAssert (pInfo != NULL);
|
||
|
||
// Abandon the primary response if needed
|
||
//
|
||
if (pInfo->uMsgID[1] != INVALID_MSG_ID)
|
||
::ldap_abandon (pInfo->ld, pInfo->uMsgID[1]);
|
||
|
||
// Abandon the secondary response if needed
|
||
//
|
||
if (pInfo->uMsgID[0] != INVALID_MSG_ID)
|
||
::ldap_abandon (pInfo->ld, pInfo->uMsgID[0]);
|
||
|
||
// SP_CSession::Disconnect() and ldap_msgfree() will be called in destructor
|
||
//
|
||
delete pItem;
|
||
}
|
||
|
||
return ((pItem == NULL) ? ILS_E_NOTIFY_ID : S_OK);
|
||
}
|
||
|
||
|
||
/* ---------- protected methods ----------- */
|
||
|
||
|
||
/* ---------- private methods ----------- */
|
||
|
||
|
||
|
||
/* ==================== utilities ====================== */
|
||
|
||
|
||
VOID
|
||
FillDefRespInfo (
|
||
RESP_INFO *pInfo,
|
||
ULONG uRespID,
|
||
LDAP *ld,
|
||
ULONG uMsgID,
|
||
ULONG u2ndMsgID )
|
||
{
|
||
// Clean up
|
||
//
|
||
ZeroMemory (pInfo, sizeof (*pInfo));
|
||
|
||
// Cache the ldap session
|
||
//
|
||
pInfo->ld = ld;
|
||
|
||
// Generate a unique notify id
|
||
//
|
||
pInfo->uRespID = uRespID;
|
||
|
||
// Store the primary and seconary msg ids
|
||
//
|
||
pInfo->uMsgID[0] = uMsgID;
|
||
pInfo->uMsgID[1] = u2ndMsgID;
|
||
}
|
||
|
||
|
||
|
||
/* ---------- REFRESH SCHEDULER ----------- */
|
||
|
||
|
||
/* ---------- public methods ----------- */
|
||
|
||
|
||
SP_CRefreshScheduler::
|
||
SP_CRefreshScheduler ( VOID )
|
||
:
|
||
m_ListHead (NULL) // Initialize the item list
|
||
{
|
||
// Create a critical section for thread safe access
|
||
//
|
||
::MyInitializeCriticalSection (&m_csRefreshScheduler);
|
||
}
|
||
|
||
|
||
SP_CRefreshScheduler::
|
||
~SP_CRefreshScheduler ( VOID )
|
||
{
|
||
WriteLock ();
|
||
|
||
// Clean up the item list
|
||
//
|
||
REFRESH_ITEM *p, *next;
|
||
for (p = m_ListHead; p != NULL; p = next)
|
||
{
|
||
next = p->next;
|
||
MemFree (p);
|
||
}
|
||
m_ListHead = NULL;
|
||
|
||
WriteUnlock ();
|
||
|
||
// Delete the critical section
|
||
//
|
||
::MyDeleteCriticalSection (&m_csRefreshScheduler);
|
||
}
|
||
|
||
|
||
HRESULT SP_CRefreshScheduler::
|
||
SendRefreshMessages ( UINT uTimerID )
|
||
{
|
||
SP_CClient *pClient;
|
||
#ifdef ENABLE_MEETING_PLACE
|
||
SP_CMeeting *pMtg;
|
||
#endif
|
||
REFRESH_ITEM *prev, *curr;
|
||
INT nIndex;
|
||
|
||
// Lock the lists
|
||
//
|
||
ReadLock ();
|
||
|
||
// Locate this object in the list
|
||
//
|
||
nIndex = TimerID2Index (uTimerID);
|
||
for (prev = NULL, curr = m_ListHead;
|
||
curr != NULL;
|
||
curr = (prev = curr)->next)
|
||
{
|
||
if (curr->nIndex == nIndex)
|
||
{
|
||
// Find it. Let's send a refresh message for this object
|
||
//
|
||
switch (curr->ObjectType)
|
||
{
|
||
case CLIENT_OBJ:
|
||
pClient = (SP_CClient *) curr->pObject;
|
||
|
||
// Make sure this object is not deleted already
|
||
//
|
||
if (! MyIsBadWritePtr (pClient, sizeof (*pClient)) &&
|
||
pClient->IsValidObject ())
|
||
{
|
||
// Make sure this object is valid and registered
|
||
//
|
||
if (pClient->IsRegistered ())
|
||
{
|
||
MyDebugMsg ((ZONE_KA, "KA: send refresh msg for client\r\n"));
|
||
|
||
// Let's send a refresh message for this client object
|
||
// and update the new ttl value
|
||
//
|
||
pClient->AddRef ();
|
||
pClient->SendRefreshMsg ();
|
||
curr->uTTL = pClient->GetTTL ();
|
||
pClient->Release ();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
MyAssert (FALSE);
|
||
}
|
||
break;
|
||
|
||
#ifdef ENABLE_MEETING_PLACE
|
||
case MTG_OBJ:
|
||
pMtg = (SP_CMeeting *) curr->pObject;
|
||
|
||
// Make sure this object is not deleted already
|
||
//
|
||
if (! MyIsBadWritePtr (pMtg, sizeof (*pMtg)) &&
|
||
pMtg->IsValidObject ())
|
||
{
|
||
// Make sure this object is valid and registered
|
||
//
|
||
if (pMtg->IsRegistered ())
|
||
{
|
||
MyDebugMsg ((ZONE_KA, "KA: send refresh msg for mtg\r\n"));
|
||
|
||
// Let's send a refresh message for this user object
|
||
// and update the new ttl value
|
||
//
|
||
pMtg->AddRef ();
|
||
pMtg->SendRefreshMsg ();
|
||
curr->uTTL = pMtg->GetTTL ();
|
||
pMtg->Release ();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
MyAssert (FALSE);
|
||
}
|
||
break;
|
||
#endif
|
||
|
||
default:
|
||
MyAssert (FALSE);
|
||
break;
|
||
}
|
||
|
||
// Start the timer again and exit
|
||
// Note that curr->uTTL is the new TTL value from the server
|
||
// Also note that uTTL is in unit of minute
|
||
//
|
||
MyDebugMsg ((ZONE_KA, "KA: new ttl=%lu\r\n", curr->uTTL));
|
||
::SetTimer (g_hWndHidden, uTimerID, Minute2TickCount (curr->uTTL), NULL);
|
||
break;
|
||
} // if
|
||
} // for
|
||
|
||
ReadUnlock ();
|
||
return S_OK;
|
||
}
|
||
|
||
|
||
HRESULT SP_CRefreshScheduler::
|
||
EnterClientObject ( SP_CClient *pClient )
|
||
{
|
||
if (pClient == NULL)
|
||
return ILS_E_POINTER;
|
||
|
||
return EnterObject (CLIENT_OBJ, (VOID *) pClient, pClient->GetTTL ());
|
||
}
|
||
|
||
|
||
#ifdef ENABLE_MEETING_PLACE
|
||
HRESULT SP_CRefreshScheduler::
|
||
EnterMtgObject ( SP_CMeeting *pMtg )
|
||
{
|
||
if (pMtg == NULL)
|
||
return ILS_E_POINTER;
|
||
|
||
return EnterObject (MTG_OBJ, (VOID *) pMtg, pMtg->GetTTL ());
|
||
}
|
||
#endif
|
||
|
||
|
||
VOID *SP_CRefreshScheduler::
|
||
AllocItem ( BOOL fNeedLock )
|
||
{
|
||
REFRESH_ITEM *p, *curr, *prev;
|
||
INT nIndex, nLargestIndex;
|
||
BOOL fGotTheNewIndex;
|
||
|
||
// Allocate the structure
|
||
//
|
||
p = (REFRESH_ITEM *) MemAlloc (sizeof (REFRESH_ITEM));
|
||
if (p != NULL)
|
||
{
|
||
if (fNeedLock)
|
||
WriteLock ();
|
||
|
||
// Find out what should be the index for the new item
|
||
//
|
||
nLargestIndex = -1; // Yes, it is -1 for the case m_ListHead==NULL
|
||
fGotTheNewIndex = FALSE;
|
||
for (nIndex = 0, prev = NULL, curr = m_ListHead;
|
||
curr != NULL;
|
||
nIndex++, curr = (prev = curr)->next)
|
||
{
|
||
if (curr->nIndex > nIndex)
|
||
{
|
||
p->nIndex = nIndex;
|
||
fGotTheNewIndex = TRUE;
|
||
break;
|
||
}
|
||
|
||
nLargestIndex = curr->nIndex;
|
||
}
|
||
|
||
// Put the new item in the list in its appropriate position
|
||
//
|
||
if (fGotTheNewIndex)
|
||
{
|
||
if (prev == NULL)
|
||
{
|
||
// The new one must be the first one
|
||
//
|
||
MyAssert (p->nIndex == 0);
|
||
p->next = m_ListHead;
|
||
m_ListHead = p;
|
||
}
|
||
else
|
||
{
|
||
// The new one in the middle of the list
|
||
//
|
||
MyAssert (prev->nIndex < p->nIndex && p->nIndex < curr->nIndex);
|
||
MyAssert (prev->next == curr);
|
||
(prev->next = p)->next = curr;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
MyAssert (m_ListHead == NULL || prev != NULL);
|
||
|
||
if (m_ListHead == NULL)
|
||
{
|
||
// The new one will be the only one in the list
|
||
//
|
||
p->nIndex = 0;
|
||
(m_ListHead = p)->next = NULL;
|
||
}
|
||
else
|
||
{
|
||
// The new one is at the end of the list
|
||
//
|
||
MyAssert (prev != NULL && prev->next == NULL && curr == NULL);
|
||
p->nIndex = nLargestIndex + 1;
|
||
(prev->next = p)->next = curr;
|
||
}
|
||
}
|
||
|
||
if (fNeedLock)
|
||
WriteUnlock ();
|
||
} // if (p != NULL)
|
||
|
||
return p;
|
||
}
|
||
|
||
|
||
HRESULT SP_CRefreshScheduler::
|
||
EnterObject ( PrivateObjType ObjectType, VOID *pObject, ULONG uInitialTTL )
|
||
{
|
||
HRESULT hr = S_OK;
|
||
|
||
WriteLock ();
|
||
|
||
// Enter this object to the list
|
||
//
|
||
REFRESH_ITEM *p = (REFRESH_ITEM *) AllocItem (FALSE);
|
||
if (p == NULL)
|
||
{
|
||
hr = ILS_E_MEMORY;
|
||
goto MyExit;
|
||
}
|
||
|
||
// Fill in fields
|
||
//
|
||
p->ObjectType = ObjectType;
|
||
p->pObject = pObject;
|
||
p->uTTL = uInitialTTL;
|
||
|
||
// Turn on the timer
|
||
// Note that uTTL is in unit of minutes...
|
||
//
|
||
::SetTimer (g_hWndHidden, Index2TimerID (p->nIndex), Minute2TickCount (p->uTTL), NULL);
|
||
|
||
MyExit:
|
||
|
||
WriteUnlock ();
|
||
|
||
return hr;
|
||
}
|
||
|
||
|
||
HRESULT SP_CRefreshScheduler::
|
||
RemoveObject ( VOID *pObject )
|
||
{
|
||
REFRESH_ITEM *prev, *curr;
|
||
|
||
WriteLock ();
|
||
|
||
// Locate this object in the list
|
||
//
|
||
for (prev = NULL, curr = m_ListHead;
|
||
curr != NULL;
|
||
curr = (prev = curr)->next)
|
||
{
|
||
if (curr->pObject == pObject)
|
||
{
|
||
// Find it, let's kill the timer first
|
||
//
|
||
KillTimer (g_hWndHidden, Index2TimerID (curr->nIndex));
|
||
|
||
// Remove it from the list
|
||
//
|
||
if (prev == NULL)
|
||
{
|
||
// This one is the first one on the list
|
||
//
|
||
MyAssert (m_ListHead == curr);
|
||
m_ListHead = curr->next;
|
||
}
|
||
else
|
||
{
|
||
// This one is in the middle of the list
|
||
//
|
||
MyAssert (prev->next == curr);
|
||
prev->next = curr->next;
|
||
}
|
||
::MemFree(curr);
|
||
|
||
// Exit the loop
|
||
//
|
||
break;
|
||
}
|
||
}
|
||
|
||
WriteUnlock ();
|
||
|
||
return (curr != NULL ? S_OK : S_FALSE);
|
||
}
|
||
|
||
|
||
|
||
|
||
extern BOOL NotifyGeneric ( HRESULT, SP_CResponse * );
|
||
extern BOOL NotifyRegister ( HRESULT, SP_CResponse * );
|
||
extern BOOL NotifyResolveClient ( HRESULT, SP_CResponse * );
|
||
extern BOOL NotifyEnumClients ( HRESULT, SP_CResponse * );
|
||
extern BOOL NotifyEnumClientInfos ( HRESULT, SP_CResponse * );
|
||
extern BOOL NotifyResolveProt ( HRESULT, SP_CResponse * );
|
||
extern BOOL NotifyEnumProts ( HRESULT, SP_CResponse * );
|
||
extern BOOL NotifyResolveMtg ( HRESULT, SP_CResponse * );
|
||
extern BOOL NotifyEnumMtgInfos ( HRESULT, SP_CResponse * );
|
||
extern BOOL NotifyEnumMtgs ( HRESULT, SP_CResponse * );
|
||
extern BOOL NotifyEnumAttendees ( HRESULT, SP_CResponse * );
|
||
|
||
extern LPARAM AsynReq_RegisterClient ( MARSHAL_REQ * );
|
||
extern LPARAM AsynReq_RegisterProtocol ( MARSHAL_REQ * );
|
||
extern LPARAM AsynReq_RegisterMeeting ( MARSHAL_REQ * );
|
||
extern LPARAM AsynReq_UnRegisterClient ( MARSHAL_REQ * );
|
||
extern LPARAM AsynReq_UnRegisterProt ( MARSHAL_REQ * );
|
||
extern LPARAM AsynReq_UnRegisterMeeting ( MARSHAL_REQ * );
|
||
extern LPARAM AsynReq_SetClientInfo ( MARSHAL_REQ * );
|
||
extern LPARAM AsynReq_SetProtocolInfo ( MARSHAL_REQ * );
|
||
extern LPARAM AsynReq_SetMeetingInfo ( MARSHAL_REQ * );
|
||
extern LPARAM AsynReq_EnumClientsEx ( MARSHAL_REQ * );
|
||
extern LPARAM AsynReq_EnumProtocols ( MARSHAL_REQ * );
|
||
extern LPARAM AsynReq_EnumMtgsEx ( MARSHAL_REQ * );
|
||
extern LPARAM AsynReq_EnumAttendees ( MARSHAL_REQ * );
|
||
extern LPARAM AsynReq_ResolveClient ( MARSHAL_REQ * );
|
||
extern LPARAM AsynReq_ResolveProtocol ( MARSHAL_REQ * );
|
||
extern LPARAM AsynReq_ResolveMeeting ( MARSHAL_REQ * );
|
||
extern LPARAM AsynReq_UpdateAttendees ( MARSHAL_REQ * );
|
||
extern LPARAM AsynReq_Cancel ( MARSHAL_REQ * );
|
||
|
||
|
||
typedef struct
|
||
{
|
||
#ifdef DEBUG
|
||
LONG nMsg;
|
||
#endif
|
||
RESPONSE_HANDLER *pfnRespHdl;
|
||
REQUEST_HANDLER *pfnReqHdl;
|
||
}
|
||
RES_HDL_TBL;
|
||
|
||
|
||
|
||
RES_HDL_TBL g_ResHdlTbl[] =
|
||
{
|
||
{
|
||
#ifdef DEBUG
|
||
WM_ILS_REGISTER_CLIENT,
|
||
#endif
|
||
NotifyRegister,
|
||
AsynReq_RegisterClient
|
||
},
|
||
{
|
||
#ifdef DEBUG
|
||
WM_ILS_UNREGISTER_CLIENT,
|
||
#endif
|
||
NotifyGeneric,
|
||
AsynReq_UnRegisterClient
|
||
},
|
||
{
|
||
#ifdef DEBUG
|
||
WM_ILS_SET_CLIENT_INFO,
|
||
#endif
|
||
NotifyGeneric,
|
||
AsynReq_SetClientInfo
|
||
},
|
||
{
|
||
#ifdef DEBUG
|
||
WM_ILS_RESOLVE_CLIENT,
|
||
#endif
|
||
NotifyResolveClient,
|
||
AsynReq_ResolveClient
|
||
},
|
||
{
|
||
#ifdef DEBUG
|
||
WM_ILS_ENUM_CLIENTS,
|
||
#endif
|
||
NotifyEnumClients,
|
||
AsynReq_EnumClientsEx
|
||
},
|
||
{
|
||
#ifdef DEBUG
|
||
WM_ILS_ENUM_CLIENTINFOS,
|
||
#endif
|
||
NotifyEnumClientInfos,
|
||
AsynReq_EnumClientsEx
|
||
},
|
||
|
||
{
|
||
#ifdef DEBUG
|
||
WM_ILS_REGISTER_PROTOCOL,
|
||
#endif
|
||
NotifyRegister,
|
||
AsynReq_RegisterProtocol
|
||
},
|
||
{
|
||
#ifdef DEBUG
|
||
WM_ILS_UNREGISTER_PROTOCOL,
|
||
#endif
|
||
NotifyGeneric,
|
||
AsynReq_UnRegisterProt
|
||
},
|
||
{
|
||
#ifdef DEBUG
|
||
WM_ILS_SET_PROTOCOL_INFO,
|
||
#endif
|
||
NotifyGeneric,
|
||
AsynReq_SetProtocolInfo
|
||
},
|
||
{
|
||
#ifdef DEBUG
|
||
WM_ILS_RESOLVE_PROTOCOL,
|
||
#endif
|
||
NotifyResolveProt,
|
||
AsynReq_ResolveProtocol
|
||
},
|
||
{
|
||
#ifdef DEBUG
|
||
WM_ILS_ENUM_PROTOCOLS,
|
||
#endif
|
||
NotifyEnumProts,
|
||
AsynReq_EnumProtocols
|
||
},
|
||
|
||
#ifdef ENABLE_MEETING_PLACE
|
||
{
|
||
#ifdef DEBUG
|
||
WM_ILS_REGISTER_MEETING,
|
||
#endif
|
||
NotifyRegister,
|
||
AsynReq_RegisterMeeting
|
||
},
|
||
{
|
||
#ifdef DEBUG
|
||
WM_ILS_UNREGISTER_MEETING,
|
||
#endif
|
||
NotifyGeneric,
|
||
AsynReq_UnRegisterMeeting
|
||
},
|
||
{
|
||
#ifdef DEBUG
|
||
WM_ILS_SET_MEETING_INFO,
|
||
#endif
|
||
NotifyGeneric,
|
||
AsynReq_SetMeetingInfo
|
||
},
|
||
{
|
||
#ifdef DEBUG
|
||
WM_ILS_RESOLVE_MEETING,
|
||
#endif
|
||
NotifyResolveMtg,
|
||
AsynReq_ResolveMeeting
|
||
},
|
||
{
|
||
#ifdef DEBUG
|
||
WM_ILS_ENUM_MEETINGINFOS,
|
||
#endif
|
||
NotifyEnumMtgInfos,
|
||
AsynReq_EnumMtgsEx
|
||
},
|
||
{
|
||
#ifdef DEBUG
|
||
WM_ILS_ENUM_MEETINGS,
|
||
#endif
|
||
NotifyEnumMtgs,
|
||
AsynReq_EnumMtgsEx
|
||
},
|
||
{
|
||
#ifdef DEBUG
|
||
WM_ILS_ADD_ATTENDEE,
|
||
#endif
|
||
NotifyGeneric,
|
||
AsynReq_UpdateAttendees
|
||
},
|
||
{
|
||
#ifdef DEBUG
|
||
WM_ILS_REMOVE_ATTENDEE,
|
||
#endif
|
||
NotifyGeneric,
|
||
AsynReq_UpdateAttendees
|
||
},
|
||
{
|
||
#ifdef DEBUG
|
||
WM_ILS_ENUM_ATTENDEES,
|
||
#endif
|
||
NotifyEnumAttendees,
|
||
AsynReq_EnumAttendees
|
||
},
|
||
#endif // ENABLE_MEETING_PLACE
|
||
|
||
{
|
||
#ifdef DEBUG
|
||
WM_ILS_CANCEL,
|
||
#endif
|
||
NULL,
|
||
AsynReq_Cancel
|
||
}
|
||
};
|
||
|
||
|
||
|
||
#ifdef DEBUG
|
||
VOID DbgValidateHandlerTable ( VOID )
|
||
{
|
||
MyAssert (ARRAY_ELEMENTS (g_ResHdlTbl) == WM_ILS_LAST_ONE - WM_ILS_ASYNC_RES + 1);
|
||
|
||
for (LONG i = 0; i < ARRAY_ELEMENTS (g_ResHdlTbl); i++)
|
||
{
|
||
if (g_ResHdlTbl[i].nMsg - WM_ILS_ASYNC_RES != i)
|
||
{
|
||
MyAssert (FALSE);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
|
||
|
||
RES_HDL_TBL *
|
||
GetHandlerTableEntry ( ULONG uNotifyMsg )
|
||
{
|
||
ULONG nIndex = uNotifyMsg - WM_ILS_ASYNC_RES;
|
||
|
||
if (nIndex > WM_ILS_LAST_ONE)
|
||
{
|
||
MyAssert (FALSE);
|
||
return NULL;
|
||
}
|
||
|
||
return &g_ResHdlTbl[nIndex];
|
||
}
|
||
|
||
|
||
RESPONSE_HANDLER *
|
||
GetResponseHandler ( ULONG uNotifyMsg )
|
||
{
|
||
RES_HDL_TBL *p = GetHandlerTableEntry (uNotifyMsg);
|
||
return ((p != NULL) ? p->pfnRespHdl : NULL);
|
||
}
|
||
|
||
|
||
REQUEST_HANDLER *
|
||
GetRequestHandler ( ULONG uNotifyMsg )
|
||
{
|
||
RES_HDL_TBL *p = GetHandlerTableEntry (uNotifyMsg);
|
||
return ((p != NULL) ? p->pfnReqHdl : NULL);
|
||
}
|
||
|
||
|
||
|
||
|