You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1613 lines
32 KiB
1613 lines
32 KiB
/* ----------------------------------------------------------------------
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
|
|
|