1
0
mirror of https://github.com/chylex/Backup-Essentials.git synced 2025-07-27 18:59:09 +02:00

Added the backup logic w/taskbar and a placeholder window

This commit is contained in:
chylex 2015-04-05 23:49:19 +02:00
parent 9913d51b38
commit 83815ecd27
6 changed files with 248 additions and 4 deletions

View File

@ -1,4 +1,5 @@
using BackupEssentials.Utils;
using BackupEssentials.Backup;
using BackupEssentials.Utils;
using System;
using System.Diagnostics;
using System.IO;
@ -16,7 +17,7 @@ namespace BackupEssentials{
ProgramArgsParser parser = new ProgramArgsParser(args.Args);
if (parser.HasFlag("runshell")){
new BackupWindow(new BackupRunner(parser.GetValue("src",null),parser.GetValue("dest",null))).Show();
}
else{
Process[] running = Process.GetProcessesByName(Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location));
@ -45,8 +46,7 @@ namespace BackupEssentials{
}
}
MainWindow window = new MainWindow();
window.Show();
new MainWindow().Show();
}
}

View File

@ -0,0 +1,192 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Windows.Shell;
using System.Linq;
using BackupEssentials.Utils;
namespace BackupEssentials.Backup{
public class BackupRunner{
private BackgroundWorker Worker;
private Tuple<string,string> WorkerData;
private TaskbarItemInfo Taskbar;
private int ActionCount;
public BackupRunner(string source, string destFolder){
WorkerData = new Tuple<string,string>(source,destFolder);
}
public void Start(TaskbarItemInfo taskbar){
if (Worker != null)return;
this.Taskbar = taskbar;
Taskbar.ProgressState = TaskbarItemProgressState.Indeterminate;
Worker = new BackgroundWorker();
Worker.WorkerReportsProgress = true;
Worker.WorkerSupportsCancellation = true;
Worker.ProgressChanged += new ProgressChangedEventHandler(WorkerProgressUpdate);
Worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(WorkerCompleted);
Worker.DoWork += new DoWorkEventHandler(WorkerDoWork);
Worker.RunWorkerAsync(WorkerData);
}
private void WorkerProgressUpdate(object sender, ProgressChangedEventArgs e){
Taskbar.ProgressState = TaskbarItemProgressState.Normal;
Taskbar.ProgressValue = e.ProgressPercentage/100D;
if (e.ProgressPercentage == 0 && e.UserState is int)ActionCount = (int)e.UserState;
}
private void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e){
if (e.Error != null){
Debug.WriteLine(e.Error.ToString());
return;
}
}
private void WorkerDoWork(object sender, DoWorkEventArgs e){
BackgroundWorker worker = (BackgroundWorker)sender;
Tuple<string,string> data = (Tuple<string,string>)e.Argument;
string src = data.Item1;
string destFolder = data.Item2;
// Prepare the destination
if (!Directory.Exists(destFolder))Directory.CreateDirectory(destFolder);
// Figure out the file and directory lists
Dictionary<string,IOEntry> srcEntries = new Dictionary<string,IOEntry>(), dstEntries = new Dictionary<string,IOEntry>();
if (File.GetAttributes(src).HasFlag(FileAttributes.Directory)){
int srcLen = src.Length;
foreach(string dir in Directory.GetDirectories(src,"*",SearchOption.AllDirectories))srcEntries.Add(dir.Remove(0,srcLen),new IOEntry(){ Type = IOType.Directory, AbsolutePath = dir });
foreach(string file in Directory.GetFiles(src,"*.*",SearchOption.AllDirectories))srcEntries.Add(file.Remove(0,srcLen),new IOEntry(){ Type = IOType.File, AbsolutePath = file });
}
else srcEntries.Add(Path.GetFileName(src),new IOEntry(){ Type = IOType.File, AbsolutePath = src });
int destFolderLen = destFolder.Length;
foreach(string dir in Directory.GetDirectories(destFolder,"*",SearchOption.AllDirectories))dstEntries.Add(dir.Remove(0,destFolderLen),new IOEntry(){ Type = IOType.Directory, AbsolutePath = dir });
foreach(string file in Directory.GetFiles(destFolder,"*.*",SearchOption.AllDirectories))dstEntries.Add(file.Remove(0,destFolderLen),new IOEntry(){ Type = IOType.File, AbsolutePath = file });
// Generate the IO actions
List<IOActionEntry> actions = new List<IOActionEntry>();
KeyEqualityComparer<string,IOEntry> keyComparer = new KeyEqualityComparer<string,IOEntry>();
IEnumerable<KeyValuePair<string,IOEntry>> ioDeleted = dstEntries.Except(srcEntries,keyComparer);
IEnumerable<KeyValuePair<string,IOEntry>> ioAdded = srcEntries.Except(dstEntries,keyComparer);
IEnumerable<string> ioIntersecting = srcEntries.Keys.Intersect(dstEntries.Keys);
foreach(KeyValuePair<string,IOEntry> deleted in ioDeleted){
actions.Add(new IOActionEntry(){ Type = deleted.Value.Type, Action = IOAction.Delete, RelativePath = deleted.Key });
}
foreach(KeyValuePair<string,IOEntry> added in ioAdded){
actions.Add(new IOActionEntry(){ Type = added.Value.Type, Action = IOAction.Create, RelativePath = added.Key });
}
foreach(string intersecting in ioIntersecting){
IOEntry srcEntry = srcEntries[intersecting];
if (srcEntry.Type == IOType.Directory)continue;
FileInfo srcFileInfo = new FileInfo(srcEntry.AbsolutePath);
FileInfo dstFileInfo = new FileInfo(dstEntries[intersecting].AbsolutePath);
if (srcFileInfo.Length == dstFileInfo.Length && srcFileInfo.LastWriteTime == dstFileInfo.LastWriteTime)continue;
actions.Add(new IOActionEntry(){ Type = IOType.File, Action = IOAction.Replace, RelativePath = intersecting });
}
actions.Sort((entry1, entry2) => {
if (entry1.Type == IOType.Directory && entry2.Type == IOType.File)return -1;
else if (entry2.Type == IOType.Directory && entry1.Type == IOType.File)return 1;
else return 0;
});
// Report a state update
worker.ReportProgress(0,actions.Count);
// Start working
List<int> indexesToRemove = new List<int>();
int totalActions = actions.Count, attempts = 10;
string path;
while(actions.Count > 0 && --attempts > 0){
for(int index = 0; index < actions.Count; index++){
IOActionEntry entry = actions[index];
try{
if (entry.Action == IOAction.Delete){
path = Path.Combine(destFolder,entry.RelativePath);
if (entry.Type == IOType.File){
if (File.Exists(path))File.Delete(path);
}
else if (entry.Type == IOType.Directory){
if (Directory.Exists(path))Directory.Delete(path,true);
}
}
else if (entry.Action == IOAction.Create){
path = Path.Combine(destFolder,entry.RelativePath);
if (entry.Type == IOType.File)File.Copy(Path.Combine(src,entry.RelativePath),path,false);
else if (entry.Type == IOType.Directory){
if (!Directory.Exists(path))Directory.CreateDirectory(path);
}
}
else if (entry.Action == IOAction.Replace){
File.Copy(Path.Combine(src,entry.RelativePath),Path.Combine(destFolder,entry.RelativePath),true);
}
indexesToRemove.Add(index-indexesToRemove.Count); // goes from 0 to actions.Count, removing each index will move the structure
Debug.WriteLine("Finished: "+entry.ToString());
worker.ReportProgress((int)Math.Ceiling(((totalActions-actions.Count+indexesToRemove.Count)*100D)/totalActions));
}catch(Exception exception){ // if an action failed, it will not be removed
Debug.WriteLine("Failed: "+entry.ToString());
Debug.WriteLine(exception.Message);
// TODO handle special exceptions (security etc)
}
}
foreach(int index in indexesToRemove)actions.RemoveAt(index);
indexesToRemove.Clear();
}
if (attempts == 0)throw new Exception("Ran out of attempts.");
}
private enum IOType{
None, File, Directory
}
private enum IOAction{
None, Create, Replace, Delete
}
private class IOEntry{
public IOType Type;
public string AbsolutePath;
public override string ToString(){
return "{ Type: "+Type.ToString()+", AbsolutePath: "+(AbsolutePath == null ? "<null>" : AbsolutePath.ToString())+" }";
}
}
private class IOActionEntry{
private string _relativePath;
public IOType Type;
public IOAction Action;
public string RelativePath { get { return _relativePath; } set { _relativePath = value; if (_relativePath.StartsWith("\\"))_relativePath = _relativePath.Substring(1); } }
public override string ToString(){
return "{ Type: "+Type.ToString()+", Action: "+Action.ToString()+", RelativePath: "+(RelativePath == null ? "<null>" : RelativePath.ToString())+" }";
}
}
}
}

