#pragma warning(disable:4290)
#pragma warning(disable:4511)
#pragma warning(disable:4127)

#include <windows.h>
#include <wtypes.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <assert.h>

#include <vss.h>
#include <vswriter.h>

#define	GET_STATUS_FROM_BOOL(_bSucceeded)	((_bSucceeded)       ? NOERROR : HRESULT_FROM_WIN32 (GetLastError()))
#define GET_STATUS_FROM_HANDLE(_handle)		((NULL != (_handle)) ? NOERROR : HRESULT_FROM_WIN32 (GetLastError()))
#define GET_STATUS_FROM_POINTER(_ptr)		((NULL != (_ptr))    ? NOERROR : E_OUTOFMEMORY)

#define SIZEOF_ARRAY(_aBase)			(sizeof (_aBase) / sizeof ((_aBase)[0]))

typedef enum FAIL_PHASE
    {
    PHASE_UNDEFINED = 0,
    PHASE_IDENTIFY,
    PHASE_PREPARE_FOR_BACKUP,
    PHASE_PREPARE_FOR_SNAPSHOT,
    PHASE_FREEZE,
    PHASE_THAW,
    PHASE_ABORT,
    PHASE_BACKUP_COMPLETE,
    PHASE_RESTORE
    } FAIL_PHASE;

HRESULT SelectFailureStatus (VOID)
    {
    HRESULT	hrStatus;

    switch (rand () / (RAND_MAX / 5))
	{
	case 0: hrStatus = VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT; break;
	case 1: hrStatus = VSS_E_WRITERERROR_OUTOFRESOURCES;       break;
	case 2: hrStatus = VSS_E_WRITERERROR_TIMEOUT;              break;
	case 3: hrStatus = VSS_E_WRITERERROR_NONRETRYABLE;         break;
	case 4: hrStatus = VSS_E_WRITERERROR_RETRYABLE;            break;

	default:
	    assert (FALSE);
	    break;
	}

    return (hrStatus);
    }

void ForceSleep(INT sec)
{
  wprintf(L"\tGoing to sleep for %d seconds...\n", sec) ;
  while(sec>0) {
    Sleep(1000) ;
    sec-- ;
    if(sec%10==0) {
      wprintf(L"\tSeconds left=%d\n", sec) ;
    }
  }
  wprintf(L"\tAwaken!\n") ;
}

LPCWSTR GetStringFromFailureType (HRESULT hrStatus)
    {
    LPCWSTR pwszFailureType;

    switch (hrStatus)
	{
	case NOERROR:                                pwszFailureType = L"";                     break;
	case VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT: pwszFailureType = L"InconsistentSnapshot"; break;
	case VSS_E_WRITERERROR_OUTOFRESOURCES:       pwszFailureType = L"OutOfResources";       break;
	case VSS_E_WRITERERROR_TIMEOUT:              pwszFailureType = L"Timeout";              break;
	case VSS_E_WRITERERROR_NONRETRYABLE:         pwszFailureType = L"Non-Retryable";        break;
	case VSS_E_WRITERERROR_RETRYABLE:            pwszFailureType = L"Retryable";            break;
	default:                                     pwszFailureType = L"UNDEFINED";            break;
	}

    return (pwszFailureType);
    }

LPCWSTR GetStringFromWriterType (VSS_USAGE_TYPE wtWriterType)
    {
    LPCWSTR pwszWriterType;

    switch (wtWriterType)
	{
	case VSS_UT_BOOTABLESYSTEMSTATE: pwszWriterType = L"BootableSystemState"; break;
	case VSS_UT_SYSTEMSERVICE:       pwszWriterType = L"SystemServiceState";  break;
	case VSS_UT_USERDATA:            pwszWriterType = L"UserData";            break;
	case VSS_UT_OTHER:               pwszWriterType = L"Other";               break;
	default:                         pwszWriterType = L"UNDEFINED";           break;
	}

    return (pwszWriterType);
    }

LPCWSTR GetStringFromWaitPhase (FAIL_PHASE fpWaitPhase)
    {
    LPCWSTR pwszWaitPhase;

    switch (fpWaitPhase)
	{
	case PHASE_IDENTIFY:             pwszWaitPhase = L"Identify";           break;
	case PHASE_PREPARE_FOR_BACKUP:   pwszWaitPhase = L"PrepareForBackup";   break;
	case PHASE_PREPARE_FOR_SNAPSHOT: pwszWaitPhase = L"PrepareForSnapshot"; break;
	case PHASE_FREEZE:               pwszWaitPhase = L"Freeze";             break;
	case PHASE_THAW:                 pwszWaitPhase = L"Thaw";               break;
	case PHASE_ABORT:                pwszWaitPhase = L"Abort";              break;
	case PHASE_BACKUP_COMPLETE:      pwszWaitPhase = L"BackupComplete";     break;
	case PHASE_RESTORE:              pwszWaitPhase = L"Restore";            break;
	default:                         pwszWaitPhase = L"UNDEFINED";          break;
	}

    return (pwszWaitPhase);
    }

static volatile BOOL       bContinue   = TRUE;
static volatile FAIL_PHASE fpWaitPhase = PHASE_FREEZE;
static volatile INT        nWaitTime   = 10 ;

