//------------------------------------------------------------------------------
// File: CtlUtil.cpp
//
// Desc: DirectShow base classes.
//
// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------------------------
 
 
// Base classes implementing IDispatch parsing for the basic control dual
// interfaces. Derive from these and implement just the custom method and
// property methods. We also implement CPosPassThru that can be used by
// renderers and transforms to pass by IMediaPosition and IMediaSeeking
 
 
#include "streams.h"
#include <limits.h>
#include "seekpt.h"
 
// 'bool' non standard reserved word
#pragma warning(disable:4237)
 
 
// --- CBaseDispatch implementation ----------
CBaseDispatch::~CBaseDispatch()
{
    if (m_pti) {
	m_pti->Release();
    }
}
 
 
// return 1 if we support GetTypeInfo
 
STDMETHODIMP
CBaseDispatch::GetTypeInfoCount(__out UINT * pctinfo)
{
    CheckPointer(pctinfo,E_POINTER);
    ValidateReadWritePtr(pctinfo,sizeof(UINT *));
    *pctinfo = 1;
    return S_OK;
}
 
 
typedef HRESULT (STDAPICALLTYPE *LPLOADTYPELIB)(
			    const OLECHAR FAR *szFile,
			    __deref_out ITypeLib FAR* FAR* pptlib);
 
typedef HRESULT (STDAPICALLTYPE *LPLOADREGTYPELIB)(REFGUID rguid,
			    WORD wVerMajor,
			    WORD wVerMinor,
			    LCID lcid,
			    __deref_out ITypeLib FAR* FAR* pptlib);
 
// attempt to find our type library
 
STDMETHODIMP
CBaseDispatch::GetTypeInfo(
  REFIID riid,
  UINT itinfo,
  LCID lcid,
  __deref_out ITypeInfo ** pptinfo)
{
    CheckPointer(pptinfo,E_POINTER);
    ValidateReadWritePtr(pptinfo,sizeof(ITypeInfo *));
    HRESULT hr;
 
    *pptinfo = NULL;
 
    // we only support one type element
    if (0 != itinfo) {
	return TYPE_E_ELEMENTNOTFOUND;
    }
 
    // always look for neutral
    if (NULL == m_pti) {
 
	LPLOADTYPELIB	    lpfnLoadTypeLib;
	LPLOADREGTYPELIB    lpfnLoadRegTypeLib;
	ITypeLib	    *ptlib;
	HINSTANCE	    hInst;
 
	static const char  szTypeLib[]	  = "LoadTypeLib";
	static const char  szRegTypeLib[] = "LoadRegTypeLib";
	static const WCHAR szControl[]	  = L"control.tlb";
 
	//
	// Try to get the Ole32Aut.dll module handle.
	//
 
	hInst = LoadOLEAut32();
	if (hInst == NULL) {
	    DWORD dwError = GetLastError();
	    return AmHresultFromWin32(dwError);
	}
	lpfnLoadRegTypeLib = (LPLOADREGTYPELIB)GetProcAddress(hInst,
							      szRegTypeLib);
	if (lpfnLoadRegTypeLib == NULL) {
	    DWORD dwError = GetLastError();
	    return AmHresultFromWin32(dwError);
	}
 
	hr = (*lpfnLoadRegTypeLib)(LIBID_QuartzTypeLib, 1, 0, // version 1.0
				   lcid, &ptlib);
 
	if (FAILED(hr)) {
 
	    // attempt to load directly - this will fill the
	    // registry in if it finds it
 
	    lpfnLoadTypeLib = (LPLOADTYPELIB)GetProcAddress(hInst, szTypeLib);
	    if (lpfnLoadTypeLib == NULL) {
		DWORD dwError = GetLastError();
		return AmHresultFromWin32(dwError);
	    }
 
	    hr = (*lpfnLoadTypeLib)(szControl, &ptlib);
	    if (FAILED(hr)) {
		return hr;
	    }
	}
 
	hr = ptlib->GetTypeInfoOfGuid(
		    riid,
		    &m_pti);
 
	ptlib->Release();
 
	if (FAILED(hr)) {
	    return hr;
	}
    }
 
    *pptinfo = m_pti;
    m_pti->AddRef();
    return S_OK;
}
 
 
STDMETHODIMP
CBaseDispatch::GetIDsOfNames(
  REFIID riid,
  __in_ecount(cNames) LPOLESTR * rgszNames,
  UINT cNames,
  LCID lcid,
  __out_ecount(cNames) DISPID * rgdispid)
{
    // although the IDispatch riid is dead, we use this to pass from
    // the interface implementation class to us the iid we are talking about.
 
    ITypeInfo * pti;
    HRESULT hr = GetTypeInfo(riid, 0, lcid, &pti);
 
    if (SUCCEEDED(hr)) {
	hr = pti->GetIDsOfNames(rgszNames, cNames, rgdispid);
 
	pti->Release();
    }
    return hr;
}
 
 
// --- CMediaControl implementation ---------
 
CMediaControl::CMediaControl(const TCHAR * name,LPUNKNOWN pUnk) :
    CUnknown(name, pUnk)
{
}
 
// expose our interfaces IMediaControl and IUnknown
 
STDMETHODIMP
CMediaControl::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
{
    ValidateReadWritePtr(ppv,sizeof(PVOID));
    if (riid == IID_IMediaControl) {
	return GetInterface( (IMediaControl *) this, ppv);
    } else {
	return CUnknown::NonDelegatingQueryInterface(riid, ppv);
    }
}
 
 
// return 1 if we support GetTypeInfo
 
STDMETHODIMP
CMediaControl::GetTypeInfoCount(__out UINT * pctinfo)
{
    return m_basedisp.GetTypeInfoCount(pctinfo);
}
 
 
// attempt to find our type library
 
STDMETHODIMP
CMediaControl::GetTypeInfo(
  UINT itinfo,
  LCID lcid,
  __deref_out ITypeInfo ** pptinfo)
{
    return m_basedisp.GetTypeInfo(
		IID_IMediaControl,
		itinfo,
		lcid,
		pptinfo);
}
 
 
STDMETHODIMP
CMediaControl::GetIDsOfNames(
  REFIID riid,
  __in_ecount(cNames) LPOLESTR * rgszNames,
  UINT cNames,
  LCID lcid,
  __out_ecount(cNames) DISPID * rgdispid)
{
    return m_basedisp.GetIDsOfNames(
			IID_IMediaControl,
			rgszNames,
			cNames,
			lcid,
			rgdispid);
}
 
 
STDMETHODIMP
CMediaControl::Invoke(
  DISPID dispidMember,
  REFIID riid,
  LCID lcid,
  WORD wFlags,
  __in DISPPARAMS * pdispparams,
  __out_opt VARIANT * pvarResult,
  __out_opt EXCEPINFO * pexcepinfo,
  __out_opt UINT * puArgErr)
{
    // this parameter is a dead leftover from an earlier interface
    if (IID_NULL != riid) {
	return DISP_E_UNKNOWNINTERFACE;
    }
 
    ITypeInfo * pti;
    HRESULT hr = GetTypeInfo(0, lcid, &pti);
 
    if (FAILED(hr)) {
	return hr;
    }
 
    hr = pti->Invoke(
	    (IMediaControl *)this,
	    dispidMember,
	    wFlags,
	    pdispparams,
	    pvarResult,
	    pexcepinfo,
	    puArgErr);
 
    pti->Release();
    return hr;
}
 
 
// --- CMediaEvent implementation ----------
 
 
CMediaEvent::CMediaEvent(__in_opt LPCTSTR name,__in_opt LPUNKNOWN pUnk) :
    CUnknown(name, pUnk)
{
}
 
 
// expose our interfaces IMediaEvent and IUnknown
 
