So one of the neat concepts that started in Windows ME (and XP) is that of "restore points." These are a way for the user to roll their system's state back to a specific point in time where the configuration was good. It doesn't touch user-files, just system files, the registry, etc.
Well, if your application is going to be doing some heavy-duty mucking of things, and is generally considered dangerous (such as an installer application might be, or a registry cleaner), then you may want to provide your users with the chance to create a restore point. You can accomplish this via the SRSetRestorePoint API, exposed in the SrClient DLL. Again, this is on ME, XP and higher (older versions won't have this functionality installed).
The way this API works is that you tell the OS "hey, I'm about to start an operation." The OS then logs all of the changes you've made and it archives the old versions of the data. So, if you touch the registry, it saves a copy of the old registry state before your changes. If you delete some files and add some new ones, I keeps track of the files deleted and added, so that it can roll your changes back. Once you are all done making your changes, you call the API again to say "Ok, I'm done now." The system then takes the journal of your changes and stores it for the user to roll back to if they'd like.
The generic code for the API looks like this:[rbcode]Private Function SRSetRestorePoint(change as Integer, reason as Integer, id as Int64 = 0, description as String = "") As Integer
#if TargetWin32
Soft Declare Function SRSetRestorePointW Lib "SrClient" ( spec as Ptr, status as Ptr ) as Boolean
Soft Declare Function SRSetRestorePointA Lib "SrClient" ( spec as Ptr, status as Ptr ) as Boolean
Const BEGIN_SYSTEM_CHANGE = 100
Const APPLICATION_INSTALL = 0
Const ERROR_SUCCESS = 0
if System.IsFunctionAvailable( "SRSetRestorePointW", "SrClient" ) then
dim mb as new MemoryBlock( 528 )
mb.Long( 0 ) = change
mb.Long( 4 ) = reason
mb.Int64Value( 8 ) = id
if description <> "" then
mb.WString( 16 ) = description
end if
dim status as new MemoryBlock( 12 )
dim ret as Boolean = SRSetRestorePointW( mb, status )
if not ret or status.Long( 0 ) <> ERROR_SUCCESS then return -1
// Save the id off so we know what to close later
return status.Int64Value( 4 )
else
dim mb as new MemoryBlock( 80 )
mb.Long( 0 ) = change
mb.Long( 4 ) = reason
mb.Int64Value( 8 ) = id
if description <> "" then
mb.CString( 16 ) = description
end if
dim status as new MemoryBlock( 12 )
dim ret as Boolean = SRSetRestorePointA( mb, status )
if not ret or status.Long( 0 ) <> ERROR_SUCCESS then return -1
// Save the id off so we know what to close later
return status.Int64Value( 4 )
end if
#endif
return -1
End Function[/rbcode]
One thing which you'll notice is that the W version allocates a lot more space (not just double) -- that's because the wide char version realized that 64 bytes of description wasn't quite enough, so they bumped the limit up. So now that we have the helper function, let's wrap a Start and End function around it to complete the API:[rbcode]
Protected Function StartRestorePoint(type as Integer, description as String) As Boolean
if mRestorePointID <> 0 then return false
Const BEGIN_SYSTEM_CHANGE = 100
mRestorePointID = SRSetRestorePoint( BEGIN_SYSTEM_CHANGE, type, 0, description )
if mRestorePointID = -1 then
mRestorePointID = 0
return false
end if
return true
End Function
Protected Sub EndRestorePoint(cancel as Boolean)
if mRestorePointID = 0 then return
Const END_SYSTEM_CHANGE = 101
Const CANCELLED_OPERATION = 13
if cancel then
call SRSetRestorePoint( END_SYSTEM_CHANGE, CANCELLED_OPERATION, mRestorePointID )
else
call SRSetRestorePoint( END_SYSTEM_CHANGE, 0, mRestorePointID )
end if
End Sub[/rbcode]
So now the user has the chance to call the start and end functions, and can even specify what type of operation it is using a set of constant values (I won't list them here; they're well documented in MSDN). The way you'd use this API is simple:[rbcode]
StartRestorePoint( kRestorePointApplicationInstall, "This is my test installation" )
DesktopFolder.Child( "Roll me back" ).CreateAsFolder
EndRestorePoint( false )[/rbcode]
If you run this code on a supported system, you'll see a long pause, followed by a new folder on your desktop. If you go into the system's restore utility, then you'll notice that there's a new restore point for today with our description. Rolling back to it, you'll see the folder you made on the desktop disappear.
Before we close the topic down, I would like to point out some caveats. The first is that you can only make one restore point at a time, so do not call this recursively. Doing so can be very strange, and there's usually no good point to making a recursive restore point anyhow. The second is that on Windows ME, you cannot end a restore point while there are still pending renames (which means you have to reboot the machine before calling the EndRestorePoint method). So if you have any pending moves, deletes or renames, you should store the RestorePointID somewhere until after the reboot. This isn't something you will encounter often, but it's something for you to be aware of. Finally, this requires 2006r1 or up to compile due to the Int64 support. You could modify the function to work with a Double as well, but it's more of a pain than I felt like dealing with.
Enjoy!
Leave a comment