65.9K
CodeProject is changing. Read more.
Home

An Autoit Program for Automatic BackUp and Encryption of Files

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2 votes)

Jan 15, 2018

CPOL

3 min read

viewsIcon

11661

downloadIcon

130

A program for automatic backup with possibly encrypt

Introduction

This is the third article on the use of some advanced features of the Autoit[1] language for receiving notifications from the Operating System (OS) on writing files and to be able to make a copy of these (possibly encrypted).

The previous articles deal with the use of COM (Component Object Model) objects, the first shows how to grab data from an MSWord document by COM objects for MSWord and send them to an internet application using WinHttpRequest COM object which implements the Ajax protocol; the second besides the COM objects for MSWord uses properties and methods of the COM objects for Power Point.

Steps to Receive Notifications from the File System

(See also the article of Thomas Caudal Shell Notifications in C#)

  • Register a message, this should be a string unique in the system:
    Local $iMsg = _WinAPI_RegisterWindowMessage('CHANGENOTIFY')
  • Associates the message with an Autoit function:
    GUIRegisterMsg($iMsg, 'WM_CHANGENOTIFY') ; the message is associated to function WM_CHANGENOTIFY
  • Registers a window that receive notifications from the file system:
    $g_iID = _WinAPI_ShellChangeNotifyRegister($hWnd,$iMsg,$SHCNE_ALLEVENTS,BitOR($SHCNRF_INTERRUPTLEVEL, $SHCNRF_SHELLLEVEL, $SHCNRF_RECURSIVEINTERRUPT), $Path)

The program below is a step to develop the program BackUp object of this article; I used it to test the operation in the case of several contemporary folders.

#include <APIShellExConstants.au3>
#include <WinAPI.au3>
#include <WinAPIShellEx.au3>
#include <GUIConstantsEx.au3>
Opt('TrayAutoPause', 0)
OnAutoItExitRegister('Deregister')
Global $hWnd = GUICreate("Try change on folders", 400, 300)
GUISetState(@SW_SHOW, $hWnd)
Global $ID1 = Guard("D:\Condor", 1)
Global $ID2 = Guard("C:\www", 2)
While 1
	If GUIGetMsg() = $GUI_EVENT_CLOSE Then ExitLoop
WEnd
Func Guard($path, $indx)
	$Message = 'CHANGENOTIFY' & $indx
	Local $iMsg = _WinAPI_RegisterWindowMessage($Message)
	GUIRegisterMsg($iMsg, 'WM_CHANGENOTIFY') ; the message is associated to function WM_CHANGENOTIFY
	;	local $g_iID = _WinAPI_ShellChangeNotifyRegister_
        ($hWnd,$iMsg,  $SHCNE_ALLEVENTS, BitOR($SHCNRF_INTERRUPTLEVEL, $SHCNRF_SHELLLEVEL, _
         $SHCNRF_RECURSIVEINTERRUPT), $Path, true)
	Local $g_iID = _WinAPI_ShellChangeNotifyRegister($hWnd, $iMsg, $SHCNE_UPDATEITEM, _
         BitOR($SHCNRF_INTERRUPTLEVEL, $SHCNRF_SHELLLEVEL, $SHCNRF_RECURSIVEINTERRUPT), $path)
	If @error Then
		MsgBox(BitOR($MB_ICONERROR, $MB_SYSTEMMODAL), 'Error', 'Window does not registered.')
		Exit
	EndIf
	Return $g_iID
EndFunc   ;==>Guard
Func WM_CHANGENOTIFY($hWnd, $iMsg, $wParam, $lParam)
	#forceref $hWnd, $iMsg
	Local $sPath = _WinAPI_ShellGetPathFromIDList(DllStructGetData(DllStructCreate
                   ('dword Item1; dword Item2', $wParam), 'Item1'))
	If $SHCNE_UPDATEITEM = $lParam Then ConsoleWrite($sPath & " writed" & @CRLF)
EndFunc   ;==>WM_CHANGENOTIFY
Func Deregister()
	ConsoleWrite("Deregister" & @CRLF)
	If $ID1 Then _WinAPI_ShellChangeNotifyDeregister($ID2)
	If $ID2 Then _WinAPI_ShellChangeNotifyDeregister($ID2)
EndFunc   ;==>Deregister

The Backup Program

As already anticipated in the overlying introduction, the script deals with automatic backup of files when they are modified: a form permits to choose the file(s) to guard, where to backup and a possible encrypt of the copy; this last option is for those who do not trust the storage of their data on the cloud.

Some clarifications on the data and on the operation of the program:

  • The buttons of the form are associated with the functions invoked with the pressure of the same
  • The data in the form is accessible through a dictionary where the key is the name associated to a control, for example, the name of the backup folder is Folder;
  • Finally, every file to watch is stored in a dictionary where the key is fileName to backup (with path) and the value is an array containing:
    • the origin folder
    • the backup file (complete path)
    • the possible encryption password
    • the handle returned by _WinAPI_ShellUPDATENOTIFYRegister
    • the fileName

When the button Guard is pressed, the code below is executed:

Func genGuard($data)
	Local $sDrive = "", $sDir = "", $sExtension = "", $Path = "", $file = ""
	$psw = $data.item("Password")
	$fileToGuard = $data.item("File")
	Local $aPathSplit = _PathSplit($fileToGuard, $sDrive, $Path, $File, $sExtension)
	$destFolder = $data.item("Folder")
	if StringRight($destFolder,1) <> "\" then $destFolder = $destFolder & "\"
	$Path = $sDrive & $Path
	Local $dataFile[] = [$Path,$destFolder,$psw, 0,$File & $sExtension]
	if $data.item("runProgram") = "1" Then ShellExecute($data.item("File"),@SW_MAXIMIZE)
	if isNewFolder($Path) Then
		Local $iMsg = _WinAPI_RegisterWindowMessage('UPDATENOTIFY' & $fileDict.count)
		GUIRegisterMsg($iMsg, 'WM_UPDATENOTIFY')	; the message is associated to _
                                function WM_UPDATENOTIFY
		$dataFile[3] =  _WinAPI_ShellChangeNotifyRegister($data.item("fg_winhandle"), _
						$iMsg, _
						$SHCNE_UPDATEITEM, _	; guard update file system
						BitOR($SHCNRF_INTERRUPTLEVEL, $SHCNRF_SHELLLEVEL, $SHCNRF_RECURSIVEINTERRUPT), _
						$Path)
		If @error Then
			MsgBox(BitOR($MB_ICONERROR, $MB_SYSTEMMODAL),'Error','Window does not registered.')
			Exit
		EndIf
	EndIf
	ConsoleWrite("Folder " & $path & " guarded" & @CRLF)
	$fileDict.Item($fileToGuard) = $dataFile
EndFunc

For every folder to guard must be created a new message by the function _WinAPI_RegisterWindowMessage, this is achieved by creating a message with a constant part and a variable number: 'UPDATENOTIFY' & $fileDict.count.

The file can be opened if the Check Box runProgram is checked by its associated program through the function ShellExecute.

With the function _WinAPI_ShellChangeNotifyRegister, the program tells the Operating System (OS) to call the function WM_UPDATENOTIFY when the event $SHCNE_UPDATEITEM[2] i.e., an update on the folder $Path occurs.

When the event occurs, the OS calls the function WM_UPDATENOTIFY:

Func WM_UPDATENOTIFY($hWnd, $iMsg, $wParam, $lParam)
	#forceref $hWnd, $iMsg
	Local $sPath = _WinAPI_ShellGetPathFromIDList(DllStructGetData_
                   (DllStructCreate('dword Item1; dword Item2', $wParam), 'Item1'))
	If $sPath Then
		if $fileDict.Exists($sPath) Then		; a file has been updated
			Local $dataFile = $fileDict.Item($sPath)
			If $dataFile[2] = "" Then		; there is a password?
				FileCopy($sPath,$dataFile[1], $FC_OVERWRITE + $FC_CREATEPATH)
			Else
				_Crypt_EncryptFile($sPath, $dataFile[1] & $dataFile[4], _
                              $dataFile[2],$CALG_AES_192)	; Encrypt the file.
				If @error <> 0 Then
					consoleWrite("Error when encrypting: " & @error & @CRLF)
				EndIf
			EndIf
			consoleWrite($sPath & " saved")
			if $dataFile[2] <> "" Then ConsoleWrite(" and encrypted")
			ConsoleWrite(@CRLF)
		EndIf
	EndIf
EndFunc   ;==>WM_UPDATENOTIFY

If the event is related to a file under control, this is copied to the backup folder or it is copied encrypted[3] if there is a keyword.

Other Features of the Program

I added a subform to recover the encrypted files and in the menu, under File, the sub menu Show guarded which shows the files currently under control:

...
	& "BL,Restore,Restore,,,Restore the file,Restore;" _
	& "Menu,File,&File;" _
	& "SubMenu,Showguard,&Show guarded,Showguard;" _
...
Func showGuard()
	$aDict = getIniFormat($fileDict)
	Local $iOrgWidth = 500, $iHeight = 300
	Local $hGUI = GUICreate("Files on guard", $iOrgWidth, $iHeight)
	Local $aiGUISize = WinGetClientSize($hGUI)
	Local $idListview = GUICtrlCreateListView(StringFormat_
           ("File   %40s |Folder %40s |Encript key","",""), 10, 10, 480, 240)
	For $i=1 to $aDict[0][0]
		$a = $aDict[$i][1]
		Local $idItem = GUICtrlCreateListViewItem_
           ($aDict[$i][0] & "|" & $a[1] & "|"  & $a[2],$idListview)
	Next
	GUISetState(@SW_SHOW, $hGUI)
    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop
        EndSwitch
    WEnd
	GUIDelete()
EndFunc
Func Restore($data)
	$psw = $data.item("PasswordR")
	$fileToRestore = $data.item("FileR")
	Local $sDrive = "", $sDir = "", $sExtension = "", $Pth = "", $file
	Local $aPathSplit = _PathSplit($fileToRestore, $sDrive, $Pth, $File, $sExtension)
	$File = $File & $sExtension
	$destFile = $data.item("FolderR")
	if StringRight($destFile,1) <> "\" then $destFile = $destFile & "\"
	$destFile = $destFile & $File
	if $psw = "" Then
		FileCopy($fileToRestore,$destFile, $FC_OVERWRITE + $FC_CREATEPATH)
	Else
		_Crypt_DecryptFile($fileToRestore, $destFile, $psw,$CALG_AES_192)	; Decrypt the file.
	EndIf
	msgbox(0,"Restore",$fileToRestore,1.5)
	ConsoleWrite("Restored file " & $fileToRestore & " from " & $Pth & @CRLF)
EndFunc

Notes

  1. ^AutoIt is a free-ware BASIC-like scripting language, an alternative to PowerShell, designed in origin for automating the interaction with Windows GUI. AutoIt can run on Windows interpreted or compiled. It comes with many libraries that enable, among other things, access COM objects and create graphical interfaces; those task are greatly enhanced by the rich Help with examples.
  2. ^In the documentation we can find a list of all events, in particular $SHCNE_ALLEVENTS matches all events.
  3. ^I used the AES (192bit) algorithm but AutoIt supports some others that can be found in the documentation.