Announcement

Collapse
No announcement yet.

Create System Restore Point Programmatically

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Create System Restore Point Programmatically

    I use Innosetup for creating install packages, and I use some Pascal scripting code to have the Windows WMI offer to create a System Restore point before Innosetup does the actual install. Translating that to PB seems to be more complicated than I anticipated, so I figured I'd ask. Has anyone written any PB code to do this, either via WMI or directly to the Windows API? Is so any help would be appreciated. Pascal script for Innosetup is below in case anyone else uses Innosetup and would like the installer to offer to create a System Restore point.

    Code:
    const
    // RestorePointTypes
    APPLICATION_INSTALL = 0;
    APPLICATION_UNINSTALL = 1;
    DEVICE_DRIVER_INSTALL = 10;
    MODIFY_SETTINGS = 12;
    CANCELLED_OPERATION = 13;
    
    // EventTypes
    BEGIN_SYSTEM_CHANGE = 100;
    END_SYSTEM_CHANGE = 101;
    BEGIN_NESTED_SYSTEM_CHANGE = 102;
    END_NESTED_SYSTEM_CHANGE = 103;
    
    
    function CreateRestorePoint(sDescription: String): Boolean;
    var
         ScriptControl: Variant;
         oWMI: Variant;
         ErrCode: Integer;
    
    begin
         try
              // Create the ScriptControl object.
              ScriptControl := CreateOleObject('ScriptControl');
    
              // Set the Language property (VBScript or JavaScript)
              ScriptControl.Language := 'VBScript';
    
              // Based on this: https://docs.microsoft.com/en-us/windows/desktop/sr/createrestorepoint-systemrestore
              // oWMI := ScriptControl.Eval('GetObject("winmgmts:{impersonationLevel=impersonate}!root/default:SystemRestore")');
              oWMI := ScriptControl.Eval('GetObject("winmgmts:\\.\root\default:Systemrestore")');
    
              // Create the restore point.
              ErrCode := oWMI.CreateRestorePoint(sDescription, APPLICATION_INSTALL, BEGIN_SYSTEM_CHANGE);
    
              // Return the error code, if any. A value of zero indicates success.
              Result := (ErrCode = 0);
         except
              Result := FALSE;
         end;
    
    end;
    // Windows 8, 10 & 11 default to not allowing Restore Points to be made at intervals less than 24 hours. This can be changed in the System Registry.
    // Info about restore intervals here: https://www.tenforums.com/tutorials/94682-change-system-restore-point-creation-frequency-windows-10-a.html
    //
    
    function InitializeSetup(): Boolean;
    var
         bResult: Boolean;
         //bResult: Integer;
    
    begin
         if MsgBox('Would you like to create a System Restore Point? (Recommended)'+Chr(13)+Chr(10)+Chr(13)+Chr(10)+'Note that Windows may restrict how often System Restore Points can be created. The default in Windows 8, 10 & 11  is one System Restore Point every 24 hours.'+Chr(13)+Chr(10)+Chr(13)+Chr(10)+'There are other settings your computer may have that can also result in the System reporting that it successfully created a Restore Point when in fact it did not.'+Chr(13)+Chr(10)+Chr(13)+Chr(10)+'If you select yes, there will be a short delay while the Restore Point is created.', mbConfirmation, MB_YESNO or MB_DEFBUTTON1) = IDYES then
              begin
                   bResult := CreateRestorePoint ('Installed NDC');
    
                   If bResult then begin
                        Msgbox ('Create System Restore Point succeeded.', mbInformation, MB_OK);
                   end else begin
                        Msgbox ('Create System Restore Point failed.', mbInformation, MB_OK);
                   end;
         end;
    
         Result := TRUE;
    end;
    Michael Burns

  • #2
    Michael, here is one way using various Windows APIs. Based on some Microsoft demo code.

    Code:
    '1) You must initialize COM security to allow NetworkService, LocalService and System
    '   to call back into any process that uses SRSetRestorePoint.
    '   This is necessary for SRSetRestorePoint to operate properly.
    '2) Application must be executed as Administrtator
    '3) Microsoft recommand to load SRSetRestorePoint via LoadLibrary() & GetProcAddress() + FreeLibrary()
    '   in case the DLL doesn't exist on the Windows server. I did not use this way in the demo for code clarity.
    '4) Windows 8 to 11, default to not allowing Restore Points to be made at intervals less than 24 hours.
    '   This can be changed in the System Registry, add or set registry interval to 0
    '   HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SystemRestore\SystemRestorePointCreationFrequency:DWORD
    '   Default: 1440 (In minutes aka 24 hours) or 0 aka no time restriction.
    '   Note that while interval restriction the CreateRestorePoint method returns S_OK even if no creation is done.
    '5) Use José Roca includes.
    
    #COMPILE EXE 'Win 10.04
    #DIM ALL
    %UNICODE = 1
    '#RESOURCE MANIFEST, 1, "XPThemeAdmin.xml" 'Add a Manifest that ask for Administrator right
    #INCLUDE "Win32Api.inc"
    #INCLUDE "SrRestorePtApi.inc"
    #INCLUDE "AclApi.inc"
    
    $AppName               = "Restore point create"
    %COINIT_MULTITHREADED  = &H0??? 'OLE calls objects on any thread
    %SECURITY_MAX_SID_SIZE = 68     'Maximum size of a security SID
    %FAIL                  = 0      'Unsuccessful return value
    '_____________________________________________________________________________
    
    FUNCTION InitializeComSecurity() AS LONG
     'You must initialize COM security to allow NetworkService, LocalService and System to call back
     'into any process that uses SRSetRestorePoint. This is necessary for SRSetRestorePoint to operate properly.
     DIM ExplicitAccess(0 TO 4) AS EXPLICIT_ACCESSW
     LOCAL securityDesc         AS SECURITY_DESCRIPTOR
     LOCAL pAcl                 AS ACL POINTER
     LOCAL srgSidBA             AS ASCIIZ * %SECURITY_MAX_SID_SIZE + 1
     LOCAL srgSidLS             AS ASCIIZ * %SECURITY_MAX_SID_SIZE + 1
     LOCAL srgSidNS             AS ASCIIZ * %SECURITY_MAX_SID_SIZE + 1
     LOCAL srgSidPS             AS ASCIIZ * %SECURITY_MAX_SID_SIZE + 1
     LOCAL srgSidSY             AS ASCIIZ * %SECURITY_MAX_SID_SIZE + 1
     LOCAL cbSid                AS DWORD
     LOCAL RetVal               AS LONG
    
     'Initialize the security descriptor
     IF InitializeSecurityDescriptor(securityDesc, %SECURITY_DESCRIPTOR_REVISION) THEN
    
       'Create an administrator group security identifier (SID)
       cbSid = %SECURITY_MAX_SID_SIZE
       IF CreateWellKnownSid(%WinBuiltinAdministratorsSid, %NULL, srgSidBA, cbSid) THEN
    
         'Create a local service security identifier (SID)
         cbSid = %SECURITY_MAX_SID_SIZE
         IF CreateWellKnownSid(%WinLocalServiceSid, %NULL, srgSidLS, cbSid) THEN
    
           'Create a network service security identifier (SID)
           cbSid = %SECURITY_MAX_SID_SIZE
           IF CreateWellKnownSid(%WinNetworkServiceSid, %NULL, srgSidNS, cbSid) THEN
    
             'Create a personal account security identifier (SID)
             cbSid = %SECURITY_MAX_SID_SIZE
             IF CreateWellKnownSid(%WinSelfSid, %NULL, srgSidPS, cbSid) THEN
    
               'Create a local service security identifier (SID)
               cbSid = %SECURITY_MAX_SID_SIZE
               IF CreateWellKnownSid(%WinLocalSystemSid, %NULL, srgSidSY, cbSid) THEN
                 ExplicitAccess(0).grfAccessPermissions             = %COM_RIGHTS_EXECUTE OR %COM_RIGHTS_EXECUTE_LOCAL
                 ExplicitAccess(0).grfAccessMode                    = %SET_ACCESS
                 ExplicitAccess(0).grfInheritance                   = %NO_INHERITANCE
                 ExplicitAccess(0).Trustee.pMultipleTrustee         = %NULL
                 ExplicitAccess(0).Trustee.MultipleTrusteeOperation = %NO_MULTIPLE_TRUSTEE
                 ExplicitAccess(0).Trustee.TrusteeForm              = %TRUSTEE_IS_SID
                 ExplicitAccess(0).Trustee.TrusteeType              = %TRUSTEE_IS_GROUP
                 ExplicitAccess(1)                                  = ExplicitAccess(0) 'Copy informations from ExplicitAccess(0)
                 ExplicitAccess(2)                                  = ExplicitAccess(0) 'Copy informations from ExplicitAccess(0)
                 ExplicitAccess(3)                                  = ExplicitAccess(0) 'Copy informations from ExplicitAccess(0)
                 ExplicitAccess(4)                                  = ExplicitAccess(0) 'Copy informations from ExplicitAccess(0)
                 ExplicitAccess(0).Trustee.ptstrName                = VARPTR(srgSidBA)
                 ExplicitAccess(1).Trustee.ptstrName                = VARPTR(srgSidLS)
                 ExplicitAccess(2).Trustee.ptstrName                = VARPTR(srgSidNS)
                 ExplicitAccess(3).Trustee.ptstrName                = VARPTR(srgSidPS)
                 ExplicitAccess(4).Trustee.ptstrName                = VARPTR(srgSidSY)
    
                 'Create an ACCESS CONTROL list (ACL) using this ace list
                 IF SetEntriesInAcl(5, BYVAL VARPTR(ExplicitAccess(0)), BYVAL %NULL, pAcl) = %ERROR_SUCCESS THEN '5 is array elements count
    
                   'Set the security descriptor owner to Administrators
                   IF SetSecurityDescriptorOwner(securityDesc, BYVAL VARPTR(srgSidBA), %FALSE) THEN
    
                     'Set the security descriptor group to Administrators
                     IF SetSecurityDescriptorGroup(securityDesc, BYVAL VARPTR(srgSidBA), %FALSE) THEN
    
                       'Set the discretionary ACCESS CONTROL list (DACL) TO the ACL
                       IF SetSecurityDescriptorDacl(securityDesc, %TRUE, pAcl, %FALSE) THEN
    
                         'Initialize COM.
                         IF CoInitializeSecurity(securityDesc, -1, _ '-1 tells COM to choose which authentication services to register
                                                 BYVAL 0, 0, %RPC_C_AUTHN_LEVEL_PKT_PRIVACY, %RPC_C_IMP_LEVEL_IDENTIFY, _
                                                 BYVAL 0, %EOAC_DISABLE_AAA OR %EOAC_NO_CUSTOM_MARSHAL, 0) = %S_OK THEN FUNCTION = %TRUE
                       END IF 'CoInitializeSecurity()
                     END IF 'SetSecurityDescriptorGroup()
                   END IF 'SetSecurityDescriptorOwner()
                   LocalFree(pAcl)
                 END IF 'SetEntriesInAcl()
               END IF 'CreateWellKnownSid() 5
             END IF 'CreateWellKnownSid() 4
           END IF 'CreateWellKnownSid() 3
         END IF 'CreateWellKnownSid() 2
       END IF 'CreateWellKnownSid() 1
     END IF 'InitializeSecurityDescriptor()
    
    END FUNCTION
    '_____________________________________________________________________________
    
    FUNCTION PBMAIN() AS LONG
     LOCAL RestorePointInformation AS RESTOREPOINTINFOW
     LOCAL StateManagerStatus      AS STATEMGRSTATUS
     LOCAL RetVal                  AS LONG
    
     MSGBOX "RestorePoint will be created." & $CRLF & "This may take several seconds..." , %MB_OK OR %MB_TOPMOST, $AppName
    
     RetVal = CoInitializeEx(BYVAL %NULL, %COINIT_MULTITHREADED) 'Initializes the COM library for use by the calling thread
     IF (RetVal = %S_OK) OR (RetVal = %RPC_E_CHANGED_MODE) THEN 'If CoInitializeEx() is OK (%RPC_E_CHANGED_MODE = &H80010106&)
       IF InitializeComSecurity() THEN 'If InitializeComSecurity() is OK (MS say you must initialize COM security)
    
         '*** Create a restore point. ***
         RestorePointInformation.dwEventType       = %BEGIN_SYSTEM_CHANGE
         RestorePointInformation.dwRestorePtType   = %APPLICATION_INSTALL
         RestorePointInformation.szDescription     = "Restore point test " & RIGHT$(DATE$, 4) & "-" & LEFT$(DATE$, 3) & MID$(DATE$, 4, 2) & $SPC & TIME$ 'Description
         RestorePointInformation.llSequenceNumber  = 0 'The sequence number of the restore point, 0 when creating.
    
         'SRSetRestorePoint() may take several seconds to execute.
         IF SRSetRestorePoint(RestorePointInformation, StateManagerStatus) THEN 'Return TRUE if no error.
           'Update the RESTOREPOINTINFO structure to notify the system that the operation is finished.
           RestorePointInformation.dwEventType      = %END_SYSTEM_CHANGE
           RestorePointInformation.llSequenceNumber = StateManagerStatus.llSequenceNumber 'End the system change using the sequence number from the first call to SRSetRestorePoint.
           IF SRSetRestorePoint(RestorePointInformation, StateManagerStatus) = %FALSE THEN 'If an error occured
             MSGBOX "Error A:" & STR$(StateManagerStatus.nStatus), %MB_OK OR %MB_TOPMOST, $AppName
           ELSE
             MSGBOX "Installation completed. Sequence number:" & STR$(StateManagerStatus.llSequenceNumber), %MB_OK OR %MB_TOPMOST, $AppName
             IF MessageBox(%HWND_DESKTOP, "Remove RestorePoint" & STR$(StateManagerStatus.llSequenceNumber) & " ?", $AppName, %MB_YESNO OR %MB_TOPMOST) = %IDYES THEN
               IF SRRemoveRestorePoint(StateManagerStatus.llSequenceNumber) = %ERROR_SUCCESS THEN 'Return %ERROR_SUCCESS or %ERROR_INVALID_DATA, other error codes indicate an internal error
                 MSGBOX "RestorePoint" & STR$(StateManagerStatus.llSequenceNumber) & " removed !", %MB_OK OR %MB_TOPMOST, $AppName
               ELSE
                 MSGBOX "Error, RestorePoint" & STR$(StateManagerStatus.llSequenceNumber) & " not removed !", %MB_OK OR %MB_TOPMOST, $AppName
               END IF
             END IF
           END IF
         ELSE 'SRSetRestorePoint() has failed
           IF StateManagerStatus.nStatus = %ERROR_SERVICE_DISABLED THEN 'If service is disabled
             MSGBOX "Error: %ERROR_SERVICE_DISABLED", %MB_OK OR %MB_TOPMOST, $AppName
           ELSE 'Some other error
             MSGBOX "Error B:" & STR$(StateManagerStatus.nStatus) & $CRLF & "Did you run as Administrator ?", %MB_OK OR %MB_TOPMOST, $AppName
           END IF
         END IF
       END IF
     END IF
    
    END FUNCTION
    '_____________________________________________________________________________
    '

    Comment

    Working...
    X