STDMETHODIMP
CMediaEvent::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
{
    ValidateReadWritePtr(ppv,sizeof(PVOID));
    if (riid == IID_IMediaEvent || riid == IID_IMediaEventEx) {
	return GetInterface( (IMediaEventEx *) this, ppv);
    } else {
	return CUnknown::NonDelegatingQueryInterface(riid, ppv);
    }
}
 
 
// return 1 if we support GetTypeInfo
 
STDMETHODIMP
CMediaEvent::GetTypeInfoCount(__out UINT * pctinfo)
{
    return m_basedisp.GetTypeInfoCount(pctinfo);
}
 
 
// attempt to find our type library
 
STDMETHODIMP
CMediaEvent::GetTypeInfo(
  UINT itinfo,
  LCID lcid,
  __deref_out ITypeInfo ** pptinfo)
{
    return m_basedisp.GetTypeInfo(
		IID_IMediaEvent,
		itinfo,
		lcid,
		pptinfo);
}
 
 
STDMETHODIMP
CMediaEvent::GetIDsOfNames(
  REFIID riid,
  __in_ecount(cNames) LPOLESTR * rgszNames,
  UINT cNames,
  LCID lcid,
  __out_ecount(cNames) DISPID * rgdispid)
{
    return m_basedisp.GetIDsOfNames(
			IID_IMediaEvent,
			rgszNames,
			cNames,
			lcid,
			rgdispid);
}
 
 
STDMETHODIMP
CMediaEvent::Invoke(
  DISPID dispidMember,
  REFIID riid,
  LCID lcid,
  WORD wFlags,
  __in DISPPARAMS * pdispparams,
  __out_opt VARIANT * pvarResult,
  __out_opt EXCEPINFO * pexcepinfo,
  __out_opt UINT * puArgErr)
{
    // this parameter is a dead leftover from an earlier interface
    if (IID_NULL != riid) {
	return DISP_E_UNKNOWNINTERFACE;
    }
 
    ITypeInfo * pti;
    HRESULT hr = GetTypeInfo(0, lcid, &pti);
 
    if (FAILED(hr)) {
	return hr;
    }
 
    hr = pti->Invoke(
	    (IMediaEvent *)this,
	    dispidMember,
	    wFlags,
	    pdispparams,
	    pvarResult,
	    pexcepinfo,
	    puArgErr);
 
    pti->Release();
    return hr;
}
 
 
// --- CMediaPosition implementation ----------
 
 
CMediaPosition::CMediaPosition(__in_opt LPCTSTR name,__in_opt LPUNKNOWN pUnk) :
    CUnknown(name, pUnk)
{
}
 
CMediaPosition::CMediaPosition(__in_opt LPCTSTR name,
                               __in_opt LPUNKNOWN pUnk,
                               __inout HRESULT * phr) :
    CUnknown(name, pUnk)
{
    UNREFERENCED_PARAMETER(phr);
}
 
 
// expose our interfaces IMediaPosition and IUnknown
 
STDMETHODIMP
CMediaPosition::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
{
    ValidateReadWritePtr(ppv,sizeof(PVOID));
    if (riid == IID_IMediaPosition) {
	return GetInterface( (IMediaPosition *) this, ppv);
    } else {
	return CUnknown::NonDelegatingQueryInterface(riid, ppv);
    }
}
 
 
// return 1 if we support GetTypeInfo
 
STDMETHODIMP
CMediaPosition::GetTypeInfoCount(__out UINT * pctinfo)
{
    return m_basedisp.GetTypeInfoCount(pctinfo);
}
 
 
// attempt to find our type library
 
STDMETHODIMP
CMediaPosition::GetTypeInfo(
  UINT itinfo,
  LCID lcid,
  __deref_out ITypeInfo ** pptinfo)
{
    return m_basedisp.GetTypeInfo(
		IID_IMediaPosition,
		itinfo,
		lcid,
		pptinfo);
}
 
 
STDMETHODIMP
CMediaPosition::GetIDsOfNames(
  REFIID riid,
  __in_ecount(cNames) LPOLESTR * rgszNames,
  UINT cNames,
  LCID lcid,
  __out_ecount(cNames) DISPID * rgdispid)
{
    return m_basedisp.GetIDsOfNames(
			IID_IMediaPosition,
			rgszNames,
			cNames,
			lcid,
			rgdispid);
}
 
 
STDMETHODIMP
CMediaPosition::Invoke(
  DISPID dispidMember,
  REFIID riid,
  LCID lcid,
  WORD wFlags,
  __in DISPPARAMS * pdispparams,
  __out_opt VARIANT * pvarResult,
  __out_opt EXCEPINFO * pexcepinfo,
  __out_opt UINT * puArgErr)
{
    // this parameter is a dead leftover from an earlier interface
    if (IID_NULL != riid) {
	return DISP_E_UNKNOWNINTERFACE;
    }
 
    ITypeInfo * pti;
    HRESULT hr = GetTypeInfo(0, lcid, &pti);
 
    if (FAILED(hr)) {
	return hr;
    }
 
    hr = pti->Invoke(
	    (IMediaPosition *)this,
	    dispidMember,
	    wFlags,
	    pdispparams,
	    pvarResult,
	    pexcepinfo,
	    puArgErr);
 
    pti->Release();
    return hr;
}
 
 
// --- IMediaPosition and IMediaSeeking pass through class ----------
 
 
CPosPassThru::CPosPassThru(__in_opt LPCTSTR pName,
			   __in_opt LPUNKNOWN pUnk,
			   __inout HRESULT *phr,
			   IPin *pPin) :
    CMediaPosition(pName,pUnk),
    m_pPin(pPin)
{
    if (pPin == NULL) {
	*phr = E_POINTER;
	return;
    }
}
 
 
// Expose our IMediaSeeking and IMediaPosition interfaces
 
STDMETHODIMP
CPosPassThru::NonDelegatingQueryInterface(REFIID riid,__deref_out void **ppv)
{
    CheckPointer(ppv,E_POINTER);
    *ppv = NULL;
 
    if (riid == IID_IMediaSeeking) {
	return GetInterface( static_cast<IMediaSeeking *>(this), ppv);
    }
    return CMediaPosition::NonDelegatingQueryInterface(riid,ppv);
}
 
 
// Return the IMediaPosition interface from our peer
 
