2004-05-06 01:51:59 +02:00
|
|
|
/*
|
|
|
|
* Implementation of IReferenceClock
|
|
|
|
*
|
|
|
|
* Copyright 2004 Raphael Junqueira
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define COM_NO_WINDOWS_H
|
|
|
|
#include "quartz_private.h"
|
|
|
|
|
|
|
|
#include "wine/debug.h"
|
|
|
|
#include "wine/unicode.h"
|
|
|
|
#include "uuids.h"
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(quartz);
|
|
|
|
|
|
|
|
typedef struct SystemClockAdviseEntry SystemClockAdviseEntry;
|
|
|
|
struct SystemClockAdviseEntry {
|
|
|
|
SystemClockAdviseEntry* next;
|
|
|
|
SystemClockAdviseEntry* prev;
|
|
|
|
|
|
|
|
HANDLE hEvent;
|
|
|
|
REFERENCE_TIME rtBaseTime;
|
|
|
|
REFERENCE_TIME rtIntervalTime;
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct SystemClockImpl {
|
2004-08-12 21:52:49 +02:00
|
|
|
IReferenceClockVtbl *lpVtbl;
|
2004-05-06 01:51:59 +02:00
|
|
|
ULONG ref;
|
|
|
|
|
|
|
|
/** IReferenceClock */
|
|
|
|
HANDLE adviseThread;
|
|
|
|
DWORD adviseThreadId;
|
|
|
|
BOOL adviseThreadActive;
|
|
|
|
REFERENCE_TIME lastRefTime;
|
|
|
|
DWORD lastTimeTickCount;
|
|
|
|
CRITICAL_SECTION safe;
|
|
|
|
|
|
|
|
SystemClockAdviseEntry* pSingleShotAdvise;
|
|
|
|
SystemClockAdviseEntry* pPeriodicAdvise;
|
|
|
|
} SystemClockImpl;
|
|
|
|
|
|
|
|
|
|
|
|
static void QUARTZ_RemoveAviseEntryFromQueue(SystemClockImpl* This, SystemClockAdviseEntry* pEntry) {
|
|
|
|
if (pEntry->prev) pEntry->prev->next = pEntry->next;
|
|
|
|
if (pEntry->next) pEntry->next->prev = pEntry->prev;
|
|
|
|
if (This->pSingleShotAdvise == pEntry) This->pSingleShotAdvise = pEntry->next;
|
|
|
|
if (This->pPeriodicAdvise == pEntry) This->pPeriodicAdvise = pEntry->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void QUARTZ_InsertAviseEntryFromQueue(SystemClockImpl* This, SystemClockAdviseEntry* pEntry, SystemClockAdviseEntry** pQueue) {
|
|
|
|
SystemClockAdviseEntry* prev_it = NULL;
|
|
|
|
SystemClockAdviseEntry* it = NULL;
|
|
|
|
REFERENCE_TIME bornTime = pEntry->rtBaseTime + pEntry->rtIntervalTime;
|
|
|
|
|
|
|
|
for (it = *pQueue; NULL != it && (it->rtBaseTime + it->rtIntervalTime) < bornTime; it = it->next) {
|
|
|
|
prev_it = it;
|
|
|
|
}
|
|
|
|
if (NULL == prev_it) {
|
|
|
|
pEntry->prev = NULL;
|
|
|
|
if (NULL != (*pQueue)) pEntry->next = (*pQueue)->next;
|
|
|
|
/*assert( NULL == pEntry->next->prev );*/
|
|
|
|
if (NULL != pEntry->next) pEntry->next->prev = pEntry;
|
|
|
|
(*pQueue) = pEntry;
|
|
|
|
} else {
|
|
|
|
pEntry->prev = prev_it;
|
|
|
|
pEntry->next = prev_it->next;
|
|
|
|
prev_it->next = pEntry;
|
|
|
|
if (NULL != pEntry->next) pEntry->next->prev = pEntry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define MAX_REFTIME (REFERENCE_TIME)(0x7FFFFFFFFFFFFFFF)
|
|
|
|
#define ADVISE_EXIT (WM_APP + 0)
|
|
|
|
#define ADVISE_REMOVE (WM_APP + 2)
|
|
|
|
#define ADVISE_ADD_SINGLESHOT (WM_APP + 4)
|
|
|
|
#define ADVISE_ADD_PERIODIC (WM_APP + 8)
|
|
|
|
|
|
|
|
static DWORD WINAPI SystemClockAdviseThread(LPVOID lpParam) {
|
|
|
|
SystemClockImpl* This = (SystemClockImpl*) lpParam;
|
|
|
|
DWORD timeOut = INFINITE;
|
|
|
|
DWORD tmpTimeOut;
|
|
|
|
MSG msg;
|
|
|
|
HRESULT hr;
|
|
|
|
REFERENCE_TIME curTime;
|
|
|
|
SystemClockAdviseEntry* it = NULL;
|
|
|
|
|
|
|
|
TRACE("(%p): Main Loop\n", This);
|
|
|
|
|
|
|
|
while (TRUE) {
|
|
|
|
if (timeOut > 0) MsgWaitForMultipleObjects(0, NULL, FALSE, timeOut, QS_POSTMESSAGE|QS_SENDMESSAGE|QS_TIMER);
|
|
|
|
|
|
|
|
EnterCriticalSection(&This->safe);
|
|
|
|
/*timeOut = IReferenceClock_OnTimerUpdated(This); */
|
|
|
|
hr = IReferenceClock_GetTime((IReferenceClock*) This, &curTime);
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
timeOut = INFINITE;
|
|
|
|
goto outrefresh;
|
|
|
|
}
|
2004-12-16 15:25:15 +01:00
|
|
|
|
2004-05-06 01:51:59 +02:00
|
|
|
/** First SingleShots Advice: sorted list */
|
2004-12-16 15:25:15 +01:00
|
|
|
for (it = This->pSingleShotAdvise; NULL != it && (it->rtBaseTime + it->rtIntervalTime) <= curTime; it = it->next) {
|
2004-05-06 01:51:59 +02:00
|
|
|
/** send event ... */
|
|
|
|
SetEvent((HANDLE) it->hEvent);
|
|
|
|
/** ... and Release it */
|
|
|
|
QUARTZ_RemoveAviseEntryFromQueue(This, it);
|
|
|
|
HeapFree(GetProcessHeap(), 0, it);
|
|
|
|
}
|
2004-12-16 15:25:15 +01:00
|
|
|
if (NULL != it) timeOut = (DWORD) ((it->rtBaseTime + it->rtIntervalTime) - curTime) / (REFERENCE_TIME)10000;
|
2004-05-06 01:51:59 +02:00
|
|
|
|
|
|
|
/** Now Periodics Advice: semi sorted list (sort cannot be used) */
|
|
|
|
for (it = This->pPeriodicAdvise; NULL != it; it = it->next) {
|
|
|
|
if (it->rtBaseTime <= curTime) {
|
|
|
|
DWORD nPeriods = (DWORD) ((curTime - it->rtBaseTime) / it->rtIntervalTime);
|
|
|
|
/** Release the semaphore ... */
|
|
|
|
ReleaseSemaphore((HANDLE) it->hEvent, nPeriods, NULL);
|
|
|
|
/** ... and refresh time */
|
2004-12-16 15:25:15 +01:00
|
|
|
it->rtBaseTime += nPeriods * it->rtIntervalTime;
|
2004-05-06 01:51:59 +02:00
|
|
|
/*assert( it->rtBaseTime + it->rtIntervalTime < curTime );*/
|
|
|
|
}
|
2004-12-16 15:25:15 +01:00
|
|
|
tmpTimeOut = (DWORD) ((it->rtBaseTime + it->rtIntervalTime) - curTime) / (REFERENCE_TIME)10000;
|
2004-05-06 01:51:59 +02:00
|
|
|
if (timeOut > tmpTimeOut) timeOut = tmpTimeOut;
|
|
|
|
}
|
|
|
|
|
|
|
|
outrefresh:
|
|
|
|
LeaveCriticalSection(&This->safe);
|
|
|
|
|
2004-12-09 15:07:59 +01:00
|
|
|
while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
|
2004-05-06 01:51:59 +02:00
|
|
|
/** if hwnd we suppose that is a windows event ... */
|
|
|
|
if (NULL != msg.hwnd) {
|
|
|
|
TranslateMessage(&msg);
|
|
|
|
DispatchMessageA(&msg);
|
|
|
|
} else {
|
|
|
|
switch (msg.message) {
|
|
|
|
case WM_QUIT:
|
|
|
|
case ADVISE_EXIT:
|
|
|
|
goto outofthread;
|
|
|
|
case ADVISE_ADD_SINGLESHOT:
|
|
|
|
case ADVISE_ADD_PERIODIC:
|
|
|
|
/** set timeout to 0 to do a rescan now */
|
|
|
|
timeOut = 0;
|
|
|
|
break;
|
|
|
|
case ADVISE_REMOVE:
|
|
|
|
/** hmmmm what we can do here ... */
|
|
|
|
timeOut = INFINITE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ERR("Unhandled message %u. Critical Path\n", msg.message);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
outofthread:
|
|
|
|
TRACE("(%p): Exiting\n", This);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/*static DWORD WINAPI SystemClockAdviseThread(LPVOID lpParam) { */
|
|
|
|
|
|
|
|
static BOOL SystemClockPostMessageToAdviseThread(SystemClockImpl* This, UINT iMsg) {
|
|
|
|
if (FALSE == This->adviseThreadActive) {
|
2004-12-16 15:25:15 +01:00
|
|
|
BOOL res;
|
2004-05-06 01:51:59 +02:00
|
|
|
This->adviseThread = CreateThread(NULL, 0, SystemClockAdviseThread, This, 0, &This->adviseThreadId);
|
|
|
|
if (NULL == This->adviseThread) return FALSE;
|
|
|
|
SetThreadPriority(This->adviseThread, THREAD_PRIORITY_TIME_CRITICAL);
|
|
|
|
This->adviseThreadActive = TRUE;
|
2004-12-16 15:25:15 +01:00
|
|
|
while(1) {
|
|
|
|
res = PostThreadMessageA(This->adviseThreadId, iMsg, 0, 0);
|
|
|
|
/* Let the thread creates its message queue (with MsgWaitForMultipleObjects call) by yielding and retrying */
|
|
|
|
if (!res && (GetLastError() == ERROR_INVALID_THREAD_ID))
|
|
|
|
Sleep(0);
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return res;
|
2004-05-06 01:51:59 +02:00
|
|
|
}
|
|
|
|
return PostThreadMessageA(This->adviseThreadId, iMsg, 0, 0);
|
|
|
|
}
|
|
|
|
|
2004-08-13 01:00:51 +02:00
|
|
|
IReferenceClockVtbl SystemClock_Vtbl;
|
2004-05-06 01:51:59 +02:00
|
|
|
|
|
|
|
static ULONG WINAPI SystemClockImpl_AddRef(IReferenceClock* iface) {
|
2004-09-08 03:50:37 +02:00
|
|
|
SystemClockImpl *This = (SystemClockImpl *)iface;
|
2005-01-06 20:36:47 +01:00
|
|
|
ULONG ref = InterlockedIncrement(&This->ref);
|
|
|
|
|
|
|
|
TRACE("(%p): AddRef from %ld\n", This, ref - 1);
|
|
|
|
|
|
|
|
return ref;
|
2004-05-06 01:51:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI SystemClockImpl_QueryInterface(IReferenceClock* iface, REFIID riid, void** ppobj) {
|
2004-09-08 03:50:37 +02:00
|
|
|
SystemClockImpl *This = (SystemClockImpl *)iface;
|
2004-05-06 01:51:59 +02:00
|
|
|
TRACE("(%p, %s,%p)\n", This, debugstr_guid(riid), ppobj);
|
|
|
|
|
|
|
|
if (IsEqualIID (riid, &IID_IUnknown) ||
|
|
|
|
IsEqualIID (riid, &IID_IReferenceClock)) {
|
|
|
|
SystemClockImpl_AddRef(iface);
|
|
|
|
*ppobj = This;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
WARN("(%p, %s,%p): not found\n", This, debugstr_guid(riid), ppobj);
|
|
|
|
return E_NOINTERFACE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI SystemClockImpl_Release(IReferenceClock* iface) {
|
2004-09-08 03:50:37 +02:00
|
|
|
SystemClockImpl *This = (SystemClockImpl *)iface;
|
2005-01-06 20:36:47 +01:00
|
|
|
ULONG ref = InterlockedDecrement(&This->ref);
|
|
|
|
TRACE("(%p): ReleaseRef to %ld\n", This, ref);
|
2004-05-06 01:51:59 +02:00
|
|
|
if (ref == 0) {
|
|
|
|
if (SystemClockPostMessageToAdviseThread(This, ADVISE_EXIT)) {
|
|
|
|
WaitForSingleObject(This->adviseThread, INFINITE);
|
|
|
|
CloseHandle(This->adviseThread);
|
|
|
|
}
|
|
|
|
DeleteCriticalSection(&This->safe);
|
|
|
|
HeapFree(GetProcessHeap(), 0, This);
|
|
|
|
}
|
|
|
|
return ref;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI SystemClockImpl_GetTime(IReferenceClock* iface, REFERENCE_TIME* pTime) {
|
2004-09-08 03:50:37 +02:00
|
|
|
SystemClockImpl *This = (SystemClockImpl *)iface;
|
2004-05-06 01:51:59 +02:00
|
|
|
DWORD curTimeTickCount;
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
|
|
|
|
TRACE("(%p, %p)\n", This, pTime);
|
|
|
|
|
|
|
|
if (NULL == pTime) {
|
|
|
|
return E_POINTER;
|
|
|
|
}
|
|
|
|
|
|
|
|
curTimeTickCount = GetTickCount();
|
|
|
|
|
|
|
|
EnterCriticalSection(&This->safe);
|
|
|
|
/** TODO: safe this not using * 10000 */
|
|
|
|
This->lastRefTime += (REFERENCE_TIME) (DWORD) (curTimeTickCount - This->lastTimeTickCount) * (REFERENCE_TIME) 10000;
|
|
|
|
This->lastTimeTickCount = curTimeTickCount;
|
|
|
|
LeaveCriticalSection(&This->safe);
|
|
|
|
|
|
|
|
*pTime = This->lastRefTime;
|
|
|
|
if (This->lastTimeTickCount == curTimeTickCount) hr = S_FALSE;
|
|
|
|
This->lastTimeTickCount = curTimeTickCount;
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI SystemClockImpl_AdviseTime(IReferenceClock* iface, REFERENCE_TIME rtBaseTime, REFERENCE_TIME rtStreamTime, HEVENT hEvent, DWORD_PTR* pdwAdviseCookie) {
|
2004-09-08 03:50:37 +02:00
|
|
|
SystemClockImpl *This = (SystemClockImpl *)iface;
|
2004-05-06 01:51:59 +02:00
|
|
|
SystemClockAdviseEntry* pEntry = NULL;
|
|
|
|
|
|
|
|
TRACE("(%p, %lld, %lld, %ld, %p)\n", This, rtBaseTime, rtStreamTime, hEvent, pdwAdviseCookie);
|
|
|
|
|
|
|
|
if ((HEVENT) 0 == hEvent) {
|
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
|
|
|
if (0 >= rtBaseTime + rtStreamTime) {
|
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
|
|
|
if (NULL == pdwAdviseCookie) {
|
|
|
|
return E_POINTER;
|
|
|
|
}
|
|
|
|
pEntry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SystemClockAdviseEntry));
|
|
|
|
if (NULL == pEntry) {
|
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
pEntry->hEvent = (HANDLE) hEvent;
|
|
|
|
pEntry->rtBaseTime = rtBaseTime + rtStreamTime;
|
|
|
|
pEntry->rtIntervalTime = 0;
|
|
|
|
|
|
|
|
EnterCriticalSection(&This->safe);
|
|
|
|
QUARTZ_InsertAviseEntryFromQueue(This, pEntry, &This->pSingleShotAdvise);
|
|
|
|
LeaveCriticalSection(&This->safe);
|
|
|
|
|
|
|
|
SystemClockPostMessageToAdviseThread(This, ADVISE_ADD_SINGLESHOT);
|
|
|
|
|
|
|
|
*pdwAdviseCookie = (DWORD_PTR) (pEntry);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI SystemClockImpl_AdvisePeriodic(IReferenceClock* iface, REFERENCE_TIME rtStartTime, REFERENCE_TIME rtPeriodTime, HSEMAPHORE hSemaphore, DWORD_PTR* pdwAdviseCookie) {
|
2004-09-08 03:50:37 +02:00
|
|
|
SystemClockImpl *This = (SystemClockImpl *)iface;
|
2004-05-06 01:51:59 +02:00
|
|
|
SystemClockAdviseEntry* pEntry = NULL;
|
|
|
|
|
|
|
|
TRACE("(%p, %lld, %lld, %ld, %p)\n", This, rtStartTime, rtPeriodTime, hSemaphore, pdwAdviseCookie);
|
|
|
|
|
|
|
|
if ((HSEMAPHORE) 0 == hSemaphore) {
|
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
|
|
|
if (0 >= rtStartTime || 0 >= rtPeriodTime) {
|
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
|
|
|
if (NULL == pdwAdviseCookie) {
|
|
|
|
return E_POINTER;
|
|
|
|
}
|
|
|
|
pEntry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SystemClockAdviseEntry));
|
|
|
|
if (NULL == pEntry) {
|
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
pEntry->hEvent = (HANDLE) hSemaphore;
|
|
|
|
pEntry->rtBaseTime = rtStartTime;
|
|
|
|
pEntry->rtIntervalTime = rtPeriodTime;
|
|
|
|
|
|
|
|
EnterCriticalSection(&This->safe);
|
|
|
|
QUARTZ_InsertAviseEntryFromQueue(This, pEntry, &This->pPeriodicAdvise);
|
|
|
|
LeaveCriticalSection(&This->safe);
|
|
|
|
|
|
|
|
SystemClockPostMessageToAdviseThread(This, ADVISE_ADD_PERIODIC);
|
|
|
|
|
|
|
|
*pdwAdviseCookie = (DWORD_PTR) (pEntry);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI SystemClockImpl_Unadvise(IReferenceClock* iface, DWORD_PTR dwAdviseCookie) {
|
2004-09-08 03:50:37 +02:00
|
|
|
SystemClockImpl *This = (SystemClockImpl *)iface;
|
2004-05-06 01:51:59 +02:00
|
|
|
SystemClockAdviseEntry* pEntry = NULL;
|
|
|
|
SystemClockAdviseEntry* it = NULL;
|
|
|
|
HRESULT ret = S_OK;
|
|
|
|
TRACE("(%p, %lu)\n", This, dwAdviseCookie);
|
|
|
|
|
|
|
|
pEntry = (SystemClockAdviseEntry*) dwAdviseCookie;
|
|
|
|
|
|
|
|
EnterCriticalSection(&This->safe);
|
|
|
|
for (it = This->pPeriodicAdvise; NULL != it && it != pEntry; it = it->next) ;
|
|
|
|
if (it != pEntry) {
|
|
|
|
for (it = This->pSingleShotAdvise; NULL != it && it != pEntry; it = it->next) ;
|
|
|
|
if (it != pEntry) {
|
|
|
|
ret = S_FALSE;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QUARTZ_RemoveAviseEntryFromQueue(This, pEntry);
|
|
|
|
HeapFree(GetProcessHeap(), 0, pEntry);
|
|
|
|
|
|
|
|
SystemClockPostMessageToAdviseThread(This, ADVISE_REMOVE);
|
|
|
|
|
|
|
|
out:
|
|
|
|
LeaveCriticalSection(&This->safe);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2004-08-13 01:00:51 +02:00
|
|
|
IReferenceClockVtbl SystemClock_Vtbl =
|
2004-05-06 01:51:59 +02:00
|
|
|
{
|
|
|
|
SystemClockImpl_QueryInterface,
|
|
|
|
SystemClockImpl_AddRef,
|
|
|
|
SystemClockImpl_Release,
|
|
|
|
SystemClockImpl_GetTime,
|
|
|
|
SystemClockImpl_AdviseTime,
|
|
|
|
SystemClockImpl_AdvisePeriodic,
|
|
|
|
SystemClockImpl_Unadvise
|
|
|
|
};
|
|
|
|
|
|
|
|
HRESULT QUARTZ_CreateSystemClock(IUnknown * pUnkOuter, LPVOID * ppv) {
|
|
|
|
SystemClockImpl* obj = NULL;
|
|
|
|
|
|
|
|
TRACE("(%p,%p)\n", ppv, pUnkOuter);
|
|
|
|
|
2004-12-08 20:33:09 +01:00
|
|
|
obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SystemClockImpl));
|
2004-05-06 01:51:59 +02:00
|
|
|
if (NULL == obj) {
|
|
|
|
*ppv = NULL;
|
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
}
|
|
|
|
obj->lpVtbl = &SystemClock_Vtbl;
|
|
|
|
obj->ref = 0; /* will be inited by QueryInterface */
|
|
|
|
|
|
|
|
obj->lastTimeTickCount = GetTickCount();
|
|
|
|
InitializeCriticalSection(&obj->safe);
|
|
|
|
|
|
|
|
return SystemClockImpl_QueryInterface((IReferenceClock*) obj, &IID_IReferenceClock, ppv);
|
|
|
|
}
|