⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ctlutil.cpp

📁 用DirectX制作高级动画-[Advanced.Animation.with.DirectX]
💻 CPP
📖 第 1 页 / 共 4 页
字号:
    BOOL	bStream
    ) :
	CUnknown(NAME("DeferredCommand"), pUnk),
	m_pQueue(pQ),
	m_pUnk(pUnkExecutor),
	m_iid(iid),
	m_dispidMethod(dispidMethod),
	m_wFlags(wFlags),
	m_DispParams(nArgs, pDispParams, phr),
	m_pvarResult(pvarResult),
	m_bStream(bStream),
	m_hrResult(E_ABORT)

{
    // convert REFTIME to REFERENCE_TIME
    COARefTime convertor(time);
    m_time = convertor;

    // no check of time validity - it's ok to queue a command that's
    // already late

    // check iid is supportable on pUnk by QueryInterface for it
    IUnknown * pInterface;
    HRESULT hr = m_pUnk->QueryInterface(GetIID(), (void**) &pInterface);
    if (FAILED(hr)) {
	*phr = hr;
	return;
    }
    pInterface->Release();


    // !!! check dispidMethod and param/return types using typelib
    ITypeInfo *pti;
    hr = m_Dispatch.GetTypeInfo(*iid, 0, 0, &pti);
    if (FAILED(hr)) {
	*phr = hr;
	return;
    }
    // !!! some sort of ITypeInfo validity check here
    pti->Release();


    // Fix up the dispid for put and get
    if (wFlags == DISPATCH_PROPERTYPUT) {
        m_DispParams.cNamedArgs = 1;
        m_DispId = DISPID_PROPERTYPUT;
        m_DispParams.rgdispidNamedArgs = &m_DispId;
    }

    // all checks ok - add to queue
    hr = pQ->Insert(this);
    if (FAILED(hr)) {
	*phr = hr;
    }
}


// refcounts are held by caller of InvokeAt... and by list. So if
// we get here, we can't be on the list

#if 0
CDeferredCommand::~CDeferredCommand()
{
    // this assert is invalid since if the queue is deleted while we are
    // still on the queue, we will have been removed by the queue and this
    // m_pQueue will not have been modified.
    // ASSERT(m_pQueue == NULL);

    // we don't hold a ref count on pUnk, which is the object that should
    // execute the command.
    // This is because there would otherwise be a circular refcount problem
    // since pUnk probably owns the CmdQueue object that has a refcount
    // on us.
    // The lifetime of pUnk is guaranteed by it being part of, or lifetime
    // controlled by, our parent object. As long as we are on the list, pUnk
    // must be valid. Once we are off the list, we do not use pUnk.

}
#endif


// overriden to publicise our interfaces

STDMETHODIMP
CDeferredCommand::NonDelegatingQueryInterface(REFIID riid, void **ppv)
{
    ValidateReadWritePtr(ppv,sizeof(PVOID));
    if (riid == IID_IDeferredCommand) {
	return GetInterface( (IDeferredCommand *) this, ppv);
    } else {
	return CUnknown::NonDelegatingQueryInterface(riid, ppv);
    }
}


// remove from q. this will reduce the refcount by one (since the q
// holds a count) but can't make us go away since he must have a
// refcount in order to call this method.

STDMETHODIMP
CDeferredCommand::Cancel()
{
    if (m_pQueue == NULL) {
	return VFW_E_ALREADY_CANCELLED;
    }

    HRESULT hr = m_pQueue->Remove(this);
    if (FAILED(hr)) {
	return hr;
    }

    m_pQueue = NULL;
    return S_OK;
}


STDMETHODIMP
CDeferredCommand::Confidence(LONG* pConfidence)
{
    return E_NOTIMPL;
}


STDMETHODIMP
CDeferredCommand::GetHResult(HRESULT * phrResult)
{
    CheckPointer(phrResult,E_POINTER);
    ValidateReadWritePtr(phrResult,sizeof(HRESULT));

    if (m_pQueue != NULL) {
	return E_ABORT;
    }
    *phrResult = m_hrResult;
    return S_OK;
}


// set the time to be a new time (checking that it is valid) and
// then requeue