class CVssWriterWaitsnap : public CVssWriter
	{
public:
	    bool STDMETHODCALLTYPE OnIdentify (IVssCreateWriterMetadata *pIVssCreateWriterMetadata);
	    bool STDMETHODCALLTYPE OnPrepareBackup (IVssWriterComponents *pIVssWriterComponents);
	    bool STDMETHODCALLTYPE OnPrepareSnapshot ();
	    bool STDMETHODCALLTYPE OnFreeze ();
	    bool STDMETHODCALLTYPE OnThaw ();
	    bool STDMETHODCALLTYPE OnAbort ();
	    bool STDMETHODCALLTYPE OnBackupComplete (IVssWriterComponents *pIVssWriterComponents);
	    bool STDMETHODCALLTYPE OnPostRestore (IVssWriterComponents *pIVssWriterComponents);
};

bool STDMETHODCALLTYPE CVssWriterWaitsnap::OnIdentify (IVssCreateWriterMetadata *pIVssCreateWriterMetadata)
    {
    bool	bPhaseSucceeded = (PHASE_IDENTIFY != fpWaitPhase);
    HRESULT	hrStatus        = SelectFailureStatus ();

    if (bPhaseSucceeded)
	{
	hrStatus = pIVssCreateWriterMetadata->AddComponent (VSS_CT_FILEGROUP,
							    NULL,
							    L"Waitsnap Writer Component",
							    L"Waitsnap Writer Caption",
							    NULL, 
							    0,
							    true,
							    false,
							    false);

	bPhaseSucceeded = SUCCEEDED (hrStatus);
	}

    wprintf (L"\nThreadId 0x%04x - Received event - OnIdentify ()%s%s\n", 
	     GetCurrentThreadId (), 
	     bPhaseSucceeded ? L"" : L" - FAILED ",
	     GetStringFromFailureType (hrStatus));

    if (!bPhaseSucceeded)
	{
	  ForceSleep(nWaitTime) ;
	}

    return (TRUE);
    }

bool STDMETHODCALLTYPE CVssWriterWaitsnap::OnPrepareBackup (IVssWriterComponents *pIVssWriterComponents)
    {
    bool	bPhaseSucceeded = (PHASE_PREPARE_FOR_BACKUP != fpWaitPhase);
    HRESULT	hrStatus        = bPhaseSucceeded ? NOERROR : SelectFailureStatus ();

    wprintf (L"\nThreadId 0x%04x - Received event - OnPrepareBackup ()%s%s\n", 
	     GetCurrentThreadId (), 
	     bPhaseSucceeded ? L"" : L" - FAILED ",
	     GetStringFromFailureType (hrStatus));

    if (!bPhaseSucceeded)
	{
	  ForceSleep(nWaitTime) ;
	}

    return (TRUE);
    }

bool STDMETHODCALLTYPE CVssWriterWaitsnap::OnPrepareSnapshot ()
    {
    bool	bPhaseSucceeded = (PHASE_PREPARE_FOR_SNAPSHOT != fpWaitPhase);
    HRESULT	hrStatus        = bPhaseSucceeded ? NOERROR : SelectFailureStatus ();

    wprintf (L"\nThreadId 0x%04x - Received event - OnPrepareSnapshot ()%s%s\n", 
	     GetCurrentThreadId (), 
	     bPhaseSucceeded ? L"" : L" - FAILED ",
	     GetStringFromFailureType (hrStatus));

    if (!bPhaseSucceeded)
	{
	  ForceSleep(nWaitTime) ;
	}

    return (TRUE);
    }

bool STDMETHODCALLTYPE CVssWriterWaitsnap::OnFreeze ()
    {
    bool	bPhaseSucceeded = (PHASE_FREEZE != fpWaitPhase);
    HRESULT	hrStatus        = bPhaseSucceeded ? NOERROR : SelectFailureStatus ();

    wprintf (L"\nThreadId 0x%04x - Received event - OnFreeze ()%s%s\n", 
	     GetCurrentThreadId (), 
	     bPhaseSucceeded ? L"" : L" - FAILED ",
	     GetStringFromFailureType (hrStatus));

    if (!bPhaseSucceeded)
	{
	  ForceSleep(nWaitTime) ;
	}

    return (TRUE);
    }

bool STDMETHODCALLTYPE CVssWriterWaitsnap::OnThaw ()
    {
    bool	bPhaseSucceeded = (PHASE_THAW != fpWaitPhase);
    HRESULT	hrStatus        = bPhaseSucceeded ? NOERROR : SelectFailureStatus ();

    wprintf (L"\nThreadId 0x%04x - Received event - OnThaw ()%s%s\n", 
	     GetCurrentThreadId (),
	     bPhaseSucceeded ? L"" : L" - FAILED ",
	     GetStringFromFailureType (hrStatus));

    if (!bPhaseSucceeded)
	{
	  ForceSleep(nWaitTime) ;
	}

    return (TRUE);
    }

bool STDMETHODCALLTYPE CVssWriterWaitsnap::OnAbort ()
    {
    bool	bPhaseSucceeded = (PHASE_ABORT != fpWaitPhase);
    HRESULT	hrStatus        = bPhaseSucceeded ? NOERROR : SelectFailureStatus ();

    wprintf (L"\nThreadId 0x%04x - Received event - OnAbort ()%s%s\n",
	     GetCurrentThreadId (), 
	     bPhaseSucceeded ? L"" : L" - FAILED ",
	     GetStringFromFailureType (hrStatus));

    if (!bPhaseSucceeded)
	{
	  ForceSleep(nWaitTime) ;
	}

    return (TRUE);
    }

bool STDMETHODCALLTYPE CVssWriterWaitsnap::OnBackupComplete (IVssWriterComponents *pIVssWriterComponents)
    {
    bool	bPhaseSucceeded = (PHASE_BACKUP_COMPLETE != fpWaitPhase);
    HRESULT	hrStatus        = bPhaseSucceeded ? NOERROR : SelectFailureStatus ();

    wprintf (L"\nThreadId 0x%04x - Received event - OnBackupComplete ()%s%s\n", 
	     GetCurrentThreadId (), 
	     bPhaseSucceeded ? L"" : L" - FAILED ",
	     GetStringFromFailureType (hrStatus));

    if (!bPhaseSucceeded)
	{
	  ForceSleep(nWaitTime) ;
	}

    return (TRUE);
    }

bool STDMETHODCALLTYPE CVssWriterWaitsnap::OnPostRestore (IVssWriterComponents *pIVssWriterComponents)
    {
    bool	bPhaseSucceeded = (PHASE_RESTORE != fpWaitPhase);
    HRESULT	hrStatus        = bPhaseSucceeded ? NOERROR : SelectFailureStatus ();

    wprintf (L"\nThreadId 0x%04x - Received event - OnPostRestore ()%s%s\n", 
	     GetCurrentThreadId (), 
	     bPhaseSucceeded ? L"" : L" - FAILED ",
	     GetStringFromFailureType (hrStatus));

    if (!bPhaseSucceeded)
	{
	  ForceSleep(nWaitTime) ;
	}

    return (TRUE);
    }

static BOOL AssertPrivilege (LPCWSTR privName)
    {
    HANDLE  tokenHandle;
    BOOL    stat = FALSE;

    if (OpenProcessToken (GetCurrentProcess (), TOKEN_ADJUST_PRIVILEGES, &tokenHandle))
	{
        LUID value;

        if (LookupPrivilegeValue (NULL, privName, &value))
	    {
            TOKEN_PRIVILEGES newState;
            DWORD            error;

            newState.PrivilegeCount           = 1;
            newState.Privileges[0].Luid       = value;
            newState.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

            SetLastError (ERROR_SUCCESS);

            stat = AdjustTokenPrivileges (tokenHandle,
					  FALSE,
					  &newState,
					  (DWORD)0,
					  NULL,
					  NULL);

            if ((error = GetLastError()) != ERROR_SUCCESS)
		{
                stat = FALSE;
		}

            if (!stat)
		{
                wprintf (L"AdjustTokenPrivileges for %s failed with 0x%08X",
			 privName,
			 error);
		}
	    }

        CloseHandle (tokenHandle);
	}

    return stat;
    }

BOOL WINAPI CtrlC_HandlerRoutine (IN DWORD )
	{
	bContinue = FALSE;

	return TRUE;
	}

extern "C" int __cdecl wmain (int argc, WCHAR *argv[])
    {
    HRESULT		 hrStatus            = NOERROR;
    CVssWriterWaitsnap	*pCVssWriterWaitsnap = NULL;
    BOOL		 bSucceeded          = FALSE;
    BOOL		 bComInitialized     = FALSE;
    BOOL		 bSubscribed         = FALSE;
    VSS_USAGE_TYPE	 wtWriterType        = VSS_UT_USERDATA;
    const GUID		 guidIdWriter        = {0xd335a99e,
						0x57fb,
						0x4b80,
						    {0x85, 0xb1, 0x15, 0xda, 0xa7, 0xc7, 0x4e, 0x14}};

    srand ((unsigned)time (NULL));

    SetConsoleCtrlHandler(CtrlC_HandlerRoutine, TRUE);

    if ((argc >= 2) && (wcslen (argv[1]) > 0))
	{

	switch (*argv[1])
	    {
	    case L'I': case L'i': fpWaitPhase = PHASE_IDENTIFY;             break;
	    case L'B': case L'b': fpWaitPhase = PHASE_PREPARE_FOR_BACKUP;   break;
	    case L'S': case L's': fpWaitPhase = PHASE_PREPARE_FOR_SNAPSHOT; break;
	    case L'F': case L'f': fpWaitPhase = PHASE_FREEZE;               break;
	    case L'T': case L't': fpWaitPhase = PHASE_THAW;                 break;
	    case L'A': case L'a': fpWaitPhase = PHASE_ABORT;                break;
	    case L'C': case L'c': fpWaitPhase = PHASE_BACKUP_COMPLETE;      break;
	    case L'R': case L'r': fpWaitPhase = PHASE_RESTORE;              break;

	    default:
		wprintf (L"\nWAITSNAP [phase] [writer type] [sec to wait]"
			 L"\n\n\tWaitPhases"
			 L"\n\t\ti - Identify"
			 L"\n\t\tb - PrepareForBackup"
			 L"\n\t\ts - PrepareForSnapshot"
			 L"\n\t\tf - Freeze                (default)"
			 L"\n\t\tt - Thaw"
			 L"\n\t\ta - Abort"
			 L"\n\t\tc - BackupComplete"
			 L"\n\t\tr - PostRestore"

			 L"\n\n\tWriterTypes"
			 L"\n\t\tb - BootableState writer"
			 L"\n\t\ts - ServiceState writer"
			 L"\n\t\tu - UserData writer       (default)"
			 L"\n\t\to - Other writer"
			 L"\n");

		bContinue = FALSE;
		break;
	    }

	}

    if ((argc >= 3) && (wcslen (argv[2]) > 0))
	{
	switch (*argv[2])
	    {
	    case L'B': case L'b': wtWriterType = VSS_UT_BOOTABLESYSTEMSTATE; break;
	    case L'S': case L's': wtWriterType = VSS_UT_SYSTEMSERVICE;       break;
	    case L'U': case L'u': wtWriterType = VSS_UT_USERDATA;            break;
	    case L'O': case L'o': wtWriterType = VSS_UT_OTHER;               break;

	    default:
		bContinue = FALSE;
		break;
	    }
	}

    if ((argc >= 4) && (wcslen (argv[3]) > 0))
	{
	  nWaitTime=_wtoi(argv[3]) ;
	}

    if (bContinue)
	{
	wprintf (L"\nSetting up %s writer to wait %s requests (ProcessId 0x%04x) for %d seconds",
		 GetStringFromWriterType (wtWriterType),
		 GetStringFromWaitPhase  (fpWaitPhase),
		 GetCurrentProcessId (), 
		 nWaitTime);

	wprintf (L"\nChecking privileges");

	bSubscribed = AssertPrivilege (SE_BACKUP_NAME);

	hrStatus = GET_STATUS_FROM_BOOL (bSucceeded);

	if (FAILED (hrStatus))
	    {
	    wprintf (L"\nAssertPrivilege returned error 0x%08X", hrStatus);
	    }

	}

    if (bContinue && SUCCEEDED (hrStatus))
	{
	wprintf (L"\nInitializing COM");

	hrStatus = CoInitializeEx (NULL, COINIT_MULTITHREADED);

	if (FAILED (hrStatus))
	    {
	    wprintf (L"\nCoInitialize() returned error 0x%08X", hrStatus);
	    }

	else
	    {
	    bComInitialized = TRUE;
	    }
	}

    if (bContinue && SUCCEEDED (hrStatus))
	{
	wprintf (L"\nConstructing Writer");

	pCVssWriterWaitsnap = new CVssWriterWaitsnap;

	if (NULL == pCVssWriterWaitsnap)
	    {
	    hrStatus = HRESULT_FROM_WIN32 (ERROR_NOT_ENOUGH_MEMORY);

	    wprintf (L"\nFailed to allocate CVssWriterWaitsnap : 0x%08X", hrStatus);
	    }
	}

    if (bContinue && SUCCEEDED (hrStatus))
	{
	WCHAR	awchWriterName [256];

	wprintf (L"\nInitialising the writer");

	_snwprintf (awchWriterName, 
		    SIZEOF_ARRAY (awchWriterName), 
		    L"Microsoft Test Writer - Waitsnap (%s/%s/0x%04x)",
		    GetStringFromWriterType (wtWriterType),
		    GetStringFromWaitPhase  (fpWaitPhase),
		    GetCurrentProcessId ());

	hrStatus = pCVssWriterWaitsnap->Initialize (guidIdWriter,
						    awchWriterName,
						    wtWriterType,
						    VSS_ST_OTHER);

	if (FAILED (hrStatus))
	    {
	    wprintf (L"\nFailed to initialize the writer : 0x%08X", hrStatus);
	    }
	}

    if (bContinue && SUCCEEDED (hrStatus))
	{
	wprintf (L"\nSubscribing to snapshot events");

	hrStatus = pCVssWriterWaitsnap->Subscribe ();

	if (FAILED (hrStatus))
	    {
	    wprintf (L"\nFailed to subscribe to snapshot events : 0x%08X", hrStatus);
	    }

	else
	    {
	    bSubscribed = TRUE;
	    }
	}

    if (bContinue && SUCCEEDED (hrStatus))
	{
	wprintf (L"\nWaiting for snapshot events (or Ctrl-C)");

	while (bContinue)
	    {
	    Sleep (100);
	    }
	}

    if (bSubscribed)
	{
	wprintf (L"\nUn-Subscribing from snapshot events");

	pCVssWriterWaitsnap->Unsubscribe ();
	}

    if (NULL != pCVssWriterWaitsnap)
	{
	wprintf (L"\nDeconstructing Writer");

	delete pCVssWriterWaitsnap;
	}

    if (bComInitialized)
	{
	wprintf (L"\nUnInitialising COM");

	CoUninitialize();
	}

    return (hrStatus);
    }
/*
**++
**
** Copyright (c) 2000-2001  Microsoft Corporation
**
**
** Module Name:
**
**	waitsnap.cpp
**
**
** Abstract:
**
**	Test program that starts a VSS writer and waits upon receiving a specific event
**
** Author:
**
**	Charles Chung   [cchung]        04-Dec-2001
**
**
** Revision History:
**      1.0 Altered from Failsnap.cpp
**
**--
*/

/*
** Defines
**
**
**	   C4290: C++ Exception Specification ignored
** warning C4511: 'CVssCOMApplication' : copy constructor could not be generated
** warning C4127: conditional expression is constant
*/
#pragma warning(disable:4290)
#pragma warning(disable:4511)
#pragma warning(disable:4127)


/*
** Includes
*/
#include <windows.h>
#include <wtypes.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <assert.h>

#include <vss.h>
#include <vswriter.h>



#define	GET_STATUS_FROM_BOOL(_bSucceeded)	((_bSucceeded)       ? NOERROR : HRESULT_FROM_WIN32 (GetLastError()))
#define GET_STATUS_FROM_HANDLE(_handle)		((NULL != (_handle)) ? NOERROR : HRESULT_FROM_WIN32 (GetLastError()))
#define GET_STATUS_FROM_POINTER(_ptr)		((NULL != (_ptr))    ? NOERROR : E_OUTOFMEMORY)

#define SIZEOF_ARRAY(_aBase)			(sizeof (_aBase) / sizeof ((_aBase)[0]))



typedef enum FAIL_PHASE
    {
    PHASE_UNDEFINED = 0,
    PHASE_IDENTIFY,
    PHASE_PREPARE_FOR_BACKUP,
    PHASE_PREPARE_FOR_SNAPSHOT,
    PHASE_FREEZE,
    PHASE_THAW,
    PHASE_ABORT,
    PHASE_BACKUP_COMPLETE,
    PHASE_RESTORE
    } FAIL_PHASE;


HRESULT SelectFailureStatus (VOID)
    {
    HRESULT	hrStatus;

    switch (rand () / (RAND_MAX / 5))
	{
	case 0: hrStatus = VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT; break;
	case 1: hrStatus = VSS_E_WRITERERROR_OUTOFRESOURCES;       break;
	case 2: hrStatus = VSS_E_WRITERERROR_TIMEOUT;              break;
	case 3: hrStatus = VSS_E_WRITERERROR_NONRETRYABLE;         break;
	case 4: hrStatus = VSS_E_WRITERERROR_RETRYABLE;            break;

	default:
	    assert (FALSE);
	    break;
	}

    return (hrStatus);
    }



void ForceSleep(INT sec)
{
  wprintf(L"\tGoing to sleep for %d seconds...\n", sec) ;
  while(sec>0) {
    Sleep(1000) ;
    sec-- ;
    if(sec%10==0) {
      wprintf(L"\tSeconds left=%d\n", sec) ;
    }
  }
  wprintf(L"\tAwaken!\n") ;
}

LPCWSTR GetStringFromFailureType (HRESULT hrStatus)
    {
    LPCWSTR pwszFailureType;

    switch (hrStatus)
	{
	case NOERROR:                                pwszFailureType = L"";                     break;
	case VSS_E_WRITERERROR_INCONSISTENTSNAPSHOT: pwszFailureType = L"InconsistentSnapshot"; break;
	case VSS_E_WRITERERROR_OUTOFRESOURCES:       pwszFailureType = L"OutOfResources";       break;
	case VSS_E_WRITERERROR_TIMEOUT:              pwszFailureType = L"Timeout";              break;
	case VSS_E_WRITERERROR_NONRETRYABLE:         pwszFailureType = L"Non-Retryable";        break;
	case VSS_E_WRITERERROR_RETRYABLE:            pwszFailureType = L"Retryable";            break;
	default:                                     pwszFailureType = L"UNDEFINED";            break;
	}

    return (pwszFailureType);
    }



LPCWSTR GetStringFromWriterType (VSS_USAGE_TYPE wtWriterType)
    {
    LPCWSTR pwszWriterType;

    switch (wtWriterType)
	{
	case VSS_UT_BOOTABLESYSTEMSTATE: pwszWriterType = L"BootableSystemState"; break;
	case VSS_UT_SYSTEMSERVICE:       pwszWriterType = L"SystemServiceState";  break;
	case VSS_UT_USERDATA:            pwszWriterType = L"UserData";            break;
	case VSS_UT_OTHER:               pwszWriterType = L"Other";               break;
	default:                         pwszWriterType = L"UNDEFINED";           break;
	}

    return (pwszWriterType);
    }



LPCWSTR GetStringFromWaitPhase (FAIL_PHASE fpWaitPhase)
    {
    LPCWSTR pwszWaitPhase;


    switch (fpWaitPhase)
	{
	case PHASE_IDENTIFY:             pwszWaitPhase = L"Identify";           break;
	case PHASE_PREPARE_FOR_BACKUP:   pwszWaitPhase = L"PrepareForBackup";   break;
	case PHASE_PREPARE_FOR_SNAPSHOT: pwszWaitPhase = L"PrepareForSnapshot"; break;
	case PHASE_FREEZE:               pwszWaitPhase = L"Freeze";             break;
	case PHASE_THAW:                 pwszWaitPhase = L"Thaw";               break;
	case PHASE_ABORT:                pwszWaitPhase = L"Abort";              break;
	case PHASE_BACKUP_COMPLETE:      pwszWaitPhase = L"BackupComplete";     break;
	case PHASE_RESTORE:              pwszWaitPhase = L"Restore";            break;
	default:                         pwszWaitPhase = L"UNDEFINED";          break;
	}
    
    return (pwszWaitPhase);
    }





static volatile BOOL       bContinue   = TRUE;
static volatile FAIL_PHASE fpWaitPhase = PHASE_FREEZE;
static volatile INT        nWaitTime   = 10 ;


class CVssWriterWaitsnap : public CVssWriter
	{
public:
	    bool STDMETHODCALLTYPE OnIdentify (IVssCreateWriterMetadata *pIVssCreateWriterMetadata);
	    bool STDMETHODCALLTYPE OnPrepareBackup (IVssWriterComponents *pIVssWriterComponents);
	    bool STDMETHODCALLTYPE OnPrepareSnapshot ();
	    bool STDMETHODCALLTYPE OnFreeze ();
	    bool STDMETHODCALLTYPE OnThaw ();
	    bool STDMETHODCALLTYPE OnAbort ();
	    bool STDMETHODCALLTYPE OnBackupComplete (IVssWriterComponents *pIVssWriterComponents);
	    bool STDMETHODCALLTYPE OnPostRestore (IVssWriterComponents *pIVssWriterComponents);
};



bool STDMETHODCALLTYPE CVssWriterWaitsnap::OnIdentify (IVssCreateWriterMetadata *pIVssCreateWriterMetadata)
    {
    bool	bPhaseSucceeded = (PHASE_IDENTIFY != fpWaitPhase);
    HRESULT	hrStatus        = SelectFailureStatus ();

    if (bPhaseSucceeded)
	{
	hrStatus = pIVssCreateWriterMetadata->AddComponent (VSS_CT_FILEGROUP,
							    NULL,
							    L"Waitsnap Writer Component",
							    L"Waitsnap Writer Caption",
							    NULL, // icon
							    0,
							    true,
							    false,
							    false);

	bPhaseSucceeded = SUCCEEDED (hrStatus);
	}


    wprintf (L"\nThreadId 0x%04x - Received event - OnIdentify ()%s%s\n", 
	     GetCurrentThreadId (), 
	     bPhaseSucceeded ? L"" : L" - FAILED ",
	     GetStringFromFailureType (hrStatus));


    

    if (!bPhaseSucceeded)
	{
	  ForceSleep(nWaitTime) ;
	}

    return (TRUE);
    }


bool STDMETHODCALLTYPE CVssWriterWaitsnap::OnPrepareBackup (IVssWriterComponents *pIVssWriterComponents)
    {
    bool	bPhaseSucceeded = (PHASE_PREPARE_FOR_BACKUP != fpWaitPhase);
    HRESULT	hrStatus        = bPhaseSucceeded ? NOERROR : SelectFailureStatus ();


    wprintf (L"\nThreadId 0x%04x - Received event - OnPrepareBackup ()%s%s\n", 
	     GetCurrentThreadId (), 
	     bPhaseSucceeded ? L"" : L" - FAILED ",
	     GetStringFromFailureType (hrStatus));


    if (!bPhaseSucceeded)
	{
	  ForceSleep(nWaitTime) ;
	}


    return (TRUE);
    }


bool STDMETHODCALLTYPE CVssWriterWaitsnap::OnPrepareSnapshot ()
    {
    bool	bPhaseSucceeded = (PHASE_PREPARE_FOR_SNAPSHOT != fpWaitPhase);
    HRESULT	hrStatus        = bPhaseSucceeded ? NOERROR : SelectFailureStatus ();


    wprintf (L"\nThreadId 0x%04x - Received event - OnPrepareSnapshot ()%s%s\n", 
	     GetCurrentThreadId (), 
	     bPhaseSucceeded ? L"" : L" - FAILED ",
	     GetStringFromFailureType (hrStatus));


    if (!bPhaseSucceeded)
	{
	  ForceSleep(nWaitTime) ;
	}

    return (TRUE);
    }


bool STDMETHODCALLTYPE CVssWriterWaitsnap::OnFreeze ()
    {
    bool	bPhaseSucceeded = (PHASE_FREEZE != fpWaitPhase);
    HRESULT	hrStatus        = bPhaseSucceeded ? NOERROR : SelectFailureStatus ();


    wprintf (L"\nThreadId 0x%04x - Received event - OnFreeze ()%s%s\n", 
	     GetCurrentThreadId (), 
	     bPhaseSucceeded ? L"" : L" - FAILED ",
	     GetStringFromFailureType (hrStatus));


    if (!bPhaseSucceeded)
	{
	  ForceSleep(nWaitTime) ;
	}

    return (TRUE);
    }


bool STDMETHODCALLTYPE CVssWriterWaitsnap::OnThaw ()
    {
    bool	bPhaseSucceeded = (PHASE_THAW != fpWaitPhase);
    HRESULT	hrStatus        = bPhaseSucceeded ? NOERROR : SelectFailureStatus ();


    wprintf (L"\nThreadId 0x%04x - Received event - OnThaw ()%s%s\n", 
	     GetCurrentThreadId (),
	     bPhaseSucceeded ? L"" : L" - FAILED ",
	     GetStringFromFailureType (hrStatus));


    if (!bPhaseSucceeded)
	{
	  ForceSleep(nWaitTime) ;
	}

    return (TRUE);
    }


bool STDMETHODCALLTYPE CVssWriterWaitsnap::OnAbort ()
    {
    bool	bPhaseSucceeded = (PHASE_ABORT != fpWaitPhase);
    HRESULT	hrStatus        = bPhaseSucceeded ? NOERROR : SelectFailureStatus ();


    wprintf (L"\nThreadId 0x%04x - Received event - OnAbort ()%s%s\n",
	     GetCurrentThreadId (), 
	     bPhaseSucceeded ? L"" : L" - FAILED ",
	     GetStringFromFailureType (hrStatus));


    if (!bPhaseSucceeded)
	{
	  ForceSleep(nWaitTime) ;
	}

    return (TRUE);
    }


bool STDMETHODCALLTYPE CVssWriterWaitsnap::OnBackupComplete (IVssWriterComponents *pIVssWriterComponents)
    {
    bool	bPhaseSucceeded = (PHASE_BACKUP_COMPLETE != fpWaitPhase);
    HRESULT	hrStatus        = bPhaseSucceeded ? NOERROR : SelectFailureStatus ();


    wprintf (L"\nThreadId 0x%04x - Received event - OnBackupComplete ()%s%s\n", 
	     GetCurrentThreadId (), 
	     bPhaseSucceeded ? L"" : L" - FAILED ",
	     GetStringFromFailureType (hrStatus));

    if (!bPhaseSucceeded)
	{
	  ForceSleep(nWaitTime) ;
	}

    return (TRUE);
    }


bool STDMETHODCALLTYPE CVssWriterWaitsnap::OnPostRestore (IVssWriterComponents *pIVssWriterComponents)
    {
    bool	bPhaseSucceeded = (PHASE_RESTORE != fpWaitPhase);
    HRESULT	hrStatus        = bPhaseSucceeded ? NOERROR : SelectFailureStatus ();


    wprintf (L"\nThreadId 0x%04x - Received event - OnPostRestore ()%s%s\n", 
	     GetCurrentThreadId (), 
	     bPhaseSucceeded ? L"" : L" - FAILED ",
	     GetStringFromFailureType (hrStatus));


    if (!bPhaseSucceeded)
	{
	  ForceSleep(nWaitTime) ;
	}

    return (TRUE);
    }






static BOOL AssertPrivilege (LPCWSTR privName)
    {
    HANDLE  tokenHandle;
    BOOL    stat = FALSE;

    if (OpenProcessToken (GetCurrentProcess (), TOKEN_ADJUST_PRIVILEGES, &tokenHandle))
	{
        LUID value;

        if (LookupPrivilegeValue (NULL, privName, &value))
	    {
            TOKEN_PRIVILEGES newState;
            DWORD            error;

            newState.PrivilegeCount           = 1;
            newState.Privileges[0].Luid       = value;
            newState.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

            /*
            ** We will always call GetLastError below, so clear
            ** any prior error values on this thread.
            */
            SetLastError (ERROR_SUCCESS);

            stat = AdjustTokenPrivileges (tokenHandle,
					  FALSE,
					  &newState,
					  (DWORD)0,
					  NULL,
					  NULL);

            /*
            ** Supposedly, AdjustTokenPriveleges always returns TRUE
            ** (even when it fails). So, call GetLastError to be
            ** extra sure everything's cool.
            */
            if ((error = GetLastError()) != ERROR_SUCCESS)
		{
                stat = FALSE;
		}

            if (!stat)
		{
                wprintf (L"AdjustTokenPrivileges for %s failed with 0x%08X",
			 privName,
			 error);
		}
	    }


        CloseHandle (tokenHandle);
	}

    return stat;
    }



BOOL WINAPI CtrlC_HandlerRoutine (IN DWORD /* dwType */)
	{
	bContinue = FALSE;

	// Mark that the break was handled.
	return TRUE;
	}



extern "C" int __cdecl wmain (int argc, WCHAR *argv[])
    {
    HRESULT		 hrStatus            = NOERROR;
    CVssWriterWaitsnap	*pCVssWriterWaitsnap = NULL;
    BOOL		 bSucceeded          = FALSE;
    BOOL		 bComInitialized     = FALSE;
    BOOL		 bSubscribed         = FALSE;
    VSS_USAGE_TYPE	 wtWriterType        = VSS_UT_USERDATA;
    const GUID		 guidIdWriter        = {0xd335a99e,
						0x57fb,
						0x4b80,
						    {0x85, 0xb1, 0x15, 0xda, 0xa7, 0xc7, 0x4e, 0x14}};


    srand ((unsigned)time (NULL));

    SetConsoleCtrlHandler(CtrlC_HandlerRoutine, TRUE);


    if ((argc >= 2) && (wcslen (argv[1]) > 0))
	{
	  //get the stage which the writer is to fail
	switch (*argv[1])
	    {
	    case L'I': case L'i': fpWaitPhase = PHASE_IDENTIFY;             break;
	    case L'B': case L'b': fpWaitPhase = PHASE_PREPARE_FOR_BACKUP;   break;
	    case L'S': case L's': fpWaitPhase = PHASE_PREPARE_FOR_SNAPSHOT; break;
	    case L'F': case L'f': fpWaitPhase = PHASE_FREEZE;               break;
	    case L'T': case L't': fpWaitPhase = PHASE_THAW;                 break;
	    case L'A': case L'a': fpWaitPhase = PHASE_ABORT;                break;
	    case L'C': case L'c': fpWaitPhase = PHASE_BACKUP_COMPLETE;      break;
	    case L'R': case L'r': fpWaitPhase = PHASE_RESTORE;              break;

	    default:
		wprintf (L"\nWAITSNAP [phase] [writer type] [sec to wait]"
			 L"\n\n\tWaitPhases"
			 L"\n\t\ti - Identify"
			 L"\n\t\tb - PrepareForBackup"
			 L"\n\t\ts - PrepareForSnapshot"
			 L"\n\t\tf - Freeze                (default)"
			 L"\n\t\tt - Thaw"
			 L"\n\t\ta - Abort"
			 L"\n\t\tc - BackupComplete"
			 L"\n\t\tr - PostRestore"

			 L"\n\n\tWriterTypes"
			 L"\n\t\tb - BootableState writer"
			 L"\n\t\ts - ServiceState writer"
			 L"\n\t\tu - UserData writer       (default)"
			 L"\n\t\to - Other writer"
			 L"\n");



		bContinue = FALSE;
		break;
	    }

	
	    //get the amount of time the stage is to wait
	    
	}



    if ((argc >= 3) && (wcslen (argv[2]) > 0))
	{
	switch (*argv[2])
	    {
	    case L'B': case L'b': wtWriterType = VSS_UT_BOOTABLESYSTEMSTATE; break;
	    case L'S': case L's': wtWriterType = VSS_UT_SYSTEMSERVICE;       break;
	    case L'U': case L'u': wtWriterType = VSS_UT_USERDATA;            break;
	    case L'O': case L'o': wtWriterType = VSS_UT_OTHER;               break;

	    default:
		bContinue = FALSE;
		break;
	    }
	}


    //get the amount of seconds to wait
    if ((argc >= 4) && (wcslen (argv[3]) > 0))
	{
	  nWaitTime=_wtoi(argv[3]) ;
	}




    if (bContinue)
	{
	wprintf (L"\nSetting up %s writer to wait %s requests (ProcessId 0x%04x) for %d seconds",
		 GetStringFromWriterType (wtWriterType),
		 GetStringFromWaitPhase  (fpWaitPhase),
		 GetCurrentProcessId (), 
		 nWaitTime);


	wprintf (L"\nChecking privileges");

	bSubscribed = AssertPrivilege (SE_BACKUP_NAME);

	hrStatus = GET_STATUS_FROM_BOOL (bSucceeded);


	if (FAILED (hrStatus))
	    {
	    wprintf (L"\nAssertPrivilege returned error 0x%08X", hrStatus);
	    }

	}


    if (bContinue && SUCCEEDED (hrStatus))
	{
	wprintf (L"\nInitializing COM");

	hrStatus = CoInitializeEx (NULL, COINIT_MULTITHREADED);

	if (FAILED (hrStatus))
	    {
	    wprintf (L"\nCoInitialize() returned error 0x%08X", hrStatus);
	    }

	else
	    {
	    bComInitialized = TRUE;
	    }
	}



    if (bContinue && SUCCEEDED (hrStatus))
	{
	wprintf (L"\nConstructing Writer");

	pCVssWriterWaitsnap = new CVssWriterWaitsnap;

	if (NULL == pCVssWriterWaitsnap)
	    {
	    hrStatus = HRESULT_FROM_WIN32 (ERROR_NOT_ENOUGH_MEMORY);

	    wprintf (L"\nFailed to allocate CVssWriterWaitsnap : 0x%08X", hrStatus);
	    }
	}



    if (bContinue && SUCCEEDED (hrStatus))
	{
	WCHAR	awchWriterName [256];


	wprintf (L"\nInitialising the writer");

	_snwprintf (awchWriterName, 
		    SIZEOF_ARRAY (awchWriterName), 
		    L"Microsoft Test Writer - Waitsnap (%s/%s/0x%04x)",
		    GetStringFromWriterType (wtWriterType),
		    GetStringFromWaitPhase  (fpWaitPhase),
		    GetCurrentProcessId ());


	hrStatus = pCVssWriterWaitsnap->Initialize (guidIdWriter,
						    awchWriterName,
						    wtWriterType,
						    VSS_ST_OTHER);

	if (FAILED (hrStatus))
	    {
	    wprintf (L"\nFailed to initialize the writer : 0x%08X", hrStatus);
	    }
	}



    if (bContinue && SUCCEEDED (hrStatus))
	{
	wprintf (L"\nSubscribing to snapshot events");

	hrStatus = pCVssWriterWaitsnap->Subscribe ();

	if (FAILED (hrStatus))
	    {
	    wprintf (L"\nFailed to subscribe to snapshot events : 0x%08X", hrStatus);
	    }

	else
	    {
	    bSubscribed = TRUE;
	    }
	}



    if (bContinue && SUCCEEDED (hrStatus))
	{
	wprintf (L"\nWaiting for snapshot events (or Ctrl-C)");
	
	while (bContinue)
	    {
	    Sleep (100);
	    }
	}



    if (bSubscribed)
	{
	wprintf (L"\nUn-Subscribing from snapshot events");

	pCVssWriterWaitsnap->Unsubscribe ();
	}


    if (NULL != pCVssWriterWaitsnap)
	{
	wprintf (L"\nDeconstructing Writer");

	delete pCVssWriterWaitsnap;
	}


    if (bComInitialized)
	{
	wprintf (L"\nUnInitialising COM");

	CoUninitialize();
	}

    return (hrStatus);
    }
