Configuring Windows 8/8.1/10 File History via command line
tl;dr: Is there a way to configure windows 8 file history via unattended command-line/some script?
long story: I'm configuring a lot of stand-alone (non-domain) PCs and I think some backup is better than no backup. On Windows 7, I even configure its standard backup, despite it's retarded – by default it significantly slows computer every Monday, unless by some obscure reason user didn't turn off their PC before weekend, and it fills backup medium and stops backing up.
And I resigned to try to automate its configuration, but at least there I can set it up once as admin, and it's effective for all, including new, users. And I'm scheduling (scheduling is automated) custom script to remove old backups when backup media is low on space, so it's does not require maintenance afterwards (set once and forget).
Since Windows 8, there is this thingy called File History, which is a lot less intrusive for users (it's performance impact is barely noticeable), and it can clean up for itself (automatic history management based on age or free space). It's real back up solution, for real now :) But it also requires manual configuration for each user. Which is not acceptable, as users will forget to configure it.
My plan is to configure it per-user using some runonce logon script. I'm already doing some things via runonce-logon scripts, so installing such a script is not a problem. But I don't know how to configure File History via a script.
I tried some research, but the only official (there are some unofficial findings, see below screenshots) related tool I found is FhManagew.exe, which removes old file versions. And I need to set it up via GUI first. On the link, there is complete API, and seemingly File History can be configured via that API. But I assume this is API for .NET apps, so it can't be used via command line. Or can it?
Tried registry search, but file history location is not found anywhere :(
For specifics, let's say I want to set up file history to \\localhost\FileHistory$
and to keep file version until free space needed (by default it keeps forever). Here's how it should look:
For reference: backup to local share is workaround to make File History work on single HDD, otherwise it claims it's unsafe.
Here is what I found via comments: configuration is stored in xml files in %LocalAppData%\Microsoft\Windows\FileHistory\Configuration
. It's referenced in registry key HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\fhsvc\Parameters\Configs
with value name = path to configuration folder (as above), and value data = REG_DWORD:1
.
But simply adding these files and registry values and starting the service (there's script for it) does not start backups, neither configuration gets updated in Control Panel. However if File History is configured already, updates to xml files get reflected in control panel.
Solution 1:
Unfortunately the API is not very straightforward to use - most of the relevant calls are to a COM object that doesn't implement IDispatch
. It would therefore be exceedingly difficult and messy to call these functions from PowerShell. These are the calls we need to make:
-
CoCreateInstance
to get theFhConfigMgr
object -
IFhConfigMgr::CreateDefaultConfiguration
to create the configuration or replace an old one -
IFhConfigMgr::ProvisionAndSetNewTarget
to set the backup target folder -
IFhConfigMgr::SetLocalPolicy
to set the policy to only delete when space is needed -
IFhConfigMgr::SetBackupStatus
to turn on File History -
IFhConfigMgr::SaveConfiguration
to commit the changes -
FhServiceOpenPipe
to get a handle to the File History service -
FhServiceReloadConfiguration
to tell the service to reload its settings -
FhServiceClosePipe
to close the handle to the service
To call all these functions, we can use my open-source application SprintDLL. I wrote this script for you, commented with faux-C++ equivalents of each section:
// CoCreateInstance(CLSID_FhConfigMgr, NULL, CLSCTX_INPROC_SERVER, IID_IFhConfigMgr, &fh)
newslot native fhPtr
call ole32.dll!CoCreateInstance /return uint (blockptr(guid {ED43BB3C-09E9-498a-9DF6-2177244C6DB4}), nullptr, int 1, blockptr(guid {6A5FEA5B-BF8F-4EE5-B8C3-44D8A0D7331C}), slotptr fhPtr)
newslot native fh
copyslot fh = fhPtr dereferenced
newslot block vtbl = nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
copyslot vtbl = fh dereferenced
// fh->CreateDefaultConfiguration(TRUE)
newslot native createDefaultConfiguration
copyslot createDefaultConfiguration = vtbl field 4
call funcat createDefaultConfiguration /call thiscall /return uint (slotdata fhPtr, int 1)
// fh->ProvisionAndSetNewTarget("\\localhost\FileHistory$\", "Local Disk")
newslot native provisionAndSetNewTarget
copyslot provisionAndSetNewTarget = vtbl field 14
call funcat provisionAndSetNewTarget /call thiscall /return uint (slotdata fhPtr, bstr "\\\\localhost\\FileHistory$\\", bstr "Local Disk")
// fh->SetLocalPolicy(FH_RETENTION_TYPE, FH_RETENTION_UNLIMITED)
newslot native setLocalPolicy
copyslot setLocalPolicy = vtbl field 9
call funcat setLocalPolicy /call thiscall /return uint (slotdata fhPtr, int 1, int 1)
// fh->SetBackupStatus(FH_STATUS_ENABLED)
newslot native setBackupStatus
copyslot setBackupStatus = vtbl field 11
call funcat setBackupStatus /call thiscall /return uint (slotdata fhPtr, int 2)
// fh->SaveConfiguration()
newslot native saveConfiguration
copyslot saveConfiguration = vtbl field 5
call funcat saveConfiguration /call thiscall /return uint (slotdata fhPtr)
// FhServiceOpenPipe(TRUE, &fhPipe)
newslot native fhPipe
call fhsvcctl.dll!FhServiceOpenPipe /return int (int 1, slotptr fhPipe)
// FhServiceReloadConfiguration(fhPipe)
call fhsvcctl.dll!FhServiceReloadConfiguration /return int (slotdata fhPipe)
// FhServiceClosePipe(fhPipe)
call fhsvcctl.dll!FhServiceClosePipe /return int (slotdata fhPipe)
I got the VTable field IDs from the positions of the functions in the C-style COM interface for the File History manager COM object. If the script succeeds, it will say that every function returned zero.
To use the script, save it to a file, e.g. filehistory.sprint
. You can then run it from a batch file like this:
sprintdll run filehistory.sprint
If deploying across the network, you might want to include a command that copies the utility and the script onto the local machine first.