IAMOVIE.CPP

//==========================================================================; 
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1992 - 1997 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;

#include <streams.h>
#include "Ext_Enum.h"

class CAMovie : public IAMovie, public CUnknown
{
private:
IGraphBuilder * pGB;
IMediaControl * pMC;
IMediaEvent * pME;
IMediaPosition * pMP;

protected:
~CAMovie();
CAMovie(TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr);

// Disconnect all pins. If a filter has no pins, remove it completely
// This is to part-destroy a filter graph, leaving only those filters
// that may be usefult for rendering a file or pin after this function
// has been called.
HRESULT DisconnectAllAndRemoveIsolates();

// For all filters in graph, if the filter has pins, and all are unconnected,
// then remove the filter from the graph.
HRESULT RemoveAllUnconnected();

public:
DECLARE_IUNKNOWN

static CUnknown * WINAPI CreateInstance(LPUNKNOWN pUnk, HRESULT *phr);

STDMETHODIMP NonDelegatingQueryInterface(REFIID iid, void ** ppv);

// IFilterGraph

// Add a filter to the graph and name it with *pName.
// If the name is not unique, The request will fail.
// The Filter graph will call the JoinFilterGraph
// member function of the filter to inform it.
// This must be called before attempting Connect, ConnectDirect or Render
// for pins of the filter.

STDMETHODIMP AddFilter
( IBaseFilter * pFilter,
LPCWSTR pName
);

// Remove a filter from the graph. The filter graph implementation
// will inform the filter that it is being removed.

STDMETHODIMP RemoveFilter
( IBaseFilter * pFilter
);

// Set *ppEnum to be an enumerator for all filters in the graph.

STDMETHODIMP EnumFilters
( IEnumFilters **ppEnum
);

// Set *ppFilter to be the filter which was added with the name *pName
// Will fail and set *ppFilter to NULL if the name is not in this graph.

STDMETHODIMP FindFilterByName
( LPCWSTR pName,
IBaseFilter ** ppFilter
);

//==========================================================================
// Low level connection functions
//==========================================================================

// Connect these two pins directly (i.e. without intervening filters)
// the media type is optional, and may be partially specified (that is
// the subtype and/or format type may be GUID_NULL). See IPin::Connect
// for details of the media type parameter.
STDMETHODIMP ConnectDirect
( IPin * ppinOut, // the output pin
IPin * ppinIn, // the input pin
const AM_MEDIA_TYPE* pmt // optional mediatype
);

// On a separate thread (which will not hold any relevant locks)
// Break the connection that this pin has and reconnect it to the
// same other pin.

STDMETHODIMP Reconnect
( IPin * ppin // the pin to disconnect and reconnect
);

// Disconnect this pin, if connected. Successful no-op if not connected.

STDMETHODIMP Disconnect
( IPin * ppin
);

//==========================================================================
// intelligent connectivity - now in IGraphBuilder, axextend.idl
//==========================================================================

//==========================================================================
// Whole graph functions
//==========================================================================

// Once a graph is built, it can behave as a (composite) filter.
// To control this filter, QueryInterface for IMediaFilter.

// The filtergraph will by default ensure that the graph has a sync source
// when it is made to Run. SetSyncSource(NULL) will prevent that and allow
// all the filters to run unsynchronised until further notice.
// SetDefaultSyncSource will set the default sync source (the same as would
// have been set by default on the first call to Run).
STDMETHODIMP SetDefaultSyncSource(void);



// IGraphBuilder

// Connect these two pins directly or indirectly, using transform filters
// if necessary.

STDMETHODIMP Connect
( IPin * ppinOut, // the output pin
IPin * ppinIn // the input pin
);

// Connect this output pin directly or indirectly, using transform filters
// if necessary to something that will render it.

STDMETHODIMP Render
( IPin * ppinOut // the output pin
);

// This pair of methods are a cross between the IGraphBuilder ones and the IMediaControl ones,
// unfortunate perhaps, but they do make the nicest C / C++ interfaces .

// adds and connects filters needed to play the specified file
STDMETHODIMP RenderFile( LPCWSTR strFilename );

// adds to the graph the source filter that can read this file,
// and returns an IFilterInfo object for it
STDMETHODIMP AddSourceFilter( LPCWSTR strFilename, IBaseFilter ** ppUnk);


// IMediaControl methods
STDMETHODIMP Run();
STDMETHODIMP Pause();
STDMETHODIMP Stop();

//returns the state. same semantics as IMediaFilter::GetState
STDMETHODIMP GetState(
LONG msTimeout,
FILTER_STATE* pfs);

// IMediaEvent

// get back the event handle. This is manual-reset
// (don't - it's reset by the event mechanism) and remains set
// when events are queued, and reset when the queue is empty.
STDMETHODIMP GetEventHandle(
HEVENT * hEvent);

// remove the next event notification from the head of the queue and
// return it. Waits up to msTimeout millisecs if there are no events.
// if a timeout occurs without any events, this method will return
// E_ABORT, and the value of the event code and other parameters
// is undefined.
STDMETHODIMP GetEvent(
long * lEventCode,
long * lParam1,
long * lParam2,
long msTimeout
);
// Free any resources associated with the parameters to an event.
// Event parameters may be LONGs, IUnknown* or BSTR. No action
// is taken with LONGs. IUnknown are passed addrefed and need a
// Release call. BSTR are allocated by the task allocator and will be
// freed by calling the task allocator.
STDMETHODIMP FreeEventParams(
long lEvCode,
long lParam1,
long lParam2);

// Calls GetEvent repeatedly discarding events until it finds a
// completion event (EC_COMPLETE, EC_ERRORABORT, or EC_USERABORT).
// The completion event is removed from the queue and returned
// in pEvCode. Note that the object is still in running mode until
// a Pause or Stop call is made.
// If the timeout occurs, *pEvCode will be 0 and E_ABORT will be
// returned.
STDMETHODIMP WaitForCompletion(
long msTimeout,
long * pEvCode);

// cancels any system handling of the specified event code
// and ensures that the events are passed straight to the application
// (via GetEvent) and not handled. A good example of this is
// EC_REPAINT: default handling for this ensures the painting of the
// window and does not get posted to the app.
STDMETHODIMP CancelDefaultHandling(
long lEvCode);

// restore the normal system default handling that may have been
// cancelled by CancelDefaultHandling().
STDMETHODIMP RestoreDefaultHandling( long lEvCode);


// IMediaPosition
// properties
STDMETHODIMP get_Duration(
REFTIME* plength);

STDMETHODIMP put_CurrentPosition(
REFTIME llTime);

STDMETHODIMP get_CurrentPosition(
REFTIME* pllTime);

STDMETHODIMP get_StopTime(
REFTIME* pllTime);

STDMETHODIMP put_StopTime(
REFTIME llTime);

STDMETHODIMP get_PrerollTime(
REFTIME* pllTime);
STDMETHODIMP put_PrerollTime(
REFTIME llTime);

STDMETHODIMP put_Rate(
double dRate);
STDMETHODIMP get_Rate(
double * pdRate);

/* New methods */

// Remove all filters
STDMETHODIMP RemoveAllFilters();

// If the filter graph can be Run to completion, run it and wait for it to complete
STDMETHODIMP Play();
// As above, but RenderNewFile first
STDMETHODIMP PlayFile( LPCWSTR strFilename );

// Render all unconnected output pins
STDMETHODIMP RenderAll();
// Render supplied files "as if" the filter graoh were first emptied
STDMETHODIMP RenderNewFile( LPCWSTR strFilename );

STDMETHODIMP EnumFiltersByInterface( REFIID iid, IEnumFilters ** pEnumFilters );
// Enum pins over the entire graph
STDMETHODIMP EnumPins( IEnumPins ** ppEnum );
STDMETHODIMP EnumPinsIn( IEnumPins ** ppEnum );
STDMETHODIMP EnumPinsOut( IEnumPins ** ppEnum );
};