STDMETHODIMP
CDeferredCommand::Postpone(REFTIME newtime)
{

    // check that this time is not past
    // convert REFTIME to REFERENCE_TIME
    COARefTime convertor(newtime);

    // check that the time has not passed
    if (m_pQueue->CheckTime(convertor, IsStreamTime())) {
	return VFW_E_TIME_ALREADY_PASSED;
    }

    // extract from list
    HRESULT hr = m_pQueue->Remove(this);
    if (FAILED(hr)) {
	return hr;
    }

    // change time
    m_time = convertor;

    // requeue
    hr = m_pQueue->Insert(this);

    return hr;
}


HRESULT
CDeferredCommand::Invoke()
{
    // check that we are still outstanding
    if (m_pQueue == NULL) {
	return VFW_E_ALREADY_CANCELLED;
    }

    // get the type info
    ITypeInfo* pti;
    HRESULT hr = m_Dispatch.GetTypeInfo(GetIID(), 0, 0, &pti);
    if (FAILED(hr)) {
	return hr;
    }

    // qi for the expected interface and then invoke it. Note that we have to
    // treat the returned interface as IUnknown since we don't know its type.
    IUnknown* pInterface;

    hr = m_pUnk->QueryInterface(GetIID(), (void**) &pInterface);
    if (FAILED(hr)) {
	pti->Release();
	return hr;
    }

    EXCEPINFO expinfo;
    UINT uArgErr;
    m_hrResult = pti->Invoke(
	pInterface,
	GetMethod(),
	GetFlags(),
	GetParams(),
	GetResult(),
	&expinfo,
	&uArgErr);

    // release the interface we QI'd for
    pInterface->Release();
    pti->Release();


    // remove from list whether or not successful
    // or we loop indefinitely
    hr = m_pQueue->Remove(this);
    m_pQueue = NULL;
    return hr;
}



// --- CCmdQueue methods ----------


CCmdQueue::CCmdQueue() :
    m_listPresentation(NAME("Presentation time command list")),
    m_listStream(NAME("Stream time command list")),
    m_evDue(TRUE),    // manual reset
    m_dwAdvise(0),
    m_pClock(NULL),
    m_bRunning(FALSE)
{
}


CCmdQueue::~CCmdQueue()
{
    // empty all our lists

    // we hold a refcount on each, so traverse and Release each
    // entry then RemoveAll to empty the list
    POSITION pos = m_listPresentation.GetHeadPosition();

    while(pos) {
	CDeferredCommand* pCmd = m_listPresentation.GetNext(pos);
	pCmd->Release();
    }
    m_listPresentation.RemoveAll();

    pos = m_listStream.GetHeadPosition();

    while(pos) {
	CDeferredCommand* pCmd = m_listStream.GetNext(pos);
	pCmd->Release();
    }
    m_listStream.RemoveAll();

    if (m_pClock) {
	if (m_dwAdvise) {
	    m_pClock->Unadvise(m_dwAdvise);
	    m_dwAdvise = 0;
	}
	m_pClock->Release();
    }
}


// returns a new CDeferredCommand object that will be initialised with
// the parameters and will be added to the queue during construction.
// returns S_OK if successfully created otherwise an error and
// no object has been queued.

HRESULT
CCmdQueue::New(
    CDeferredCommand **ppCmd,
    LPUNKNOWN	pUnk,		// this object will execute command
    REFTIME	time,
    GUID*	iid,
    long	dispidMethod,
    short	wFlags,
    long	cArgs,
    VARIANT*	pDispParams,
    VARIANT*	pvarResult,
    short*	puArgErr,
    BOOL	bStream
)
{
    CAutoLock lock(&m_Lock);

    HRESULT hr = S_OK;
    *ppCmd = NULL;

    CDeferredCommand* pCmd;
    pCmd = new CDeferredCommand(
		    this,
		    NULL,	    // not aggregated
		    &hr,
		    pUnk,	    // this guy will execute
		    time,
		    iid,
		    dispidMethod,
		    wFlags,
		    cArgs,
		    pDispParams,
		    pvarResult,
		    puArgErr,
		    bStream);

    if (pCmd == NULL) {
	hr = E_OUTOFMEMORY;
    } else {
	*ppCmd = pCmd;
    }
    return hr;
}


