Using Microsoft SyncToy through PowerShell

4745206997_ac577b3248_m
This post is about running Microsoft SyncToy via PowerShell. For those that don’t know SyncToy:

SyncToy 2.1 is a free application that synchronizes files and folders between locations. Typical uses include sharing files, such as photos, with other computers and creating backup copies of files and folders.

SyncToy has been around already since good old Windows XP times and even though there are alternative freeware applications it’s still one of my favorite tools for the job.
While SyncToy already comes with a commandline version out of the box, it’s lacking quite some features as compared to the graphical user interface:

  • No option to preview the sync operation
  • No progress indication
  • No option to exclude subfolders
  • No option to exclude files by attributes (e.g. hidden, system)
  • No option to specify recursion
  • No option to specify action for overwritten files

Googling around for solutions I came across two related posts on codeproject.com:

Following translating the suggest approach to PowerShell it seemed to be quite easy to accomplish what I wanted utilizing the SyncToyEngine.dll .NET assembly that comes with the SyncToy installation. Considering that I would have setup already a folder pairing called ‘Test’ using the GUI, the following code should initiate the sync operation (it’s important to use the correct version of PowerShell to test this (i.e. SyncToy(x64) needs to be run via PowerShell x64):

$syncToyEnginePath = Resolve-Path 'c:\Program Files*\SyncToy 2.1\SyncToyEngine.dll'
#load the dll
Add-Type -Path $syncToyEnginePath

#retrieve the sync engine configuration
$syncToyEngineConfigPath = "$env:LOCALAPPDATA\Microsoft\SyncToy\2.0\SyncToyDirPairs.bin"
$bf = New-Object Runtime.Serialization.Formatters.Binary.BinaryFormatter 
$sr = New-Object IO.StreamReader($syncToyEngineConfigPath)
do{
    $seConfig = [SyncToy.SyncEngineConfig]$bf.Deserialize($sr.BaseStream)
    if ($seConfig.Name -eq 'Test'){
        $engineConfig = $seConfig
        break
    }
}
while($sr.BaseStream.Position -lt $sr.BaseStream.Length)
$sr.Close()
$sr.Dispose()

#invoke the sync
$syncEngine = New-Object SyncToy.SyncEngine($engineConfig)
$syncEngine.Sync()

But unfortunately the last line causes PowerShell to hang. After multiple unsuccessful attempts to work around this (also implementing the same as a C# PowerShell cmdlet). I ended up writing a C# executable that only takes care of the synchronization and preview part, since I wanted to keep as much as possible of the code in PowerShell. The end result is a PowerShell module ‘SyncToy.psm1’ providing three functions:

Name Description
Get-SyncConfig To retrieve an existing sync configuration (FolderPair) either setup via Set-SyncConfig or GUI
Set-SyncConfig To configure a new Sync Configuration (FolderPair). Those can be stored into the default configuration that the GUI uses (default behaviour) or into a custom path
Invoke-Sync To preview a sync operation or to run the actual sync operation showing results and a progress bar

Let’s have a look at an example usage. Setting up two folders and a sync between the two. The below code is part of the module (Test-SyncToy), and can be downloaded via GitHub:

#Import the module
Import-Module $PSScriptRoot\SyncToy.psm1
#create folders for testing
#leftDir containing some content
$leftDir = "$env:TEMP\Left"
mkdir $leftDir | Out-Null
foreach ($num in 1..30){
    mkdir "$leftDir\test$num" | Out-Null
    foreach ($num2 in 1..10){
        $extension = '.txt'
        if ($num2 % 2){
            $extension = '.ps1'
        }
        "Test $num2" | Set-Content -Path ("$leftDir\test$num\test$num2" + $extension)
    }
}
#rightDir as the initial destination
$rightDir = "$env:TEMP\Right"
mkdir $rightDir | Out-Null

#exclude test10-test29 sub-folders from sync
$excludeFolders = (dir "$leftDir\test[1-2][0-9]" -Directory).FullName 

#setup the sync configuration
Set-SyncConfig -folderPairName 'Test' -leftDir $leftDir -rightDir $rightDir -syncMode Synchronize `
    -includedFilesPattern '*.ps1' -excludedSubFolders $excludeFolders 

#preview the sync
$previewResults = Invoke-Sync -folderPairName 'Test' -previewOnly
$previewResults
$previewResults.Action
#run the snyc
$results = Invoke-Sync -folderPairName 'Test' 
$results

Please let me know if you have any further suggestions, questions or comments about the module.
shareThoughts


Photo Credit: Tatters ❀ via Compfight cc

Advertisements

4 thoughts on “Using Microsoft SyncToy through PowerShell

  1. Env. variables are fine in a shell, but I tend to avoid them in my scripts. Consider replacing them with .Net methods.

    They have built-in sanity checks and generally more bulletproof. For example [System.IO.Path]::GetTempPath() will call GetTempPath from kernel32.dll which will check all env. variables for you and return the first valid one or default path in Windows directory. More details: https://msdn.microsoft.com/en-us/library/aa364992(VS.85).aspx

    The same goes for special folders: you can use [Environment]::GetFolderPath(‘LocalApplicationData’) instead of $env:LOCALAPPDATA .

    Like

    • Hi beatcracker,
      thanks for your comment. I prefer to keep things as simple as possible though and use native PowerShell unless speed of execution or functionality (reading the documentation and comments it seems to me that GetTempPath is not flawless either) require another means. By now I never encountered any problems using $env, do you have a real world example where $env failed?
      Dirk

      Like

      • Yep, GetTempPath isn’t perfect (but who is?), one still have to do some checks to determine if path is valid and writable. What I’m trying to say that using $env is fine controlled environments, but in the wild, weird stuff happens.

        Regarding real world examples of failed $env: I’ve personally encountered empty\misconfigured TMP/TEMP variables. Usually it’s not noticeable because most of the software uses GetTempPath and falls back to alternative paths, but sometimes it breaks the old programs\scripts with hardcoded env. variables names. Not that it happens often, but I’ve seen it.

        Like

I'd love to hear what you think

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s