HRESULT
CPosPassThru::GetPeer(IMediaPosition ** ppMP)
{
    *ppMP = NULL;
 
    IPin *pConnected;
    HRESULT hr = m_pPin->ConnectedTo(&pConnected);
    if (FAILED(hr)) {
	return E_NOTIMPL;
    }
    IMediaPosition * pMP;
    hr = pConnected->QueryInterface(IID_IMediaPosition, (void **) &pMP);
    pConnected->Release();
    if (FAILED(hr)) {
	return E_NOTIMPL;
    }
 
    *ppMP = pMP;
    return S_OK;
}
 
 
// Return the IMediaSeeking interface from our peer
 
HRESULT
CPosPassThru::GetPeerSeeking(__deref_out IMediaSeeking ** ppMS)
{
    *ppMS = NULL;
 
    IPin *pConnected;
    HRESULT hr = m_pPin->ConnectedTo(&pConnected);
    if (FAILED(hr)) {
	return E_NOTIMPL;
    }
    IMediaSeeking * pMS;
    hr = pConnected->QueryInterface(IID_IMediaSeeking, (void **) &pMS);
    pConnected->Release();
    if (FAILED(hr)) {
	return E_NOTIMPL;
    }
 
    *ppMS = pMS;
    return S_OK;
}
 
 
// --- IMediaSeeking methods ----------
 
 
STDMETHODIMP
CPosPassThru::GetCapabilities(__out DWORD * pCaps)
{
    IMediaSeeking* pMS;
    HRESULT hr = GetPeerSeeking(&pMS);
    if (FAILED(hr)) {
	return hr;
    }
 
    hr = pMS->GetCapabilities(pCaps);
    pMS->Release();
    return hr;
}
 
STDMETHODIMP
CPosPassThru::CheckCapabilities(__inout DWORD * pCaps)
{
    IMediaSeeking* pMS;
    HRESULT hr = GetPeerSeeking(&pMS);
    if (FAILED(hr)) {
	return hr;
    }
 
    hr = pMS->CheckCapabilities(pCaps);
    pMS->Release();
    return hr;
}
 
STDMETHODIMP
CPosPassThru::IsFormatSupported(const GUID * pFormat)
{
    IMediaSeeking* pMS;
    HRESULT hr = GetPeerSeeking(&pMS);
    if (FAILED(hr)) {
	return hr;
    }
 
    hr = pMS->IsFormatSupported(pFormat);
    pMS->Release();
    return hr;
}
 
 
STDMETHODIMP
CPosPassThru::QueryPreferredFormat(__out GUID *pFormat)
{
    IMediaSeeking* pMS;
    HRESULT hr = GetPeerSeeking(&pMS);
    if (FAILED(hr)) {
	return hr;
    }
 
    hr = pMS->QueryPreferredFormat(pFormat);
    pMS->Release();
    return hr;
}
 
 
STDMETHODIMP
CPosPassThru::SetTimeFormat(const GUID * pFormat)
{
    IMediaSeeking* pMS;
    HRESULT hr = GetPeerSeeking(&pMS);
    if (FAILED(hr)) {
	return hr;
    }
 
    hr = pMS->SetTimeFormat(pFormat);
    pMS->Release();
    return hr;
}
 
 
STDMETHODIMP
CPosPassThru::GetTimeFormat(__out GUID *pFormat)
{
    IMediaSeeking* pMS;
    HRESULT hr = GetPeerSeeking(&pMS);
    if (FAILED(hr)) {
	return hr;
    }
 
    hr = pMS->GetTimeFormat(pFormat);
    pMS->Release();
    return hr;
}
 
 
STDMETHODIMP
CPosPassThru::IsUsingTimeFormat(const GUID * pFormat)
{
    IMediaSeeking* pMS;
    HRESULT hr = GetPeerSeeking(&pMS);
    if (FAILED(hr)) {
	return hr;
    }
 
    hr = pMS->IsUsingTimeFormat(pFormat);
    pMS->Release();
    return hr;
}
 
 
STDMETHODIMP
CPosPassThru::ConvertTimeFormat(__out LONGLONG * pTarget, 
                                __in_opt const GUID * pTargetFormat,
				LONGLONG Source, 
                                __in_opt const GUID * pSourceFormat )
{
    IMediaSeeking* pMS;
    HRESULT hr = GetPeerSeeking(&pMS);
    if (FAILED(hr)) {
	return hr;
    }
 
    hr = pMS->ConvertTimeFormat(pTarget, pTargetFormat, Source, pSourceFormat );
    pMS->Release();
    return hr;
}
 
 
STDMETHODIMP
CPosPassThru::SetPositions( __inout_opt LONGLONG * pCurrent, 
                            DWORD CurrentFlags, 
                            __inout_opt LONGLONG * pStop, 
                            DWORD StopFlags )
{
    IMediaSeeking* pMS;
    HRESULT hr = GetPeerSeeking(&pMS);
    if (FAILED(hr)) {
	return hr;
    }
 
    hr = pMS->SetPositions(pCurrent, CurrentFlags, pStop, StopFlags );
    pMS->Release();
    return hr;
}
 
STDMETHODIMP
CPosPassThru::GetPositions(__out_opt LONGLONG *pCurrent, __out_opt LONGLONG * pStop)
{
    IMediaSeeking* pMS;
    HRESULT hr = GetPeerSeeking(&pMS);
    if (FAILED(hr)) {
	return hr;
    }
 
    hr = pMS->GetPositions(pCurrent,pStop);
    pMS->Release();
    return hr;
}
 
HRESULT
CPosPassThru::GetSeekingLongLong
( HRESULT (__stdcall IMediaSeeking::*pMethod)( __out LONGLONG * )
, LONGLONG * pll
)
{
    IMediaSeeking* pMS;
    HRESULT hr = GetPeerSeeking(&pMS);
    if (SUCCEEDED(hr))
    {
	hr = (pMS->*pMethod)(pll);
	pMS->Release();
    }
    return hr;
}
 
// If we don't have a current position then ask upstream
 
STDMETHODIMP
CPosPassThru::GetCurrentPosition(__out LONGLONG *pCurrent)
{
    // Can we report the current position
    HRESULT hr = GetMediaTime(pCurrent,NULL);
    if (SUCCEEDED(hr)) hr = NOERROR;
    else hr = GetSeekingLongLong( &IMediaSeeking::GetCurrentPosition, pCurrent );
    return hr;
}
 
 
STDMETHODIMP
CPosPassThru::GetStopPosition(__out LONGLONG *pStop)
{
    return GetSeekingLongLong( &IMediaSeeking::GetStopPosition, pStop );
}
 