CFactoryTemplate g_Templates[]=
{ {L"ActiveMovie Sample PID", &CLSID_AMovie, CAMovie::CreateInstance},
};
int g_cTemplates = sizeof(g_Templates)/sizeof(g_Templates[0]);


CAMovie::~CAMovie()
{ /* nothing to do */ }

CAMovie::CAMovie(TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr)
: CUnknown( pName, pUnk )
, pGB(0), pMC(0), pME(0), pMP(0)
{
if (!pUnk) *phr = VFW_E_NEED_OWNER;
else if SUCCEEDED(*phr)
{
*phr = pUnk->QueryInterface( IID_IGraphBuilder, reinterpret_cast<void**>(&pGB) );
if SUCCEEDED(*phr)
{
pGB->Release();
*phr = pUnk->QueryInterface( IID_IMediaControl, reinterpret_cast<void**>(&pMC) );
if SUCCEEDED(*phr)
{
pMC->Release();
*phr = pUnk->QueryInterface( IID_IMediaEvent, reinterpret_cast<void**>(&pME) );
if SUCCEEDED(*phr)
{
pME->Release();
*phr = pUnk->QueryInterface( IID_IMediaPosition, reinterpret_cast<void**>(&pMP) );
if SUCCEEDED(*phr) pMP->Release();
}
}
}
}
}

