You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
536 lines
17 KiB
536 lines
17 KiB
/*++
|
|
|
|
Copyright (c) 1999-2001 Microsoft Corporation
|
|
|
|
Abstract:
|
|
|
|
@doc
|
|
@module process.cpp | The processing functions for the VSS admin CLI
|
|
@end
|
|
|
|
Author:
|
|
|
|
Adi Oltean [aoltean] 04/04/2000
|
|
|
|
TBD:
|
|
|
|
Add comments.
|
|
|
|
Revision History:
|
|
|
|
Name Date Comments
|
|
aoltean 04/04/2000 Created
|
|
ssteiner 10/20/2000 Changed List SnapshotSets to use more limited VSS queries.
|
|
|
|
--*/
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Includes
|
|
|
|
// The rest of includes are specified here
|
|
#include "vssadmin.h"
|
|
#include "vswriter.h"
|
|
#include "vsbackup.h"
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// Standard foo for file name aliasing. This code block must be after
|
|
// all includes of VSS header files.
|
|
//
|
|
#ifdef VSS_FILE_ALIAS
|
|
#undef VSS_FILE_ALIAS
|
|
#endif
|
|
#define VSS_FILE_ALIAS "ADMPROCC"
|
|
//
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Implementation
|
|
|
|
|
|
class CVssAdmSnapshotSetEntry {
|
|
public:
|
|
// Constructor - Throws NOTHING
|
|
CVssAdmSnapshotSetEntry(
|
|
IN VSS_ID SnapshotSetId,
|
|
IN INT nOriginalSnapshotsCount
|
|
) : m_SnapshotSetId( SnapshotSetId ),
|
|
m_nOriginalSnapshotCount(nOriginalSnapshotsCount){ }
|
|
|
|
~CVssAdmSnapshotSetEntry()
|
|
{
|
|
// Have to delete all snapshots entries
|
|
int iCount = GetSnapshotCount();
|
|
for ( int i = 0; i < iCount; ++i )
|
|
{
|
|
VSS_SNAPSHOT_PROP *pSSProp;
|
|
pSSProp = GetSnapshotAt( i );
|
|
::VssFreeSnapshotProperties(pSSProp);
|
|
|
|
delete pSSProp;
|
|
}
|
|
|
|
}
|
|
|
|
// Add new snapshot to the snapshot set
|
|
HRESULT AddSnapshot(
|
|
IN CVssFunctionTracer &ft,
|
|
IN VSS_SNAPSHOT_PROP *pVssSnapshotProp )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
try
|
|
{
|
|
VSS_SNAPSHOT_PROP *pNewVssSnapshotProp = new VSS_SNAPSHOT_PROP;
|
|
if ( pNewVssSnapshotProp == NULL )
|
|
ft.Throw( VSSDBG_VSSADMIN, E_OUTOFMEMORY, L"Out of memory" );
|
|
|
|
*pNewVssSnapshotProp = *pVssSnapshotProp;
|
|
if ( !m_mapSnapshots.Add( pNewVssSnapshotProp->m_SnapshotId, pNewVssSnapshotProp ) )
|
|
{
|
|
delete pNewVssSnapshotProp;
|
|
ft.Throw( VSSDBG_VSSADMIN, E_OUTOFMEMORY, L"Out of memory" );
|
|
}
|
|
}
|
|
BS_STANDARD_CATCH();
|
|
|
|
return hr;
|
|
}
|
|
|
|
INT GetSnapshotCount() { return m_mapSnapshots.GetSize(); }
|
|
|
|
INT GetOriginalSnapshotCount() { return m_nOriginalSnapshotCount; }
|
|
|
|
VSS_ID GetSnapshotSetId() { return m_SnapshotSetId; }
|
|
|
|
VSS_SNAPSHOT_PROP *GetSnapshotAt(
|
|
IN int nIndex )
|
|
{
|
|
BS_ASSERT( !(nIndex < 0 || nIndex >= GetSnapshotCount()) );
|
|
return m_mapSnapshots.GetValueAt( nIndex );
|
|
}
|
|
|
|
private:
|
|
VSS_ID m_SnapshotSetId;
|
|
INT m_nOriginalSnapshotCount;
|
|
CVssSimpleMap<VSS_ID, VSS_SNAPSHOT_PROP *> m_mapSnapshots;
|
|
};
|
|
|
|
|
|
// This class queries the list of all snapshots and assembles from the query
|
|
// the list of snapshotsets and the volumes which are in the snapshotset.
|
|
class CVssAdmSnapshotSets
|
|
{
|
|
public:
|
|
// Constructor - Throws HRESULTS
|
|
CVssAdmSnapshotSets(
|
|
IN VSS_ID FilteredSnapshotSetId = GUID_NULL )
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"CVssAdmSnapshotSets::CVssAdmSnapshotSets" );
|
|
|
|
bool bFiltered = !( FilteredSnapshotSetId == GUID_NULL );
|
|
|
|
// Create the coordinator object
|
|
CComPtr<IVssCoordinator> pICoord;
|
|
|
|
ft.LogVssStartupAttempt();
|
|
ft.hr = pICoord.CoCreateInstance( CLSID_VSSCoordinator );
|
|
if ( ft.HrFailed() )
|
|
ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"Connection failed with hr = 0x%08lx", ft.hr);
|
|
|
|
// Get list all snapshots
|
|
CComPtr<IVssEnumObject> pIEnumSnapshots;
|
|
ft.hr = pICoord->Query( GUID_NULL,
|
|
VSS_OBJECT_NONE,
|
|
VSS_OBJECT_SNAPSHOT,
|
|
&pIEnumSnapshots );
|
|
if ( ft.HrFailed() )
|
|
ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"Query failed with hr = 0x%08lx", ft.hr);
|
|
|
|
// For all snapshots do...
|
|
VSS_OBJECT_PROP Prop;
|
|
VSS_SNAPSHOT_PROP& Snap = Prop.Obj.Snap;
|
|
for(;;) {
|
|
// Get next element
|
|
ULONG ulFetched;
|
|
ft.hr = pIEnumSnapshots->Next( 1, &Prop, &ulFetched );
|
|
if ( ft.HrFailed() )
|
|
ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"Next failed with hr = 0x%08lx", ft.hr);
|
|
|
|
// Test if the cycle is finished
|
|
if (ft.hr == S_FALSE) {
|
|
BS_ASSERT( ulFetched == 0);
|
|
break;
|
|
}
|
|
|
|
// If filtering, skip entry if snapshot is not in the specified snapshot set
|
|
if ( bFiltered && !( Snap.m_SnapshotSetId == FilteredSnapshotSetId ) )
|
|
continue;
|
|
|
|
ft.Trace( VSSDBG_VSSADMIN, L"Snapshot: %s", Snap.m_pwszOriginalVolumeName );
|
|
|
|
// Look up the snapshot set id in the list of snapshot sets
|
|
CVssAdmSnapshotSetEntry *pcSSE;
|
|
pcSSE = m_mapSnapshotSets.Lookup( Snap.m_SnapshotSetId );
|
|
if ( pcSSE == NULL )
|
|
{
|
|
// Haven't seen this snapshot set before, add it to list
|
|
pcSSE = new CVssAdmSnapshotSetEntry( Snap.m_SnapshotSetId,
|
|
Snap.m_lSnapshotsCount );
|
|
if ( pcSSE == NULL )
|
|
ft.Throw( VSSDBG_VSSADMIN, E_OUTOFMEMORY, L"Out of memory" );
|
|
if ( !m_mapSnapshotSets.Add( Snap.m_SnapshotSetId, pcSSE ) )
|
|
{
|
|
delete pcSSE;
|
|
ft.Throw( VSSDBG_VSSADMIN, E_OUTOFMEMORY, L"Out of memory" );
|
|
}
|
|
}
|
|
|
|
// Now add the snapshot to the snapshot set
|
|
ft.hr = pcSSE->AddSnapshot( ft, &Snap );
|
|
if ( ft.HrFailed() )
|
|
ft.Throw( VSSDBG_VSSADMIN, ft.hr, L"AddSnapshot failed" );
|
|
}
|
|
}
|
|
|
|
~CVssAdmSnapshotSets()
|
|
{
|
|
CVssFunctionTracer ft( VSSDBG_VSSADMIN, L"CVssAdmSnapshotSets::~CVssAdmSnapshotSets" );
|
|
// Have to delete all
|
|
int iCount;
|
|
iCount = m_mapSnapshotSets.GetSize();
|
|
for ( int i = 0; i < iCount; ++i )
|
|
{
|
|
delete m_mapSnapshotSets.GetValueAt( i );
|
|
}
|
|
}
|
|
|
|
INT GetSnapshotSetCount() { return m_mapSnapshotSets.GetSize(); }
|
|
|
|
CVssAdmSnapshotSetEntry *GetSnapshotSetAt(
|
|
IN int nIndex )
|
|
{
|
|
BS_ASSERT( !(nIndex < 0 || nIndex >= GetSnapshotSetCount()) );
|
|
return m_mapSnapshotSets.GetValueAt( nIndex );
|
|
}
|
|
|
|
|
|
private:
|
|
CVssSimpleMap<VSS_ID, CVssAdmSnapshotSetEntry *> m_mapSnapshotSets;
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Implementation
|
|
|
|
|
|
void CVssAdminCLI::PrintUsage(
|
|
IN CVssFunctionTracer& ft
|
|
) throw(HRESULT)
|
|
{
|
|
// Usage:\n\n
|
|
// vssadmin list snapshots [-set {snapshot set guid}]\n
|
|
// \tWill list all snapshots in the system, grouped by snapshot set Id.\n\n
|
|
// vssadmin list writers\n
|
|
// \tWill list all writers in the system\n\n
|
|
// vssadmin list providers\n
|
|
// \tWill list all currently installed snapshot providers\n
|
|
Output( ft, IDS_USAGE );
|
|
Output( ft, IDS_USAGE_SNAPSHOTS,
|
|
wszVssOptVssadmin, wszVssOptList, wszVssOptSnapshots, wszVssOptSet );
|
|
Output( ft, IDS_USAGE_WRITERS,
|
|
wszVssOptVssadmin, wszVssOptList, wszVssOptWriters );
|
|
Output( ft, IDS_USAGE_PROVIDERS,
|
|
wszVssOptVssadmin, wszVssOptList, wszVssOptProviders);
|
|
}
|
|
|
|
|
|
void CVssAdminCLI::ListSnapshots(
|
|
IN CVssFunctionTracer& ft
|
|
) throw(HRESULT)
|
|
{
|
|
bool bNonEmptyResult = false;
|
|
|
|
// Check the filter type
|
|
switch ( m_eFilterObjectType ) {
|
|
case VSS_OBJECT_SNAPSHOT_SET:
|
|
case VSS_OBJECT_NONE:
|
|
break;
|
|
|
|
default:
|
|
BS_ASSERT(false);
|
|
ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"Invalid object type %d", m_eFilterObjectType);
|
|
}
|
|
|
|
CVssAdmSnapshotSets cVssAdmSS( m_FilterSnapshotSetId );
|
|
|
|
INT iSnapshotSetCount = cVssAdmSS.GetSnapshotSetCount();
|
|
|
|
// If there are no present snapshots then display a message.
|
|
if (iSnapshotSetCount == 0) {
|
|
Output( ft,
|
|
(m_eFilterObjectType == VSS_OBJECT_SNAPSHOT_SET)?
|
|
IDS_NO_SNAPSHOTS_FOR_SET: IDS_NO_SNAPSHOTS );
|
|
}
|
|
|
|
// For all snapshot sets do...
|
|
for ( INT iSSS = 0; iSSS < iSnapshotSetCount; ++iSSS )
|
|
{
|
|
CVssAdmSnapshotSetEntry *pcSSE;
|
|
|
|
pcSSE = cVssAdmSS.GetSnapshotSetAt( iSSS );
|
|
BS_ASSERT( pcSSE != NULL );
|
|
|
|
// Print each snapshot set
|
|
Output( ft, IDS_SNAPSHOT_SET_HEADER,
|
|
GUID_PRINTF_ARG( pcSSE->GetSnapshotSetId() ),
|
|
pcSSE->GetOriginalSnapshotCount(), pcSSE->GetSnapshotCount());
|
|
|
|
// TBD: add creation time from the first snapshot.
|
|
|
|
INT iSnapshotCount = pcSSE->GetSnapshotCount();
|
|
|
|
VSS_SNAPSHOT_PROP *pSnap;
|
|
for( INT iSS = 0; iSS < iSnapshotCount; ++iSS ) {
|
|
pSnap = pcSSE->GetSnapshotAt( iSS );
|
|
BS_ASSERT( pSnap != NULL );
|
|
|
|
// Get the provider name
|
|
LPCWSTR pwszProviderName =
|
|
GetProviderName(ft, pSnap->m_ProviderId);
|
|
|
|
// Print each snapshot
|
|
Output( ft, IDS_SNAPSHOT_CONTENTS,
|
|
pwszProviderName? pwszProviderName: L"",
|
|
GUID_PRINTF_ARG(pSnap->m_SnapshotId),
|
|
pSnap->m_pwszOriginalVolumeName
|
|
);
|
|
|
|
Output( ft, wszVssFmtNewline );
|
|
|
|
bNonEmptyResult = true;
|
|
}
|
|
}
|
|
|
|
m_nReturnValue = bNonEmptyResult? VSS_CMDRET_SUCCESS: VSS_CMDRET_EMPTY_RESULT;
|
|
}
|
|
|
|
|
|
void CVssAdminCLI::ListWriters(
|
|
IN CVssFunctionTracer& ft
|
|
) throw(HRESULT)
|
|
{
|
|
bool bNonEmptyResult = false;
|
|
|
|
// Get the backup components object
|
|
CComPtr<IVssBackupComponents> pBackupComp;
|
|
CComPtr<IVssAsync> pAsync;
|
|
ft.hr = ::CreateVssBackupComponents(&pBackupComp);
|
|
if (ft.HrFailed())
|
|
ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"CreateVssBackupComponents failed with hr = 0x%08lx", ft.hr);
|
|
|
|
// BUGBUG Initialize for backup
|
|
ft.hr = pBackupComp->InitializeForBackup();
|
|
if (ft.HrFailed())
|
|
ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"InitializeForBackup failed with hr = 0x%08lx", ft.hr);
|
|
|
|
UINT unWritersCount;
|
|
// get metadata for all writers
|
|
ft.hr = pBackupComp->GatherWriterMetadata(&pAsync);
|
|
if (ft.HrFailed())
|
|
ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"GatherWriterMetadata failed with hr = 0x%08lx", ft.hr);
|
|
|
|
// Using polling, try to obtain the list of writers as soon as possible
|
|
HRESULT hrReturned = S_OK;
|
|
for (int nRetries = 0; nRetries < MAX_RETRIES_COUNT; nRetries++ ) {
|
|
|
|
// Wait a little
|
|
::Sleep(nPollingInterval);
|
|
|
|
// Check if finished
|
|
INT nReserved = 0;
|
|
ft.hr = pAsync->QueryStatus(
|
|
&hrReturned,
|
|
&nReserved
|
|
);
|
|
if (ft.HrFailed())
|
|
ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED,
|
|
L"IVssAsync::QueryStatus failed with hr = 0x%08lx", ft.hr);
|
|
if (hrReturned == VSS_S_ASYNC_FINISHED)
|
|
break;
|
|
if (hrReturned == VSS_S_ASYNC_PENDING)
|
|
continue;
|
|
ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED,
|
|
L"IVssAsync::QueryStatus returned hr = 0x%08lx", hrReturned);
|
|
}
|
|
|
|
// If still not ready, then print the "waiting for responses" message and wait.
|
|
if (hrReturned == VSS_S_ASYNC_PENDING) {
|
|
Output( ft, IDS_WAITING_RESPONSES );
|
|
ft.hr = pAsync->Wait();
|
|
if (ft.HrFailed())
|
|
ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"IVssAsync::Wait failed with hr = 0x%08lx", ft.hr);
|
|
}
|
|
|
|
pAsync = NULL;
|
|
|
|
// Gather the status of all writers
|
|
ft.hr = pBackupComp->GatherWriterStatus(&pAsync);
|
|
if (ft.HrFailed())
|
|
ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"GatherWriterMetadata failed with hr = 0x%08lx", ft.hr);
|
|
|
|
ft.hr = pAsync->Wait();
|
|
if (ft.HrFailed())
|
|
ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"IVssAsync::Wait failed with hr = 0x%08lx", ft.hr);
|
|
|
|
pAsync = NULL;
|
|
|
|
ft.hr = pBackupComp->GetWriterStatusCount(&unWritersCount);
|
|
if (ft.HrFailed())
|
|
ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"GetWriterStatusCount failed with hr = 0x%08lx", ft.hr);
|
|
|
|
// Print each writer status+supplementary info
|
|
for(UINT unIndex = 0; unIndex < unWritersCount; unIndex++)
|
|
{
|
|
VSS_ID idInstance;
|
|
VSS_ID idWriter;
|
|
CComBSTR bstrWriter;
|
|
VSS_WRITER_STATE eStatus;
|
|
HRESULT hrWriterFailure;
|
|
|
|
// Get the status for the (unIndex)-th writer
|
|
ft.hr = pBackupComp->GetWriterStatus(unIndex, &idInstance, &idWriter, &bstrWriter, &eStatus, &hrWriterFailure);
|
|
if (ft.HrFailed())
|
|
ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"GetWriterStatus failed with hr = 0x%08lx", ft.hr);
|
|
|
|
// Get the status description strings
|
|
LPCWSTR pwszStatusDescription;
|
|
switch (eStatus) {
|
|
case VSS_WS_STABLE:
|
|
pwszStatusDescription = LoadString( ft, IDS_WRITER_STATUS_STABLE);
|
|
break;
|
|
case VSS_WS_WAITING_FOR_FREEZE:
|
|
pwszStatusDescription = LoadString( ft, IDS_WRITER_STATUS_WAITING_FOR_FREEZE);
|
|
break;
|
|
case VSS_WS_WAITING_FOR_THAW:
|
|
pwszStatusDescription = LoadString( ft, IDS_WRITER_STATUS_FROZEN);
|
|
break;
|
|
case VSS_WS_WAITING_FOR_BACKUP_COMPLETE:
|
|
pwszStatusDescription = LoadString( ft, IDS_WRITER_STATUS_WAITING_FOR_COMPLETION);
|
|
break;
|
|
case VSS_WS_FAILED_AT_IDENTIFY:
|
|
case VSS_WS_FAILED_AT_PREPARE_BACKUP:
|
|
case VSS_WS_FAILED_AT_PREPARE_SNAPSHOT:
|
|
case VSS_WS_FAILED_AT_FREEZE:
|
|
case VSS_WS_FAILED_AT_THAW:
|
|
pwszStatusDescription = LoadString( ft, IDS_WRITER_STATUS_FAILED);
|
|
break;
|
|
default:
|
|
pwszStatusDescription = LoadString( ft, IDS_WRITER_STATUS_UNKNOWN);
|
|
break;
|
|
}
|
|
BS_ASSERT(pwszStatusDescription);
|
|
|
|
// Print status+info about each writer
|
|
Output( ft, IDS_WRITER_CONTENTS,
|
|
(LPWSTR)bstrWriter? (LPWSTR)bstrWriter: L"",
|
|
GUID_PRINTF_ARG(idWriter),
|
|
GUID_PRINTF_ARG(idInstance),
|
|
(INT)eStatus,
|
|
pwszStatusDescription
|
|
);
|
|
|
|
Output( ft, wszVssFmtNewline );
|
|
|
|
bNonEmptyResult = true;
|
|
}
|
|
|
|
ft.hr = pBackupComp->FreeWriterStatus();
|
|
if (ft.HrFailed())
|
|
ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"FreeWriterStatus failed with hr = 0x%08lx", ft.hr);
|
|
|
|
m_nReturnValue = bNonEmptyResult? VSS_CMDRET_SUCCESS: VSS_CMDRET_EMPTY_RESULT;
|
|
}
|
|
|
|
|
|
void CVssAdminCLI::ListProviders(
|
|
IN CVssFunctionTracer& ft
|
|
) throw(HRESULT)
|
|
{
|
|
bool bNonEmptyResult = false;
|
|
|
|
// Check the filter type
|
|
switch ( m_eFilterObjectType ) {
|
|
|
|
case VSS_OBJECT_NONE:
|
|
break;
|
|
|
|
default:
|
|
BS_ASSERT(false);
|
|
ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"Invalid object type %d", m_eFilterObjectType);
|
|
}
|
|
|
|
// Create the coordinator object
|
|
CComPtr<IVssCoordinator> pICoord;
|
|
|
|
ft.LogVssStartupAttempt();
|
|
ft.hr = pICoord.CoCreateInstance( CLSID_VSSCoordinator );
|
|
if ( ft.HrFailed() )
|
|
ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"Connection failed with hr = 0x%08lx", ft.hr);
|
|
|
|
// Query all (filtered) snapshot sets
|
|
CComPtr<IVssEnumObject> pIEnumProv;
|
|
ft.hr = pICoord->Query( GUID_NULL,
|
|
VSS_OBJECT_NONE,
|
|
VSS_OBJECT_PROVIDER,
|
|
&pIEnumProv );
|
|
if ( ft.HrFailed() )
|
|
ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"Query failed with hr = 0x%08lx", ft.hr);
|
|
|
|
// For all snapshot sets do...
|
|
VSS_OBJECT_PROP Prop;
|
|
VSS_PROVIDER_PROP& Prov = Prop.Obj.Prov;
|
|
for(;;) {
|
|
// Get next element
|
|
ULONG ulFetched;
|
|
ft.hr = pIEnumProv->Next( 1, &Prop, &ulFetched );
|
|
if ( ft.HrFailed() )
|
|
ft.Throw( VSSDBG_VSSADMIN, E_UNEXPECTED, L"Next failed with hr = 0x%08lx", ft.hr);
|
|
|
|
// Test if the cycle is ended
|
|
if (ft.hr == S_FALSE) {
|
|
BS_ASSERT( ulFetched == 0);
|
|
break;
|
|
}
|
|
|
|
// Get the provider type strings
|
|
LPCWSTR pwszProviderType;
|
|
switch (Prov.m_eProviderType) {
|
|
case VSS_PROV_SYSTEM:
|
|
pwszProviderType = LoadString( ft, IDS_PROV_TYPE_SYSTEM);
|
|
break;
|
|
case VSS_PROV_SOFTWARE:
|
|
pwszProviderType = LoadString( ft, IDS_PROV_TYPE_SOFTWARE);
|
|
break;
|
|
case VSS_PROV_HARDWARE:
|
|
pwszProviderType = LoadString( ft, IDS_PROV_TYPE_HARDWARE);
|
|
break;
|
|
default:
|
|
pwszProviderType = LoadString( ft, IDS_PROV_TYPE_UNKNOWN);
|
|
break;
|
|
}
|
|
BS_ASSERT(pwszProviderType);
|
|
|
|
// Print each snapshot set
|
|
Output( ft, IDS_PROVIDER_CONTENTS,
|
|
Prov.m_pwszProviderName? Prov.m_pwszProviderName: L"",
|
|
pwszProviderType,
|
|
GUID_PRINTF_ARG(Prov.m_ProviderId),
|
|
Prov.m_pwszProviderVersion? Prov.m_pwszProviderVersion: L"");
|
|
|
|
::CoTaskMemFree(Prov.m_pwszProviderName);
|
|
::CoTaskMemFree(Prov.m_pwszProviderVersion);
|
|
|
|
bNonEmptyResult = true;
|
|
}
|
|
|
|
m_nReturnValue = bNonEmptyResult? VSS_CMDRET_SUCCESS: VSS_CMDRET_EMPTY_RESULT;
|
|
}
|