1
0
mirror of https://github.com/chylex/TweetDuck.git synced 2025-09-14 19:32:10 +02:00

Compare commits

...

18 Commits
0.9.1 ... 1.0

Author SHA1 Message Date
7de79786c9 Disable start menu icons (handled by installer) and delete shortcuts that cannot be moved 2016-04-14 18:11:43 +02:00
d3d1fc808e Update assembly description 2016-04-14 18:09:20 +02:00
1ab9e35c6e Update version to 1.0 2016-04-14 17:27:08 +02:00
e6e4d0ba75 Change migration link generator to not delete existing links 2016-04-14 14:10:25 +02:00
147c268ef8 Add exception handling and logging 2016-04-14 13:13:38 +02:00
ebd17e1544 Work on migration (registry cleanup, refactoring) 2016-04-14 13:13:18 +02:00
039233c782 Fix browser opening when clicking on list urls, and change == to === 2016-04-14 12:37:42 +02:00
57484c8bf7 Add uninstall prompt since public beta did not uninstall TweetDeck correctly 2016-04-14 12:36:35 +02:00
74fec18146 Cleanup resources from the uninstaller process 2016-04-14 02:41:44 +02:00
00341e984c Fix TweetDeck uninstaller 2016-04-14 02:40:58 +02:00
c172e7aa3d Protect lnk replacement if the target filename already exists 2016-04-13 20:55:54 +02:00
1030555bb5 Add start menu lnk migration 2016-04-13 15:52:23 +02:00
c53636c013 Disable moving the notification window if not in Settings menu & having Custom pos enabled 2016-04-13 11:22:45 +02:00
cf450447e5 Fix support for old config files 2016-04-13 11:11:53 +02:00
81c29ab50c Fix some issues with multi-monitor setups (change default loc, fix config saving before window loaded) 2016-04-13 02:33:21 +02:00
f99435ff6a Add a null check to FlatProgressBar.brush 2016-04-13 02:24:24 +02:00
39f554693d Address static code analysis issues (disposing objects, neutral language and CLSCompliant attribute) 2016-04-12 23:33:37 +02:00
38539c4a9b Add a post-build batch file to delete unwanted files from the release folders 2016-04-12 18:07:37 +02:00
12 changed files with 173 additions and 48 deletions

View File

@@ -3,6 +3,7 @@ using System.Drawing;
using System.IO; using System.IO;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary; using System.Runtime.Serialization.Formatters.Binary;
using System.Windows.Forms;
using TweetDick.Core.Handling; using TweetDick.Core.Handling;
namespace TweetDick.Configuration{ namespace TweetDick.Configuration{
@@ -13,6 +14,7 @@ namespace TweetDick.Configuration{
// START OF CONFIGURATION // START OF CONFIGURATION
public bool IgnoreMigration { get; set; } public bool IgnoreMigration { get; set; }
public bool IgnoreUninstallCheck { get; set; }
public bool IsMaximized { get; set; } public bool IsMaximized { get; set; }
public Point WindowLocation { get; set; } public Point WindowLocation { get; set; }
@@ -26,13 +28,13 @@ namespace TweetDick.Configuration{
public bool IsCustomWindowLocationSet{ public bool IsCustomWindowLocationSet{
get{ get{
return WindowLocation.X != 32000; return WindowLocation.X != -32000 && WindowLocation.X != 32000;
} }
} }
public bool IsCustomNotificationPositionSet{ public bool IsCustomNotificationPositionSet{
get{ get{
return CustomNotificationPosition.X != 32000; return CustomNotificationPosition.X != -32000 && CustomNotificationPosition.X != 32000;
} }
} }
@@ -45,10 +47,10 @@ namespace TweetDick.Configuration{
this.file = file; this.file = file;
IsMaximized = true; IsMaximized = true;
WindowLocation = new Point(32000,32000); WindowLocation = new Point(-32000,-32000);
NotificationDuration = TweetNotification.Duration.Medium; NotificationDuration = TweetNotification.Duration.Medium;
NotificationPosition = TweetNotification.Position.TopRight; NotificationPosition = TweetNotification.Position.TopRight;
CustomNotificationPosition = new Point(32000,32000); CustomNotificationPosition = new Point(-32000,-32000);
NotificationEdgeDistance = 8; NotificationEdgeDistance = 8;
} }
@@ -70,8 +72,8 @@ namespace TweetDick.Configuration{
} }
return true; return true;
}catch(Exception){ }catch(Exception e){
// TODO Program.HandleException("Could not save the configuration file.",e);
return false; return false;
} }
} }
@@ -89,8 +91,8 @@ namespace TweetDick.Configuration{
break; break;
}catch(FileNotFoundException){ }catch(FileNotFoundException){
}catch(Exception){ }catch(Exception e){
// TODO Program.HandleException("Could not open the configuration file.",e);
} }
} }

