Search code examples
vb.netshadow-copyvolume-shadow-service

VB.Net: How To Display Previous Shadow Copy Versions of File Allowing User to Choose One


I'm writing an Excel file recovery program with VB.Net that tries to be a convenient place to gather and access Microsoft's recommended methods. If your interested in my probably kludgy, error filled, and lacking enough cleanup code it's here: http://pastebin.com/v4GgDteY. The basic functionality seems to work although I haven't tested graph macro table recovery yet.

It occurred to me that Vista and Windows 7 users could benefit from being offered a list of previous versions of the file within my application if the Shadow Copy Service is on and there are previous copies. How do I do this?

I looked at a lot of web pages but found no easy to crib code. One possibility I guess would be to use vssadmin via the shell but that is pretty cumbersome. I just want to display a dialogue box like the Previous Versions property sheet and allow users to pick one of the previous versions. I guess I could just display the previous version property sheet via the shell by programmatically invoking the context menu and the "Restore previous versions choice", however I also want to be able to offer the list for Vista Home Basic and Premium Users who don't have access to that tab even though apparently the previous versions still exist. Additionally if it possible I would like to offer XP users the same functionality although I'm pretty sure with XP only the System files are in the shadow copies.

I looked at MSDN on the Shadow Copy Service and went through all the pages, I also looked at AlphaVSS and AlphaFS and all the comments. I'm kind of guessing that I need to use AlphaVss and AlphFS and do the following?

  1. Find out the list of snapshots/restore points that exist on the computer.
  2. Mount those snapshots.
  3. Navigate in the mounted volumes to the Excel file the user wants to recover and make a list of those paths.
  4. With the list of paths handy, compare with some kind of diff program, the shadow copies of the files with the original.
  5. Pull out the youngest or oldest version (I don't think it matters) of those shadow copies that differ from the recovery target.
  6. List those versions of the files that are found to be different.

This seems cumbersome and slow, but maybe is the fastest way to do things. I just need some confirmation that is the way to go now.


Solution

  • I finally decided to go ahead and start coding. Please make suggestions for speeding up the code or what do with files that are found to be different from the recovery file target. Is there a simpler way to do this with AlphaVSS and AlphaFS?

    Private Sub Button1_Click_2(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    
        'Find out the number of vss shadow snapshots (restore 
        'points). All shadows apparently have a linkable path 
        '\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy#,
        'where # is a simple one, two or three digit integer.
        Dim objProcess As New Process()
        objProcess.StartInfo.UseShellExecute = False
        objProcess.StartInfo.RedirectStandardOutput = True
        objProcess.StartInfo.CreateNoWindow = True
        objProcess.StartInfo.RedirectStandardError = True
        objProcess.StartInfo.FileName() = "vssadmin"
        objProcess.StartInfo.Arguments() = "List Shadows"
        objProcess.Start()
    
        Dim burp As String = objProcess.StandardOutput.ReadToEnd
        Dim strError As String = objProcess.StandardError.ReadToEnd()
        objProcess.WaitForExit()
        Dim xnum As Integer = 0
        Dim counterVariable As Integer = 1
        ' Call Regex.Matches method.
        Dim matches As MatchCollection = Regex.Matches(burp, _
                                "HarddiskVolumeShadowCopy")
        ' Loop over matches.
        For Each m As Match In matches
            xnum = xnum + 1
            'At the max xnum + 1 is the number of shadows that exist
        Next
        objProcess.Close()
    
        Do
            'Here we make symbolic links to all the shadows, one at a time 
            'and loop through until all shadows are exposed as folders in C:\.
            Dim myProcess As New Process()
            myProcess.StartInfo.FileName = "cmd.exe"
            myProcess.StartInfo.UseShellExecute = False
            myProcess.StartInfo.RedirectStandardInput = True
            myProcess.StartInfo.RedirectStandardOutput = True
            myProcess.StartInfo.CreateNoWindow = True
            myProcess.Start()
            Dim myStreamWriter As StreamWriter = myProcess.StandardInput
            myStreamWriter.WriteLine("mklink /d C:\shadow" & counterVariable _
                & " \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy" _
                & counterVariable & "\")
            myStreamWriter.Close()
            myProcess.WaitForExit()
            myProcess.Close()
    
            ' Here I compare our recovery target file against the shadow copies
            Dim sFile As String = PathTb.Text
            Dim sFileShadowPath As String = "C:\shadow" & _
                counterVariable & DelFromLeft("C:", sFile)
            Dim jingle As New Process()
            jingle.StartInfo.FileName = "cmd.exe"
            jingle.StartInfo.UseShellExecute = False
            jingle.StartInfo.RedirectStandardInput = True
            jingle.StartInfo.RedirectStandardOutput = True
            jingle.StartInfo.CreateNoWindow = True
            jingle.Start()
            Dim jingleWriter As StreamWriter = jingle.StandardInput
            jingleWriter.WriteLine("fc """ & sFile & """ """ _
                                   & sFileShadowPath & """")
            jingleWriter.Close()
            jingle.WaitForExit()
            Dim jingleReader As StreamReader = jingle.StandardOutput
            Dim JingleCompOut As String = jingleReader.ReadToEnd
            jingleReader.Close()
            jingle.WaitForExit()
            jingle.Close()
            Dim jingleBoolean As Boolean = JingleCompOut.Contains( _
                "no differences encountered").ToString
            If jingleBoolean = "True" Then
                MsgBox(jingleBoolean)
            Else
                'I haven't decided what to do with the paths of 
                'files that are different from the recovery target.
                MsgBox("No")
            End If
    
            counterVariable = counterVariable + 1
        Loop Until counterVariable = xnum + 1
    
    End Sub