123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659 |
- #include <kopano/platform.h>
- #include <new>
- #include <stdexcept>
- #include <utility>
- #include <kopano/lockhelper.hpp>
- #include <kopano/memory.hpp>
- #include <mapispi.h>
- #include <mapix.h>
- #include <kopano/ECDebug.h>
- #include "ECMsgStore.h"
- #include "ECNotifyClient.h"
- #include "ECSessionGroupManager.h"
- #include <kopano/ECGuid.h>
- #include "SOAPUtils.h"
- #include "WSUtil.h"
- #include <kopano/Util.h>
- #include <kopano/stringutil.h>
- #include <kopano/mapiext.h>
- #define MAX_NOTIFS_PER_CALL 64
- struct ECADVISE {
- ULONG cbKey;
- BYTE *lpKey;
- ULONG ulEventMask;
- IMAPIAdviseSink *lpAdviseSink;
- ULONG ulConnection;
- GUID guid;
- ULONG ulSupportConnection;
- };
- struct ECCHANGEADVISE {
- ULONG ulSyncId;
- ULONG ulChangeId;
- ULONG ulEventMask;
- IECChangeAdviseSink *lpAdviseSink;
- ULONG ulConnection;
- GUID guid;
- };
- using namespace KCHL;
- static inline std::pair<ULONG,ULONG> SyncAdviseToConnection(const SSyncAdvise &sSyncAdvise) {
- return std::make_pair(sSyncAdvise.sSyncState.ulSyncId,sSyncAdvise.ulConnection);
- }
- ECNotifyClient::ECNotifyClient(ULONG ulProviderType, void *lpProvider,
- ULONG ulFlags, LPMAPISUP lpSupport) :
- ECUnknown("ECNotifyClient"), m_lpSupport(lpSupport),
- m_lpProvider(lpProvider), m_ulProviderType(ulProviderType)
- {
- TRACE_MAPI(TRACE_ENTRY, "ECNotifyClient::ECNotifyClient","");
- ECSESSIONID ecSessionId;
- if(m_ulProviderType == MAPI_STORE)
- m_lpTransport = ((ECMsgStore*)m_lpProvider)->lpTransport;
- else if(m_ulProviderType == MAPI_ADDRBOOK)
- m_lpTransport = ((ECABLogon*)m_lpProvider)->m_lpTransport;
- else
- throw std::runtime_error("Unknown m_ulProviderType");
-
- if (m_lpTransport->HrGetSessionId(&ecSessionId, &m_ecSessionGroupId) != hrSuccess)
- throw std::runtime_error("ECNotifyClient/HrGetSessionId failed");
-
- if (g_ecSessionManager.GetSessionGroupData(m_ecSessionGroupId, m_lpTransport->GetProfileProps(), &m_lpSessionGroup) != hrSuccess)
- throw std::runtime_error("ECNotifyClient/GetSessionGroupData failed");
- if (m_lpSessionGroup->GetOrCreateNotifyMaster(&m_lpNotifyMaster) != hrSuccess)
- throw std::runtime_error("ECNotifyClient/GetOrCreateNotifyMaster failed");
- m_lpNotifyMaster->AddSession(this);
- }
- ECNotifyClient::~ECNotifyClient()
- {
- TRACE_MAPI(TRACE_ENTRY, "ECNotifyClient::~ECNotifyClient","");
- if (m_lpNotifyMaster)
- m_lpNotifyMaster->ReleaseSession(this);
- if (m_lpSessionGroup)
- m_lpSessionGroup->Release();
-
- g_ecSessionManager.DeleteSessionGroupDataIfOrphan(m_ecSessionGroupId);
-
- ulock_rec biglock(m_hMutex);
- for (const auto &i : m_mapAdvise) {
- if (i.second->lpAdviseSink != NULL)
- i.second->lpAdviseSink->Release();
- MAPIFreeBuffer(i.second);
- }
- m_mapAdvise.clear();
- for (const auto &i : m_mapChangeAdvise) {
- if (i.second->lpAdviseSink != NULL)
- i.second->lpAdviseSink->Release();
- MAPIFreeBuffer(i.second);
- }
- m_mapChangeAdvise.clear();
- biglock.unlock();
- TRACE_MAPI(TRACE_RETURN, "ECNotifyClient::~ECNotifyClient","");
- }
- HRESULT ECNotifyClient::Create(ULONG ulProviderType, void *lpProvider, ULONG ulFlags, LPMAPISUP lpSupport, ECNotifyClient**lppNotifyClient)
- {
- auto lpNotifyClient = new(std::nothrow) ECNotifyClient(ulProviderType,
- lpProvider, ulFlags, lpSupport);
- if (lpNotifyClient == nullptr)
- return MAPI_E_NOT_ENOUGH_MEMORY;
- HRESULT hr = lpNotifyClient->QueryInterface(IID_ECNotifyClient, (void **)lppNotifyClient);
- if (hr != hrSuccess)
- delete lpNotifyClient;
- return hr;
- }
- HRESULT ECNotifyClient::QueryInterface(REFIID refiid, void **lppInterface)
- {
- REGISTER_INTERFACE2(ECNotifyClient, this);
- return MAPI_E_INTERFACE_NOT_SUPPORTED;
- }
- HRESULT ECNotifyClient::RegisterAdvise(ULONG cbKey, LPBYTE lpKey, ULONG ulEventMask, bool bSynchronous, LPMAPIADVISESINK lpAdviseSink, ULONG *lpulConnection)
- {
- HRESULT hr = MAPI_E_NO_SUPPORT;
- memory_ptr<ECADVISE> pEcAdvise;
- ULONG ulConnection = 0;
- if (lpKey == nullptr)
- return MAPI_E_INVALID_PARAMETER;
- hr = MAPIAllocateBuffer(sizeof(ECADVISE), &~pEcAdvise);
- if (hr != hrSuccess)
- return hr;
- *lpulConnection = 0;
- memset(pEcAdvise, 0, sizeof(ECADVISE));
-
- pEcAdvise->lpKey = NULL;
- pEcAdvise->cbKey = cbKey;
- hr = MAPIAllocateMore(cbKey, pEcAdvise, (LPVOID*)&pEcAdvise->lpKey);
- if (hr != hrSuccess)
- return hr;
- memcpy(pEcAdvise->lpKey, lpKey, cbKey);
-
- pEcAdvise->lpAdviseSink = lpAdviseSink;
- pEcAdvise->ulEventMask = ulEventMask;
- pEcAdvise->ulSupportConnection = 0;
-
- hr = m_lpNotifyMaster->ReserveConnection(&ulConnection);
- if(hr != hrSuccess)
- return hr;
-
- lpAdviseSink->AddRef();
- #ifdef NOTIFY_THROUGH_SUPPORT_OBJECT
- memory_ptr<NOTIFKEY> lpKeySupport;
- if(!bSynchronous) {
- hr = MAPIAllocateBuffer(CbNewNOTIFKEY(sizeof(GUID)), &~lpKeySupport);
- if(hr != hrSuccess)
- return hr;
- lpKeySupport->cb = sizeof(GUID);
- hr = CoCreateGuid((GUID *)lpKeySupport->ab);
- if(hr != hrSuccess)
- return hr;
-
- hr = m_lpSupport->Subscribe(lpKeySupport, (ulEventMask&~fnevLongTermEntryIDs), 0, lpAdviseSink, &pEcAdvise->ulSupportConnection);
- if(hr != hrSuccess)
- return hr;
- memcpy(&pEcAdvise->guid, lpKeySupport->ab, sizeof(GUID));
- }
- #endif
- {
- scoped_rlock biglock(m_hMutex);
- m_mapAdvise.insert(ECMAPADVISE::value_type(ulConnection, pEcAdvise.release()));
- }
-
- hr = m_lpNotifyMaster->ClaimConnection(this, &ECNotifyClient::Notify, ulConnection);
- if(hr != hrSuccess)
- return hr;
-
- *lpulConnection = ulConnection;
- return hrSuccess;
- }
- HRESULT ECNotifyClient::RegisterChangeAdvise(ULONG ulSyncId, ULONG ulChangeId,
- IECChangeAdviseSink *lpChangeAdviseSink, ULONG *lpulConnection)
- {
- HRESULT hr = MAPI_E_NO_SUPPORT;
- memory_ptr<ECCHANGEADVISE> pEcAdvise;
- ULONG ulConnection = 0;
- hr = MAPIAllocateBuffer(sizeof(ECCHANGEADVISE), &~pEcAdvise);
- if (hr != hrSuccess)
- return hr;
- *lpulConnection = 0;
- memset(pEcAdvise, 0, sizeof(ECCHANGEADVISE));
-
- pEcAdvise->ulSyncId = ulSyncId;
- pEcAdvise->ulChangeId = ulChangeId;
- pEcAdvise->lpAdviseSink = lpChangeAdviseSink;
- pEcAdvise->ulEventMask = fnevKopanoIcsChange;
-
- hr = m_lpNotifyMaster->ReserveConnection(&ulConnection);
- if(hr != hrSuccess)
- return hr;
-
- {
- scoped_rlock biglock(m_hMutex);
- lpChangeAdviseSink->AddRef();
- m_mapChangeAdvise.insert(ECMAPCHANGEADVISE::value_type(ulConnection, pEcAdvise.release()));
- }
-
- hr = m_lpNotifyMaster->ClaimConnection(this, &ECNotifyClient::NotifyChange, ulConnection);
- if(hr != hrSuccess)
- return hr;
-
- *lpulConnection = ulConnection;
- return hrSuccess;
- }
- HRESULT ECNotifyClient::UnRegisterAdvise(ULONG ulConnection)
- {
-
- HRESULT hr = m_lpNotifyMaster->DropConnection(ulConnection);
- if (hr != hrSuccess)
- return hr;
-
- scoped_rlock lock(m_hMutex);
- auto iIterAdvise = m_mapAdvise.find(ulConnection);
- if (iIterAdvise != m_mapAdvise.cend()) {
- if(iIterAdvise->second->ulSupportConnection)
- m_lpSupport->Unsubscribe(iIterAdvise->second->ulSupportConnection);
- if (iIterAdvise->second->lpAdviseSink != NULL)
- iIterAdvise->second->lpAdviseSink->Release();
- MAPIFreeBuffer(iIterAdvise->second);
- m_mapAdvise.erase(iIterAdvise);
- return hr;
- }
- auto iIterChangeAdvise = m_mapChangeAdvise.find(ulConnection);
- if (iIterChangeAdvise == m_mapChangeAdvise.cend())
- return hr;
- if (iIterChangeAdvise->second->lpAdviseSink != NULL)
- iIterChangeAdvise->second->lpAdviseSink->Release();
- MAPIFreeBuffer(iIterChangeAdvise->second);
- m_mapChangeAdvise.erase(iIterChangeAdvise);
- return hr;
- }
- HRESULT ECNotifyClient::Advise(ULONG cbKey, LPBYTE lpKey, ULONG ulEventMask, LPMAPIADVISESINK lpAdviseSink, ULONG *lpulConnection){
- TRACE_NOTIFY(TRACE_ENTRY, "ECNotifyClient::Advise", "");
- HRESULT hr = MAPI_E_NO_SUPPORT;
- ULONG ulConnection = 0;
-
- hr = RegisterAdvise(cbKey, lpKey, ulEventMask, false, lpAdviseSink, &ulConnection);
- if (hr != hrSuccess)
- goto exit;
-
- hr = m_lpTransport->HrSubscribe(cbKey, lpKey, ulConnection, ulEventMask);
- if(hr != hrSuccess) {
- UnRegisterAdvise(ulConnection);
- hr = MAPI_E_NO_SUPPORT;
- goto exit;
- }
-
-
- *lpulConnection = ulConnection;
- hr = hrSuccess;
- exit:
- TRACE_NOTIFY(TRACE_RETURN, "ECNotifyClient::Advise", "hr=0x%08X connection=%d", hr, *lpulConnection);
- return hr;
- }
- HRESULT ECNotifyClient::Advise(const ECLISTSYNCSTATE &lstSyncStates,
- IECChangeAdviseSink *lpChangeAdviseSink, ECLISTCONNECTION *lplstConnections)
- {
- TRACE_NOTIFY(TRACE_ENTRY, "ECNotifyClient::AdviseICS", "");
- HRESULT hr = MAPI_E_NO_SUPPORT;
- ECLISTSYNCADVISE lstAdvises;
- for (const auto &state : lstSyncStates) {
- SSyncAdvise sSyncAdvise = {{0}};
- hr = RegisterChangeAdvise(state.ulSyncId, state.ulChangeId, lpChangeAdviseSink, &sSyncAdvise.ulConnection);
- if (hr != hrSuccess)
- goto exit;
- sSyncAdvise.sSyncState = state;
- lstAdvises.push_back(std::move(sSyncAdvise));
- }
- hr = m_lpTransport->HrSubscribeMulti(lstAdvises, fnevKopanoIcsChange);
- if (hr != hrSuccess) {
-
- for (auto iSyncAdvise = lstAdvises.cbegin();
- iSyncAdvise != lstAdvises.cend(); ++iSyncAdvise) {
- hr = m_lpTransport->HrSubscribe(iSyncAdvise->sSyncState.ulSyncId, iSyncAdvise->sSyncState.ulChangeId, iSyncAdvise->ulConnection, fnevKopanoIcsChange);
- if (hr != hrSuccess) {
-
-
- for (auto iSyncUnadvise = lstAdvises.cbegin();
- iSyncUnadvise != iSyncAdvise; ++iSyncUnadvise)
- m_lpTransport->HrUnSubscribe(iSyncUnadvise->ulConnection);
-
- hr = MAPI_E_NO_SUPPORT;
- goto exit;
- }
- }
- }
- std::transform(lstAdvises.begin(), lstAdvises.end(), std::back_inserter(*lplstConnections), &SyncAdviseToConnection);
- exit:
- if (hr != hrSuccess) {
-
- for (auto iSyncAdvise = lstAdvises.cbegin();
- iSyncAdvise != lstAdvises.cend(); ++iSyncAdvise)
- UnRegisterAdvise(iSyncAdvise->ulConnection);
- }
- TRACE_NOTIFY(TRACE_RETURN, "ECNotifyClient::AdviseICS", "hr=0x%08X", hr);
- return hr;
- }
- HRESULT ECNotifyClient::Unadvise(ULONG ulConnection)
- {
- TRACE_NOTIFY(TRACE_ENTRY, "ECNotifyClient::Unadvise", "%d", ulConnection);
- HRESULT hr = MAPI_E_NO_SUPPORT;
-
- hr = m_lpTransport->HrUnSubscribe(ulConnection);
- if (hr != hrSuccess)
- goto exit;
- hr = UnRegisterAdvise(ulConnection);
- if (hr != hrSuccess)
- goto exit;
- exit:
- TRACE_NOTIFY(TRACE_RETURN, "ECNotifyClient::Unadvise", "hr=0x%08X", hr);
- return hr;
- }
- HRESULT ECNotifyClient::Unadvise(const ECLISTCONNECTION &lstConnections)
- {
- TRACE_NOTIFY(TRACE_ENTRY, "ECNotifyClient::Unadvise", "");
- HRESULT hr = MAPI_E_NO_SUPPORT;
- HRESULT hrTmp;
- bool bWithErrors = false;
-
- hr = m_lpTransport->HrUnSubscribeMulti(lstConnections);
- if (hr != hrSuccess) {
- hr = hrSuccess;
- for (const auto &p : lstConnections) {
- hrTmp = m_lpTransport->HrUnSubscribe(p.second);
- if (FAILED(hrTmp))
- bWithErrors = true;
- }
- }
- for (const auto &p : lstConnections) {
- hrTmp = UnRegisterAdvise(p.second);
- if (FAILED(hrTmp))
- bWithErrors = true;
- }
- if (SUCCEEDED(hr) && bWithErrors)
- hr = MAPI_W_ERRORS_RETURNED;
- TRACE_NOTIFY(TRACE_RETURN, "ECNotifyClient::Unadvise", "hr=0x%08X", hr);
- return hr;
- }
- HRESULT ECNotifyClient::Reregister(ULONG ulConnection, ULONG cbKey, LPBYTE lpKey)
- {
- scoped_rlock biglock(m_hMutex);
- ECMAPADVISE::const_iterator iter = m_mapAdvise.find(ulConnection);
- if (iter == m_mapAdvise.cend())
- return MAPI_E_NOT_FOUND;
- if(cbKey) {
-
-
-
-
- if (cbKey > iter->second->cbKey) {
- HRESULT hr = MAPIAllocateMore(cbKey, iter->second,
- reinterpret_cast<void **>(&iter->second->lpKey));
- if (hr != hrSuccess)
- return hr;
- }
- memcpy(iter->second->lpKey, lpKey, cbKey);
- iter->second->cbKey = cbKey;
- }
- return m_lpTransport->HrSubscribe(iter->second->cbKey,
- iter->second->lpKey, ulConnection, iter->second->ulEventMask);
- }
- HRESULT ECNotifyClient::ReleaseAll()
- {
- scoped_rlock biglock(m_hMutex);
- for (auto &p : m_mapAdvise) {
- p.second->lpAdviseSink->Release();
- p.second->lpAdviseSink = NULL;
- }
- return hrSuccess;
- }
- typedef std::list<NOTIFICATION *> NOTIFICATIONLIST;
- typedef std::list<SBinary *> BINARYLIST;
- HRESULT ECNotifyClient::NotifyReload()
- {
- HRESULT hr = hrSuccess;
- struct notification notif;
- struct notificationTable table;
- NOTIFYLIST notifications;
- memset(¬if, 0, sizeof(notif));
- memset(&table, 0, sizeof(table));
- notif.ulEventType = fnevTableModified;
- notif.tab = &table;
- notif.tab->ulTableEvent = TABLE_RELOAD;
-
- notifications.push_back(¬if);
-
-
-
-
-
- scoped_rlock biglock(m_hMutex);
- for (const auto &p : m_mapAdvise)
- if (p.second->cbKey == 4)
- Notify(p.first, notifications);
- return hr;
- }
- HRESULT ECNotifyClient::Notify(ULONG ulConnection, const NOTIFYLIST &lNotifications)
- {
- HRESULT hr = hrSuccess;
- ECMAPADVISE::const_iterator iterAdvise;
- NOTIFICATIONLIST notifications;
- for (auto notp : lNotifications) {
- LPNOTIFICATION tmp = NULL;
- hr = CopySOAPNotificationToMAPINotification(m_lpProvider, notp, &tmp);
- if (hr != hrSuccess)
- continue;
- TRACE_NOTIFY(TRACE_ENTRY, "ECNotifyClient::Notify", "id=%d\n%s", notp->ulConnection, NotificationToString(1, tmp).c_str());
- notifications.push_back(tmp);
- }
- ulock_rec biglock(m_hMutex);
-
- iterAdvise = m_mapAdvise.find(ulConnection);
- if (iterAdvise == m_mapAdvise.cend() ||
- iterAdvise->second->lpAdviseSink == NULL) {
- TRACE_NOTIFY(TRACE_WARNING, "ECNotifyClient::Notify", "Unknown Notification id %d", ulConnection);
- goto exit;
- }
- if (!notifications.empty()) {
-
- auto iterNotification = notifications.cbegin();
- while (iterNotification != notifications.cend()) {
- memory_ptr<NOTIFICATION> lpNotifs;
-
- hr = MAPIAllocateBuffer(sizeof(NOTIFICATION) * MAX_NOTIFS_PER_CALL, &~lpNotifs);
- if (hr != hrSuccess)
- continue;
- ULONG i = 0;
- while (iterNotification != notifications.cend() && i < MAX_NOTIFS_PER_CALL) {
-
- memcpy(&lpNotifs[i++], *iterNotification, sizeof(NOTIFICATION));
- ++iterNotification;
- }
-
- if (!iterAdvise->second->ulSupportConnection) {
- if (iterAdvise->second->lpAdviseSink->OnNotify(i, lpNotifs) != 0)
- TRACE_NOTIFY(TRACE_WARNING, "ECNotifyClient::Notify", "Error by notify a client");
- } else {
- memory_ptr<NOTIFKEY> lpKey;
- ULONG ulResult = 0;
- hr = MAPIAllocateBuffer(CbNewNOTIFKEY(sizeof(GUID)), &~lpKey);
- if (hr != hrSuccess)
- goto exit;
- lpKey->cb = sizeof(GUID);
- memcpy(lpKey->ab, &iterAdvise->second->guid, sizeof(GUID));
-
- m_lpSupport->Notify(lpKey, i, lpNotifs, &ulResult);
- }
- }
- }
- exit:
- biglock.unlock();
-
- for (auto notp : notifications)
- MAPIFreeBuffer(notp);
- return hr;
- }
- HRESULT ECNotifyClient::NotifyChange(ULONG ulConnection, const NOTIFYLIST &lNotifications)
- {
- HRESULT hr = hrSuccess;
- memory_ptr<ENTRYLIST> lpSyncStates;
- ECMAPCHANGEADVISE::const_iterator iterAdvise;
- BINARYLIST syncStates;
- ulock_rec biglock(m_hMutex, std::defer_lock_t());
-
- hr = MAPIAllocateBuffer(sizeof *lpSyncStates, &~lpSyncStates);
- if (hr != hrSuccess)
- return hr;
- memset(lpSyncStates, 0, sizeof *lpSyncStates);
- hr = MAPIAllocateMore(sizeof *lpSyncStates->lpbin * MAX_NOTIFS_PER_CALL, lpSyncStates, (void**)&lpSyncStates->lpbin);
- if (hr != hrSuccess)
- return hr;
- memset(lpSyncStates->lpbin, 0, sizeof *lpSyncStates->lpbin * MAX_NOTIFS_PER_CALL);
- for (auto notp : lNotifications) {
- LPSBinary tmp = NULL;
- hr = CopySOAPChangeNotificationToSyncState(notp, &tmp, lpSyncStates);
- if (hr != hrSuccess)
- continue;
- TRACE_NOTIFY(TRACE_ENTRY, "ECNotifyClient::NotifyChange", "id=%d\n%s", notp->ulConnection, bin2hex(tmp->cb, tmp->lpb).c_str());
- syncStates.push_back(tmp);
- }
-
- biglock.lock();
- iterAdvise = m_mapChangeAdvise.find(ulConnection);
- if (iterAdvise == m_mapChangeAdvise.cend() ||
- iterAdvise->second->lpAdviseSink == NULL) {
- TRACE_NOTIFY(TRACE_WARNING, "ECNotifyClient::NotifyChange", "Unknown Notification id %d", ulConnection);
- return hr;
- }
- if (!syncStates.empty()) {
-
- auto iterSyncStates = syncStates.cbegin();
- while (iterSyncStates != syncStates.cend()) {
- lpSyncStates->cValues = 0;
- while (iterSyncStates != syncStates.cend() &&
- lpSyncStates->cValues < MAX_NOTIFS_PER_CALL) {
-
- memcpy(&lpSyncStates->lpbin[lpSyncStates->cValues++], *iterSyncStates, sizeof *lpSyncStates->lpbin);
- ++iterSyncStates;
- }
-
- if (iterAdvise->second->lpAdviseSink->OnNotify(0, lpSyncStates) != 0)
- TRACE_NOTIFY(TRACE_WARNING, "ECNotifyClient::NotifyChange", "Error by notify a client");
- }
- }
- return hrSuccess;
- }
- HRESULT ECNotifyClient::UpdateSyncStates(const ECLISTSYNCID &lstSyncId, ECLISTSYNCSTATE *lplstSyncState)
- {
- return m_lpTransport->HrGetSyncStates(lstSyncId, lplstSyncState);
- }
|