Introduction
Often, you want to have multiple versions of a file, but not rely on Windows NTFS versioning. Here is a simple Win32 class that you can easily use.
Background
You need:
The ZIP includes a ready-to-use Notepad which demonstrates the library in a text editor (Finalize()
and keyboard shortcuts are not there yet). A simple 1.stxt file is there to open.
Using the Code
class FILESNAP
{
public:
struct alignas(8) FSITEM
{
CLSID cid = FILESNAP::GUID_HEADER;
unsigned long long DiffAt = 0;
FILETIME created = ti();
FILETIME updated = ti();
unsigned long long i = 0;
unsigned long long at = 0;
unsigned long long sz = 0;
unsigned long long extradatasize = 0;
};
inline bool BuildMap(vector<FSITEM>& fsx);
FILESNAP(const wchar_t* fi);
inline bool SetSnap(size_t idx,int CommitForce = 0);
inline bool Create(DWORD Access= GENERIC_READ | GENERIC_WRITE, DWORD Share = 0,
LPSECURITY_ATTRIBUTES Atrs = 0, DWORD Disp = CREATE_NEW, DWORD Flags = 0);
inline unsigned long long Size();
inline bool Finalize();
inline bool Commit(size_t At,int Mode);
inline HANDLE GetH();
inline BOOL Write(const char* d, unsigned long long sz2);
inline BOOL Read(char* d, unsigned long long sz2);
inline unsigned long long SetFilePointer(unsigned long long s, DWORD m = FILE_BEGIN);
inline BOOL SetEnd();
inline void Close();
};
Working with the Class
- You want to create a new file, you simply pass the name to the
FILESNAP
constructor. - You call
Create()
with the CreateFile
flag CREATE_NEW
to create the file. - You write to the file using
Write()
. - When you want to save the current version, you call
Commit(0,0)
. For the first save, Commit()
ignores the two parameters. - You keep writing to the file using
Write()
. - The next
Commit()
call will save the current contents as a differential backup. If the Mode
parameter is 0
, then Commit()
saves as a differential backup to the first commit always. If the Mode
parameter is 1
, then Commit()
saves as a differential backup to the specified commit number as the At
parameter. This allows you to either use differential or incremental backups. - You keep writing, reading or else to the current handle using the
Read()
, Write()
, SetEnd()
, SetFilePointer()
, Size()
and GetH()
function which returns the current HANDLE
that you can use in other file functions. - If you want to revert to a specific saving, use
SetSnap
. CommitForce
can be 0
, 1
or 2
:
0
means that if the file exists already, the function fails. 1
means that if the file exists already, a commit is taken before reverting. 2
does not commit before reverting.
- After you have used
SetSnap()
, Read()
/Write()
/GetH()
etc. operate on the reverted data. Close()
will close the file (it does not commit), and delete all temporary files used by the class.
When you open an existing file, the class automatically calls SetSnap()
on the last snapshot found in the file. You can call BuildMap()
to return a FSITEM
array of all snapshots included in the file.
If you build files with mostly differential backups (related to the first snapshot), then the file will be larger, but quicker to open because only one operation will be done (the comparison between the first and the opened snapshot).
If you build files with mostly incremental backups (related to the last snapshot), then the file will be smaller, but slower to open because all snapshots must be processed when a snapshot is loaded.
Use GetH()
with care, because the HANDLE
it returns is not valid anymore when Commit()
or SetSnap()
is called.
Have fun!
History
- 19/10/2017 - Some typos
- 8/10/2017 - First release