diff --git a/QuickLook.Common/Helpers/ProcessHelper.cs b/QuickLook.Common/Helpers/ProcessHelper.cs index 2afebbb..35262cc 100644 --- a/QuickLook.Common/Helpers/ProcessHelper.cs +++ b/QuickLook.Common/Helpers/ProcessHelper.cs @@ -16,6 +16,7 @@ // along with this program. If not, see . using System; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Threading.Tasks; @@ -63,6 +64,8 @@ namespace QuickLook.Common.Helpers public static void WriteLog(string msg) { + Debug.WriteLine(msg); + var logFilePath = Path.Combine(SettingHelper.LocalDataPath, @"QuickLook.Exception.log"); using (var writer = new StreamWriter(new FileStream(logFilePath, FileMode.OpenOrCreate, diff --git a/QuickLook.Plugin/QuickLook.Plugin.PluginInstaller/Plugin.cs b/QuickLook.Plugin/QuickLook.Plugin.PluginInstaller/Plugin.cs index c0d93f2..5bb906a 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.PluginInstaller/Plugin.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.PluginInstaller/Plugin.cs @@ -33,12 +33,12 @@ namespace QuickLook.Plugin.PluginInstaller public bool CanHandle(string path) { - return !Directory.Exists(path) && path.ToLower().EndsWith(".qlviewer"); + return !Directory.Exists(path) && path.ToLower().EndsWith(".qlplugin"); } public void Prepare(string path, ContextObject context) { - context.PreferredSize = new Size { Width = 400, Height = 172 }; + context.PreferredSize = new Size { Width = 460, Height = 200 }; context.Title = ""; context.TitlebarOverlap = false; @@ -50,7 +50,7 @@ namespace QuickLook.Plugin.PluginInstaller public void View(string path, ContextObject context) { - context.ViewerContent = new PluginInfoPanel(path); + context.ViewerContent = new PluginInfoPanel(path, context); context.IsBusy = false; } diff --git a/QuickLook.Plugin/QuickLook.Plugin.PluginInstaller/PluginInfoPanel.xaml b/QuickLook.Plugin/QuickLook.Plugin.PluginInstaller/PluginInfoPanel.xaml index 694466c..4420267 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.PluginInstaller/PluginInfoPanel.xaml +++ b/QuickLook.Plugin/QuickLook.Plugin.PluginInstaller/PluginInfoPanel.xaml @@ -4,7 +4,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" FontSize="14" - mc:Ignorable="d" Width="400" Height="172" UseLayoutRounding="True"> + mc:Ignorable="d" Width="460" Height="200"> diff --git a/QuickLook.Plugin/QuickLook.Plugin.PluginInstaller/PluginInfoPanel.xaml.cs b/QuickLook.Plugin/QuickLook.Plugin.PluginInstaller/PluginInfoPanel.xaml.cs index f536695..fe73360 100644 --- a/QuickLook.Plugin/QuickLook.Plugin.PluginInstaller/PluginInfoPanel.xaml.cs +++ b/QuickLook.Plugin/QuickLook.Plugin.PluginInstaller/PluginInfoPanel.xaml.cs @@ -16,19 +16,24 @@ // along with this program. If not, see . using System; -using System.Globalization; using System.IO; using System.IO.Compression; +using System.Threading.Tasks; +using System.Windows; using System.Windows.Controls; using System.Xml; +using QuickLook.Common.ExtensionMethods; +using QuickLook.Common.Plugin; namespace QuickLook.Plugin.PluginInstaller { public partial class PluginInfoPanel : UserControl { + private readonly ContextObject _context; private readonly string _path; + private string _namespace; - public PluginInfoPanel(string path) + public PluginInfoPanel(string path, ContextObject context) { InitializeComponent(); @@ -36,19 +41,87 @@ namespace QuickLook.Plugin.PluginInstaller Resources.MergedDictionaries[0].Clear(); _path = path; + _context = context; ReadInfo(); + + btnInstall.Click += BtnInstall_Click; } + private void BtnInstall_Click(object sender, RoutedEventArgs e) + { + btnInstall.Content = "Installing ..."; + btnInstall.IsEnabled = false; + + var t=DoInstall(); + t.ContinueWith(_ => + Dispatcher.BeginInvoke(new Action(() => btnInstall.Content = "Done! Please restart QuickLook."))); + t.Start(); + } + + private Task DoInstall() + { + var targetFolder = Path.Combine(App.UserPluginPath, _namespace); + return new Task(() => + { + CleanUp(); + + try + { + ZipFile.ExtractToDirectory(_path, targetFolder); + } + catch (Exception ex) + { + Dispatcher.BeginInvoke(new Action(() => description.Text = ex.Message)); + CleanUp(); + } + }); + + void CleanUp() + { + if (!Directory.Exists(targetFolder)) + { + Directory.CreateDirectory(targetFolder); + return; + } + + try + { + Directory.GetFiles(targetFolder, "*", SearchOption.AllDirectories) + .ForEach(file => File.Move(file, new Guid() + ".to_be_deleted")); + } + catch (Exception ex) + { + Dispatcher.BeginInvoke(new Action(() => description.Text = ex.Message)); + } + } + } + + private void ReadInfo() { filename.Text = Path.GetFileNameWithoutExtension(_path); - var xml = LoadXml(GetFileFromZip(_path, "Metadata.config")); + var xml = LoadXml(GetFileFromZip(_path, "QuickLook.Plugin.Metadata.config")); + + _namespace = GetString(xml, @"/Metadata/Namespace"); + + var okay = _namespace != null && _namespace.StartsWith("QuickLook.Plugin."); + + filename.Text = okay ? _namespace : "Invalid plugin."; + description.Text = GetString(xml, @"/Metadata/Description", string.Empty); + + btnInstall.Visibility = okay ? Visibility.Visible : Visibility.Collapsed; } - private XmlDocument LoadXml(Stream data) + private static string GetString(XmlNode xml, string xpath, string def = null) { + var n = xml?.SelectSingleNode(xpath); + return n?.InnerText; + } + + private static XmlDocument LoadXml(Stream data) + { var doc = new XmlDocument(); try { @@ -61,18 +134,26 @@ namespace QuickLook.Plugin.PluginInstaller } } - private Stream GetFileFromZip(string archive, string entry) + private static MemoryStream GetFileFromZip(string archive, string entry) { var ms = new MemoryStream(); - using (var zip = ZipFile.Open(archive, ZipArchiveMode.Read)) + try { - using (var s = zip.GetEntry(entry)?.Open()) + using (var zip = ZipFile.Open(archive, ZipArchiveMode.Read)) { - s?.CopyTo(ms); + using (var s = zip?.GetEntry(entry)?.Open()) + { + s?.CopyTo(ms); + } } } + catch (Exception) + { + return ms; + } + ms.Position = 0; return ms; } } diff --git a/QuickLook/App.xaml.cs b/QuickLook/App.xaml.cs index 4b02637..2dd0efd 100644 --- a/QuickLook/App.xaml.cs +++ b/QuickLook/App.xaml.cs @@ -34,6 +34,7 @@ namespace QuickLook /// public partial class App : Application { + public static readonly string UserPluginPath = Path.Combine(SettingHelper.LocalDataPath, "QuickLook.Plugin\\"); public static readonly string AppFullPath = Assembly.GetExecutingAssembly().Location; public static readonly string AppPath = Path.GetDirectoryName(AppFullPath); public static readonly bool Is64Bit = Environment.Is64BitProcess; diff --git a/QuickLook/PluginManager.cs b/QuickLook/PluginManager.cs index cf174db..d465dc8 100644 --- a/QuickLook/PluginManager.cs +++ b/QuickLook/PluginManager.cs @@ -22,6 +22,7 @@ using System.IO; using System.Linq; using System.Reflection; using QuickLook.Common.ExtensionMethods; +using QuickLook.Common.Helpers; using QuickLook.Common.Plugin; namespace QuickLook @@ -32,7 +33,10 @@ namespace QuickLook private PluginManager() { - LoadPlugins(); + CleanupOldPlugins(App.UserPluginPath); + LoadPlugins(App.UserPluginPath); + LoadPlugins(Path.Combine(App.AppPath, "QuickLook.Plugin\\")); + InitLoadedPlugins(); } internal IViewer DefaultPlugin { get; } = new Plugin.InfoPanel.Plugin(); @@ -74,9 +78,12 @@ namespace QuickLook return (matched ?? DefaultPlugin).GetType().CreateInstance(); } - private void LoadPlugins() + private void LoadPlugins(string folder) { - Directory.GetFiles(Path.Combine(App.AppPath, "QuickLook.Plugin\\"), "QuickLook.Plugin.*.dll", + if (!Directory.Exists(folder)) + return; + + Directory.GetFiles(folder, "QuickLook.Plugin.*.dll", SearchOption.AllDirectories) .ToList() .ForEach( @@ -90,7 +97,10 @@ namespace QuickLook }); LoadedPlugins = LoadedPlugins.OrderByDescending(i => i.Priority).ToList(); + } + private void InitLoadedPlugins() + { LoadedPlugins.ForEach(i => { try @@ -99,7 +109,25 @@ namespace QuickLook } catch (Exception e) { - Debug.WriteLine(e); + ProcessHelper.WriteLog(e.ToString()); + } + }); + } + + private static void CleanupOldPlugins(string folder) + { + if (!Directory.Exists(folder)) + return; + + Directory.GetFiles(folder, "*.to_be_deleted", SearchOption.AllDirectories).ForEach(file => + { + try + { + File.Delete(file); + } + catch (Exception) + { + // ignored } }); }