View File

@@ -10,6 +10,8 @@
/// </summary> /// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) { protected override void Dispose(bool disposing) {
if (brush != null)brush.Dispose();
if (disposing && (components != null)) { if (disposing && (components != null)) {
components.Dispose(); components.Dispose();
} }

View File

@@ -31,7 +31,7 @@
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Icon = TweetDick.Properties.Resources.icon; this.Icon = TweetDick.Properties.Resources.icon;
this.Location = new System.Drawing.Point(32000, 32000); this.Location = new System.Drawing.Point(-32000, -32000);
this.Name = "FormBrowser"; this.Name = "FormBrowser";
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
this.ResizeEnd += new System.EventHandler(this.FormBrowser_ResizeEnd); this.ResizeEnd += new System.EventHandler(this.FormBrowser_ResizeEnd);

View File

@@ -23,6 +23,7 @@ namespace TweetDick.Core{
private FormSettings currentFormSettings; private FormSettings currentFormSettings;
private FormAbout currentFormAbout; private FormAbout currentFormAbout;
private bool isLoaded;
public FormBrowser(){ public FormBrowser(){
InitializeComponent(); InitializeComponent();
@@ -38,7 +39,9 @@ namespace TweetDick.Core{
Controls.Add(browser); Controls.Add(browser);
notification = new FormNotification(this,bridge,true); Disposed += (sender, args) => browser.Dispose();
notification = new FormNotification(this,bridge,true){ CanMoveWindow = () => false };
notification.Show(); notification.Show();
} }
@@ -70,6 +73,8 @@ namespace TweetDick.Core{
Size = Screen.PrimaryScreen.WorkingArea.Size; Size = Screen.PrimaryScreen.WorkingArea.Size;
WindowState = FormWindowState.Maximized; WindowState = FormWindowState.Maximized;
} }
isLoaded = true;
} }
// active event handlers // active event handlers
@@ -92,12 +97,16 @@ namespace TweetDick.Core{
} }
private void FormBrowser_ResizeEnd(object sender, EventArgs e){ // also triggers when the window moves private void FormBrowser_ResizeEnd(object sender, EventArgs e){ // also triggers when the window moves
if (!isLoaded)return;
Config.WindowLocation = Location; Config.WindowLocation = Location;
Config.WindowSize = Size; Config.WindowSize = Size;
Config.Save(); Config.Save();
} }
private void FormBrowser_WindowStateChanged(object sender, EventArgs e){ private void FormBrowser_WindowStateChanged(object sender, EventArgs e){
if (!isLoaded)return;
Config.IsMaximized = WindowState != FormWindowState.Normal; Config.IsMaximized = WindowState != FormWindowState.Normal;
FormBrowser_ResizeEnd(sender,e); FormBrowser_ResizeEnd(sender,e);
} }

View File

@@ -65,7 +65,7 @@
this.Controls.Add(this.progressBarTimer); this.Controls.Add(this.progressBarTimer);
this.Controls.Add(this.panelBrowser); this.Controls.Add(this.panelBrowser);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
this.Location = new System.Drawing.Point(32000, 32000); this.Location = new System.Drawing.Point(-32000, -32000);
this.Name = "FormNotification"; this.Name = "FormNotification";
this.ShowInTaskbar = false; this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;

View File