CUnknown * WINAPI CAMovie::CreateInstance(LPUNKNOWN pUnk, HRESULT *phr)
{
CUnknown * result = 0;
result = new CAMovie( NAME("IAMovie Class"), pUnk, phr );
if ( !result ) *phr = E_OUTOFMEMORY;
return result;
}

STDMETHODIMP CAMovie::NonDelegatingQueryInterface(REFIID iid, void ** ppv)
{
CheckPointer(ppv,E_POINTER);

if ( iid == IID_IAMovie )
{
return GetInterface(static_cast<IAMovie *>(this), ppv );
}
else
{
return CUnknown::NonDelegatingQueryInterface(iid, ppv);
}
}


// IFilterGraph (pGB)
STDMETHODIMP CAMovie::AddFilter( IBaseFilter * pFilter, LPCWSTR pName )
{ return pGB->AddFilter( pFilter, pName ); }
STDMETHODIMP CAMovie::RemoveFilter( IBaseFilter * pFilter )
{ return pGB->RemoveFilter( pFilter ); }
STDMETHODIMP CAMovie::EnumFilters( IEnumFilters **ppEnum )
{ return pGB->EnumFilters( ppEnum ); }
STDMETHODIMP CAMovie::FindFilterByName( LPCWSTR pName, IBaseFilter ** ppFilter )
{ return pGB->FindFilterByName( pName, ppFilter ); }
STDMETHODIMP CAMovie::ConnectDirect( IPin * ppinOut, IPin * ppinIn, const AM_MEDIA_TYPE* pmt )
{ return pGB->ConnectDirect( ppinOut, ppinIn, pmt ); }
STDMETHODIMP CAMovie::Reconnect( IPin * ppin )
{ return pGB->Reconnect( ppin ); }
STDMETHODIMP CAMovie::Disconnect( IPin * ppin )
{ return pGB->Disconnect( ppin ); }
STDMETHODIMP CAMovie::SetDefaultSyncSource( )
{ return pGB->SetDefaultSyncSource( ); }


// IGraphBuilder (pGB)
STDMETHODIMP CAMovie::Connect( IPin * ppinOut, IPin * ppinIn )
{ return pGB->Connect( ppinOut, ppinIn ); }
STDMETHODIMP CAMovie::Render( IPin * ppinOut )
{ return pGB->Render( ppinOut ); }


// This pair of methods are a cross between the IGraphBuilder ones and the IMediaControl ones,
// unfortunate perhaps, but they do make the nicest C / C++ interfaces .
STDMETHODIMP CAMovie::AddSourceFilter( LPCWSTR strFilename, IBaseFilter ** ppUnk )
{
const LPWSTR fn = const_cast<LPWSTR>(strFilename);
return pGB->AddSourceFilter( fn, fn, ppUnk );
}

STDMETHODIMP CAMovie::RenderFile( LPCWSTR strFilename )
{
const LPWSTR fn = const_cast<LPWSTR>(strFilename);
return pGB->RenderFile( fn, fn );
}


// IMediaControl (pMC)
STDMETHODIMP CAMovie::Run( )
{ return pMC->Run( ); }
STDMETHODIMP CAMovie::Pause( )
{ return pMC->Pause( ); }
STDMETHODIMP CAMovie::Stop( )
{ return pMC->Stop( ); }
STDMETHODIMP CAMovie::GetState( LONG msTimeout, FILTER_STATE* pfs )
{ return pMC->GetState( msTimeout, (long *)pfs ); }



