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?

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:

Create a snapshot set using StartSnapshotSet

Add a volume to the snapshot set using AddToSnapshotSet

Initialize the volume snapshot using the DoSnapshotSet

Wait until QueryStatus indicates that the snapshot has been initialized

Get the volume name of the snapshot using GetSnapshotProperties

Use the FindFile and CopyFile APIs to access the shadow volume

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:

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.

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.

Comments and Discussions

Downloaded the code and the Imports VSS line shows as an error. In the References section for VSS I see "The system cannot find the reference specified". So what do I need to install to make this reference work?

Hello,
Whenever I use this MakeShadowCopy class, it seems like the shadow "expires" or something after the first couple of files it copies.. The copy process returns error codes of 55, 2, and 3, which all relate to a file/path not existing.

Does anyone know if shadow copies expire after a certain amount of time, and if there is anything I can do to keep it alive during this process?

publicstruct VSSErrHelper
{
publiclong ErrorId;
public string ErrorMsg;
public VSSErrHelper(long errorId, string errorMsg)
{
ErrorId = errorId;
ErrorMsg = errorMsg;
}
}
publicstatic VSSErrHelper[] VSSErrors =
{
new VSSErrHelper(VSS_E_BAD_STATE, "A function call was invalid because of the state of either the\nbackup extensions or the coordinator. For example calling AddToSnapshot\nset prior to calling StartSnapshotSet."),
new VSSErrHelper(VSS_E_PROVIDER_ALREADY_REGISTERED, "Calling RegisterProvider"),
new VSSErrHelper(VSS_E_PROVIDER_NOT_REGISTERED, "Calling UnregisterProvider"),
new VSSErrHelper(VSS_E_PROVIDER_VETO, "Calling DoSnapshotSet"),
new VSSErrHelper(VSS_E_PROVIDER_IN_USE, "Calling UnregisterProvider, StartSnapshotSet"),
new VSSErrHelper(VSS_E_OBJECT_NOT_FOUND, "Calling DeleteSnapshots, Query"),
new VSSErrHelper(VSS_S_ASYNC_PENDING, "Calling IVssAsync::QueryStatus"),
new VSSErrHelper(VSS_S_ASYNC_FINISHED, "Calling IVssAsync::QueryStatus"),
new VSSErrHelper(VSS_S_ASYNC_CANCELLED, "Calling IVssAsync::QueryStatus"),
new VSSErrHelper(VSS_E_VOLUME_NOT_SUPPORTED, "Calling AddToSnapshotSet"),
new VSSErrHelper(VSS_E_VOLUME_NOT_SUPPORTED_BY_PROVIDER, "Calling AddToSnapshotSet"),
new VSSErrHelper(VSS_E_OBJECT_ALREADY_EXISTS, "Calling ExposeCurrentState"),
new VSSErrHelper(VSS_E_UNEXPECTED_PROVIDER_ERROR, "Calling several methods supported by the providers."),
new VSSErrHelper(VSS_E_CORRUPT_XML_DOCUMENT, "XML document unexpectedly does not match schema."),
new VSSErrHelper(VSS_E_CORRUPT_XML_DOCUMENT, "An XML document passes as an argument is not valid, i.e., is either\nnot correctly formed XML or does not match the schema"),
new VSSErrHelper(VSS_E_MAXIMUM_NUMBER_OF_VOLUMES_REACHED, "We cannot add any more volumes since we passed the maximum limit."),
new VSSErrHelper(VSS_E_FLUSH_WRITES_TIMEOUT, "VSS couldn't flush I/O writes anymore."),
new VSSErrHelper(VSS_E_HOLD_WRITES_TIMEOUT, "VSS couldn't hold I/O writes anymore."),
new VSSErrHelper(VSS_E_UNEXPECTED_WRITER_ERROR, "VSS encountered problems while sending events to writers"),
new VSSErrHelper(VSS_E_SNAPSHOT_SET_IN_PROGRESS, "StartSnapshotSet was called when another snapshot set in the\nprocess of being created."),
new VSSErrHelper(VSS_E_MAXIMUM_NUMBER_OF_SNAPSHOTS_REACHED, "AddToSnapshotSet was called on a volume that has already reached\nits maxinum number"),
new VSSErrHelper(VSS_E_WRITER_INFRASTRUCTURE, "The Writer infrastructure is not operating properly. Check that the\nEvent Service and the Volume Snapshot Service are started and check for\nerrors associdated with these services in the error log."),
new VSSErrHelper(VSS_E_WRITER_NOT_RESPONDING, "A writer did not respond to a GetWriterStatus call. This means that\nthe process containing the writer died or is hung."),
new VSSErrHelper(VSS_E_WRITER_ALREADY_SUBSCRIBED, "A writer has already sucessfully called the Subscribe function. It cannot call\nsubscribe multiple times."),
new VSSErrHelper(VSS_E_UNSUPPORTED_CONTEXT, "Attempt to use an unsupported context."),
new VSSErrHelper(VSS_E_VOLUME_IN_USE, "Calling ChangeDiffAreaMaximumSize"),
new VSSErrHelper(VSS_E_MAXIMUM_DIFFAREA_ASSOCIATIONS_REACHED, "Calling AddDiffArea"),
new VSSErrHelper(VSS_E_INSUFFICIENT_STORAGE, "Calling EndPrepareSnapshots, ChangeDiffAreaMaximumSize"),
new VSSErrHelper(VSS_E_NO_SNAPSHOTS_IMPORTED, "Calling ImportSnapshots, no volumes were successfully imported"),
new VSSErrHelper(VSS_S_SOME_SNAPSHOTS_NOT_IMPORTED, "Calling ImportSnapshots, some volumes were not successfully imported")/*,
new VSSErrHelper(VSS_E_SOME_SNAPSHOTS_NOT_IMPORTED, "Calling ImportSnapshots, some volumes were not successfully imported"),
new VSSErrHelper(VSS_E_MAXIMUM_NUMBER_OF_REMOTE_MACHINES_REACHED, "While calling AddToSnapshotSet - cannot add shares to snapshot from other machines\nsince we passed the maximum remote machines limit."),
new VSSErrHelper(VSS_E_REMOTE_SERVER_UNAVAILABLE, "Remote machine that exposes a share to snapshot is not available"),
new VSSErrHelper(VSS_E_REMOTE_SERVER_UNSUPPORTED, "Remote machine that exposes the share to snapshot runs a VSS version\nthat does not support creation of snapshots for shares"),
new VSSErrHelper(VSS_E_REVERT_IN_PROGRESS, "A revert is currently in progress for the specified volume. Another revert\ncannot be initiated until the current revert completes"),
new VSSErrHelper(VSS_E_REVERT_VOLUME_LOST, "The volume being reverted was lost during revert.")*/
};
privatestatic Exception getVSSErrMsg(Exception e)
{
//Message "Exception from HRESULT: 0x80000000"
int indexOfErrorId = -1;
long errorId = 0;
string srcErrMsg = "";
indexOfErrorId = e.Message.IndexOf("HRESULT:");
if (indexOfErrorId >= 0) {
srcErrMsg = e.Message.Substring(indexOfErrorId);
try {
errorId = Convert.ToInt64(e.Message.Substring(indexOfErrorId + 8).Trim(), 16);
} catch { errorId = 0; }
}
if (errorId > 0) {
foreach (VSSErrHelper dscr in VSSErrors) {
if (dscr.ErrorId == errorId)
returnnew Exception("VSS ERROR: " + dscr.ErrorMsg + " (" +
srcErrMsg + ")", e);
}
}
return e;
}