@@ -10,6 +10,8 @@ using TweetDick.Resources;
namespace TweetDick.Core{ namespace TweetDick.Core{
sealed partial class FormNotification : Form{ sealed partial class FormNotification : Form{
public Func<bool> CanMoveWindow = () => true;
private readonly Form owner; private readonly Form owner;
private readonly ChromiumWebBrowser browser; private readonly ChromiumWebBrowser browser;
@@ -39,6 +41,8 @@ namespace TweetDick.Core{
} }
panelBrowser.Controls.Add(browser); panelBrowser.Controls.Add(browser);
Disposed += (sender, args) => browser.Dispose();
} }
public FormNotification(Form owner, bool autoHide) : this(owner,null,autoHide){} public FormNotification(Form owner, bool autoHide) : this(owner,null,autoHide){}
@@ -49,6 +53,14 @@ namespace TweetDick.Core{
} }
} }
protected override void WndProc(ref Message m){
if (m.Msg == 0x0112 && (m.WParam.ToInt32() & 0xFFF0) == 0xF010 && !CanMoveWindow()){ // WM_SYSCOMMAND, SC_MOVE
return;
}
base.WndProc(ref m);
}
public void ShowNotification(TweetNotification notification){ public void ShowNotification(TweetNotification notification){
MoveToVisibleLocation(); MoveToVisibleLocation();
@@ -76,7 +88,7 @@ namespace TweetDick.Core{
public void HideNotification(){ public void HideNotification(){
browser.LoadHtml("","about:blank"); browser.LoadHtml("","about:blank");
Location = new Point(32000,32000); Location = new Point(-32000,-32000);
TopMost = false; TopMost = false;
timerProgress.Stop(); timerProgress.Stop();
} }

View File

@@ -20,8 +20,7 @@ namespace TweetDick.Core.Other{
Text = Program.BrandName+" Settings"; Text = Program.BrandName+" Settings";
notification = new FormNotification(browserForm,false); notification = new FormNotification(browserForm,false){ CanMoveWindow = () => radioLocCustom.Checked };
notification.Show(this);
notification.Move += (sender, args) => { notification.Move += (sender, args) => {
if (radioLocCustom.Checked){ if (radioLocCustom.Checked){
@@ -29,6 +28,8 @@ namespace TweetDick.Core.Other{
} }
}; };
notification.Show(this);
switch(Config.NotificationPosition){ switch(Config.NotificationPosition){
case TweetNotification.Position.TopLeft: radioLocTL.Checked = true; break; case TweetNotification.Position.TopLeft: radioLocTL.Checked = true; break;
case TweetNotification.Position.TopRight: radioLocTR.Checked = true; break; case TweetNotification.Position.TopRight: radioLocTR.Checked = true; break;

View File

@@ -1,4 +1,6 @@
using System; using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
@@ -14,8 +16,11 @@ namespace TweetDick.Migration{
public static void Run(){ public static void Run(){
if (!Program.UserConfig.IgnoreMigration && Directory.Exists(TweetDeckPath)){ if (!Program.UserConfig.IgnoreMigration && Directory.Exists(TweetDeckPath)){
FormMigrationQuestion formQuestion = new FormMigrationQuestion(); MigrationDecision decision;
MigrationDecision decision = formQuestion.ShowDialog() == DialogResult.OK ? formQuestion.Decision : MigrationDecision.AskLater;
using(FormMigrationQuestion formQuestion = new FormMigrationQuestion()){
decision = formQuestion.ShowDialog() == DialogResult.OK ? formQuestion.Decision : MigrationDecision.AskLater;
}
switch(decision){ switch(decision){
case MigrationDecision.MigratePurge: case MigrationDecision.MigratePurge:
@@ -25,12 +30,13 @@ namespace TweetDick.Migration{
formWait.ShowWorkDialog(() => { formWait.ShowWorkDialog(() => {
if (!BeginMigration(decision,ex => formWait.Invoke(new Action(() => { if (!BeginMigration(decision,ex => formWait.Invoke(new Action(() => {
if (ex != null){
MessageBox.Show(ex.ToString()); // TODO
}
formWait.Close(); formWait.Close();
if (ex != null){
Program.HandleException("An unexpected exception has occurred during the migration process.",ex);
return;
}
Program.UserConfig.IgnoreMigration = true; Program.UserConfig.IgnoreMigration = true;
Program.UserConfig.Save(); Program.UserConfig.Save();
})))){ })))){
@@ -46,6 +52,17 @@ namespace TweetDick.Migration{
break; break;
} }
} }
else if (!Program.UserConfig.IgnoreUninstallCheck){
string guid = ProgramRegistrySearch.FindByDisplayName("TweetDeck");
if (guid != null && MessageBox.Show("TweetDeck is still installed on your computer, do you want to uninstall it?","Uninstall TweetDeck",MessageBoxButtons.YesNo,MessageBoxIcon.Question,MessageBoxDefaultButton.Button2) == DialogResult.Yes){
RunUninstaller(guid,0);
CleanupTweetDeck();
}
Program.UserConfig.IgnoreUninstallCheck = true;
Program.UserConfig.Save();
}
} }
private static bool BeginMigration(MigrationDecision decision, Action<Exception> onFinished){ private static bool BeginMigration(MigrationDecision decision, Action<Exception> onFinished){
@@ -98,14 +115,10 @@ namespace TweetDick.Migration{
} }
if (decision == MigrationDecision.MigratePurge){ if (decision == MigrationDecision.MigratePurge){
// update the lnk files wherever possible (desktop icons, pinned taskbar) // update the lnk files wherever possible (desktop icons, pinned taskbar, start menu)
string[] locations = { foreach(string location in GetLnkDirectories()){
Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), if (location == string.Empty)continue;
Environment.GetFolderPath(Environment.SpecialFolder.CommonDesktopDirectory),
Environment.ExpandEnvironmentVariables(@"%APPDATA%\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar")
};
foreach(string location in locations){
string linkFile = Path.Combine(location,"TweetDeck.lnk"); string linkFile = Path.Combine(location,"TweetDeck.lnk");
if (File.Exists(linkFile)){ if (File.Exists(linkFile)){
@@ -115,7 +128,18 @@ namespace TweetDick.Migration{
lnk.SetComment(Program.BrandName); // TODO add a tagline lnk.SetComment(Program.BrandName); // TODO add a tagline
lnk.Save(); lnk.Save();
File.Move(linkFile,Path.Combine(location,Program.BrandName+".lnk")); string renamed = Path.Combine(location,Program.BrandName+".lnk");
try{
if (!File.Exists(renamed)){
File.Move(linkFile,renamed);
}
else{
File.Delete(linkFile);
}
}catch{
// eh, too bad
}
} }
} }
@@ -125,13 +149,12 @@ namespace TweetDick.Migration{
string guid = ProgramRegistrySearch.FindByDisplayName("TweetDeck"); string guid = ProgramRegistrySearch.FindByDisplayName("TweetDeck");
if (guid != null){ if (guid != null){
Process uninstaller = Process.Start("msiexec.exe","/x"+guid+" /quiet /qn"); RunUninstaller(guid,5000);
if (uninstaller != null){
uninstaller.WaitForExit();
}
} }
// registry cleanup
CleanupTweetDeck();
// migration finished like a boss // migration finished like a boss
} }
}); });
@@ -149,5 +172,41 @@ namespace TweetDick.Migration{
}catch(DirectoryNotFoundException){ }catch(DirectoryNotFoundException){
} }
} }
private static IEnumerable<string> GetLnkDirectories(){
yield return Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
yield return Environment.GetFolderPath(Environment.SpecialFolder.CommonDesktopDirectory);
yield return Environment.ExpandEnvironmentVariables(@"%APPDATA%\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar");
/* already handled by the installer
string startMenu = Environment.GetFolderPath(Environment.SpecialFolder.StartMenu);
string[] sub = Directory.GetDirectories(startMenu);
if (sub.Length > 0){
yield return Path.Combine(startMenu,sub[0],"TweetDeck");
}
*/
}
private static void RunUninstaller(string guid, int timeout){
Process uninstaller = Process.Start("msiexec.exe","/x "+guid+" /quiet /qn");
if (uninstaller != null){
if (timeout > 0){
uninstaller.WaitForExit(timeout); // it appears that the process is restarted or something that triggers this, but it shouldn't be a problem
}
uninstaller.Close();
}
}
private static void CleanupTweetDeck(){
try{
Registry.CurrentUser.DeleteSubKeyTree(@"Software\Twitter\TweetDeck",true);
Registry.CurrentUser.DeleteSubKey(@"Software\Twitter"); // only if empty
}catch(Exception){
// not found or too bad
}
}
} }
} }

View File

@@ -1,13 +1,16 @@
using CefSharp; using CefSharp;
using System; using System;
using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDick.Configuration; using TweetDick.Configuration;
using TweetDick.Core; using TweetDick.Core;
using TweetDick.Migration; using TweetDick.Migration;
[assembly: CLSCompliant(true)]
namespace TweetDick{ namespace TweetDick{
static class Program{ static class Program{
#if DUCK #if DUCK
@@ -19,7 +22,7 @@ namespace TweetDick{
#endif #endif
public static readonly string StoragePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),BrandName); public static readonly string StoragePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),BrandName);
private static readonly LockManager LockManager; private static readonly LockManager LockManager = new LockManager(Path.Combine(StoragePath,".lock"));
public static UserConfig UserConfig { get; private set; } public static UserConfig UserConfig { get; private set; }
@@ -36,10 +39,6 @@ namespace TweetDick{
} }
} }
static Program(){
LockManager = new LockManager(Path.Combine(StoragePath,".lock"));
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto)] [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr LoadLibrary(string name); public static extern IntPtr LoadLibrary(string name);
@@ -82,6 +81,14 @@ namespace TweetDick{
#endif #endif
}); });
AppDomain.CurrentDomain.UnhandledException += (sender, args) => {
Exception ex = args.ExceptionObject as Exception;
if (ex != null){
HandleException("An unhandled exception has occurred.",ex);
}
};
Application.ApplicationExit += (sender, args) => { Application.ApplicationExit += (sender, args) => {
UserConfig.Save(); UserConfig.Save();
LockManager.Unlock(); LockManager.Unlock();
@@ -90,5 +97,30 @@ namespace TweetDick{
Application.Run(new FormBrowser()); Application.Run(new FormBrowser());
} }
public static void HandleException(string message, Exception e){
Log(e.ToString());
if (MessageBox.Show(message+"\r\nDo you want to open the log file to report the issue?",BrandName+" Has Failed :(",MessageBoxButtons.YesNo,MessageBoxIcon.Error,MessageBoxDefaultButton.Button2) == DialogResult.Yes){
Process.Start(Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"td-log.txt"));
}
}
public static void Log(string data){
StringBuilder build = new StringBuilder();
if (!File.Exists("td-log.txt")){
build.Append("Please, report all issues to: https://github.com/chylex/TweetDick/issues\r\n\r\n");
}
build.Append("[").Append(DateTime.Now.ToString("G")).Append("]\r\n");
build.Append(data).Append("\r\n\r\n");
try{
File.AppendAllText("td-log.txt",build.ToString(),Encoding.UTF8);
}catch{
// oops
}
}
} }
} }