HRESULT
CCmdQueue::Insert(CDeferredCommand* pCmd)
{
    CAutoLock lock(&m_Lock);

    // addref the item
    pCmd->AddRef();

    CGenericList<CDeferredCommand> * pList;
    if (pCmd->IsStreamTime()) {
	pList = &m_listStream;
    } else {
	pList = &m_listPresentation;
    }
    POSITION pos = pList->GetHeadPosition();

    // seek past all items that are before us
    while (pos &&
	(pList->Get(pos)->GetTime() <= pCmd->GetTime())) {

	pList->GetNext(pos);
    }

    // now at end of list or in front of items that come later
    if (!pos) {
	pList->AddTail(pCmd);
    } else {
	pList->AddBefore(pos, pCmd);
    }

    SetTimeAdvise();
    return S_OK;
}


HRESULT
CCmdQueue::Remove(CDeferredCommand* pCmd)
{
    CAutoLock lock(&m_Lock);
    HRESULT hr = S_OK;

    CGenericList<CDeferredCommand> * pList;
    if (pCmd->IsStreamTime()) {
	pList = &m_listStream;
    } else {
	pList = &m_listPresentation;
    }
    POSITION pos = pList->GetHeadPosition();

    // traverse the list
    while (pos && (pList->Get(pos) != pCmd)) {
	pList->GetNext(pos);
    }

    // did we drop off the end?
    if (!pos) {
	hr = VFW_E_NOT_FOUND;
    } else {

	// found it - now take off list
	pList->Remove(pos);

	// Insert did an AddRef, so release it
	pCmd->Release();

	// check that timer request is still for earliest time
	SetTimeAdvise();
    }
    return hr;
}


// set the clock used for timing

HRESULT
CCmdQueue::SetSyncSource(IReferenceClock* pClock)
{
    CAutoLock lock(&m_Lock);

    // addref the new clock first in case they are the same
    if (pClock) {
	pClock->AddRef();
    }

    // kill any advise on the old clock
    if (m_pClock) {
	if (m_dwAdvise) {
	    m_pClock->Unadvise(m_dwAdvise);
	    m_dwAdvise = 0;
	}
	m_pClock->Release();
    }
    m_pClock = pClock;

    // set up a new advise
    SetTimeAdvise();
    return S_OK;
}


// set up a timer event with the reference clock

void
CCmdQueue::SetTimeAdvise(void)
{
    // make sure we have a clock to use
    if (!m_pClock) {
	return;
    }

    // reset the event whenever we are requesting a new signal
    m_evDue.Reset();

    // time 0 is earliest
    CRefTime current;

    // find the earliest presentation time
    if (m_listPresentation.GetCount() > 0) {

	POSITION pos = m_listPresentation.GetHeadPosition();
	current = m_listPresentation.Get(pos)->GetTime();
    }

    // if we're running, check the stream times too
    if (m_bRunning) {

	CRefTime t;

	if (m_listStream.GetCount() > 0) {

	    POSITION pos = m_listStream.GetHeadPosition();
	    t = m_listStream.Get(pos)->GetTime();

	    // add on stream time offset to get presentation time
	    t += m_StreamTimeOffset;

	    // is this earlier?
	    if ((current == TimeZero) || (t < current)) {
		current = t;
	    }
	}
    }

    // need to change?
    if ((current > TimeZero) && (current != m_tCurrentAdvise)) {
	if (m_dwAdvise) {
	    m_pClock->Unadvise(m_dwAdvise);
	    // reset the event whenever we are requesting a new signal
	    m_evDue.Reset();
	}

	// ask for time advice - the first two params are either
	// stream time offset and stream time or
	// presentation time and 0. we always use the latter
	HRESULT hr = m_pClock->AdviseTime(
		    (REFERENCE_TIME)current,
		    TimeZero,
		    (HEVENT) HANDLE(m_evDue),
		    &m_dwAdvise);

	ASSERT(SUCCEEDED(hr));
	m_tCurrentAdvise = current;
    }
}


// switch to run mode. Streamtime to Presentation time mapping known.