// IMediaEvent (pME)
STDMETHODIMP CAMovie::GetEventHandle( HEVENT * hEvent )
{ return pME->GetEventHandle( (long *)hEvent ); }
STDMETHODIMP CAMovie::GetEvent( long * lEventCode, long * lParam1, long * lParam2, long msTimeout )
{ return pME->GetEvent( lEventCode, lParam1, lParam2, msTimeout ); }
STDMETHODIMP CAMovie::FreeEventParams( long lEventCode, long lParam1, long lParam2 )
{ return pME->FreeEventParams( lEventCode, lParam1, lParam2 ); }
STDMETHODIMP CAMovie::WaitForCompletion( long msTimeout, long * pEvCode )
{ return pME->WaitForCompletion( msTimeout, pEvCode ); }
STDMETHODIMP CAMovie::CancelDefaultHandling( long lEvCode )
{ return pME->CancelDefaultHandling( lEvCode ); }
STDMETHODIMP CAMovie::RestoreDefaultHandling( long lEvCode )
{ return pME->RestoreDefaultHandling( lEvCode ); }


// IMediaPosition (pMP )
STDMETHODIMP CAMovie::get_Duration( REFTIME* plength )
{ return pMP->get_Duration( plength ); }
STDMETHODIMP CAMovie::put_CurrentPosition( REFTIME llTime )
{ return pMP->put_CurrentPosition( llTime ); }
STDMETHODIMP CAMovie::get_CurrentPosition( REFTIME* pllTime )
{ return pMP->get_CurrentPosition( pllTime ); }
STDMETHODIMP CAMovie::get_StopTime( REFTIME* pllTime )
{ return pMP->get_StopTime( pllTime ); }
STDMETHODIMP CAMovie::put_StopTime( REFTIME llTime )
{ return pMP->put_StopTime( llTime ); }
STDMETHODIMP CAMovie::get_PrerollTime( REFTIME* pllTime )
{ return pMP->get_PrerollTime( pllTime ); }
STDMETHODIMP CAMovie::put_PrerollTime( REFTIME llTime )
{ return pMP->put_PrerollTime( llTime ); }
STDMETHODIMP CAMovie::put_Rate( double dRate )
{ return pMP->put_Rate( dRate ); }
STDMETHODIMP CAMovie::get_Rate( double * pdRate )
{ return pMP->get_Rate( pdRate ); }



// New methods

// Remove all filters
STDMETHODIMP CAMovie::RemoveAllFilters()
{
HRESULT hr;

IEnumFilters * piEnumFilters;
hr = EnumFilters( &piEnumFilters );
if SUCCEEDED(hr)
{
CGenericList<IBaseFilter> FilterList("List of filters to be removed");

IBaseFilter * piFilter;
while ( piEnumFilters->Next(1, &piFilter, 0) == NOERROR )
{
FilterList.AddTail( piFilter );
}
piEnumFilters->Release();
while ( piFilter = FilterList.RemoveHead() )
{
hr = RemoveFilter( piFilter );
piFilter->Release();
}
}
return hr;
}

// If the filter graph can be Run to completion, run it and wait for it to complete
STDMETHODIMP CAMovie::Play()
{
HRESULT hr;
REFTIME duration;
hr = get_Duration( &duration );
if ( SUCCEEDED( hr ) && duration > 0 )
{
hr = Run();
if SUCCEEDED(hr)
{
long evCode;
WaitForCompletion( INFINITE, &evCode );
}
Stop();
}
return hr;
}

STDMETHODIMP CAMovie::PlayFile( LPCWSTR strFilename )
{
HRESULT hr;
hr = RenderNewFile( strFilename );
if SUCCEEDED(hr)
{
hr = Play();
}
return hr;
}

STDMETHODIMP CAMovie::RenderAll( )
{
HRESULT hr = NOERROR;
CImpIEnumPinsOutOverGraph EnumOutPins( this, &hr );
if SUCCEEDED(hr)
{
IPin * piPin;

while ( EnumOutPins.Next( 1, &piPin, 0 ) == NOERROR )
{
Render( piPin );
}
}
return hr;
}

STDMETHODIMP CAMovie::RenderNewFile( LPCWSTR strFilename )
{
HRESULT hr;
DisconnectAllAndRemoveIsolates();
hr = RenderFile( strFilename );
RemoveAllUnconnected();
return hr;
}

STDMETHODIMP CAMovie::EnumFiltersByInterface( REFIID iid, IEnumFilters ** ppEnum )
{
HRESULT hr = NOERROR;
*ppEnum = new CImpIEnumFilters( pGB, iid );
if (!*ppEnum) hr = E_OUTOFMEMORY;
else if FAILED(hr)
{
delete *ppEnum;
*ppEnum = 0;
}
return hr;
}

