Title: How do I use System Restore in my program?
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:
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):
CODE
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.