STDMETHODIMP
CPosPassThru::GetDuration(__out LONGLONG *pDuration)
{
    return GetSeekingLongLong( &IMediaSeeking::GetDuration, pDuration );
}
 
 
STDMETHODIMP
CPosPassThru::GetPreroll(__out LONGLONG *pllPreroll)
{
    return GetSeekingLongLong( &IMediaSeeking::GetPreroll, pllPreroll );
}
 
 
STDMETHODIMP
CPosPassThru::GetAvailable( __out_opt LONGLONG *pEarliest, __out_opt LONGLONG *pLatest )
{
    IMediaSeeking* pMS;
    HRESULT hr = GetPeerSeeking(&pMS);
    if (FAILED(hr)) {
	return hr;
    }
 
    hr = pMS->GetAvailable( pEarliest, pLatest );
    pMS->Release();
    return hr;
}
 
 
STDMETHODIMP
CPosPassThru::GetRate(__out double * pdRate)
{
    IMediaSeeking* pMS;
    HRESULT hr = GetPeerSeeking(&pMS);
    if (FAILED(hr)) {
	return hr;
    }
    hr = pMS->GetRate(pdRate);
    pMS->Release();
    return hr;
}
 
 
STDMETHODIMP
CPosPassThru::SetRate(double dRate)
{
    if (0.0 == dRate) {
		return E_INVALIDARG;
    }
 
    IMediaSeeking* pMS;
    HRESULT hr = GetPeerSeeking(&pMS);
    if (FAILED(hr)) {
	return hr;
    }
    hr = pMS->SetRate(dRate);
    pMS->Release();
    return hr;
}
 
 
 
 
// --- IMediaPosition methods ----------
 
 
STDMETHODIMP
CPosPassThru::get_Duration(__out REFTIME * plength)
{
    IMediaPosition* pMP;
    HRESULT hr = GetPeer(&pMP);
    if (FAILED(hr)) {
	return hr;
    }
 
    hr = pMP->get_Duration(plength);
    pMP->Release();
    return hr;
}
 
 
STDMETHODIMP
CPosPassThru::get_CurrentPosition(__out REFTIME * pllTime)
{
    IMediaPosition* pMP;
    HRESULT hr = GetPeer(&pMP);
    if (FAILED(hr)) {
	return hr;
    }
    hr = pMP->get_CurrentPosition(pllTime);
    pMP->Release();
    return hr;
}
 
 
STDMETHODIMP
CPosPassThru::put_CurrentPosition(REFTIME llTime)
{
    IMediaPosition* pMP;
    HRESULT hr = GetPeer(&pMP);
    if (FAILED(hr)) {
	return hr;
    }
    hr = pMP->put_CurrentPosition(llTime);
    pMP->Release();
    return hr;
}
 
 
STDMETHODIMP
CPosPassThru::get_StopTime(__out REFTIME * pllTime)
{
    IMediaPosition* pMP;
    HRESULT hr = GetPeer(&pMP);
    if (FAILED(hr)) {
	return hr;
    }
    hr = pMP->get_StopTime(pllTime);
    pMP->Release();
    return hr;
}
 
 
STDMETHODIMP
CPosPassThru::put_StopTime(REFTIME llTime)
{
    IMediaPosition* pMP;
    HRESULT hr = GetPeer(&pMP);
    if (FAILED(hr)) {
	return hr;
    }
    hr = pMP->put_StopTime(llTime);
    pMP->Release();
    return hr;
}
 
 
STDMETHODIMP
CPosPassThru::get_PrerollTime(__out REFTIME * pllTime)
{
    IMediaPosition* pMP;
    HRESULT hr = GetPeer(&pMP);
    if (FAILED(hr)) {
	return hr;
    }
    hr = pMP->get_PrerollTime(pllTime);
    pMP->Release();
    return hr;
}
 
 
STDMETHODIMP
CPosPassThru::put_PrerollTime(REFTIME llTime)
{
    IMediaPosition* pMP;
    HRESULT hr = GetPeer(&pMP);
    if (FAILED(hr)) {
	return hr;
    }
    hr = pMP->put_PrerollTime(llTime);
    pMP->Release();
    return hr;
}
 
 
STDMETHODIMP
CPosPassThru::get_Rate(__out double * pdRate)
{
    IMediaPosition* pMP;
    HRESULT hr = GetPeer(&pMP);
    if (FAILED(hr)) {
	return hr;
    }
    hr = pMP->get_Rate(pdRate);
    pMP->Release();
    return hr;
}
 
 
STDMETHODIMP
CPosPassThru::put_Rate(double dRate)
{
    if (0.0 == dRate) {
		return E_INVALIDARG;
    }
 
    IMediaPosition* pMP;
    HRESULT hr = GetPeer(&pMP);
    if (FAILED(hr)) {
	return hr;
    }
    hr = pMP->put_Rate(dRate);
    pMP->Release();
    return hr;
}
 
 
STDMETHODIMP
CPosPassThru::CanSeekForward(__out LONG *pCanSeekForward)
{
    IMediaPosition* pMP;
    HRESULT hr = GetPeer(&pMP);
    if (FAILED(hr)) {
	return hr;
    }
    hr = pMP->CanSeekForward(pCanSeekForward);
    pMP->Release();
    return hr;
}
 
 
STDMETHODIMP
CPosPassThru::CanSeekBackward(__out LONG *pCanSeekBackward)
{
    IMediaPosition* pMP;
    HRESULT hr = GetPeer(&pMP);
    if (FAILED(hr)) {
	return hr;
    }
    hr = pMP->CanSeekBackward(pCanSeekBackward);
    pMP->Release();
    return hr;
}
 
 
// --- Implements the CRendererPosPassThru class ----------
 
 
// Media times (eg current frame, field, sample etc) are passed through the
// filtergraph in media samples. When a renderer gets a sample with media
// times in it, it will call one of the RegisterMediaTime methods we expose
// (one takes an IMediaSample, the other takes the media times direct). We
// store the media times internally and return them in GetCurrentPosition.
 
CRendererPosPassThru::CRendererPosPassThru(__in_opt LPCTSTR pName,
					   __in_opt LPUNKNOWN pUnk,
					   __inout HRESULT *phr,
					   IPin *pPin) :
    CPosPassThru(pName,pUnk,phr,pPin),
    m_StartMedia(0),
    m_EndMedia(0),
    m_bReset(TRUE)
{
}
 
 
// Sets the media times the object should report
 
HRESULT
CRendererPosPassThru::RegisterMediaTime(IMediaSample *pMediaSample)
{
    ASSERT(pMediaSample);
    LONGLONG StartMedia;
    LONGLONG EndMedia;
 
    CAutoLock cAutoLock(&m_PositionLock);
 
    // Get the media times from the sample
 
    HRESULT hr = pMediaSample->GetTime(&StartMedia,&EndMedia);
    if (FAILED(hr))
    {
	ASSERT(hr == VFW_E_SAMPLE_TIME_NOT_SET);
	return hr;
    }
 
    m_StartMedia = StartMedia;
    m_EndMedia = EndMedia;
    m_bReset = FALSE;
    return NOERROR;
}
 
 
// Sets the media times the object should report
 
HRESULT
CRendererPosPassThru::RegisterMediaTime(LONGLONG StartTime,LONGLONG EndTime)
{
    CAutoLock cAutoLock(&m_PositionLock);
    m_StartMedia = StartTime;
    m_EndMedia = EndTime;
    m_bReset = FALSE;
    return NOERROR;
}
 
 
// Return the current media times registered in the object
 