View File

@@ -1,12 +1,13 @@
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using TweetDick; using TweetDick;
using System.Resources;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
// associated with an assembly. // associated with an assembly.
[assembly: AssemblyTitle(Program.BrandName)] [assembly: AssemblyTitle("TweetDeck client for Windows")]
[assembly: AssemblyDescription("")] [assembly: AssemblyDescription("TweetDeck client for Windows")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("")]
[assembly: AssemblyProduct(Program.BrandName)] [assembly: AssemblyProduct(Program.BrandName)]
@@ -32,5 +33,7 @@ using TweetDick;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.9.1.0")] [assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("0.9.1.0")] [assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: NeutralResourcesLanguageAttribute("en")]

View File

@@ -22,7 +22,7 @@
$("[data-action='settings-menu']").click(function(){ $("[data-action='settings-menu']").click(function(){
setTimeout(function(){ setTimeout(function(){
var menu = $(".js-dropdown-content").children("ul").first(); var menu = $(".js-dropdown-content").children("ul").first();
if (menu.length == 0)return; if (menu.length === 0)return;
menu.children(".drp-h-divider").last().after('<li class="is-selectable" data-std><a href="#" data-action>'+$TD.brandName+'</a></li><li class="drp-h-divider"></li>'); menu.children(".drp-h-divider").last().after('<li class="is-selectable" data-std><a href="#" data-action>'+$TD.brandName+'</a></li><li class="drp-h-divider"></li>');
@@ -83,7 +83,7 @@
mid = mid.children(".js-column-scroller").first(); mid = mid.children(".js-column-scroller").first();
var container = mid.children(".js-chirp-container").first(); var container = mid.children(".js-chirp-container").first();
if (container.length == 0)return; if (container.length === 0)return;
var scroller = container.parent(); var scroller = container.parent();
@@ -100,7 +100,7 @@
Array.prototype.forEach.call(mutations,function(mutation){ Array.prototype.forEach.call(mutations,function(mutation){
Array.prototype.forEach.call(mutation.addedNodes,function(node){ Array.prototype.forEach.call(mutation.addedNodes,function(node){
if (node.tagName != "ARTICLE")return; if (node.tagName !== "ARTICLE")return;
onNewTweet(column,node); onNewTweet(column,node);
}); });
@@ -213,8 +213,9 @@
if (urlWait)return; if (urlWait)return;
var me = $(this); var me = $(this);
var rel = me.attr("rel");
if (!me.is(".link-complex") && !(me.attr("rel") == "mediaPreview" && me.closest("#open-modal").length == 0)){ if (!me.is(".link-complex") && !(rel === "mediaPreview" && me.closest("#open-modal").length === 0) && rel !== "list"){
$TD.openBrowser(me.attr("href")); $TD.openBrowser(me.attr("href"));
} }

4
_postbuild.bat Normal file
View File

@@ -0,0 +1,4 @@
del "bin\x86\Release Dick\*.xml"
del "bin\x86\Release Dick\devtools_resources.pak"
del "bin\x86\Release Duck\*.xml"
del "bin\x86\Release Duck\devtools_resources.pak"