STDMETHODIMP CAMovie::EnumPins( IEnumPins ** ppEnum )
{
HRESULT hr = NOERROR;
*ppEnum = new CImpIEnumPinsOverGraph( pGB, &hr );
if (!*ppEnum) hr = E_OUTOFMEMORY;
else if FAILED(hr)
{
delete *ppEnum;
*ppEnum = 0;
}
return hr;
}

STDMETHODIMP CAMovie::EnumPinsIn( IEnumPins ** ppEnum )
{
HRESULT hr = NOERROR;
*ppEnum = new CImpIEnumPinsInOverGraph( pGB, &hr );
if (!*ppEnum) hr = E_OUTOFMEMORY;
else if FAILED(hr)
{
delete *ppEnum;
*ppEnum = 0;
}
return hr;
}

STDMETHODIMP CAMovie::EnumPinsOut( IEnumPins ** ppEnum )
{
HRESULT hr = NOERROR;
*ppEnum = new CImpIEnumPinsOutOverGraph( pGB, &hr );
if (!*ppEnum) hr = E_OUTOFMEMORY;
else if FAILED(hr)
{
delete *ppEnum;
*ppEnum = 0;
}
return hr;
}


// Protected methods

// #include <iostream.h>
// #include <SampIOS.h>

// Disconnect all pins. If a filter has no pins, remove it completely
// This is to part-destroy a filter graph, leaving only those filters
// that may be usefult for rendering a file or pin after this function
// has been called.
HRESULT CAMovie::DisconnectAllAndRemoveIsolates()
{
// cout << "Pre DisconnectAllAndRemoveIsolates()\n" << pGB << endl;
HRESULT hr;

IEnumFilters * piEnumFilters;
hr = EnumFilters( &piEnumFilters );
if SUCCEEDED(hr)
{
CGenericList<IPin> PinList("List of pins to be disconnected");
CGenericList<IBaseFilter> FilterList("List of filters to be removed");
IBaseFilter * piFilter;
IPin * piPin;
while ( piEnumFilters->Next(1, &piFilter, 0) == NOERROR )
{
BOOL connected = FALSE, connectable = FALSE;
IEnumPins * piEnumPins;
hr = piFilter->EnumPins( &piEnumPins );
if SUCCEEDED(hr)
{
while ( piEnumPins->Next(1, &piPin, 0) == NOERROR )
{
connectable = TRUE;
IPin * piPinConnectedTo;
piPin->ConnectedTo( &piPinConnectedTo );
if (piPinConnectedTo)
{
PinList.AddTail( piPin );
piPinConnectedTo->Release();
}
else piPin->Release();
}
piEnumPins->Release();
}
if (!connectable) FilterList.AddTail( piFilter );
else piFilter->Release();
}
piEnumFilters->Release();
while ( piPin = PinList.RemoveHead() )
{
hr = Disconnect( piPin );
piPin->Release();
}
while ( piFilter = FilterList.RemoveHead() )
{
hr = RemoveFilter( piFilter );
piFilter->Release();
}
}
// cout << "Post DisconnectAllAndRemoveIsolates()\n" << pGB << endl;
return hr;
}

// For all filters in graph, if the filter has pins, and all are unconnected,
// then remove the filter from the graph.
HRESULT CAMovie::RemoveAllUnconnected()
{
// cout << "Pre RemoveAllUnconnected()\n" << pGB << endl;
HRESULT hr;

IEnumFilters * piEnumFilters;
hr = EnumFilters( &piEnumFilters );
if SUCCEEDED(hr)
{
CGenericList<IBaseFilter> FilterList("List of filters to be removed");
IBaseFilter * piFilter;
while ( piEnumFilters->Next(1, &piFilter, 0) == NOERROR )
{
BOOL connected = FALSE, connectable = FALSE;
IEnumPins * piEnumPins;
hr = piFilter->EnumPins( &piEnumPins );
if SUCCEEDED(hr)
{
IPin * piPin;
while ( !connected && piEnumPins->Next(1, &piPin, 0) == NOERROR )
{
connectable = TRUE;
IPin * piPinConnectedTo;
piPin->ConnectedTo( &piPinConnectedTo );
if (piPinConnectedTo)
{
connected = TRUE;
piPinConnectedTo->Release();
}
piPin->Release();
}
piEnumPins->Release();
}
if (connectable && !connected) FilterList.AddTail( piFilter );
else piFilter->Release();
}
piEnumFilters->Release();
while ( piFilter = FilterList.RemoveHead() )
{
hr = RemoveFilter( piFilter );
piFilter->Release();
}
}
// cout << "Post RemoveAllUnconnected()\n" << pGB << endl;
return hr;
}


