777 lines
20 KiB
C++
777 lines
20 KiB
C++
|
|
|
|
#include <pch.cxx>
|
|
#pragma hdrstop
|
|
|
|
#define TRKDATA_ALLOCATE
|
|
#include "trkcom.hxx"
|
|
#include "trklib.hxx"
|
|
|
|
#if !defined(_UNICODE) || defined(OLE2ANSI)
|
|
#error This ILinkTrack implementation is only compatible on a Unicode build
|
|
#endif
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CTrackFile/~CTrackFile
|
|
//
|
|
// Synopsis: Construction/Destruction
|
|
//
|
|
// Arguments: None
|
|
//
|
|
// Returns: None
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
CTrackFile::CTrackFile()
|
|
{
|
|
_cRefs = 0;
|
|
_fDirty = FALSE;
|
|
_fLoaded = FALSE;
|
|
memset( &_PersistentState, 0, sizeof(_PersistentState) );
|
|
}
|
|
|
|
CTrackFile::~CTrackFile()
|
|
{
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: IUnknown methods
|
|
//
|
|
// Synopsis: IUnknown
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
ULONG
|
|
CTrackFile::AddRef()
|
|
{
|
|
long cNew;
|
|
cNew = InterlockedIncrement( &_cRefs );
|
|
return( cNew );
|
|
}
|
|
|
|
ULONG
|
|
CTrackFile::Release()
|
|
{
|
|
long cNew;
|
|
cNew = InterlockedDecrement( &_cRefs );
|
|
if( 0 == cNew )
|
|
delete this;
|
|
|
|
return( cNew >= 0 ? cNew : 0 );
|
|
}
|
|
|
|
HRESULT
|
|
CTrackFile::QueryInterface( REFIID iid, void ** ppvObject )
|
|
{
|
|
HRESULT hr = E_NOINTERFACE;
|
|
|
|
// Parameter validation
|
|
|
|
if( NULL == ppvObject )
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
*ppvObject = NULL;
|
|
|
|
if( IID_IUnknown == iid
|
|
||
|
|
IID_ITrackFile == iid )
|
|
{
|
|
AddRef();
|
|
*ppvObject = (void*) (IUnknown*) (ITrackFile*) this;
|
|
hr = S_OK;
|
|
}
|
|
else if( IID_ITrackFileRestricted == iid )
|
|
{
|
|
AddRef();
|
|
*ppvObject = (void*) (ITrackFileRestricted*) this;
|
|
hr = S_OK;
|
|
}
|
|
else if( IID_IPersistMemory == iid )
|
|
{
|
|
AddRef();
|
|
*ppvObject = (void*) (IPersistMemory*) this;
|
|
hr = S_OK;
|
|
}
|
|
else if( IID_IPersistStreamInit == iid )
|
|
{
|
|
AddRef();
|
|
*ppvObject = (void*) (IPersistStreamInit*) this;
|
|
hr = S_OK;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( hr );
|
|
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: CreateFromPath (ITrack*)
|
|
//
|
|
// Synopsis: Create a link client for a link source file.
|
|
//
|
|
// Arguments: [poszPath] (in)
|
|
// The file to which to link.
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CTrackFile::CreateFromPath( const OLECHAR * poszPath )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
CDomainRelativeObjId droidCurrent, droidBirth;
|
|
|
|
// Parameter validation
|
|
|
|
if( NULL == poszPath )
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto Exit;
|
|
}
|
|
|
|
__try
|
|
{
|
|
status = GetDroids( poszPath, &droidCurrent, &droidBirth, RGO_GET_OBJECTID );
|
|
if( !NT_SUCCESS(status) )
|
|
{
|
|
hr = HRESULT_FROM_NT(status);
|
|
goto Exit;
|
|
}
|
|
}
|
|
__except( BreakOnDebuggableException() )
|
|
{
|
|
hr = GetExceptionCode();
|
|
}
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
_fLoaded = FALSE;
|
|
InitNew();
|
|
|
|
_fDirty = TRUE;
|
|
_PersistentState.droidCurrent = droidCurrent;
|
|
_PersistentState.droidBirth = droidBirth;
|
|
|
|
Exit:
|
|
|
|
if( SUCCEEDED(hr) )
|
|
TrkLog(( TRKDBG_CREATE, TEXT("Link created to %s"), poszPath ));
|
|
|
|
hr = MapTR2HR( hr );
|
|
|
|
return( hr );
|
|
}
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: Resolve (ITrack*)
|
|
//
|
|
// Synopsis: Determine the current path of a link source.
|
|
//
|
|
// Arguments: [pcbPath] (in/out)
|
|
// In: The size of the poszPath buffer
|
|
// Out: The actual path length (including the null terminator)
|
|
// [poszPath] (out)
|
|
// The link source's current path.
|
|
// [dwMillisecondTimeout] (in)
|
|
// A suggestion as to when this method should give up
|
|
// and return if it hasn't yet found the file.
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
// BUGBUG P1: Optionally return the GetFileAttributesEx info, so that the
|
|
// shell doesn't have to re-open the file.
|
|
|
|
HRESULT
|
|
CTrackFile::Resolve( DWORD *pcbPath, OLECHAR * poszPath, DWORD dwMillisecondTimeout )
|
|
{
|
|
return( Resolve( pcbPath, poszPath, dwMillisecondTimeout, TRK_MEND_DEFAULT ));
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CTrackFile::Resolve( DWORD *pcbPath, OLECHAR * poszPath, DWORD dwMillisecondTimeout,
|
|
DWORD Restrictions )
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
CMachineId mcidLocal( MCID_LOCAL );
|
|
CRpcClientBinding rc;
|
|
CDomainRelativeObjId droidNew;
|
|
CDomainRelativeObjId droidBirth;
|
|
CDomainRelativeObjId droidCurrent;
|
|
OLECHAR oszPathActual[ MAX_PATH + 1 ];
|
|
DWORD cbPathActual = 0;
|
|
CFILETIME cftDue;
|
|
|
|
// Parameter validation
|
|
|
|
if( NULL == pcbPath
|
|
||
|
|
NULL == poszPath )
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto Exit;
|
|
}
|
|
else if( !_fLoaded )
|
|
{
|
|
hr = E_UNEXPECTED;
|
|
goto Exit;
|
|
}
|
|
|
|
__try
|
|
{
|
|
cftDue.IncrementMilliseconds( dwMillisecondTimeout );
|
|
|
|
droidBirth = _PersistentState.droidBirth;
|
|
droidCurrent = _PersistentState.droidCurrent;
|
|
|
|
rc.RcInitialize( mcidLocal, s_tszTrkWksLocalRpcProtocol, s_tszTrkWksLocalRpcEndPoint );
|
|
|
|
RpcTryExcept
|
|
{
|
|
CMachineId mcidLast, mcidCurrent;
|
|
ULONG cbFileName = (MAX_PATH + 1) * sizeof(TCHAR);
|
|
CDomainRelativeObjId droidBirthNew;
|
|
|
|
hr = LnkMendLink( rc,
|
|
cftDue,
|
|
Restrictions,
|
|
&droidBirth,
|
|
&droidCurrent,
|
|
&mcidLast,
|
|
&droidBirthNew,
|
|
&droidNew,
|
|
&mcidCurrent,
|
|
&cbFileName,
|
|
oszPathActual );
|
|
}
|
|
RpcExcept( BreakOnDebuggableException() )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( RpcExceptionCode() );
|
|
}
|
|
RpcEndExcept;
|
|
|
|
if( FAILED(hr) ) goto Exit;
|
|
|
|
|
|
// Compare droidBirth and droidCurrent with the ones in _PersistentState.
|
|
// If the same, do not set _fDirty.
|
|
if(droidBirth != _PersistentState.droidBirth)
|
|
{
|
|
_PersistentState.droidBirth = droidBirth;
|
|
_fDirty = TRUE;
|
|
}
|
|
if(droidNew != _PersistentState.droidCurrent)
|
|
{
|
|
_PersistentState.droidCurrent = droidNew;
|
|
_fDirty = TRUE;
|
|
}
|
|
|
|
cbPathActual = ( ocslen(oszPathActual) + 1 ) * sizeof(OLECHAR);
|
|
if( cbPathActual > *pcbPath )
|
|
hr = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
|
|
else
|
|
ocscpy( poszPath, oszPathActual );
|
|
|
|
*pcbPath = cbPathActual;
|
|
if( FAILED(hr) ) goto Exit;
|
|
}
|
|
__except( EXCEPTION_EXECUTE_HANDLER )
|
|
{
|
|
hr = GetExceptionCode();
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
Exit:
|
|
|
|
return( hr );
|
|
|
|
}
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: Open (ITrackFile)
|
|
//
|
|
// Synopsis: Open the referent file ensure that its object ID is
|
|
// correct. If the object ID is not correct, or the
|
|
// file could not be found, then perform a Resolve.
|
|
//
|
|
// Arguments: [pcbPathHint] (in/out)
|
|
// In: The size of the poszPathHint buffer
|
|
// Out: The actual path length (including the null terminator)
|
|
// [poszPathHint] (in/out)
|
|
// The suggested path to the file. If the path turns out not
|
|
// to be correct, an updated path is returned.
|
|
// [dwMillisecondTimeout] (in)
|
|
// A suggestion as to when this method should give up
|
|
// and return if it hasn't yet found the file.
|
|
// [dwDesiredAccess] (in)
|
|
// Access mode for the open file (see Win32 CreateFile)
|
|
// [dwShareMode] (in)
|
|
// Sharing for the open file (see Win32 CreateFile)
|
|
// [dwFlags] (in)
|
|
// Specifies the flags for the file (see the FILE_FLAG_*
|
|
// values in the Win32 CreateFile).
|
|
// [phFile] (out)
|
|
// The open file handle. It is because of this parameter
|
|
// That the ITrackFile interface is [local].
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP CTrackFile::Open( /*in, out*/ DWORD * pcbPathHint,
|
|
/*in, out, size_is(*pcbPathHint), string*/ OLECHAR * poszPathHint,
|
|
/*in*/ DWORD dwMillisecondTimeout,
|
|
/*in*/ DWORD dwDesiredAccess, // access (read-write) mode
|
|
/*in*/ DWORD dwShareMode, // share mode
|
|
/*in*/ DWORD dwFlags,
|
|
/*out*/ HANDLE * phFile )
|
|
{
|
|
return E_NOTIMPL;
|
|
|
|
/*
|
|
|
|
HRESULT hr = S_OK;
|
|
BOOL fTimeout = TRUE;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
FILE_OBJECTID_BUFFER fobOID;
|
|
IO_STATUS_BLOCK Iosb;
|
|
CObjId cobjidFile;
|
|
DWORD dwTickCountTimeout, dwTickCountNow;
|
|
|
|
// ----------
|
|
// Initialize
|
|
// ----------
|
|
|
|
*phFile = INVALID_HANDLE_VALUE;
|
|
|
|
// Ensure we have an ObjectID to check against.
|
|
|
|
if( !_fLoaded )
|
|
{
|
|
hr = E_UNEXPECTED;
|
|
goto Exit;
|
|
}
|
|
|
|
// Calculate the absolute deadline.
|
|
|
|
dwTickCountNow = GetTickCount();
|
|
dwTickCountTimeout = dwTickCountNow + dwMillisecondTimeout;
|
|
if( dwTickCountTimeout < dwTickCountNow )
|
|
{
|
|
// Bad dwMillisecondTimeout value
|
|
hr = E_INVALIDARG;
|
|
goto Exit;
|
|
}
|
|
|
|
// -------------
|
|
// Open the File
|
|
// -------------
|
|
|
|
do
|
|
{
|
|
// Open the file
|
|
|
|
hFile = CreateFile( poszPathHint, dwDesiredAccess, dwShareMode, NULL,
|
|
OPEN_EXISTING, dwFlags, NULL );
|
|
if( INVALID_HANDLE_VALUE == hFile )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto Exit;
|
|
}
|
|
|
|
// Get the object ID
|
|
|
|
status = NtFsControlFile(
|
|
hFile,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&Iosb,
|
|
FSCTL_GET_OBJECT_ID,
|
|
NULL, // In buffer
|
|
0, // In buffer size
|
|
&fobOID, // Out buffer
|
|
sizeof(fobOID) ); // Out buffer size
|
|
|
|
if( !NT_SUCCESS(status) )
|
|
{
|
|
hr = HRESULT_FROM_NT( status );
|
|
goto Exit;
|
|
}
|
|
|
|
// Verify the object ID
|
|
|
|
cobjidFile.Load( fobOID, LINK_TYPE_FILE );
|
|
|
|
if( cobjidFile == _PersistentState.droidCurrent.GetObjId() )
|
|
{
|
|
// We found a good file and we're done.
|
|
fTimeout = FALSE;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Try to find the correct file.
|
|
hr = Resolve( pcbPathHint, poszPathHint, dwTickCountTimeout - GetTickCount() );
|
|
if( FAILED(hr) ) goto Exit;
|
|
}
|
|
|
|
} while( GetTickCount() < dwTickCountTimeout );
|
|
|
|
// Did the previous loop end because of a timeout?
|
|
|
|
if( fTimeout )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( ERROR_TIMEOUT );
|
|
goto Exit;
|
|
}
|
|
|
|
// We completed successfully.
|
|
|
|
*phFile = hFile;
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
hr = S_OK;
|
|
|
|
// ----
|
|
// Exit
|
|
// ----
|
|
|
|
Exit:
|
|
|
|
if( INVALID_HANDLE_VALUE != hFile )
|
|
CloseHandle( hFile );
|
|
|
|
return( hr );
|
|
*/
|
|
} // CTrackFile::Open()
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: GetClassID (IPersistMemory & IPersistStreamInit)
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
CTrackFile::GetClassID( CLSID *pClassID )
|
|
{
|
|
if(NULL == pClassID)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
*pClassID = IID_ITrackFile;
|
|
return( S_OK );
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: IsDirty (IPersistMemory & IPersistStreamInit)
|
|
//
|
|
// Returns: HRESULT
|
|
// S_OK => Dirty, S_FALSE => clean
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
|
|
STDMETHODIMP
|
|
CTrackFile::IsDirty()
|
|
{
|
|
return( _fDirty ? S_OK : S_FALSE );
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: Load (IPersistMemory)
|
|
//
|
|
// Synopsis: Load the TrackFile object from persistent state.
|
|
//
|
|
// Arguments: [pvMem]
|
|
// Points to the serialization buffer.
|
|
// [cbSize]
|
|
// Size of the serialization buffer.
|
|
//
|
|
// Returns: None
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
CTrackFile::Load( void * pvMem, ULONG cbSize )
|
|
{
|
|
LinkTrackPersistentState PersistentState;
|
|
|
|
if( NULL == pvMem )
|
|
return( E_POINTER );
|
|
|
|
else if( _fLoaded )
|
|
return( E_UNEXPECTED );
|
|
|
|
else if( sizeof(_PersistentState) > cbSize )
|
|
return( E_INVALIDARG );
|
|
|
|
else if( ( (LinkTrackPersistentState*) pvMem )->clsid != IID_ITrackFile )
|
|
return( E_INVALIDARG );
|
|
|
|
else if( ( (LinkTrackPersistentState*) pvMem )->cbSize < sizeof(_PersistentState) )
|
|
return( E_INVALIDARG );
|
|
|
|
else
|
|
{
|
|
_PersistentState = *(LinkTrackPersistentState*) pvMem;
|
|
_fLoaded = TRUE;
|
|
return( S_OK );
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: Save(IPersistMemory)
|
|
//
|
|
// Synopsis: Save the persistent state to a memory buffer.
|
|
//
|
|
// Arguments: [pvMem]
|
|
// The buffer to which we'll save.
|
|
// [fClearDirty]
|
|
// TRUE => we'll set _fDirty to FALSE on a successful save.
|
|
// [cbSize]
|
|
// The available buffer in pvMem.
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
CTrackFile::Save( void* pvMem, BOOL fClearDirty, ULONG cbSize )
|
|
{
|
|
if( NULL == pvMem )
|
|
return( E_POINTER );
|
|
|
|
else if( !_fLoaded )
|
|
return( E_UNEXPECTED );
|
|
|
|
else if( sizeof(_PersistentState) > cbSize )
|
|
return( E_INVALIDARG );
|
|
|
|
else
|
|
{
|
|
*(LinkTrackPersistentState*) pvMem = _PersistentState;
|
|
|
|
if( fClearDirty )
|
|
_fDirty = FALSE;
|
|
|
|
return( S_OK );
|
|
}
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: InitNew
|
|
//
|
|
// Synopsis: Initialize the TrackFile object.
|
|
//
|
|
// Arguments: None
|
|
//
|
|
// Returns: None
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
CTrackFile::InitNew()
|
|
{
|
|
if( _fLoaded )
|
|
return( E_UNEXPECTED );
|
|
else
|
|
{
|
|
memset( &_PersistentState, 0, sizeof(_PersistentState) );
|
|
_PersistentState.cbSize = sizeof(_PersistentState);
|
|
_PersistentState.clsid = IID_ITrackFile;
|
|
_fLoaded = TRUE;
|
|
return( S_OK );
|
|
}
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: GetSizeMax (IPersistMemory)
|
|
//
|
|
// Synopsis: Returns the size necessary to pass to IPersist:Save
|
|
//
|
|
// Arguments: [pcbSize]
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
CTrackFile::GetSizeMax( ULONG *pcbSize )
|
|
{
|
|
if( NULL == pcbSize )
|
|
return( E_POINTER );
|
|
else
|
|
{
|
|
*pcbSize = sizeof(_PersistentState);
|
|
return( S_OK );
|
|
}
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: GetSizeMax (IPersistStreamInit)
|
|
//
|
|
// Synopsis: Returns the size necessary.
|
|
//
|
|
// Arguments: [pcbSize]
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
CTrackFile::GetSizeMax( ULARGE_INTEGER* pcbSize )
|
|
{
|
|
if( NULL == pcbSize )
|
|
return( E_POINTER );
|
|
else
|
|
{
|
|
pcbSize->QuadPart = sizeof(_PersistentState);
|
|
return( S_OK );
|
|
}
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: Load (IPersistStreamInit)
|
|
//
|
|
// Synopsis: Load the TrackFile object from a stream.
|
|
//
|
|
// Arguments: [pStm]
|
|
// Points to the IStream interface.
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
CTrackFile::Load(IStream* pStm)
|
|
{
|
|
HRESULT hr; // return value
|
|
LinkTrackPersistentState PersistentState; // tmp storage
|
|
ULONG cbRead; // # of bytes read
|
|
LARGE_INTEGER cbOffset; // = -cbRead
|
|
ULONG cbSize = sizeof(_PersistentState);
|
|
|
|
if(NULL == pStm)
|
|
return(E_POINTER);
|
|
|
|
else if(_fLoaded)
|
|
return(E_UNEXPECTED);
|
|
|
|
// Read _PersistentState from the stream and check if the read is
|
|
// successful. If not, revert back the seek pointer in pStm, and
|
|
// return the HRESULT.
|
|
hr = pStm->Read((byte*)&PersistentState, cbSize, &cbRead);
|
|
if(FAILED(hr) || cbSize != cbRead)
|
|
{
|
|
cbOffset.QuadPart = -static_cast<LONGLONG>(cbRead);
|
|
goto Exit;
|
|
}
|
|
|
|
// So now we successfully read the _PersistentState into memory, check to
|
|
// see if we read garbage. If so, revert and return the error.
|
|
// xxx What error message should be returned for this?
|
|
if(PersistentState.clsid != IID_ITrackFile)
|
|
{
|
|
cbOffset.QuadPart = -static_cast<LONGLONG>(cbRead);
|
|
hr = E_FAIL;
|
|
goto Exit;
|
|
}
|
|
|
|
// Everything went well. Now we can copy _PersistentState from its
|
|
// temporary storage to its real storage.
|
|
_PersistentState = PersistentState;
|
|
_fLoaded = TRUE;
|
|
return(S_OK);
|
|
|
|
Exit:
|
|
|
|
pStm->Seek(cbOffset, STREAM_SEEK_CUR, NULL);
|
|
return(hr);
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Method: Save (IPersistStreamInit)
|
|
//
|
|
// Synopsis: Save the persistent state to a stream.
|
|
//
|
|
// Arguments: [pStm]
|
|
// The IStream interface we use to save.
|
|
// [fClearDirty]
|
|
// TRUE => we'll set _fDirty to FALSE on a successful save.
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
CTrackFile::Save(IStream* pStm, BOOL fClearDirty)
|
|
{
|
|
HRESULT hr;
|
|
ULONG cbSize = sizeof(_PersistentState);
|
|
ULONG cbWritten; // # of bytes written
|
|
LARGE_INTEGER cbOffset; // same as cbWritten
|
|
|
|
if(NULL == pStm)
|
|
return(E_POINTER);
|
|
|
|
else if( !_fLoaded )
|
|
return(E_UNEXPECTED);
|
|
|
|
else
|
|
{
|
|
// Write the _PersistentState to the stream and check the return value.
|
|
// If failed, revert the changes in IStream and return the HRESULT.
|
|
hr = pStm->Write((byte*)&_PersistentState, cbSize, &cbWritten);
|
|
if(FAILED(hr))
|
|
{
|
|
cbOffset.QuadPart = -static_cast<LONGLONG>(cbWritten);
|
|
pStm->Seek(cbOffset, STREAM_SEEK_CUR, NULL);
|
|
return hr;
|
|
}
|
|
|
|
if(fClearDirty)
|
|
_fDirty = FALSE;
|
|
|
|
return(S_OK);
|
|
}
|
|
}
|