Using System Restore

Post date: Aug 18, 2010 12:32:45 AM

This is intended to describe how to use the System Restore facility that exists in Windows ME and XP in your Delphi program. System restore enables the user to revert the computer to a previous state before that change is made. You will see it done commonly with install programs, but can also be done with any kind of code or registry change made to a system. Sample code, including a unit and a main program will be shown.

Using System Restore requires that the SRCLIENT.DLL be present on the system. The general recommendation is to load the functions dynamically, so you will see that in the initialization of the unit. There are two functions that interest us:

1) 'SRSetRestorePointW' (also an A version for ANSI, W is Unicode)

2) 'SRRemoveRestorePoint'

The first does everything we want in the creation of a restore point. The second removes restore points.

In the creation of a restore point, a call is made to start the restore point, then the program does what is restorable, then a call to end the point is made. There is also the option to cancel a restore point. Calls for all three options exist in the unit below, along with the remove restore point call. Specific code examples can be gleaned from it. Also, a error string routine is provided for documentation purposes.

When a restore point is made, an index number is created. This index number is provided in the function call of the unit. This number is required to make any subsequent references to the restore point.

Sample code is provided below to handle the unit calls. Button1 creates a restore point session where a test file is written. Button2 will delete all system restore points.

To test the restore point function (Button1):

1) Run the program and use the Button1 option. You will see a text file named TEST.DLL file created.

2) Go into the System Restore Utility, select to restore using the point name created.

3) once the reboot is completed, you should not find the TEST.DLL file anymore.

Unit Code:

unit sysrestore;
    {
    system restore access unit - provides access to the system restore
    function of Windows in order to set/cancel a restore point.
    Created using Delphi 3.0 by Glenn9999 at tektips.com
    }
interface
  uses windows;
   const
    { restore point types }
    APPLICATION_INSTALL = 0;
    APPLICATION_UNINSTALL = 1;
    DEVICE_DRIVER_INSTALL = 10;
    MODIFY_SETTINGS = 12;
    CANCELLED_OPERATION = 13;
    { event types }
    BEGIN_SYSTEM_CHANGE = 100;
    END_SYSTEM_CHANGE = 101;
    { other stuff }
    MAX_DESC = 256;
  type
    int64 = comp; { comment this if you are on a Delphi that supports int64 }
    restoreptinfo = record
      dwEventType: DWord;
      dwRestorePtType: DWord;
      llSequenceNumber: int64;
      szDescription: array[0..max_desc] of widechar;
    end;
    prestoreptinfo = ^restoreptinfo;
    statemgrstatus = record
      nStatus: DWord;
      llSequenceNumber: int64;
    end;
    pstatemgrstatus = ^statemgrstatus;
    set_func = function(restptinfo: prestoreptinfo;
                        status: pstatemgrstatus): boolean; stdcall;
    remove_func = function(dwRPNum: DWord): DWord; stdcall;
  var
    DLLHandle: THandle;
    set_restore: set_func;
    remove_restore: remove_func;
  function begin_restore(var seqnum: int64; instr: widestring): integer;
  function end_restore(seqnum: int64): integer;
  function cancel_restore(seqnum: int64): integer;
  function error_report(inerr: integer): string;
  implementation
     uses sysutils, dialogs;
  function begin_restore(var seqnum: int64; instr: widestring): integer;
    { starts a restore point }
    var
      r_point: restoreptinfo;
      smgr: statemgrstatus;
      fret: boolean;
      retval: integer;
    begin
      retval := 0;
      r_point.dwEventType := BEGIN_SYSTEM_CHANGE;
      r_point.dwRestorePtType := APPLICATION_INSTALL;
      move(instr[1], r_point.szDescription, max_desc);
      r_point.llSequenceNumber := 0;
      fret := set_restore(@r_point, @smgr);
      if fret = false then
        retval := smgr.nStatus;
      seqnum := smgr.llSequenceNumber;
      begin_restore := retval;
    end;
  function end_restore(seqnum: int64): integer;
    { ends restore point }
    var
      r_point: restoreptinfo;
      smgr: statemgrstatus;
      fret: boolean;
      retval: integer;
    begin
      retval := 0;
      r_point.dwEventType := END_SYSTEM_CHANGE;
      r_point.llSequenceNumber := seqnum;
      fret := set_restore(@r_point, @smgr);
      if fret = false then
         retval := smgr.nStatus;
      end_restore := retval;
    end;
  function cancel_restore(seqnum: int64): integer;
    { cancels restore point in progress}
    var
      r_point: restoreptinfo;
      smgr: statemgrstatus;
      retval: integer;
      fret: boolean;
    begin
      retval := 0;
      r_point.dwEventType := END_SYSTEM_CHANGE;
      r_point.dwRestorePtType := CANCELLED_OPERATION;
      r_point.llSequenceNumber := seqnum;
      fret := set_restore(@r_point, @smgr);
      if fret = false then
         retval := smgr.nStatus;
      cancel_restore := retval;
    end;
  function error_report(inerr: integer): string;
    { error reporting, takes error, returns string }
    const
      SERROR_SUCCESS = 'Call Successful.';
      SERROR_BAD_ENVIRONMENT = 'The function was called in safe mode.';
      SERROR_DISK_FULL = 'System Restore is in Standby Mode because disk space is low.';
      SERROR_FILE_EXISTS = 'Pending file rename operations exist.';
      SERROR_INTERNAL_ERROR = 'An internal error occurred.';
      SERROR_INVALID_DATA = 'The sequence number is invalid.';
      SERROR_SERVICE_DISABLED = 'System Restore is disabled.';
      SERROR_TIMEOUT = 'The call timed out.';
    begin
      case inerr of
        ERROR_SUCCESS: error_report := SERROR_SUCCESS;
        ERROR_BAD_ENVIRONMENT: error_report := SERROR_BAD_ENVIRONMENT;
        ERROR_DISK_FULL: error_report := SERROR_DISK_FULL;
        ERROR_FILE_EXISTS: error_report := SERROR_FILE_EXISTS;
        ERROR_INTERNAL_ERROR: error_report := SERROR_INTERNAL_ERROR;
        ERROR_INVALID_DATA: error_report := SERROR_INVALID_DATA;
        ERROR_SERVICE_DISABLED: error_report := SERROR_SERVICE_DISABLED;
        ERROR_TIMEOUT: error_report := SERROR_TIMEOUT;
      else
        error_report := IntToStr(inerr);
      end;
    end;
  initialization
    { find library functions and enable them }
    DLLHandle := LoadLibraryW('SRCLIENT.DLL');
    if DLLHandle <> 0 then
      begin
        @set_restore := GetProcAddress(DLLHandle, 'SRSetRestorePointW');
        if @set_restore = nil then
           begin
             messagedlg('Did not find SRSetRestorePointW', mtWarning, [mbOK], 0);
             halt(1);
           end;
        @remove_restore := GetProcAddress(DLLHandle, 'SRRemoveRestorePoint');
        if @remove_restore = nil then
           begin
             messagedlg('Did not find SRRemoveRestorePoint', mtWarning, [mbOK], 0);
             halt(1);
           end;
      end
    else
      begin
        messagedlg('System Restore Interface Not Present.', mtWarning, [mbOK], 0);
        halt(1);
      end;