HRESULT
CRendererPosPassThru::GetMediaTime(__out LONGLONG *pStartTime, __out_opt LONGLONG *pEndTime)
{
    ASSERT(pStartTime);
 
    CAutoLock cAutoLock(&m_PositionLock);
    if (m_bReset == TRUE) {
	return E_FAIL;
    }
 
    // We don't have to return the end time
 
    HRESULT hr = ConvertTimeFormat( pStartTime, 0, m_StartMedia, &TIME_FORMAT_MEDIA_TIME );
    if (pEndTime && SUCCEEDED(hr)) {
	hr = ConvertTimeFormat( pEndTime, 0, m_EndMedia, &TIME_FORMAT_MEDIA_TIME );
    }
    return hr;
}
 
 
// Resets the media times we hold
 
HRESULT
CRendererPosPassThru::ResetMediaTime()
{
    CAutoLock cAutoLock(&m_PositionLock);
    m_StartMedia = 0;
    m_EndMedia = 0;
    m_bReset = TRUE;
    return NOERROR;
}
 
// Intended to be called by the owing filter during EOS processing so
// that the media times can be adjusted to the stop time.  This ensures
// that the GetCurrentPosition will actully get to the stop position.
HRESULT
CRendererPosPassThru::EOS()
{
    HRESULT hr;
 
    if ( m_bReset == TRUE ) hr = E_FAIL;
    else
    {
	LONGLONG llStop;
	if (SUCCEEDED(hr=GetStopPosition(&llStop)))
	{
	    CAutoLock cAutoLock(&m_PositionLock);
	    m_StartMedia =
	    m_EndMedia	 = llStop;
	}
    }
    return hr;
}
 
// -- CSourceSeeking implementation ------------
 
CSourceSeeking::CSourceSeeking(
    __in_opt LPCTSTR pName,
    __in_opt LPUNKNOWN pUnk,
    __inout HRESULT* phr,
    __in CCritSec * pLock) :
        CUnknown(pName, pUnk),
        m_pLock(pLock),
        m_rtStart((long)0)
{
    m_rtStop = _I64_MAX / 2;
    m_rtDuration = m_rtStop;
    m_dRateSeeking = 1.0;
 
    m_dwSeekingCaps = AM_SEEKING_CanSeekForwards
        | AM_SEEKING_CanSeekBackwards
        | AM_SEEKING_CanSeekAbsolute
        | AM_SEEKING_CanGetStopPos
        | AM_SEEKING_CanGetDuration;
}
 
HRESULT CSourceSeeking::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
{
    if(riid == IID_IMediaSeeking) {
        CheckPointer(ppv, E_POINTER);
        return GetInterface(static_cast<IMediaSeeking *>(this), ppv);
    }
    else {
        return CUnknown::NonDelegatingQueryInterface(riid, ppv);
    }
}
 
 
HRESULT CSourceSeeking::IsFormatSupported(const GUID * pFormat)
{
    CheckPointer(pFormat, E_POINTER);
    // only seeking in time (REFERENCE_TIME units) is supported
    return *pFormat == TIME_FORMAT_MEDIA_TIME ? S_OK : S_FALSE;
}
 
HRESULT CSourceSeeking::QueryPreferredFormat(__out GUID *pFormat)
{
    CheckPointer(pFormat, E_POINTER);
    *pFormat = TIME_FORMAT_MEDIA_TIME;
    return S_OK;
}
 
HRESULT CSourceSeeking::SetTimeFormat(const GUID * pFormat)
{
    CheckPointer(pFormat, E_POINTER);
 
    // nothing to set; just check that it's TIME_FORMAT_TIME
    return *pFormat == TIME_FORMAT_MEDIA_TIME ? S_OK : E_INVALIDARG;
}
 
HRESULT CSourceSeeking::IsUsingTimeFormat(const GUID * pFormat)
{
    CheckPointer(pFormat, E_POINTER);
    return *pFormat == TIME_FORMAT_MEDIA_TIME ? S_OK : S_FALSE;
}
 
HRESULT CSourceSeeking::GetTimeFormat(__out GUID *pFormat)
{
    CheckPointer(pFormat, E_POINTER);
    *pFormat = TIME_FORMAT_MEDIA_TIME;
    return S_OK;
}
 
HRESULT CSourceSeeking::GetDuration(__out LONGLONG *pDuration)
{
    CheckPointer(pDuration, E_POINTER);
    CAutoLock lock(m_pLock);
    *pDuration = m_rtDuration;
    return S_OK;
}
 
HRESULT CSourceSeeking::GetStopPosition(__out LONGLONG *pStop)
{
    CheckPointer(pStop, E_POINTER);
    CAutoLock lock(m_pLock);
    *pStop = m_rtStop;
    return S_OK;
}
 
HRESULT CSourceSeeking::GetCurrentPosition(__out LONGLONG *pCurrent)
{
    // GetCurrentPosition is typically supported only in renderers and
    // not in source filters.
    return E_NOTIMPL;
}
 
HRESULT CSourceSeeking::GetCapabilities( __out DWORD * pCapabilities )
{
    CheckPointer(pCapabilities, E_POINTER);
    *pCapabilities = m_dwSeekingCaps;
    return S_OK;
}
 
HRESULT CSourceSeeking::CheckCapabilities( __inout DWORD * pCapabilities )
{
    CheckPointer(pCapabilities, E_POINTER);
 
    // make sure all requested capabilities are in our mask
    return (~m_dwSeekingCaps & *pCapabilities) ? S_FALSE : S_OK;
}
 