//---------------------------------------------------------------------------
//
// AMovieRegisterInterface()
//
//
//
//---------------------------------------------------------------------------

HRESULT
AMovieRegisterInterface( CLSID ClsidInterface
, LPTSTR strName
, CLSID ClsidDistributor )
{
TCHAR achKey[MAX_PATH];
TCHAR achClsidInterface[CHARS_IN_GUID];
TCHAR achClsidDistributor[CHARS_IN_GUID];
OLECHAR achTemp[CHARS_IN_GUID];
HKEY hkey, hsubkey;
LONG lReturn;
HRESULT hr;

hr = StringFromGUID2( ClsidInterface
, achTemp
, CHARS_IN_GUID );
if( SUCCEEDED(hr) )
{
wsprintf( achClsidInterface, "%ls", achTemp );

hr = StringFromGUID2( ClsidDistributor
, achTemp
, CHARS_IN_GUID );
}

if( FAILED(hr) ) return hr;

wsprintf( achClsidDistributor, "%ls", achTemp );

wsprintf( achKey, "Interface\\%s", achClsidInterface );
lReturn = RegCreateKey( HKEY_CLASSES_ROOT
, achKey
, &hkey );
if( ERROR_SUCCESS == lReturn )
{
if( NULL != strName )
{
RegSetValue( hkey
, NULL
, REG_SZ
, strName
, lstrlen(strName)+1 );
}

lReturn = RegCreateKey( hkey
, "Distributor"
, &hsubkey );
if( ERROR_SUCCESS == lReturn )
{

RegSetValue( hsubkey
, NULL
, REG_SZ
, achClsidDistributor
, lstrlen(achClsidDistributor)+1 );

RegCloseKey( hsubkey );
}

RegCloseKey( hkey );
}

return HRESULT_FROM_WIN32(lReturn);

}


//---------------------------------------------------------------------------
//
// AMovieUnregisterInterface()
//
//
//
//---------------------------------------------------------------------------

HRESULT
AMovieUnregisterInterface( REFCLSID ClsidInterface )
{
HKEY hkey;
LONG lReturn;
HRESULT hr;
TCHAR achClsidInterface[CHARS_IN_GUID];
OLECHAR achTemp[CHARS_IN_GUID];

// comvert CLSID to OLE string
//
hr = StringFromGUID2( ClsidInterface, achTemp, CHARS_IN_GUID );
if( FAILED(hr) ) return hr;

// convert OLE string to ANSI (as that's
// all that WIN95 Reg* calls can handle
//
wsprintf( achClsidInterface, "%ls", achTemp );

// open Interface key
//
lReturn = RegOpenKeyEx( HKEY_CLASSES_ROOT
, TEXT("Interface")
, 0
, MAXIMUM_ALLOWED
, &hkey );
if( ERROR_SUCCESS == lReturn )
{
// if successful, eliminate subkey and close
// Interface key
//
EliminateSubKey( hkey, achClsidInterface );
RegCloseKey( hkey );
}

return HRESULT_FROM_WIN32( lReturn );
}

//
// exported entry points for registration and
// unregistration.
//
// In this case they (un)register out one and
// only interface and then call through to the
// default implementation.
//
// UUID Interface {359ace10-7688-11cf-8b23-00805f6cef60}
// Interface name IAMovie
// UUID Distributor {5f2759c0-7685-11cf-8b23-00805f6cef60}
//
STDAPI DllRegisterServer()
{
HRESULT hr = NOERROR;

// Register interface so ActiveMovie can
// find me from my IID
//
hr = AMovieRegisterInterface( IID_IAMovie
, TEXT("IAMovie")
, CLSID_AMovie );

if( SUCCEEDED(hr) )
hr = AMovieDllRegisterServer2( TRUE );

return hr;
}

STDAPI DllUnregisterServer()
{
HRESULT hr = NOERROR;

hr = AMovieUnregisterInterface( IID_IAMovie );

if( SUCCEEDED(hr) )
hr = AMovieDllRegisterServer2( FALSE );

return hr;
}