finalization
  { release library }
   FreeLibrary(DLLHandle);
end.

Sample code (use it in a form with two buttons and a label):

unit srtool;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, sysrestore;
type
  TForm1 = class(TForm)
    Label1: TLabel;
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
 { demonstration of making a system restore session }
  var
    seqnum: int64;
    retval: integer;
    testfile: TextFile;
    i: integer;
    inputstring: string;
  begin
    InputString:= InputBox('Input Box',
                            'Prompt', 'Test System Restore Session');
    seqnum := 0;
    retval := begin_restore(seqnum, WideString(inputstring));
    if retval = 0 then
      label1.caption := 'System Restore Entry Set: ' + IntToStr(trunc(seqnum))
    else
      label1.caption := 'Error Str1: ' +  error_report(retval);
    { do stuff here we want to back out }
    AssignFile(testfile, 'TEST.DLL');
    rewrite(testfile);
    for i := 1 to 500000 do
      begin
        writeln(testfile, i);
        application.processmessages;
      end;
    closeFile(testfile);
    { end do stuff here we want to back out of }
    label1.caption := 'Finished.';
    retval := end_restore(seqnum);
    if retval <> 0 then
      label1.caption := 'Error Str2: ' + error_report(retval);
 end;
procedure TForm1.Button2Click(Sender: TObject);
   { clear system restore }
  var
    inresult: DWord;
    i: integer;
    seqnum: int64;
    topnum: integer;
  begin
    label1.caption := 'Please wait, cleaning system restore.';
    application.processmessages;
    { get last sequence number }
    begin_restore(seqnum, 'Test');
    cancel_restore(seqnum);
    topnum := trunc(seqnum) - 1;
    inresult := 0;
    i := topnum;
    while inresult = 0 do
      begin
        inresult := remove_restore(i);
        dec(i);
      end;
    label1.caption := 'Now Done.';
  end;
end.