HRESULT CSourceSeeking::ConvertTimeFormat( __out LONGLONG * pTarget, 
                                           __in_opt const GUID * pTargetFormat,
                                           LONGLONG Source, 
                                           __in_opt const GUID * pSourceFormat )
{
    CheckPointer(pTarget, E_POINTER);
    // format guids can be null to indicate current format
 
    // since we only support TIME_FORMAT_MEDIA_TIME, we don't really
    // offer any conversions.
    if(pTargetFormat == 0 || *pTargetFormat == TIME_FORMAT_MEDIA_TIME)
    {
        if(pSourceFormat == 0 || *pSourceFormat == TIME_FORMAT_MEDIA_TIME)
        {
            *pTarget = Source;
            return S_OK;
        }
    }
 
    return E_INVALIDARG;
}
 
 
HRESULT CSourceSeeking::SetPositions( __inout_opt LONGLONG * pCurrent,  
                                      DWORD CurrentFlags, 
                                      __inout_opt LONGLONG * pStop,  
                                      DWORD StopFlags )
{
    DWORD StopPosBits = StopFlags & AM_SEEKING_PositioningBitsMask;
    DWORD StartPosBits = CurrentFlags & AM_SEEKING_PositioningBitsMask;
 
    if(StopFlags) {
        CheckPointer(pStop, E_POINTER);
 
        // accept only relative, incremental, or absolute positioning
        if(StopPosBits != StopFlags) {
            return E_INVALIDARG;
        }
    }
 
    if(CurrentFlags) {
        CheckPointer(pCurrent, E_POINTER);
        if(StartPosBits != AM_SEEKING_AbsolutePositioning &&
           StartPosBits != AM_SEEKING_RelativePositioning) {
            return E_INVALIDARG;
        }
    }
 
 
    // scope for autolock
    {
        CAutoLock lock(m_pLock);
 
        // set start position
        if(StartPosBits == AM_SEEKING_AbsolutePositioning)
        {
            m_rtStart = *pCurrent;
        }
        else if(StartPosBits == AM_SEEKING_RelativePositioning)
        {
            m_rtStart += *pCurrent;
        }
 
        // set stop position
        if(StopPosBits == AM_SEEKING_AbsolutePositioning)
        {
            m_rtStop = *pStop;
        }
        else if(StopPosBits == AM_SEEKING_IncrementalPositioning)
        {
            m_rtStop = m_rtStart + *pStop;
        }
        else if(StopPosBits == AM_SEEKING_RelativePositioning)
        {
            m_rtStop = m_rtStop + *pStop;
        }
    }
 
 
    HRESULT hr = S_OK;
    if(SUCCEEDED(hr) && StopPosBits) {
        hr = ChangeStop();
    }
    if(StartPosBits) {
        hr = ChangeStart();
    }
 
    return hr;
}
 
 
HRESULT CSourceSeeking::GetPositions( __out_opt LONGLONG * pCurrent, __out_opt LONGLONG * pStop )
{
    if(pCurrent) {
        *pCurrent = m_rtStart;
    }
    if(pStop) {
        *pStop = m_rtStop;
    }
 
    return S_OK;
}
 
 
HRESULT CSourceSeeking::GetAvailable( __out_opt LONGLONG * pEarliest, __out_opt LONGLONG * pLatest )
{
    if(pEarliest) {
        *pEarliest = 0;
    }
    if(pLatest) {
        CAutoLock lock(m_pLock);
        *pLatest = m_rtDuration;
    }
    return S_OK;
}
 
HRESULT CSourceSeeking::SetRate( double dRate)
{
    {
        CAutoLock lock(m_pLock);
        m_dRateSeeking = dRate;
    }
    return ChangeRate();
}
 
HRESULT CSourceSeeking::GetRate( __out double * pdRate)
{
    CheckPointer(pdRate, E_POINTER);
    CAutoLock lock(m_pLock);
    *pdRate = m_dRateSeeking;
    return S_OK;
}
 
HRESULT CSourceSeeking::GetPreroll(__out LONGLONG *pPreroll)
{
    CheckPointer(pPreroll, E_POINTER);
    *pPreroll = 0;
    return S_OK;
}
 
 
 
 
 
// --- CSourcePosition implementation ----------
 
 
CSourcePosition::CSourcePosition(__in_opt LPCTSTR pName,
				 __in_opt LPUNKNOWN pUnk,
				 __inout HRESULT* phr,
				 __in CCritSec * pLock) :
    CMediaPosition(pName, pUnk),
    m_pLock(pLock),
    m_Start(CRefTime((LONGLONG)0))
{
    m_Stop = _I64_MAX;
    m_Rate = 1.0;
}
 
 
STDMETHODIMP
CSourcePosition::get_Duration(__out REFTIME * plength)
{
    CheckPointer(plength,E_POINTER);
    ValidateReadWritePtr(plength,sizeof(REFTIME));
    CAutoLock lock(m_pLock);
 
    *plength = m_Duration;
    return S_OK;
}
 
 
STDMETHODIMP
CSourcePosition::put_CurrentPosition(REFTIME llTime)
{
    m_pLock->Lock();
    m_Start = llTime;
    m_pLock->Unlock();
 
    return ChangeStart();
}
 
 
STDMETHODIMP
CSourcePosition::get_StopTime(__out REFTIME * pllTime)
{
    CheckPointer(pllTime,E_POINTER);
    ValidateReadWritePtr(pllTime,sizeof(REFTIME));
    CAutoLock lock(m_pLock);
 
    *pllTime = m_Stop;
    return S_OK;
}
 
 
STDMETHODIMP
CSourcePosition::put_StopTime(REFTIME llTime)
{
    m_pLock->Lock();
    m_Stop = llTime;
    m_pLock->Unlock();
 
    return ChangeStop();
}
 
 
STDMETHODIMP
CSourcePosition::get_PrerollTime(__out REFTIME * pllTime)
{
    CheckPointer(pllTime,E_POINTER);
    ValidateReadWritePtr(pllTime,sizeof(REFTIME));
    return E_NOTIMPL;
}
 
 
STDMETHODIMP
CSourcePosition::put_PrerollTime(REFTIME llTime)
{
    return E_NOTIMPL;
}
 
 
STDMETHODIMP
CSourcePosition::get_Rate(__out double * pdRate)
{
    CheckPointer(pdRate,E_POINTER);
    ValidateReadWritePtr(pdRate,sizeof(double));
    CAutoLock lock(m_pLock);
 
    *pdRate = m_Rate;
    return S_OK;
}
 
 
STDMETHODIMP
CSourcePosition::put_Rate(double dRate)
{
    m_pLock->Lock();
    m_Rate = dRate;
    m_pLock->Unlock();
 
    return ChangeRate();
}
 
 
// By default we can seek forwards
 
STDMETHODIMP
CSourcePosition::CanSeekForward(__out LONG *pCanSeekForward)
{
    CheckPointer(pCanSeekForward,E_POINTER);
    *pCanSeekForward = OATRUE;
    return S_OK;
}
 
 
// By default we can seek backwards
 
STDMETHODIMP
CSourcePosition::CanSeekBackward(__out LONG *pCanSeekBackward)
{
    CheckPointer(pCanSeekBackward,E_POINTER);
    *pCanSeekBackward = OATRUE;
    return S_OK;
}
 
 
// --- Implementation of CBasicAudio class ----------
 
 
CBasicAudio::CBasicAudio(__in_opt LPCTSTR pName,__in_opt LPUNKNOWN punk) :
    CUnknown(pName, punk)
{
}
 
// overriden to publicise our interfaces
 