Hi Rick, the interop is created from Visual Studio when the project is built. You need to install visual studio on each OS you want to support then the proper interop is created from the com object per located on the operating system when the project is built. Vista doesnt have the VSS com object that xp and 2k3 have so it wont work for that. I have included the interops for XP, XP x64, Windows 2003, and 2003 X64 here. http://www.etw.ca/interops.zip Just make sure you rename the file so it says interop.vss.dll to work properly. For vista & 2008 you need to download the sdk for the operating system and make the appropriate changes to the code.

I went to the work of contacting the Microsoft VSS team. I was able to create the interop for vista and 2008 however in vista and 08 the vss coordinator class is lacking the ability to create and do snapshots along with a few other things. I was informed that the vss coordinator class is unsupported and was only made for internal use thats why its not documented anywhere on msdn. The only way to do it for 08 and vista is to write something in mc++ and make calls to it. The correct class to use is the IVSS coordinator. http://msdn2.microsoft.com/en-us/library/aa382175(VS.85).aspx[^]

So you will need to create a thin interop. You need to add the headers from the vista sdk. Let me know if you come up with anything.

ovssAsync.QueryStatus(hr, x)
If hr = VSS_S_ASYNC_FINISHED Then
Exit Do
End If

Threading.Thread.Sleep(1000)
Loop

bRet = True

Catch ex As Exception
' clean up any progressing snapshots
vss.AbortAllSnapshotsInProgress()
End Try

Return bRet
End Function

I then attempt to copy a file using hte CopyFile API. On random occasions for some files and all the time for others I get the CopyFile failing with "The system cannot find the file specified.". It doesn't always happen right either, it can get X % thru the copy then fail.

You can change the ownership permisson in the registry to your active user By clicking on permissions>Advanced>Owner and change the permissions, however after trying this the same result occurred. However you are definetly onto something here i think.

vss service is different on vista and xp. for VSS; vsssdk 7.2 is used on XP and microsoft sdk is used on vista. may be the sample source code provided in the article is using VSSSDK 7.2. i suggest checking the vss version for correct version. on the other hand i haven't tried to run the code yet.

Hello - Great sample. I modified the Snapshot class to create a snapshot via a method call. But, I noticed the snapshot is removed when my WinForms app shuts down (even if I do not delete the snapshot/snapshot set). Do you know how I can prevent this from happening?