mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-09-14 19:32:10 +02:00
Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
04cd662d78 | |||
da597f076f | |||
fab3efdcf5 | |||
a55509a34d | |||
84fb1c5b2b | |||
391a90e1df | |||
e0fe39195d | |||
385fead81a | |||
648d1b9aa9 | |||
3f0028913d | |||
45e6ec8b0f | |||
a3fbaa0b34 | |||
7102cbfb3b | |||
cb61dc742f | |||
cd53f6e757 | |||
c64f7daa8d | |||
e70d792654 | |||
9ae533f907 | |||
cfe92f18e3 | |||
e2a34ea28e | |||
ec8000360e | |||
57b0821e19 |
@@ -5,6 +5,10 @@ using System.Threading;
|
|||||||
|
|
||||||
namespace TweetDck.Configuration{
|
namespace TweetDck.Configuration{
|
||||||
sealed class LockManager{
|
sealed class LockManager{
|
||||||
|
public enum Result{
|
||||||
|
Success, HasProcess, Fail
|
||||||
|
}
|
||||||
|
|
||||||
public Process LockingProcess { get; private set; }
|
public Process LockingProcess { get; private set; }
|
||||||
|
|
||||||
private readonly string file;
|
private readonly string file;
|
||||||
@@ -14,87 +18,97 @@ namespace TweetDck.Configuration{
|
|||||||
this.file = file;
|
this.file = file;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool CreateLockFile(){
|
private void CreateLockFileStream(){
|
||||||
|
lockStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read);
|
||||||
|
WriteIntToStream(lockStream, GetCurrentProcessId());
|
||||||
|
lockStream.Flush(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ReleaseLockFileStream(){
|
||||||
|
if (lockStream != null){
|
||||||
|
lockStream.Dispose();
|
||||||
|
lockStream = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result TryCreateLockFile(){
|
||||||
if (lockStream != null){
|
if (lockStream != null){
|
||||||
throw new InvalidOperationException("Lock file already exists.");
|
throw new InvalidOperationException("Lock file already exists.");
|
||||||
}
|
}
|
||||||
|
|
||||||
try{
|
try{
|
||||||
lockStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read);
|
CreateLockFileStream();
|
||||||
|
return Result.Success;
|
||||||
byte[] id = BitConverter.GetBytes(Process.GetCurrentProcess().Id);
|
}catch(DirectoryNotFoundException){
|
||||||
lockStream.Write(id, 0, id.Length);
|
try{
|
||||||
lockStream.Flush();
|
CreateLockFileStream();
|
||||||
|
return Result.Success;
|
||||||
if (LockingProcess != null){
|
}catch{
|
||||||
LockingProcess.Close();
|
ReleaseLockFileStream();
|
||||||
LockingProcess = null;
|
return Result.Fail;
|
||||||
}
|
}
|
||||||
|
}catch(IOException){
|
||||||
return true;
|
return Result.HasProcess;
|
||||||
}catch(Exception){
|
}catch{
|
||||||
if (lockStream != null){
|
ReleaseLockFileStream();
|
||||||
lockStream.Close();
|
return Result.Fail;
|
||||||
lockStream.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Lock(){
|
public Result Lock(){
|
||||||
if (lockStream != null)return true;
|
if (lockStream != null){
|
||||||
|
return Result.Success;
|
||||||
try{
|
|
||||||
byte[] bytes = new byte[4];
|
|
||||||
|
|
||||||
using(FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){
|
|
||||||
fileStream.Read(bytes, 0, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
int pid = BitConverter.ToInt32(bytes, 0);
|
|
||||||
|
|
||||||
try{
|
|
||||||
Process foundProcess = Process.GetProcessById(pid);
|
|
||||||
|
|
||||||
using(Process currentProcess = Process.GetCurrentProcess()){
|
|
||||||
if (foundProcess.ProcessName == currentProcess.ProcessName || foundProcess.MainWindowTitle == Program.BrandName){
|
|
||||||
LockingProcess = foundProcess;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}catch(ArgumentException){}
|
|
||||||
|
|
||||||
return LockingProcess == null && CreateLockFile();
|
|
||||||
}catch(DirectoryNotFoundException){
|
|
||||||
string dir = Path.GetDirectoryName(file);
|
|
||||||
|
|
||||||
if (dir != null){
|
|
||||||
Directory.CreateDirectory(dir);
|
|
||||||
return CreateLockFile();
|
|
||||||
}
|
|
||||||
}catch(FileNotFoundException){
|
|
||||||
return CreateLockFile();
|
|
||||||
}catch(Exception){
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
Result initialResult = TryCreateLockFile();
|
||||||
|
|
||||||
|
if (initialResult == Result.HasProcess){
|
||||||
|
try{
|
||||||
|
int pid;
|
||||||
|
|
||||||
|
using(FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){
|
||||||
|
pid = ReadIntFromStream(fileStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
try{
|
||||||
|
Process foundProcess = Process.GetProcessById(pid);
|
||||||
|
|
||||||
|
using(Process currentProcess = Process.GetCurrentProcess()){
|
||||||
|
if (foundProcess.MainModule.FileVersionInfo.InternalName == currentProcess.MainModule.FileVersionInfo.InternalName){
|
||||||
|
LockingProcess = foundProcess;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
foundProcess.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch{
|
||||||
|
// GetProcessById throws ArgumentException if the process is missing
|
||||||
|
// Process.MainModule can throw exceptions in some cases
|
||||||
|
}
|
||||||
|
|
||||||
|
return LockingProcess == null ? Result.Fail : Result.HasProcess;
|
||||||
|
}catch{
|
||||||
|
return Result.Fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return initialResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Unlock(){
|
public bool Unlock(){
|
||||||
bool result = true;
|
bool result = true;
|
||||||
|
|
||||||
if (lockStream != null){
|
if (ReleaseLockFileStream()){
|
||||||
lockStream.Dispose();
|
|
||||||
|
|
||||||
try{
|
try{
|
||||||
File.Delete(file);
|
File.Delete(file);
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
Program.Reporter.Log(e.ToString());
|
Program.Reporter.Log(e.ToString());
|
||||||
result = false;
|
result = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
lockStream = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -104,11 +118,9 @@ namespace TweetDck.Configuration{
|
|||||||
if (LockingProcess != null){
|
if (LockingProcess != null){
|
||||||
LockingProcess.CloseMainWindow();
|
LockingProcess.CloseMainWindow();
|
||||||
|
|
||||||
for(int waited = 0; waited < timeout && !LockingProcess.HasExited;){
|
for(int waited = 0; waited < timeout && !LockingProcess.HasExited; waited += 250){
|
||||||
LockingProcess.Refresh();
|
LockingProcess.Refresh();
|
||||||
|
Thread.Sleep(250);
|
||||||
Thread.Sleep(100);
|
|
||||||
waited += 100;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LockingProcess.HasExited){
|
if (LockingProcess.HasExited){
|
||||||
@@ -120,5 +132,24 @@ namespace TweetDck.Configuration{
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Utility functions
|
||||||
|
|
||||||
|
private static void WriteIntToStream(Stream stream, int value){
|
||||||
|
byte[] id = BitConverter.GetBytes(value);
|
||||||
|
stream.Write(id, 0, id.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int ReadIntFromStream(Stream stream){
|
||||||
|
byte[] bytes = new byte[4];
|
||||||
|
stream.Read(bytes, 0, 4);
|
||||||
|
return BitConverter.ToInt32(bytes, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetCurrentProcessId(){
|
||||||
|
using(Process process = Process.GetCurrentProcess()){
|
||||||
|
return process.Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Globalization;
|
|
||||||
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;
|
||||||
@@ -12,17 +11,12 @@ using TweetDck.Plugins;
|
|||||||
namespace TweetDck.Configuration{
|
namespace TweetDck.Configuration{
|
||||||
[Serializable]
|
[Serializable]
|
||||||
sealed class UserConfig{
|
sealed class UserConfig{
|
||||||
private static readonly IFormatter Formatter = new BinaryFormatter{
|
private static readonly IFormatter Formatter = new BinaryFormatter();
|
||||||
Binder = new SerializationCompatibilityHandler()
|
|
||||||
};
|
|
||||||
|
|
||||||
private const int CurrentFileVersion = 5;
|
private const int CurrentFileVersion = 5;
|
||||||
|
|
||||||
// START OF CONFIGURATION
|
// START OF CONFIGURATION
|
||||||
|
|
||||||
public bool IgnoreMigration { get; set; }
|
|
||||||
public bool IgnoreUninstallCheck { get; set; }
|
|
||||||
|
|
||||||
public WindowState BrowserWindow { get; set; }
|
public WindowState BrowserWindow { get; set; }
|
||||||
public bool DisplayNotificationTimer { get; set; }
|
public bool DisplayNotificationTimer { get; set; }
|
||||||
public bool NotificationTimerCountDown { get; set; }
|
public bool NotificationTimerCountDown { get; set; }
|
||||||
@@ -235,13 +229,5 @@ namespace TweetDck.Configuration{
|
|||||||
public static string GetBackupFile(string file){
|
public static string GetBackupFile(string file){
|
||||||
return file+".bak";
|
return file+".bak";
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SerializationCompatibilityHandler : SerializationBinder{
|
|
||||||
public override Type BindToType(string assemblyName, string typeName){
|
|
||||||
assemblyName = assemblyName.Replace("TweetDick", "TweetDuck");
|
|
||||||
typeName = typeName.Replace("TweetDick", "TweetDck");
|
|
||||||
return Type.GetType(string.Format(CultureInfo.CurrentCulture, "{0}, {1}", typeName, assemblyName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -35,14 +35,14 @@ namespace TweetDck.Core{
|
|||||||
|
|
||||||
private FormWindowState prevState;
|
private FormWindowState prevState;
|
||||||
|
|
||||||
public FormBrowser(PluginManager pluginManager){
|
public FormBrowser(PluginManager pluginManager, UpdaterSettings updaterSettings){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Text = Program.BrandName;
|
Text = Program.BrandName;
|
||||||
|
|
||||||
this.plugins = pluginManager;
|
this.plugins = pluginManager;
|
||||||
this.plugins.Reloaded += plugins_Reloaded;
|
this.plugins.Reloaded += plugins_Reloaded;
|
||||||
this.plugins.Config.PluginChangedState += plugins_PluginChangedState;
|
this.plugins.PluginChangedState += plugins_PluginChangedState;
|
||||||
|
|
||||||
FormNotification notification = CreateNotificationForm(true);
|
FormNotification notification = CreateNotificationForm(true);
|
||||||
notification.CanMoveWindow = () => false;
|
notification.CanMoveWindow = () => false;
|
||||||
@@ -73,7 +73,7 @@ namespace TweetDck.Core{
|
|||||||
|
|
||||||
UpdateTrayIcon();
|
UpdateTrayIcon();
|
||||||
|
|
||||||
this.updates = new UpdateHandler(browser, this);
|
this.updates = new UpdateHandler(browser, this, updaterSettings);
|
||||||
this.updates.UpdateAccepted += updates_UpdateAccepted;
|
this.updates.UpdateAccepted += updates_UpdateAccepted;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ namespace TweetDck.Core{
|
|||||||
form.Shown += (sender, args) => form.MoveToCenter(this);
|
form.Shown += (sender, args) => form.MoveToCenter(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ForceClose(){
|
public void ForceClose(){
|
||||||
trayIcon.Visible = false; // checked in FormClosing event
|
trayIcon.Visible = false; // checked in FormClosing event
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
@@ -303,11 +303,11 @@ namespace TweetDck.Core{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void OnImagePasted(){
|
public void OnImagePasted(){
|
||||||
browser.ExecuteScriptAsync("TDGF_tryPasteImage", new object[0]);
|
browser.ExecuteScriptAsync("TDGF_tryPasteImage()");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnImagePastedFinish(){
|
public void OnImagePastedFinish(){
|
||||||
browser.ExecuteScriptAsync("TDGF_tryPasteImageFinish", new object[0]);
|
browser.ExecuteScriptAsync("TDGF_tryPasteImageFinish()");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReloadBrowser(){
|
public void ReloadBrowser(){
|
||||||
|
4
Core/Other/FormMessage.Designer.cs
generated
4
Core/Other/FormMessage.Designer.cs
generated
@@ -42,8 +42,8 @@
|
|||||||
| System.Windows.Forms.AnchorStyles.Left)
|
| System.Windows.Forms.AnchorStyles.Left)
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.labelMessage.AutoSize = true;
|
this.labelMessage.AutoSize = true;
|
||||||
this.labelMessage.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
this.labelMessage.Font = System.Drawing.SystemFonts.MessageBoxFont;
|
||||||
this.labelMessage.Location = new System.Drawing.Point(62, 33);
|
this.labelMessage.Location = new System.Drawing.Point(62, 34);
|
||||||
this.labelMessage.Margin = new System.Windows.Forms.Padding(53, 24, 27, 24);
|
this.labelMessage.Margin = new System.Windows.Forms.Padding(53, 24, 27, 24);
|
||||||
this.labelMessage.MaximumSize = new System.Drawing.Size(600, 0);
|
this.labelMessage.MaximumSize = new System.Drawing.Size(600, 0);
|
||||||
this.labelMessage.MinimumSize = new System.Drawing.Size(0, 24);
|
this.labelMessage.MinimumSize = new System.Drawing.Size(0, 24);
|
||||||
|
@@ -53,6 +53,7 @@ namespace TweetDck.Core.Other{
|
|||||||
public Button AddButton(string title){
|
public Button AddButton(string title){
|
||||||
Button button = new Button{
|
Button button = new Button{
|
||||||
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
|
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
|
||||||
|
Font = SystemFonts.MessageBoxFont,
|
||||||
Location = new Point(Width-112-buttonCount*96, 12),
|
Location = new Point(Width-112-buttonCount*96, 12),
|
||||||
Size = new Size(88, 26),
|
Size = new Size(88, 26),
|
||||||
TabIndex = buttonCount,
|
TabIndex = buttonCount,
|
||||||
|
@@ -53,7 +53,6 @@ namespace TweetDck.Core.Other.Settings.Export{
|
|||||||
|
|
||||||
public bool Import(){
|
public bool Import(){
|
||||||
try{
|
try{
|
||||||
bool updatedPlugins = false;
|
|
||||||
HashSet<string> missingPlugins = new HashSet<string>();
|
HashSet<string> missingPlugins = new HashSet<string>();
|
||||||
|
|
||||||
using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.None))){
|
using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.None))){
|
||||||
@@ -71,10 +70,7 @@ namespace TweetDck.Core.Other.Settings.Export{
|
|||||||
|
|
||||||
entry.WriteToFile(Path.Combine(Program.PluginDataPath, value[0], value[1]), true);
|
entry.WriteToFile(Path.Combine(Program.PluginDataPath, value[0], value[1]), true);
|
||||||
|
|
||||||
if (plugins.IsPluginInstalled(value[0])){
|
if (!plugins.IsPluginInstalled(value[0])){
|
||||||
updatedPlugins = true;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
missingPlugins.Add(value[0]);
|
missingPlugins.Add(value[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,7 +94,7 @@ namespace TweetDck.Core.Other.Settings.Export{
|
|||||||
if (IsRestarting){
|
if (IsRestarting){
|
||||||
Program.Restart(new string[]{ "-importcookies" });
|
Program.Restart(new string[]{ "-importcookies" });
|
||||||
}
|
}
|
||||||
else if (updatedPlugins){
|
else{
|
||||||
plugins.Reload();
|
plugins.Reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -57,6 +57,10 @@ namespace TweetDck.Core.Utils{
|
|||||||
values[key.ToLowerInvariant()] = value;
|
values[key.ToLowerInvariant()] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool HasValue(string key){
|
||||||
|
return values.ContainsKey(key.ToLowerInvariant());
|
||||||
|
}
|
||||||
|
|
||||||
public string GetValue(string key, string defaultValue){
|
public string GetValue(string key, string defaultValue){
|
||||||
string val;
|
string val;
|
||||||
return values.TryGetValue(key.ToLowerInvariant(), out val) ? val : defaultValue;
|
return values.TryGetValue(key.ToLowerInvariant(), out val) ? val : defaultValue;
|
||||||
|
@@ -1,23 +1,18 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Security.AccessControl;
|
|
||||||
using System.Security.Principal;
|
|
||||||
|
|
||||||
namespace TweetDck.Core.Utils{
|
namespace TweetDck.Core.Utils{
|
||||||
static class WindowsUtils{
|
static class WindowsUtils{
|
||||||
public static bool CheckFolderPermission(string path, FileSystemRights right){
|
public static bool CheckFolderWritePermission(string path){
|
||||||
|
string testFile = Path.Combine(path, ".test");
|
||||||
|
|
||||||
try{
|
try{
|
||||||
AuthorizationRuleCollection collection = Directory.GetAccessControl(path).GetAccessRules(true, true, typeof(NTAccount));
|
Directory.CreateDirectory(path);
|
||||||
|
|
||||||
foreach(FileSystemAccessRule rule in collection){
|
using(File.Create(testFile)){}
|
||||||
if ((rule.FileSystemRights & right) == right){
|
File.Delete(testFile);
|
||||||
return true;
|
return true;
|
||||||
}
|
}catch{
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
77
Migration/FormBackgroundWork.Designer.cs
generated
77
Migration/FormBackgroundWork.Designer.cs
generated
@@ -1,77 +0,0 @@
|
|||||||
using TweetDck.Core.Controls;
|
|
||||||
|
|
||||||
namespace TweetDck.Migration {
|
|
||||||
partial class FormBackgroundWork {
|
|
||||||
/// <summary>
|
|
||||||
/// Required designer variable.
|
|
||||||
/// </summary>
|
|
||||||
private System.ComponentModel.IContainer components = null;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Clean up any resources being used.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
|
||||||
protected override void Dispose(bool disposing) {
|
|
||||||
if (disposing && (components != null)) {
|
|
||||||
components.Dispose();
|
|
||||||
}
|
|
||||||
base.Dispose(disposing);
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Windows Form Designer generated code
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Required method for Designer support - do not modify
|
|
||||||
/// the contents of this method with the code editor.
|
|
||||||
/// </summary>
|
|
||||||
private void InitializeComponent() {
|
|
||||||
this.progressBarUseless = new System.Windows.Forms.ProgressBar();
|
|
||||||
this.labelDescription = new System.Windows.Forms.Label();
|
|
||||||
this.SuspendLayout();
|
|
||||||
//
|
|
||||||
// progressBarUseless
|
|
||||||
//
|
|
||||||
this.progressBarUseless.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
|
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
|
||||||
this.progressBarUseless.Location = new System.Drawing.Point(15, 52);
|
|
||||||
this.progressBarUseless.MarqueeAnimationSpeed = 10;
|
|
||||||
this.progressBarUseless.Name = "progressBarUseless";
|
|
||||||
this.progressBarUseless.Size = new System.Drawing.Size(477, 23);
|
|
||||||
this.progressBarUseless.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
|
|
||||||
this.progressBarUseless.TabIndex = 0;
|
|
||||||
//
|
|
||||||
// labelDescription
|
|
||||||
//
|
|
||||||
this.labelDescription.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
|
||||||
| System.Windows.Forms.AnchorStyles.Left)
|
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
|
||||||
this.labelDescription.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
|
||||||
this.labelDescription.Location = new System.Drawing.Point(12, 12);
|
|
||||||
this.labelDescription.Name = "labelDescription";
|
|
||||||
this.labelDescription.Size = new System.Drawing.Size(480, 37);
|
|
||||||
this.labelDescription.TabIndex = 1;
|
|
||||||
this.labelDescription.Text = "Please, watch this informationless progress bar showcase while some magic happens" +
|
|
||||||
" in the background...";
|
|
||||||
//
|
|
||||||
// FormBackgroundWork
|
|
||||||
//
|
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
|
||||||
this.ClientSize = new System.Drawing.Size(504, 87);
|
|
||||||
this.Controls.Add(this.labelDescription);
|
|
||||||
this.Controls.Add(this.progressBarUseless);
|
|
||||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
|
||||||
this.Name = "FormBackgroundWork";
|
|
||||||
this.ShowIcon = false;
|
|
||||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
|
||||||
this.Text = "TweetDeck Migration";
|
|
||||||
this.ResumeLayout(false);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
private System.Windows.Forms.ProgressBar progressBarUseless;
|
|
||||||
private System.Windows.Forms.Label labelDescription;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,15 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Windows.Forms;
|
|
||||||
|
|
||||||
namespace TweetDck.Migration{
|
|
||||||
partial class FormBackgroundWork : Form{
|
|
||||||
public FormBackgroundWork(){
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ShowWorkDialog(Action onBegin){
|
|
||||||
Shown += (sender, args) => onBegin();
|
|
||||||
ShowDialog();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,23 +0,0 @@
|
|||||||
namespace TweetDck.Migration{
|
|
||||||
enum MigrationDecision{
|
|
||||||
/// <summary>
|
|
||||||
/// Copies the important files and then deletes the TweetDeck folder.
|
|
||||||
/// </summary>
|
|
||||||
Migrate,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Does exactly what <see cref="Migrate"/> does, but also silently uninstalls TweetDeck.
|
|
||||||
/// </summary>
|
|
||||||
MigratePurge,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Does not copy any files and does not ask the user about data migration again.
|
|
||||||
/// </summary>
|
|
||||||
Ignore,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Does not copy any files but asks the user again when the program is re-ran.
|
|
||||||
/// </summary>
|
|
||||||
AskLater
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,204 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows.Forms;
|
|
||||||
using Microsoft.Win32;
|
|
||||||
using TweetDck.Core.Other;
|
|
||||||
using System.Drawing;
|
|
||||||
using TweetDck.Core.Controls;
|
|
||||||
|
|
||||||
namespace TweetDck.Migration{
|
|
||||||
static class MigrationManager{
|
|
||||||
private static readonly string TweetDeckPathParent = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "twitter");
|
|
||||||
private static readonly string TweetDeckPath = Path.Combine(TweetDeckPathParent, "TweetDeck");
|
|
||||||
|
|
||||||
private static readonly string TweetDickStorage = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "TweetDick");
|
|
||||||
|
|
||||||
public static void Run(){
|
|
||||||
if (!Program.IsPortable && Directory.Exists(TweetDickStorage) && !Directory.Exists(Program.StoragePath)){
|
|
||||||
if (MessageBox.Show("Welcome to TweetDuck! Would you like to move your old TweetDick configuration and login data?", "TweetDick Migration", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes){
|
|
||||||
try{
|
|
||||||
Directory.Move(TweetDickStorage, Program.StoragePath);
|
|
||||||
MessageBox.Show("All done! You can now uninstall TweetDick.", "TweetDick Migration", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
||||||
}catch(Exception ex){
|
|
||||||
Program.Reporter.HandleException("Migration Error", "An unexpected error occurred during the migration process.", true, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Program.UserConfig.IgnoreMigration && Directory.Exists(TweetDeckPath)){
|
|
||||||
MigrationDecision decision;
|
|
||||||
|
|
||||||
const string prompt = "Hey there, I found some TweetDeck data! Do you want to »Migrate« it and delete the old data folder, »Ignore« the prompt, or try "+Program.BrandName+" out first? You may also »Migrate && Purge« which uninstalls TweetDeck too!";
|
|
||||||
|
|
||||||
using(FormMessage formQuestion = new FormMessage("TweetDeck Migration", prompt, MessageBoxIcon.Question)){
|
|
||||||
formQuestion.AddButton("Ask Later");
|
|
||||||
Button btnIgnore = formQuestion.AddButton("Ignore");
|
|
||||||
Button btnMigrate = formQuestion.AddButton("Migrate");
|
|
||||||
Button btnMigrateAndPurge = formQuestion.AddButton("Migrate && Purge");
|
|
||||||
|
|
||||||
btnMigrateAndPurge.Location = new Point(btnMigrateAndPurge.Location.X-20, btnMigrateAndPurge.Location.Y);
|
|
||||||
btnMigrateAndPurge.Width += 20;
|
|
||||||
btnMigrateAndPurge.SetElevated();
|
|
||||||
|
|
||||||
if (formQuestion.ShowDialog() == DialogResult.OK){
|
|
||||||
decision = formQuestion.ClickedButton == btnMigrateAndPurge ? MigrationDecision.MigratePurge :
|
|
||||||
formQuestion.ClickedButton == btnMigrate ? MigrationDecision.Migrate :
|
|
||||||
formQuestion.ClickedButton == btnIgnore ? MigrationDecision.Ignore : MigrationDecision.AskLater;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
decision = MigrationDecision.AskLater;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(decision){
|
|
||||||
case MigrationDecision.MigratePurge:
|
|
||||||
case MigrationDecision.Migrate:
|
|
||||||
FormBackgroundWork formWait = new FormBackgroundWork();
|
|
||||||
|
|
||||||
formWait.ShowWorkDialog(() => {
|
|
||||||
if (!BeginMigration(decision, ex => formWait.Invoke(new Action(() => {
|
|
||||||
formWait.Close();
|
|
||||||
|
|
||||||
if (ex != null){
|
|
||||||
Program.Reporter.HandleException("Migration Error", "An unexpected error occurred during the migration process.", true, ex);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Program.UserConfig.IgnoreMigration = true;
|
|
||||||
Program.UserConfig.Save();
|
|
||||||
})))){
|
|
||||||
formWait.Close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MigrationDecision.Ignore:
|
|
||||||
Program.UserConfig.IgnoreMigration = true;
|
|
||||||
Program.UserConfig.Save();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!Program.UserConfig.IgnoreUninstallCheck){
|
|
||||||
string guid = MigrationUtils.FindProgramGuidByDisplayName("TweetDeck");
|
|
||||||
|
|
||||||
if (guid != null){
|
|
||||||
const string prompt = "TweetDeck is still installed on your computer, do you want to uninstall it?";
|
|
||||||
|
|
||||||
using(FormMessage formQuestion = new FormMessage("Uninstall TweetDeck", prompt, MessageBoxIcon.Question)){
|
|
||||||
formQuestion.AddButton("No");
|
|
||||||
|
|
||||||
Button btnYes = formQuestion.AddButton("Yes");
|
|
||||||
btnYes.SetElevated();
|
|
||||||
|
|
||||||
if (formQuestion.ShowDialog() == DialogResult.OK && formQuestion.ClickedButton == btnYes && MigrationUtils.RunUninstaller(guid, 0)){
|
|
||||||
CleanupTweetDeck();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Program.UserConfig.IgnoreUninstallCheck = true;
|
|
||||||
Program.UserConfig.Save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool BeginMigration(MigrationDecision decision, Action<Exception> onFinished){
|
|
||||||
if (decision != MigrationDecision.MigratePurge && decision != MigrationDecision.Migrate){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Task task = new Task(() => {
|
|
||||||
Directory.CreateDirectory(Program.StoragePath);
|
|
||||||
Directory.CreateDirectory(Path.Combine(Program.StoragePath, "localStorage"));
|
|
||||||
Directory.CreateDirectory(Path.Combine(Program.StoragePath, "Local Storage"));
|
|
||||||
|
|
||||||
CopyFile("Cookies");
|
|
||||||
CopyFile("Cookies-journal");
|
|
||||||
CopyFile("localStorage"+Path.DirectorySeparatorChar+"qrc__0.localstorage");
|
|
||||||
CopyFile("Local Storage"+Path.DirectorySeparatorChar+"https_tweetdeck.twitter.com_0.localstorage");
|
|
||||||
CopyFile("Local Storage"+Path.DirectorySeparatorChar+"https_tweetdeck.twitter.com_0.localstorage-journal");
|
|
||||||
|
|
||||||
if (decision == MigrationDecision.Migrate || decision == MigrationDecision.MigratePurge){
|
|
||||||
// kill process if running
|
|
||||||
Process runningProcess = null;
|
|
||||||
|
|
||||||
try{
|
|
||||||
runningProcess = Process.GetProcessesByName("TweetDeck").FirstOrDefault(process => process.MainWindowHandle != IntPtr.Zero);
|
|
||||||
}catch(Exception){
|
|
||||||
// process not found
|
|
||||||
}
|
|
||||||
|
|
||||||
if (runningProcess != null){
|
|
||||||
runningProcess.CloseMainWindow();
|
|
||||||
|
|
||||||
for(int wait = 0; wait < 100 && !runningProcess.HasExited; wait++){ // 10 seconds
|
|
||||||
runningProcess.Refresh();
|
|
||||||
Thread.Sleep(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
runningProcess.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete folders
|
|
||||||
for(int wait = 0; wait < 50; wait++){
|
|
||||||
try{
|
|
||||||
Directory.Delete(TweetDeckPath, true);
|
|
||||||
break;
|
|
||||||
}catch(Exception){
|
|
||||||
// browser subprocess not ended yet, wait
|
|
||||||
Thread.Sleep(300);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try{
|
|
||||||
Directory.Delete(TweetDeckPathParent, false);
|
|
||||||
}catch(IOException){
|
|
||||||
// most likely not empty, ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (decision == MigrationDecision.MigratePurge){
|
|
||||||
// uninstall in the background
|
|
||||||
string guid = MigrationUtils.FindProgramGuidByDisplayName("TweetDeck");
|
|
||||||
|
|
||||||
if (guid != null && !MigrationUtils.RunUninstaller(guid, 5000)){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// registry cleanup
|
|
||||||
CleanupTweetDeck();
|
|
||||||
|
|
||||||
// migration finished like a boss
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
task.ContinueWith(originalTask => onFinished(originalTask.Exception), TaskContinuationOptions.ExecuteSynchronously);
|
|
||||||
task.Start();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void CopyFile(string relativePath){
|
|
||||||
try{
|
|
||||||
File.Copy(Path.Combine(TweetDeckPath, relativePath), Path.Combine(Program.StoragePath, relativePath), true);
|
|
||||||
}catch(FileNotFoundException){
|
|
||||||
}catch(DirectoryNotFoundException){
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,63 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.Win32;
|
|
||||||
using TweetDck.Core.Utils;
|
|
||||||
|
|
||||||
namespace TweetDck.Migration{
|
|
||||||
static class MigrationUtils{
|
|
||||||
public static bool RunUninstaller(string guid, int timeout){
|
|
||||||
try{
|
|
||||||
Process uninstaller = WindowsUtils.StartProcess("msiexec.exe", "/x "+guid+" /quiet /qn", true);
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}catch(Exception){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string FindProgramGuidByDisplayName(string displayName){
|
|
||||||
Predicate<RegistryKey> predicate = key => displayName.Equals(key.GetValue("DisplayName") as string, StringComparison.OrdinalIgnoreCase);
|
|
||||||
string guid;
|
|
||||||
|
|
||||||
return FindMatchingSubKey(Registry.LocalMachine, @"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall", predicate, out guid) ||
|
|
||||||
FindMatchingSubKey(Registry.LocalMachine, @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", predicate, out guid) ||
|
|
||||||
FindMatchingSubKey(Registry.CurrentUser, @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", predicate, out guid)
|
|
||||||
? guid : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool FindMatchingSubKey(RegistryKey keyHandle, string path, Predicate<RegistryKey> predicate, out string guid){
|
|
||||||
string outputId = null;
|
|
||||||
|
|
||||||
try{
|
|
||||||
RegistryKey parentKey = keyHandle.OpenSubKey(path, false);
|
|
||||||
if (parentKey == null)throw new InvalidOperationException();
|
|
||||||
|
|
||||||
foreach(RegistryKey subKey in parentKey.GetSubKeyNames().Select(subName => parentKey.OpenSubKey(subName, false)).Where(subKey => subKey != null)){
|
|
||||||
if (predicate(subKey)){
|
|
||||||
outputId = subKey.Name.Substring(subKey.Name.LastIndexOf('\\')+1);
|
|
||||||
subKey.Close();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
subKey.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
parentKey.Close();
|
|
||||||
}catch(Exception){
|
|
||||||
guid = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (guid = outputId) != null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -6,7 +6,7 @@ namespace TweetDck.Plugins{
|
|||||||
[Serializable]
|
[Serializable]
|
||||||
sealed class PluginConfig{
|
sealed class PluginConfig{
|
||||||
[field:NonSerialized]
|
[field:NonSerialized]
|
||||||
public event EventHandler<PluginChangedStateEventArgs> PluginChangedState;
|
public event EventHandler<PluginChangedStateEventArgs> InternalPluginChangedState; // should only be accessed from PluginManager
|
||||||
|
|
||||||
public IEnumerable<string> DisabledPlugins{
|
public IEnumerable<string> DisabledPlugins{
|
||||||
get{
|
get{
|
||||||
@@ -24,8 +24,8 @@ namespace TweetDck.Plugins{
|
|||||||
|
|
||||||
public void SetEnabled(Plugin plugin, bool enabled){
|
public void SetEnabled(Plugin plugin, bool enabled){
|
||||||
if ((enabled && Disabled.Remove(plugin.Identifier)) || (!enabled && Disabled.Add(plugin.Identifier))){
|
if ((enabled && Disabled.Remove(plugin.Identifier)) || (!enabled && Disabled.Add(plugin.Identifier))){
|
||||||
if (PluginChangedState != null){
|
if (InternalPluginChangedState != null){
|
||||||
PluginChangedState(this, new PluginChangedStateEventArgs(plugin, enabled));
|
InternalPluginChangedState(this, new PluginChangedStateEventArgs(plugin, enabled));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -21,6 +21,7 @@ namespace TweetDck.Plugins{
|
|||||||
public PluginBridge Bridge { get; private set; }
|
public PluginBridge Bridge { get; private set; }
|
||||||
|
|
||||||
public event EventHandler<PluginLoadEventArgs> Reloaded;
|
public event EventHandler<PluginLoadEventArgs> Reloaded;
|
||||||
|
public event EventHandler<PluginChangedStateEventArgs> PluginChangedState;
|
||||||
|
|
||||||
private readonly string rootPath;
|
private readonly string rootPath;
|
||||||
private readonly HashSet<Plugin> plugins = new HashSet<Plugin>();
|
private readonly HashSet<Plugin> plugins = new HashSet<Plugin>();
|
||||||
@@ -31,10 +32,21 @@ namespace TweetDck.Plugins{
|
|||||||
|
|
||||||
public PluginManager(string path, PluginConfig config){
|
public PluginManager(string path, PluginConfig config){
|
||||||
this.rootPath = path;
|
this.rootPath = path;
|
||||||
this.Config = config;
|
this.SetConfig(config);
|
||||||
this.Bridge = new PluginBridge(this);
|
this.Bridge = new PluginBridge(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetConfig(PluginConfig config){
|
||||||
|
this.Config = config;
|
||||||
|
this.Config.InternalPluginChangedState += Config_InternalPluginChangedState;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Config_InternalPluginChangedState(object sender, PluginChangedStateEventArgs e){
|
||||||
|
if (PluginChangedState != null){
|
||||||
|
PluginChangedState(this, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsPluginInstalled(string identifier){
|
public bool IsPluginInstalled(string identifier){
|
||||||
return plugins.Any(plugin => plugin.Identifier.Equals(identifier));
|
return plugins.Any(plugin => plugin.Identifier.Equals(identifier));
|
||||||
}
|
}
|
||||||
|
95
Program.cs
95
Program.cs
@@ -5,7 +5,6 @@ using System.Windows.Forms;
|
|||||||
using CefSharp;
|
using CefSharp;
|
||||||
using TweetDck.Configuration;
|
using TweetDck.Configuration;
|
||||||
using TweetDck.Core;
|
using TweetDck.Core;
|
||||||
using TweetDck.Migration;
|
|
||||||
using TweetDck.Core.Utils;
|
using TweetDck.Core.Utils;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -13,7 +12,8 @@ using TweetDck.Plugins;
|
|||||||
using TweetDck.Plugins.Events;
|
using TweetDck.Plugins.Events;
|
||||||
using TweetDck.Core.Other.Settings.Export;
|
using TweetDck.Core.Other.Settings.Export;
|
||||||
using TweetDck.Core.Handling;
|
using TweetDck.Core.Handling;
|
||||||
using System.Security.AccessControl;
|
using TweetDck.Core.Other;
|
||||||
|
using TweetDck.Updates;
|
||||||
|
|
||||||
[assembly: CLSCompliant(true)]
|
[assembly: CLSCompliant(true)]
|
||||||
namespace TweetDck{
|
namespace TweetDck{
|
||||||
@@ -21,10 +21,8 @@ namespace TweetDck{
|
|||||||
public const string BrandName = "TweetDuck";
|
public const string BrandName = "TweetDuck";
|
||||||
public const string Website = "https://tweetduck.chylex.com";
|
public const string Website = "https://tweetduck.chylex.com";
|
||||||
|
|
||||||
public const string BrowserSubprocess = BrandName+".Browser.exe";
|
public const string VersionTag = "1.5.1";
|
||||||
|
public const string VersionFull = "1.5.1.0";
|
||||||
public const string VersionTag = "1.5";
|
|
||||||
public const string VersionFull = "1.5.0.0";
|
|
||||||
|
|
||||||
public static readonly Version Version = new Version(VersionTag);
|
public static readonly Version Version = new Version(VersionTag);
|
||||||
|
|
||||||
@@ -57,48 +55,55 @@ namespace TweetDck{
|
|||||||
|
|
||||||
WindowRestoreMessage = NativeMethods.RegisterWindowMessage("TweetDuckRestore");
|
WindowRestoreMessage = NativeMethods.RegisterWindowMessage("TweetDuckRestore");
|
||||||
|
|
||||||
if (!WindowsUtils.CheckFolderPermission(StoragePath, FileSystemRights.WriteData)){
|
if (!WindowsUtils.CheckFolderWritePermission(StoragePath)){
|
||||||
MessageBox.Show(BrandName+" does not have write permissions to the storage folder: "+StoragePath, "Permission Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
MessageBox.Show(BrandName+" does not have write permissions to the storage folder: "+StoragePath, "Permission Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Reporter = new Reporter(LogFilePath);
|
Reporter = new Reporter(LogFilePath);
|
||||||
|
Reporter.SetupUnhandledExceptionHandler(BrandName+" Has Failed :(");
|
||||||
AppDomain.CurrentDomain.UnhandledException += (sender, args) => {
|
|
||||||
Exception ex = args.ExceptionObject as Exception;
|
|
||||||
|
|
||||||
if (ex != null){
|
|
||||||
Reporter.HandleException(BrandName+" Has Failed :(", "An unhandled exception has occurred.", false, ex);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (Args.HasFlag("-restart")){
|
if (Args.HasFlag("-restart")){
|
||||||
for(int attempt = 0; attempt < 41; attempt++){
|
for(int attempt = 0; attempt < 21; attempt++){
|
||||||
if (LockManager.Lock()){
|
LockManager.Result lockResult = LockManager.Lock();
|
||||||
|
|
||||||
|
if (lockResult == LockManager.Result.Success){
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (attempt == 40){
|
else if (lockResult == LockManager.Result.Fail){
|
||||||
MessageBox.Show(BrandName+" is taking too long to close, please wait and then start the application again manually.", BrandName+" Cannot Restart", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
MessageBox.Show("An unknown error occurred accessing the data folder. Please, make sure "+BrandName+" is not already running. If the problem persists, try restarting your system.", BrandName+" Has Failed :(", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if (attempt == 20){
|
||||||
|
using(FormMessage form = new FormMessage(BrandName+" Cannot Restart", BrandName+" is taking too long to close.", MessageBoxIcon.Warning)){
|
||||||
|
form.AddButton("Exit");
|
||||||
|
Button btnRetry = form.AddButton("Retry");
|
||||||
|
|
||||||
|
if (form.ShowDialog() == DialogResult.OK){
|
||||||
|
if (form.ClickedButton == btnRetry){
|
||||||
|
attempt /= 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
else{
|
else{
|
||||||
Thread.Sleep(500);
|
Thread.Sleep(500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ReloadConfig();
|
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
ReloadConfig();
|
LockManager.Result lockResult = LockManager.Lock();
|
||||||
MigrationManager.Run();
|
|
||||||
|
|
||||||
if (!LockManager.Lock()){
|
if (lockResult == LockManager.Result.HasProcess){
|
||||||
if (LockManager.LockingProcess.MainWindowHandle == IntPtr.Zero && LockManager.LockingProcess.Responding){ // restore if the original process is in tray
|
if (LockManager.LockingProcess.MainWindowHandle == IntPtr.Zero && LockManager.LockingProcess.Responding){ // restore if the original process is in tray
|
||||||
NativeMethods.SendMessage(NativeMethods.HWND_BROADCAST, WindowRestoreMessage, 0, IntPtr.Zero);
|
NativeMethods.SendMessage(NativeMethods.HWND_BROADCAST, WindowRestoreMessage, 0, IntPtr.Zero);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (MessageBox.Show("Another instance of "+BrandName+" is already running.\r\nDo you want to close it?", BrandName+" is Already Running", MessageBoxButtons.YesNo, MessageBoxIcon.Error, MessageBoxDefaultButton.Button2) == DialogResult.Yes){
|
else if (MessageBox.Show("Another instance of "+BrandName+" is already running.\r\nDo you want to close it?", BrandName+" is Already Running", MessageBoxButtons.YesNo, MessageBoxIcon.Error, MessageBoxDefaultButton.Button2) == DialogResult.Yes){
|
||||||
if (!LockManager.CloseLockingProcess(20000)){
|
if (!LockManager.CloseLockingProcess(30000)){
|
||||||
MessageBox.Show("Could not close the other process.", BrandName+" Has Failed :(", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
MessageBox.Show("Could not close the other process.", BrandName+" Has Failed :(", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -107,8 +112,14 @@ namespace TweetDck{
|
|||||||
}
|
}
|
||||||
else return;
|
else return;
|
||||||
}
|
}
|
||||||
|
else if (lockResult != LockManager.Result.Success){
|
||||||
|
MessageBox.Show("An unknown error occurred accessing the data folder. Please, make sure "+BrandName+" is not already running. If the problem persists, try restarting your system.", BrandName+" Has Failed :(", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ReloadConfig();
|
||||||
|
|
||||||
if (Args.HasFlag("-importcookies")){
|
if (Args.HasFlag("-importcookies")){
|
||||||
ExportManager.ImportCookies();
|
ExportManager.ImportCookies();
|
||||||
}
|
}
|
||||||
@@ -120,9 +131,9 @@ namespace TweetDck{
|
|||||||
UserAgent = BrowserUtils.HeaderUserAgent,
|
UserAgent = BrowserUtils.HeaderUserAgent,
|
||||||
Locale = Args.GetValue("-locale", "en"),
|
Locale = Args.GetValue("-locale", "en"),
|
||||||
CachePath = StoragePath,
|
CachePath = StoragePath,
|
||||||
BrowserSubprocessPath = File.Exists(BrowserSubprocess) ? BrowserSubprocess : "CefSharp.BrowserSubprocess.exe",
|
|
||||||
LogFile = Path.Combine(StoragePath, "TD_Console.txt"),
|
LogFile = Path.Combine(StoragePath, "TD_Console.txt"),
|
||||||
#if !DEBUG
|
#if !DEBUG
|
||||||
|
BrowserSubprocessPath = BrandName+".Browser.exe",
|
||||||
LogSeverity = Args.HasFlag("-log") ? LogSeverity.Info : LogSeverity.Disable
|
LogSeverity = Args.HasFlag("-log") ? LogSeverity.Info : LogSeverity.Disable
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
@@ -140,18 +151,22 @@ namespace TweetDck{
|
|||||||
|
|
||||||
PluginManager plugins = new PluginManager(PluginPath, UserConfig.Plugins);
|
PluginManager plugins = new PluginManager(PluginPath, UserConfig.Plugins);
|
||||||
plugins.Reloaded += plugins_Reloaded;
|
plugins.Reloaded += plugins_Reloaded;
|
||||||
plugins.Config.PluginChangedState += (sender, args) => UserConfig.Save();
|
plugins.PluginChangedState += (sender, args) => UserConfig.Save();
|
||||||
plugins.Reload();
|
plugins.Reload();
|
||||||
|
|
||||||
FormBrowser mainForm = new FormBrowser(plugins);
|
FormBrowser mainForm = new FormBrowser(plugins, new UpdaterSettings{
|
||||||
|
AllowPreReleases = Args.HasFlag("-debugupdates")
|
||||||
|
});
|
||||||
|
|
||||||
Application.Run(mainForm);
|
Application.Run(mainForm);
|
||||||
|
|
||||||
if (mainForm.UpdateInstallerPath != null){
|
if (mainForm.UpdateInstallerPath != null){
|
||||||
ExitCleanup();
|
ExitCleanup();
|
||||||
|
|
||||||
bool runElevated = !IsPortable || !WindowsUtils.CheckFolderPermission(ProgramPath, FileSystemRights.WriteData);
|
string updaterArgs = "/SP- /SILENT /CLOSEAPPLICATIONS /UPDATEPATH=\""+ProgramPath+"\" /RUNARGS=\""+GetArgsClean().ToString().Replace("\"", "^\"")+"\""+(IsPortable ? " /PORTABLE=1" : "");
|
||||||
|
bool runElevated = !IsPortable || !WindowsUtils.CheckFolderWritePermission(ProgramPath);
|
||||||
|
|
||||||
WindowsUtils.StartProcess(mainForm.UpdateInstallerPath, "/SP- /SILENT /CLOSEAPPLICATIONS /UPDATEPATH=\""+ProgramPath+"\""+(IsPortable ? " /PORTABLE=1" : ""), runElevated); // ProgramPath has a trailing backslash
|
WindowsUtils.StartProcess(mainForm.UpdateInstallerPath, updaterArgs, runElevated); // ProgramPath has a trailing backslash
|
||||||
Application.Exit();
|
Application.Exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,6 +175,8 @@ namespace TweetDck{
|
|||||||
if (!e.Success){
|
if (!e.Success){
|
||||||
MessageBox.Show("The following plugins will not be available until the issues are resolved:\n"+string.Join("\n", e.Errors), "Error Loading Plugins", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
MessageBox.Show("The following plugins will not be available until the issues are resolved:\n"+string.Join("\n", e.Errors), "Error Loading Plugins", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
((PluginManager)sender).SetConfig(UserConfig.Plugins);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetDataStoragePath(){
|
private static string GetDataStoragePath(){
|
||||||
@@ -196,17 +213,31 @@ namespace TweetDck{
|
|||||||
ReloadConfig();
|
ReloadConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static CommandLineArgs GetArgsClean(){
|
||||||
|
CommandLineArgs args = Args.Clone();
|
||||||
|
args.RemoveFlag("-importcookies");
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
public static void Restart(){
|
public static void Restart(){
|
||||||
Restart(new string[0]);
|
Restart(new string[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Restart(string[] extraArgs){
|
public static void Restart(string[] extraArgs){
|
||||||
CommandLineArgs args = Args.Clone();
|
FormBrowser browserForm = Application.OpenForms.OfType<FormBrowser>().FirstOrDefault();
|
||||||
|
|
||||||
|
if (browserForm == null){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandLineArgs args = GetArgsClean();
|
||||||
args.AddFlag("-restart");
|
args.AddFlag("-restart");
|
||||||
args.RemoveFlag("-importcookies");
|
|
||||||
|
|
||||||
CommandLineArgs.ReadStringArray('-', extraArgs, args);
|
CommandLineArgs.ReadStringArray('-', extraArgs, args);
|
||||||
|
|
||||||
|
browserForm.ForceClose();
|
||||||
|
ExitCleanup();
|
||||||
|
|
||||||
Process.Start(Application.ExecutablePath, args.ToString());
|
Process.Start(Application.ExecutablePath, args.ToString());
|
||||||
Application.Exit();
|
Application.Exit();
|
||||||
}
|
}
|
||||||
|
14
Reporter.cs
14
Reporter.cs
@@ -15,6 +15,16 @@ namespace TweetDck{
|
|||||||
this.logFile = logFile;
|
this.logFile = logFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetupUnhandledExceptionHandler(string caption){
|
||||||
|
AppDomain.CurrentDomain.UnhandledException += (sender, args) => {
|
||||||
|
Exception ex = args.ExceptionObject as Exception;
|
||||||
|
|
||||||
|
if (ex != null){
|
||||||
|
HandleException(caption, "An unhandled exception has occurred.", false, ex);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public bool Log(string data){
|
public bool Log(string data){
|
||||||
StringBuilder build = new StringBuilder();
|
StringBuilder build = new StringBuilder();
|
||||||
|
|
||||||
@@ -34,7 +44,7 @@ namespace TweetDck{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void HandleException(string caption, string message, bool canIgnore, Exception e){
|
public void HandleException(string caption, string message, bool canIgnore, Exception e){
|
||||||
Log(e.ToString());
|
bool loggedSuccessfully = Log(e.ToString());
|
||||||
|
|
||||||
FormMessage form = new FormMessage(caption, message+"\r\nError: "+e.Message, canIgnore ? MessageBoxIcon.Warning : MessageBoxIcon.Error);
|
FormMessage form = new FormMessage(caption, message+"\r\nError: "+e.Message, canIgnore ? MessageBoxIcon.Warning : MessageBoxIcon.Error);
|
||||||
|
|
||||||
@@ -43,6 +53,8 @@ namespace TweetDck{
|
|||||||
|
|
||||||
Button btnOpenLog = new Button{
|
Button btnOpenLog = new Button{
|
||||||
Anchor = AnchorStyles.Bottom | AnchorStyles.Left,
|
Anchor = AnchorStyles.Bottom | AnchorStyles.Left,
|
||||||
|
Enabled = loggedSuccessfully,
|
||||||
|
Font = SystemFonts.MessageBoxFont,
|
||||||
Location = new Point(12, 12),
|
Location = new Point(12, 12),
|
||||||
Margin = new Padding(0, 0, 48, 0),
|
Margin = new Padding(0, 0, 48, 0),
|
||||||
Size = new Size(88, 26),
|
Size = new Size(88, 26),
|
||||||
|
@@ -12,7 +12,12 @@
|
|||||||
//
|
//
|
||||||
// Constant: Url that returns JSON data about latest version.
|
// Constant: Url that returns JSON data about latest version.
|
||||||
//
|
//
|
||||||
const updateCheckUrl = "https://api.github.com/repos/chylex/"+$TDU.brandName+"/releases/latest";
|
const updateCheckUrlLatest = "https://api.github.com/repos/chylex/"+$TDU.brandName+"/releases/latest";
|
||||||
|
|
||||||
|
//
|
||||||
|
// Constant: Url that returns JSON data about all versions, including prereleases.
|
||||||
|
//
|
||||||
|
const updateCheckUrlAll = "https://api.github.com/repos/chylex/"+$TDU.brandName+"/releases";
|
||||||
|
|
||||||
//
|
//
|
||||||
// Function: Creates the update notification element. Removes the old one if already exists.
|
// Function: Creates the update notification element. Removes the old one if already exists.
|
||||||
@@ -135,18 +140,24 @@
|
|||||||
clearTimeout(updateCheckTimeoutID);
|
clearTimeout(updateCheckTimeoutID);
|
||||||
updateCheckTimeoutID = setTimeout(runUpdateCheck, 1000*60*60); // 1 hour
|
updateCheckTimeoutID = setTimeout(runUpdateCheck, 1000*60*60); // 1 hour
|
||||||
|
|
||||||
if (!$TDU.updateCheckEnabled && !force)return;
|
if (!$TDU.updateCheckEnabled && !force){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$.getJSON(updateCheckUrl, function(response){
|
var allowPre = $TDU.allowPreReleases;
|
||||||
var tagName = response.tag_name;
|
|
||||||
var hasUpdate = tagName !== $TDU.versionTag && tagName !== $TDU.dismissedVersionTag && response.assets.length > 0;
|
$.getJSON(allowPre ? updateCheckUrlAll : updateCheckUrlLatest, function(response){
|
||||||
|
var release = allowPre ? response[0] : response;
|
||||||
|
|
||||||
|
var tagName = release.tag_name;
|
||||||
|
var hasUpdate = tagName !== $TDU.versionTag && tagName !== $TDU.dismissedVersionTag && release.assets.length > 0;
|
||||||
|
|
||||||
if (hasUpdate){
|
if (hasUpdate){
|
||||||
var obj = response.assets.find(asset => asset.name === updateFileName) || response.assets[0];
|
var obj = release.assets.find(asset => asset.name === updateFileName) || release.assets[0];
|
||||||
createUpdateNotificationElement(tagName, obj.browser_download_url);
|
createUpdateNotificationElement(tagName, obj.browser_download_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eventID !== 0){
|
if (eventID){ // ignore undefined and 0
|
||||||
$TDU.onUpdateCheckFinished(eventID, hasUpdate, tagName);
|
$TDU.onUpdateCheckFinished(eventID, hasUpdate, tagName);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -180,12 +180,6 @@
|
|||||||
<Compile Include="Core\Utils\CommandLineArgsParser.cs" />
|
<Compile Include="Core\Utils\CommandLineArgsParser.cs" />
|
||||||
<Compile Include="Core\Utils\WindowState.cs" />
|
<Compile Include="Core\Utils\WindowState.cs" />
|
||||||
<Compile Include="Core\Utils\WindowsUtils.cs" />
|
<Compile Include="Core\Utils\WindowsUtils.cs" />
|
||||||
<Compile Include="Migration\FormBackgroundWork.cs">
|
|
||||||
<SubType>Form</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Migration\FormBackgroundWork.Designer.cs">
|
|
||||||
<DependentUpon>FormBackgroundWork.cs</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="Core\Handling\TweetDeckBridge.cs" />
|
<Compile Include="Core\Handling\TweetDeckBridge.cs" />
|
||||||
<Compile Include="Core\Other\FormSettings.cs">
|
<Compile Include="Core\Other\FormSettings.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
@@ -193,7 +187,6 @@
|
|||||||
<Compile Include="Core\Other\FormSettings.Designer.cs">
|
<Compile Include="Core\Other\FormSettings.Designer.cs">
|
||||||
<DependentUpon>FormSettings.cs</DependentUpon>
|
<DependentUpon>FormSettings.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Migration\MigrationUtils.cs" />
|
|
||||||
<Compile Include="Plugins\Controls\PluginControl.cs">
|
<Compile Include="Plugins\Controls\PluginControl.cs">
|
||||||
<SubType>UserControl</SubType>
|
<SubType>UserControl</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -237,8 +230,6 @@
|
|||||||
<Compile Include="Updates\UpdateCheckEventArgs.cs" />
|
<Compile Include="Updates\UpdateCheckEventArgs.cs" />
|
||||||
<Compile Include="Updates\UpdateHandler.cs" />
|
<Compile Include="Updates\UpdateHandler.cs" />
|
||||||
<Compile Include="Updates\UpdateInfo.cs" />
|
<Compile Include="Updates\UpdateInfo.cs" />
|
||||||
<Compile Include="Migration\MigrationDecision.cs" />
|
|
||||||
<Compile Include="Migration\MigrationManager.cs" />
|
|
||||||
<Compile Include="Program.cs" />
|
<Compile Include="Program.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Properties\Resources.Designer.cs">
|
<Compile Include="Properties\Resources.Designer.cs">
|
||||||
@@ -247,6 +238,7 @@
|
|||||||
<DependentUpon>Resources.resx</DependentUpon>
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Resources\ScriptLoader.cs" />
|
<Compile Include="Resources\ScriptLoader.cs" />
|
||||||
|
<Compile Include="Updates\UpdaterSettings.cs" />
|
||||||
<None Include="Configuration\app.config" />
|
<None Include="Configuration\app.config" />
|
||||||
<None Include="Configuration\packages.config" />
|
<None Include="Configuration\packages.config" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@@ -10,21 +10,24 @@ namespace TweetDck.Updates{
|
|||||||
class UpdateHandler{
|
class UpdateHandler{
|
||||||
private readonly ChromiumWebBrowser browser;
|
private readonly ChromiumWebBrowser browser;
|
||||||
private readonly FormBrowser form;
|
private readonly FormBrowser form;
|
||||||
|
private readonly UpdaterSettings settings;
|
||||||
|
|
||||||
public event EventHandler<UpdateAcceptedEventArgs> UpdateAccepted;
|
public event EventHandler<UpdateAcceptedEventArgs> UpdateAccepted;
|
||||||
public event EventHandler<UpdateCheckEventArgs> CheckFinished;
|
public event EventHandler<UpdateCheckEventArgs> CheckFinished;
|
||||||
|
|
||||||
private int lastEventId;
|
private int lastEventId;
|
||||||
|
|
||||||
public UpdateHandler(ChromiumWebBrowser browser, FormBrowser form){
|
public UpdateHandler(ChromiumWebBrowser browser, FormBrowser form, UpdaterSettings settings){
|
||||||
this.browser = browser;
|
this.browser = browser;
|
||||||
this.form = form;
|
this.form = form;
|
||||||
|
this.settings = settings;
|
||||||
|
|
||||||
browser.FrameLoadEnd += browser_FrameLoadEnd;
|
browser.FrameLoadEnd += browser_FrameLoadEnd;
|
||||||
browser.RegisterJsObject("$TDU", new Bridge(this));
|
browser.RegisterJsObject("$TDU", new Bridge(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
||||||
if (e.Frame.IsMain){
|
if (e.Frame.IsMain && BrowserUtils.IsTweetDeckWebsite(e.Frame)){
|
||||||
ScriptLoader.ExecuteFile(e.Frame, "update.js");
|
ScriptLoader.ExecuteFile(e.Frame, "update.js");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,6 +74,12 @@ namespace TweetDck.Updates{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool AllowPreReleases{
|
||||||
|
get{
|
||||||
|
return owner.settings.AllowPreReleases;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsSystemSupported{
|
public bool IsSystemSupported{
|
||||||
get{
|
get{
|
||||||
return Environment.OSVersion.Version >= new Version("6.1"); // 6.1 NT version = Windows 7
|
return Environment.OSVersion.Version >= new Version("6.1"); // 6.1 NT version = Windows 7
|
||||||
|
5
Updates/UpdaterSettings.cs
Normal file
5
Updates/UpdaterSettings.cs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
namespace TweetDck.Updates{
|
||||||
|
class UpdaterSettings{
|
||||||
|
public bool AllowPreReleases { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@@ -96,6 +96,18 @@ begin
|
|||||||
Result := (PageID = wpSelectDir) and (UpdatePath <> '')
|
Result := (PageID = wpSelectDir) and (UpdatePath <> '')
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ Check for an old TweetDeck profile and show a warning before installation. }
|
||||||
|
procedure CurStepChanged(CurStep: TSetupStep);
|
||||||
|
begin
|
||||||
|
if CurStep = ssInstall then
|
||||||
|
begin
|
||||||
|
if DirExists(ExpandConstant('{localappdata}\twitter\TweetDeck')) then
|
||||||
|
begin
|
||||||
|
MsgBox('Detected a profile from an old TweetDeck installation, you may uninstall the old client to free up some space.', mbInformation, MB_OK)
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
{ Ask user if they want to delete 'AppData\TweetDuck' and 'plugins' folders after uninstallation. }
|
{ Ask user if they want to delete 'AppData\TweetDuck' and 'plugins' folders after uninstallation. }
|
||||||
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
|
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
|
||||||
var ProfileDataFolder: String;
|
var ProfileDataFolder: String;
|
||||||
|
@@ -47,7 +47,7 @@ Source: "..\bin\x86\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesu
|
|||||||
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Check: TDIsUninstallable
|
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Check: TDIsUninstallable
|
||||||
|
|
||||||
[Run]
|
[Run]
|
||||||
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall shellexec
|
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Parameters: "{param:RUNARGS}"; Flags: nowait postinstall shellexec
|
||||||
|
|
||||||
[InstallDelete]
|
[InstallDelete]
|
||||||
Type: files; Name: "{app}\*.xml"
|
Type: files; Name: "{app}\*.xml"
|
||||||
@@ -242,7 +242,8 @@ begin
|
|||||||
WizardForm.ProgressGauge.Style := npbstMarquee;
|
WizardForm.ProgressGauge.Style := npbstMarquee;
|
||||||
|
|
||||||
try
|
try
|
||||||
if Exec(InstallFile, '/SP- /SILENT /MERGETASKS="!desktopicon" /UPDATEPATH="'+UpdatePath+'"', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then begin
|
if Exec(InstallFile, '/SP- /SILENT /MERGETASKS="!desktopicon" /UPDATEPATH="'+UpdatePath+'"', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
|
||||||
|
begin
|
||||||
if ResultCode <> 0 then
|
if ResultCode <> 0 then
|
||||||
begin
|
begin
|
||||||
DeleteFile(InstallFile);
|
DeleteFile(InstallFile);
|
||||||
|
Reference in New Issue
Block a user