View File

@ -79,7 +79,11 @@
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="AppPageManager.cs" />
<Compile Include="BackupWindow.xaml.cs">
<DependentUpon>BackupWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Backup\BackupLocation.cs" />
<Compile Include="Backup\BackupRunner.cs" />
<Compile Include="Backup\DataStorage.cs" />
<Compile Include="Backup\ExplorerIntegration.cs" />
<Compile Include="Pages\About.xaml.cs">
@ -96,11 +100,16 @@
<Compile Include="TestingWindow.xaml.cs">
<DependentUpon>TestingWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Utils\KeyEqualityComparer.cs" />
<Compile Include="Utils\SafeDictionary.cs" />
<Compile Include="Utils\StringDictionarySerializer.cs" />
<Compile Include="Utils\ProgramArgsParser.cs" />
<Compile Include="Utils\ScheduledUpdate.cs" />
<Compile Include="Utils\WindowsVersion.cs" />
<Page Include="BackupWindow.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\About.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>

View File

@ -0,0 +1,19 @@
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:custom="clr-namespace:BackupEssentials.Controls"
xmlns:Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
mc:Ignorable="d" x:Class="BackupEssentials.BackupWindow"
Title="Backup in progress..." Height="240" Width="640" WindowStartupLocation="CenterScreen" WindowStyle="None" ResizeMode="NoResize" Background="#FF000000" AllowsTransparency="True">
<Window.TaskbarItemInfo>
<TaskbarItemInfo/>
</Window.TaskbarItemInfo>
<Grid Background="#FF171717">
<Grid Background="#FF2E2E2E" Grid.Row="1" Margin="2">
</Grid>
</Grid>
</Window>

View File

@ -0,0 +1,11 @@
using BackupEssentials.Backup;
using System.Windows;
namespace BackupEssentials{
public partial class BackupWindow : Window{
public BackupWindow(BackupRunner runner){
InitializeComponent();
runner.Start(TaskbarItemInfo);
}
}
}

View File

@ -0,0 +1,13 @@
using System.Collections.Generic;
namespace BackupEssentials.Utils{
class KeyEqualityComparer<K,V> : IEqualityComparer<KeyValuePair<K,V>>{
bool IEqualityComparer<KeyValuePair<K,V>>.Equals(KeyValuePair<K,V> kvp1, KeyValuePair<K,V> kvp2){
return kvp1.Key.Equals(kvp2.Key);
}
int IEqualityComparer<KeyValuePair<K,V>>.GetHashCode(KeyValuePair<K,V> kvp){
return kvp.Key.GetHashCode();
}
}
}