STDMETHODIMP
CBasicAudio::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
{
    ValidateReadWritePtr(ppv,sizeof(PVOID));
    if (riid == IID_IBasicAudio) {
	return GetInterface( (IBasicAudio *) this, ppv);
    } else {
	return CUnknown::NonDelegatingQueryInterface(riid, ppv);
    }
}
 
 
STDMETHODIMP
CBasicAudio::GetTypeInfoCount(__out UINT * pctinfo)
{
    return m_basedisp.GetTypeInfoCount(pctinfo);
}
 
 
STDMETHODIMP
CBasicAudio::GetTypeInfo(
  UINT itinfo,
  LCID lcid,
  __deref_out ITypeInfo ** pptinfo)
{
    return m_basedisp.GetTypeInfo(
		IID_IBasicAudio,
		itinfo,
		lcid,
		pptinfo);
}
 
 
STDMETHODIMP
CBasicAudio::GetIDsOfNames(
  REFIID riid,
  __in_ecount(cNames) LPOLESTR * rgszNames,
  UINT cNames,
  LCID lcid,
  __out_ecount(cNames) DISPID * rgdispid)
{
    return m_basedisp.GetIDsOfNames(
			IID_IBasicAudio,
			rgszNames,
			cNames,
			lcid,
			rgdispid);
}
 
 
STDMETHODIMP
CBasicAudio::Invoke(
  DISPID dispidMember,
  REFIID riid,
  LCID lcid,
  WORD wFlags,
  __in DISPPARAMS * pdispparams,
  __out_opt VARIANT * pvarResult,
  __out_opt EXCEPINFO * pexcepinfo,
  __out_opt UINT * puArgErr)
{
    // this parameter is a dead leftover from an earlier interface
    if (IID_NULL != riid) {
	return DISP_E_UNKNOWNINTERFACE;
    }
 
    ITypeInfo * pti;
    HRESULT hr = GetTypeInfo(0, lcid, &pti);
 
    if (FAILED(hr)) {
	return hr;
    }
 
    hr = pti->Invoke(
	    (IBasicAudio *)this,
	    dispidMember,
	    wFlags,
	    pdispparams,
	    pvarResult,
	    pexcepinfo,
	    puArgErr);
 
    pti->Release();
    return hr;
}
 
 
// --- IVideoWindow implementation ----------
 
CBaseVideoWindow::CBaseVideoWindow(__in_opt LPCTSTR pName,__in_opt LPUNKNOWN punk) :
    CUnknown(pName, punk)
{
}
 
 
// overriden to publicise our interfaces
 
STDMETHODIMP
CBaseVideoWindow::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
{
    ValidateReadWritePtr(ppv,sizeof(PVOID));
    if (riid == IID_IVideoWindow) {
	return GetInterface( (IVideoWindow *) this, ppv);
    } else {
	return CUnknown::NonDelegatingQueryInterface(riid, ppv);
    }
}
 
 
STDMETHODIMP
CBaseVideoWindow::GetTypeInfoCount(__out UINT * pctinfo)
{
    return m_basedisp.GetTypeInfoCount(pctinfo);
}
 
 
STDMETHODIMP
CBaseVideoWindow::GetTypeInfo(
  UINT itinfo,
  LCID lcid,
  __deref_out ITypeInfo ** pptinfo)
{
    return m_basedisp.GetTypeInfo(
		IID_IVideoWindow,
		itinfo,
		lcid,
		pptinfo);
}
 
 
STDMETHODIMP
CBaseVideoWindow::GetIDsOfNames(
  REFIID riid,
  __in_ecount(cNames) LPOLESTR * rgszNames,
  UINT cNames,
  LCID lcid,
  __out_ecount(cNames) DISPID * rgdispid)
{
    return m_basedisp.GetIDsOfNames(
			IID_IVideoWindow,
			rgszNames,
			cNames,
			lcid,
			rgdispid);
}
 
 
STDMETHODIMP
CBaseVideoWindow::Invoke(
  DISPID dispidMember,
  REFIID riid,
  LCID lcid,
  WORD wFlags,
  __in DISPPARAMS * pdispparams,
  __out_opt VARIANT * pvarResult,
  __out_opt EXCEPINFO * pexcepinfo,
  __out_opt UINT * puArgErr)
{
    // this parameter is a dead leftover from an earlier interface
    if (IID_NULL != riid) {
	return DISP_E_UNKNOWNINTERFACE;
    }
 
    ITypeInfo * pti;
    HRESULT hr = GetTypeInfo(0, lcid, &pti);
 
    if (FAILED(hr)) {
	return hr;
    }
 
    hr = pti->Invoke(
	    (IVideoWindow *)this,
	    dispidMember,
	    wFlags,
	    pdispparams,
	    pvarResult,
	    pexcepinfo,
	    puArgErr);
 
    pti->Release();
    return hr;
}
 
 
// --- IBasicVideo implementation ----------
 
 
CBaseBasicVideo::CBaseBasicVideo(__in_opt LPCTSTR pName,__in_opt LPUNKNOWN punk) :
    CUnknown(pName, punk)
{
}
 
 
// overriden to publicise our interfaces
 