HRESULT
CCmdQueue::Run(REFERENCE_TIME tStreamTimeOffset)
{
    CAutoLock lock(&m_Lock);

    m_StreamTimeOffset = tStreamTimeOffset;
    m_bRunning = TRUE;

    // ensure advise is accurate
    SetTimeAdvise();
    return S_OK;
}


// switch to Stopped or Paused mode. Time mapping not known.

HRESULT
CCmdQueue::EndRun()
{
    CAutoLock lock(&m_Lock);

    m_bRunning = FALSE;

    // check timer setting - stream times
    SetTimeAdvise();
    return S_OK;
}


// return a pointer to the next due command. Blocks for msTimeout
// milliseconds until there is a due command.
// Stream-time commands will only become due between Run and Endrun calls.
// The command remains queued until invoked or cancelled.
// Returns E_ABORT if timeout occurs, otherwise S_OK (or other error).
//
// returns an AddRef'd object

HRESULT
CCmdQueue::GetDueCommand(CDeferredCommand ** ppCmd, long msTimeout)
{
    // loop until we timeout or find a due command
    for (;;) {

	{
	    CAutoLock lock(&m_Lock);


	    // find the earliest command
	    CDeferredCommand * pCmd = NULL;

	    // check the presentation time and the
	    // stream time list to find the earliest

	    if (m_listPresentation.GetCount() > 0) {
		POSITION pos = m_listPresentation.GetHeadPosition();
		pCmd = m_listPresentation.Get(pos);
	    }

	    if (m_bRunning && (m_listStream.GetCount() > 0)) {
		POSITION pos = m_listStream.GetHeadPosition();
		CDeferredCommand* pStrm = m_listStream.Get(pos);

		CRefTime t = pStrm->GetTime() + m_StreamTimeOffset;
		if (!pCmd || (t < pCmd->GetTime())) {
		    pCmd = pStrm;
		}
	    }

	    //	if we have found one, is it due?
	    if (pCmd) {
		if (CheckTime(pCmd->GetTime(), pCmd->IsStreamTime())) {

		    // yes it's due - addref it
		    pCmd->AddRef();
		    *ppCmd = pCmd;
		    return S_OK;
		}
	    }
	}

	// block until the advise is signalled
	if (WaitForSingleObject(m_evDue, msTimeout) != WAIT_OBJECT_0) {
	    return E_ABORT;
	}
    }
}


// return a pointer to a command that will be due for a given time.
// Pass in a stream time here. The stream time offset will be passed
// in via the Run method.
// Commands remain queued until invoked or cancelled.
// This method will not block. It will report E_ABORT if there are no
// commands due yet.
//
// returns an AddRef'd object

HRESULT
CCmdQueue::GetCommandDueFor(REFERENCE_TIME rtStream, CDeferredCommand**ppCmd)
{
    CAutoLock lock(&m_Lock);

    CRefTime tStream(rtStream);

    // find the earliest stream and presentation time commands
    CDeferredCommand* pStream = NULL;
    if (m_listStream.GetCount() > 0) {
	POSITION pos = m_listStream.GetHeadPosition();
	pStream = m_listStream.Get(pos);
    }
    CDeferredCommand* pPresent = NULL;
    if (m_listPresentation.GetCount() > 0) {
	POSITION pos = m_listPresentation.GetHeadPosition();
	pPresent = m_listPresentation.Get(pos);
    }

    // is there a presentation time that has passed already
    if (pPresent && CheckTime(pPresent->GetTime(), FALSE)) {
	pPresent->AddRef();
	*ppCmd = pPresent;
	return S_OK;
    }

    // is there a stream time command due before this stream time
    if (pStream && (pStream->GetTime() <= tStream)) {
	pPresent->AddRef();
	*ppCmd = pStream;
	return S_OK;
    }

    // if we are running, we can map presentation times to
    // stream time. In this case, is there a presentation time command
    // that will be due before this stream time is presented?
    if (m_bRunning && pPresent) {

	// this stream time will appear at...
	tStream += m_StreamTimeOffset;

	// due before that?
	if (pPresent->GetTime() <= tStream) {
	    *ppCmd = pPresent;
	    return S_OK;
	}
    }

    // no commands due yet
    return VFW_E_NOT_FOUND;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -