Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

Volume Shadow Copies from .NET

4.85/5 (14 votes)
7 Jan 2008Ms-PL3 min read 1   3.5K  
Use the Windows Volume Shadow Copy service from .NET.

Introduction

If you want to automatically make backup copies of locked files, such as Microsoft Outlook PST files, you'll find that your options seem limited to dedicated backup utilities such as Microsoft Backup. But what if you want to backup only your PST files, and you want the backup copy to be immediately accessible, not compressed in a BKF file?

Microsoft Volume Shadow Copy Service is the solution to this problem. It's how Microsoft Backup and other backup utilities are able to make copies of locked files.

There's a Volume Shadow Copy service SDK and documentation on MSDN but it is by no means clear how to implement VSC using .NET, or even if it's possible. With a little help from Craig Andera's blog, I have found that it is, and I show you how, here below.

Background

When I first started looking into Volume Shadow Copy, I wasn't really sure where to begin. I thought I might need to create and register a provider. But then one of the commenters to Craig Andera's HOBOCOPY blog posting referenced a VSSCoordinatorClass which turned out to be exactly what I needed.

The VSSCoordinatorClass doesn't seem to be documented. If I Google it, I get the original blog comment plus one or two other unofficial and not particularly helpful references. MSDN doesn't say anything about it either. This class does seem to expose the methods of the IVssBackupComponents interface, which is what you need to create shadow copies.

In particular, to create a volume shadow copy, here are the steps you need to take:

  1. Create a snapshot set using StartSnapshotSet
  2. Add a volume to the snapshot set using AddToSnapshotSet
  3. Initialize the volume snapshot using the DoSnapshotSet
  4. Wait until QueryStatus indicates that the snapshot has been initialized
  5. Get the volume name of the snapshot using GetSnapshotProperties
  6. Use the FindFile and CopyFile APIs to access the shadow volume
  7. When done, use the DeleteSnapshots method to indicate that the shadow volume is no longer needed

Using the code

The attached source code is a simple utility that scans a source directory and any subdirectories for PST files. Any PST files found are copied to a target directory.

To use the utility, extract the files from the Zip file. Run makeshadowcopy.exe with the following parameters:

  • -F: indicates the "from" directory. I.e.: C:\Documents and Settings\ttyree\Local Settings\Application Data\Microsoft\Outlook
  • -T: indicates the "to" directory. I.e.: C:\tedsoutlookbackup

Note that these two Windows services are, by default, set to "manual", and will need to be started for Volume Shadow Copy to work:

  • Volume Shadow Copy service
  • MS Software Shadow Copy Provider

Much of the code in the sample utility is standard, and I won't try to explain how the FindFirstFile and CopyFile APIs are used. I did encapsulate the Volume Shadow Copy as a class and am posting it here for your perusal:

The class is instantiated with a volume name (e.g.: "C:" or "E:"), and once instantiated, the DeviceName property will return the name of the shadow volume. When done with the shadow volume, release it using the Delete method.

VB
Public Class Snapshot
    'Note:  The following two windows services are stopped by default.
    '       For volume shadow copy to function
    '       they need to be started.

    '       Volume Shadow Copy service
    '       MS Software Shadow Copy Provider

    'Technet article describing how Volume Shadow Copy works:
    'http://technet2.microsoft.com/windowsserver/en/library/
    '     2b0d2457-b7d8-42c3-b6c9-59c145b7765f1033.mspx?mfr=true

    'Thanks to Craig Andera for his HOBOCOPY blog 
    'posting which provided the basis for this code.
    'http://www.pluralsight.com/blogs/craig/archive/2006/09/20/38362.aspx

    'Note: More information about programming 
    '      the volume shadow copy service is available here:
    'http://msdn2.microsoft.com/en-us/library/aa384649.aspx

    'Note: The microsoft volume shadow copy service 
    '      SDK with headers and utilties is available here:
    'http://www.microsoft.com/downloads/details.aspx?
    '     FamilyID=0B4F56E4-0CCC-4626-826A-ED2C4C95C871&displaylang=en

    Private Const VSS_E_BAD_STATE = &H80042301
    Private Const VSS_E_PROVIDER_ALREADY_REGISTERED = &H80042303
    Private Const VSS_E_PROVIDER_NOT_REGISTERED = &H80042304
    Private Const VSS_E_PROVIDER_VETO = &H80042306
    Private Const VSS_E_PROVIDER_IN_USE = &H80042307
    Private Const VSS_E_OBJECT_NOT_FOUND = &H80042308
    Private Const VSS_S_ASYNC_PENDING = &H42309
    Private Const VSS_S_ASYNC_FINISHED = &H4230A
    Private Const VSS_S_ASYNC_CANCELLED = &H4230B
    Private Const VSS_E_VOLUME_NOT_SUPPORTED = &H8004230C
    Private Const VSS_E_VOLUME_NOT_SUPPORTED_BY_PROVIDER = &H8004230E
    Private Const VSS_E_OBJECT_ALREADY_EXISTS = &H8004230D
    Private Const VSS_E_UNEXPECTED_PROVIDER_ERROR = &H8004230F
    Private Const VSS_E_CORRUPT_XML_DOCUMENT = &H80042310
    Private Const VSS_E_INVALID_XML_DOCUMENT = &H80042311
    Private Const VSS_E_MAXIMUM_NUMBER_OF_VOLUMES_REACHED = &H80042312
    Private Const VSS_E_FLUSH_WRITES_TIMEOUT = &H80042313
    Private Const VSS_E_HOLD_WRITES_TIMEOUT = &H80042314
    Private Const VSS_E_UNEXPECTED_WRITER_ERROR = &H80042315
    Private Const VSS_E_SNAPSHOT_SET_IN_PROGRESS = &H80042316
    Private Const VSS_E_MAXIMUM_NUMBER_OF_SNAPSHOTS_REACHED = &H80042317
    Private Const VSS_E_WRITER_INFRASTRUCTURE = &H80042318
    Private Const VSS_E_WRITER_NOT_RESPONDING = &H80042319
    Private Const VSS_E_WRITER_ALREADY_SUBSCRIBED = &H8004231A
    Private Const VSS_E_UNSUPPORTED_CONTEXT = &H8004231B
    Private Const VSS_E_VOLUME_IN_USE = &H8004231D
    Private Const VSS_E_MAXIMUM_DIFFAREA_ASSOCIATIONS_REACHED = &H8004231E
    Private Const VSS_E_INSUFFICIENT_STORAGE = &H8004231F
    Private Const VSS_E_NO_SNAPSHOTS_IMPORTED = &H80042320
    Private Const VSS_S_SOME_SNAPSHOTS_NOT_IMPORTED = &H42320

    Private moSnapshotSetID As New Guid
    Private moSnapshotID As New Guid
    Private msDeviceName As String

    Public Sub New(ByVal sVolume As String)
        Dim vss As New VSS.VSSCoordinatorClass

        Abort(vss)
        MakeNewSnapshot(vss, sVolume, moSnapshotSetID, moSnapshotID)
        msDeviceName = GetSnapshotDeviceName(vss, moSnapshotID)
    End Sub

    Public ReadOnly Property DeviceName() As String
        Get
            Return msDeviceName
        End Get
    End Property

    Private Sub MakeNewSnapshot( _
                ByRef vss As VSS.VSSCoordinatorClass, _
                ByVal sVolume As String, _
                ByRef output_SnapShotSetID As Guid, _
                ByRef output_SnapShotID As Guid)

        vss.StartSnapshotSet(output_SnapShotSetID)
        vss.AddToSnapshotSet(sVolume, Guid.Empty, output_SnapShotID)

        Dim ovssAsync As VSS.IVssAsync = Nothing
        Dim oCallBack As Object = Nothing

        vss.DoSnapshotSet(oCallBack, ovssAsync)

        Do While (True)
            Dim hr As Integer = 0
            Dim x As Integer = 0

            ovssAsync.QueryStatus(hr, x)
            Console.Write(".")

            If hr = VSS_S_ASYNC_FINISHED Then
                Exit Do
            End If

            Thread.Sleep(1000)
        Loop

        Console.WriteLine()
    End Sub

    Private Function GetSnapshotDeviceName( _
                    ByRef vss As VSS.VSSCoordinatorClass, _
                    ByVal oSnapShotID As Guid) As String

        Dim sDeviceName As String = ""

        Dim osnapshotProps As New VSS._VSS_SNAPSHOT_PROP
        vss.GetSnapshotProperties(oSnapShotID, osnapshotProps)
        sDeviceName = osnapshotProps.m_pwszSnapshotDeviceObject

        Return sDeviceName
    End Function

    Private Sub Abort( _
                ByRef vss As VSS.VSSCoordinatorClass)

        '   Aborting VSS Operations
        '   http://msdn2.microsoft.com/en-us/library/aa381496(VS.85).aspx

        '---Only one snapshot can be operating at a time,
        '   so abort any current snapshots.

        vss.AbortAllSnapshotsInProgress()
    End Sub

    Public Sub Delete()
        Dim iCountOfDeletedSnapshots As Integer = 0
        Dim oNonDeletedSnapshotID As New Guid

        Dim vss As New VSS.VSSCoordinatorClass
        vss.DeleteSnapshots( _
                moSnapshotSetID, _
                _VSS_OBJECT_TYPE.VSS_OBJECT_SNAPSHOT_SET, _
                True, _
                iCountOfDeletedSnapshots, _
                oNonDeletedSnapshotID)
    End Sub
End Class

Possible improvements

There's a lot that could be done here, and top of the list is better error handling. Only one shadow volume can be open at a time, and if any sort of error is encountered while the app is attempting to initialize the shadow volume, it will crash with an ugly error message. Handling this and other likely errors would be a great step forward.

Other improvements relate more to the scope of the PST backup utility. Since I included this utility mostly as a demo of what Volume Shadow Copies are good for, such improvements are beyond the scope of this project.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)