STDMETHODIMP
CBaseBasicVideo::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
{
    ValidateReadWritePtr(ppv,sizeof(PVOID));
    if (riid == IID_IBasicVideo || riid == IID_IBasicVideo2) {
	return GetInterface( static_cast<IBasicVideo2 *>(this), ppv);
    } else {
	return CUnknown::NonDelegatingQueryInterface(riid, ppv);
    }
}
 
 
STDMETHODIMP
CBaseBasicVideo::GetTypeInfoCount(__out UINT * pctinfo)
{
    return m_basedisp.GetTypeInfoCount(pctinfo);
}
 
 
STDMETHODIMP
CBaseBasicVideo::GetTypeInfo(
  UINT itinfo,
  LCID lcid,
  __deref_out ITypeInfo ** pptinfo)
{
    return m_basedisp.GetTypeInfo(
		IID_IBasicVideo,
		itinfo,
		lcid,
		pptinfo);
}
 
 
STDMETHODIMP
CBaseBasicVideo::GetIDsOfNames(
  REFIID riid,
  __in_ecount(cNames) LPOLESTR * rgszNames,
  UINT cNames,
  LCID lcid,
  __out_ecount(cNames) DISPID * rgdispid)
{
    return m_basedisp.GetIDsOfNames(
			IID_IBasicVideo,
			rgszNames,
			cNames,
			lcid,
			rgdispid);
}
 
 
STDMETHODIMP
CBaseBasicVideo::Invoke(
  DISPID dispidMember,
  REFIID riid,
  LCID lcid,
  WORD wFlags,
  __in DISPPARAMS * pdispparams,
  __out_opt VARIANT * pvarResult,
  __out_opt EXCEPINFO * pexcepinfo,
  __out_opt UINT * puArgErr)
{
    // this parameter is a dead leftover from an earlier interface
    if (IID_NULL != riid) {
	return DISP_E_UNKNOWNINTERFACE;
    }
 
    ITypeInfo * pti;
    HRESULT hr = GetTypeInfo(0, lcid, &pti);
 
    if (FAILED(hr)) {
	return hr;
    }
 
    hr = pti->Invoke(
	    (IBasicVideo *)this,
	    dispidMember,
	    wFlags,
	    pdispparams,
	    pvarResult,
	    pexcepinfo,
	    puArgErr);
 
    pti->Release();
    return hr;
}
 
 
// --- Implementation of Deferred Commands ----------
 
 
CDispParams::CDispParams(UINT nArgs, __in_ecount(nArgs) VARIANT* pArgs, __inout_opt HRESULT *phr)
{
   cNamedArgs = 0;
   rgdispidNamedArgs = NULL;
   cArgs = nArgs;
 
    if (cArgs) {
	rgvarg = new VARIANT[cArgs];
        if (NULL == rgvarg) {
            cArgs = 0;
            if (phr) {
                *phr = E_OUTOFMEMORY;
            }
            return;
        }
 
	for (UINT i = 0; i < cArgs; i++) {
 
            //  Why aren't we using VariantCopy?
 
	    VARIANT * pDest = &rgvarg[i];
	    VARIANT * pSrc = &pArgs[i];
 
	    pDest->vt = pSrc->vt;
	    switch(pDest->vt) {
 
	    case VT_I4:
		pDest->lVal = pSrc->lVal;
		break;
 
	    case VT_UI1:
		pDest->bVal = pSrc->bVal;
		break;
 
	    case VT_I2:
		pDest->iVal = pSrc->iVal;
		break;
 
	    case VT_R4:
		pDest->fltVal = pSrc->fltVal;
		break;
 
	    case VT_R8:
		pDest->dblVal = pSrc->dblVal;
		break;
 
	    case VT_BOOL:
		pDest->boolVal = pSrc->boolVal;
		break;
 
	    case VT_ERROR:
		pDest->scode = pSrc->scode;
		break;
 
	    case VT_CY:
		pDest->cyVal = pSrc->cyVal;
		break;
 
	    case VT_DATE:
		pDest->date = pSrc->date;
		break;
 
	    case VT_BSTR:
		if ((PVOID)pSrc->bstrVal == NULL) {
		    pDest->bstrVal = NULL;
		} else {
 
		    // a BSTR is a WORD followed by a UNICODE string.
		    // the pointer points just after the WORD
 
		    WORD len = * (WORD*) (pSrc->bstrVal - (sizeof(WORD) / sizeof(OLECHAR)));
		    OLECHAR* pch = new OLECHAR[len + (sizeof(WORD)/sizeof(OLECHAR))];
                    if (pch) {
        		WORD *pui = (WORD*)pch;
        		*pui = len;
         	        pDest->bstrVal = pch + (sizeof(WORD)/sizeof(OLECHAR));
         		CopyMemory(pDest->bstrVal, pSrc->bstrVal, len*sizeof(OLECHAR));
                    } else {
                        cArgs = i;
                        if (phr) {
                            *phr = E_OUTOFMEMORY;
                        }
                    }
		}
		break;
 
	    case VT_UNKNOWN:
		pDest->punkVal = pSrc->punkVal;
		pDest->punkVal->AddRef();
		break;
 
	    case VT_DISPATCH:
		pDest->pdispVal = pSrc->pdispVal;
		pDest->pdispVal->AddRef();
		break;
 
	    default:
		// a type we haven't got round to adding yet!
		ASSERT(0);
		break;
	    }
	}
 
    } else {
	rgvarg = NULL;
    }
 
}
 
 
CDispParams::~CDispParams()
{
    for (UINT i = 0; i < cArgs; i++) {
	switch(rgvarg[i].vt) {
        case VT_BSTR:
            //  Explicitly cast BSTR to PVOID to tell code scanning tools we really mean to test the pointer
	    if ((PVOID)rgvarg[i].bstrVal != NULL) {
		OLECHAR * pch = rgvarg[i].bstrVal - (sizeof(WORD)/sizeof(OLECHAR));
		delete pch;
	    }
	    break;
 
	case VT_UNKNOWN:
	    rgvarg[i].punkVal->Release();
	    break;
 
	case VT_DISPATCH:
	    rgvarg[i].pdispVal->Release();
	    break;
	}
    }
    delete[] rgvarg;
}
 
 
// lifetime is controlled by refcounts (see defer.h)
 
CDeferredCommand::CDeferredCommand(
    __inout CCmdQueue * pQ,
    __in_opt LPUNKNOWN	pUnk,
    __inout HRESULT *	phr,
    __in LPUNKNOWN	pUnkExecutor,
    REFTIME	time,
    __in GUID*	iid,
    long	dispidMethod,
    short	wFlags,
    long	nArgs,
    __in_ecount(nArgs) VARIANT*	pDispParams,
    __out VARIANT*	pvarResult,
    __out short*	puArgErr,
    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, __out 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(__out LONG* pConfidence)
{
    return E_NOTIMPL;
}
 
 
STDMETHODIMP
CDeferredCommand::GetHResult(__out 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(__inout_opt HRESULT *phr) :
    m_listPresentation(NAME("Presentation time command list")),
    m_listStream(NAME("Stream time command list")),
    m_evDue(TRUE, phr),    // 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(
    __out CDeferredCommand **ppCmd,
    __in     LPUNKNOWN	pUnk,		// this object will execute command
    REFTIME	time,
    __in GUID*	iid,
    long	dispidMethod,
    short	wFlags,
    long	cArgs,
    __in_ecount(cArgs) VARIANT*	pDispParams,
    __out VARIANT*	pvarResult,
    __out 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(__in 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->GetValid(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(__in 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->GetValid(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(__in_opt 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
    POSITION pos = m_listPresentation.GetHeadPosition();
    if (pos != NULL) {
	current = m_listPresentation.GetValid(pos)->GetTime();
    }
 
    // if we're running, check the stream times too
    if (m_bRunning) {
 
	CRefTime t;
        pos = m_listStream.GetHeadPosition();
	if (NULL != pos) {
	    t = m_listStream.GetValid(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));
	UNREFERENCED_PARAMETER(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(__out 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
 
            POSITION pos = m_listPresentation.GetHeadPosition();
 
	    if (NULL != pos) {
		pCmd = m_listPresentation.GetValid(pos);
	    }
 
	    if (m_bRunning) {
		pos = m_listStream.GetHeadPosition();
                if (NULL != pos) {
                    CDeferredCommand* pStrm = m_listStream.GetValid(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, __out CDeferredCommand**ppCmd)
{
    CAutoLock lock(&m_Lock);
 
    CRefTime tStream(rtStream);
 
    // find the earliest stream and presentation time commands
    CDeferredCommand* pStream = NULL;
    POSITION pos = m_listStream.GetHeadPosition();
    if (NULL != pos) {
	pStream = m_listStream.GetValid(pos);
    }
    CDeferredCommand* pPresent = NULL;
    pos = m_listPresentation.GetHeadPosition();
    if (NULL != pos) {
	pPresent = m_listPresentation.GetValid(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)) {
	pStream->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;
}
 

V745 A 'wchar_t *' type string is incorrectly converted to 'BSTR' type string. Consider using 'SysAllocString' function.

V560 A part of conditional expression is always true: (((HRESULT)(hr)) >= 0).

V668 There is no sense in testing the 'rgvarg' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.

V668 There is no sense in testing the 'pch' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.

V668 There is no sense in testing the 'pCmd' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.

V524 It is odd that the body of 'IsUsingTimeFormat' function is fully equivalent to the body of 'IsFormatSupported' function.

V524 It is odd that the body of 'GetTimeFormatW' function is fully equivalent to the body of 'QueryPreferredFormat' function.

V550 An odd precise comparison: 0.0 == dRate. It's probably better to use a comparison with defined precision: fabs(A - B) < Epsilon.

V550 An odd precise comparison: 0.0 == dRate. It's probably better to use a comparison with defined precision: fabs(A - B) < Epsilon.

V676 It is incorrect to compare the variable of BOOL type with TRUE. Correct expression is: 'm_bReset != FALSE'.

V676 It is incorrect to compare the variable of BOOL type with TRUE. Correct expression is: 'm_bReset != FALSE'.