Add Executable Plugin Loader

This commit is contained in:
qianlifeng 2014-07-05 23:10:34 +08:00
parent f01de3a69d
commit 659ff866e1
17 changed files with 850 additions and 542 deletions

View file

@ -59,7 +59,7 @@ namespace Wox.Plugin.SystemPlugins.CMD
}
catch (Exception) { }
context.PushResults(query, new List<Result>() { result });
context.API.PushResults(query,context.CurrentPluginMetadata, new List<Result>() { result });
pushedResults.Add(result);
IEnumerable<Result> history = CMDStorage.Instance.CMDHistory.Where(o => o.Key.Contains(cmd))
@ -95,7 +95,7 @@ namespace Wox.Plugin.SystemPlugins.CMD
return ret;
}).Where(o => o != null).Take(4);
context.PushResults(query, history.ToList());
context.API.PushResults(query,context.CurrentPluginMetadata,history.ToList());
pushedResults.AddRange(history);
try

View file

@ -11,11 +11,13 @@ namespace Wox.Plugin.SystemPlugins
public class ThirdpartyPluginIndicator : BaseSystemPlugin
{
private List<PluginPair> allPlugins = new List<PluginPair>();
private Action<string> changeQuery;
private PluginInitContext context;
protected override List<Result> QueryInternal(Query query)
{
List<Result> results = new List<Result>();
if(allPlugins.Count == 0) allPlugins = context.API.GetAllPlugins();
foreach (PluginMetadata metadata in allPlugins.Select(o => o.Metadata))
{
@ -36,7 +38,7 @@ namespace Wox.Plugin.SystemPlugins
IcoPath = "Images/work.png",
Action = (c) =>
{
changeQuery(metadataCopy.ActionKeyword + " ");
context.ChangeQuery(metadataCopy.ActionKeyword + " ");
return false;
},
};
@ -52,7 +54,7 @@ namespace Wox.Plugin.SystemPlugins
IcoPath = "Images/work.png",
Action = (c) =>
{
changeQuery(n.ActionWord + " ");
context.ChangeQuery(n.ActionWord + " ");
return false;
}
}));
@ -62,8 +64,7 @@ namespace Wox.Plugin.SystemPlugins
protected override void InitInternal(PluginInitContext context)
{
allPlugins = context.Plugins;
changeQuery = context.ChangeQuery;
this.context = context;
}

View file

@ -32,7 +32,7 @@ namespace Wox.Plugin.SystemPlugins
title = subtitle;
subtitle = null;
}
context.PushResults(query, new List<Result>()
context.API.PushResults(query,context.CurrentPluginMetadata, new List<Result>()
{
new Result()
{
@ -54,7 +54,7 @@ namespace Wox.Plugin.SystemPlugins
var result = sugg.GetSuggestions(keyword);
if (result != null)
{
context.PushResults(query, result.Select(o => new Result()
context.API.PushResults(query,context.CurrentPluginMetadata, result.Select(o => new Result()
{
Title = o,
SubTitle = subtitle,

View file

@ -17,9 +17,16 @@ namespace Wox.Plugin
get { return "csharp"; }
}
public static string ExecutableFile
{
get { return "ExecutableFile"; }
}
public static bool IsAllowed(string language)
{
return language.ToUpper() == Python.ToUpper() || language.ToUpper() == CSharp.ToUpper();
return language.ToUpper() == Python.ToUpper()
|| language.ToUpper() == CSharp.ToUpper()
|| language.ToUpper() == ExecutableFile.ToUpper();
}
}
}

38
Wox.Plugin/IPublicAPI.cs Normal file
View file

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Windows.Documents;
namespace Wox.Plugin
{
public interface IPublicAPI
{
void PushResults(Query query,PluginMetadata plugin, List<Result> results);
bool ShellRun(string cmd);
void ChangeQuery(string query, bool requery = false);
void CloseApp();
void HideApp();
void ShowApp();
void ShowMsg(string title, string subTitle, string iconPath);
void OpenSettingDialog();
void ShowCurrentResultItemTooltip(string tooltip);
void StartLoadingBar();
void StopLoadingBar();
void InstallPlugin(string path);
void ReloadPlugins();
List<PluginPair> GetAllPlugins();
}
}

View file

@ -7,31 +7,87 @@ namespace Wox.Plugin
{
public class PluginInitContext
{
public List<PluginPair> Plugins { get; set; }
public PluginMetadata CurrentPluginMetadata { get; set; }
public Action<string> ChangeQuery { get; set; }
public Action CloseApp { get; set; }
public Action HideApp { get; set; }
public Action ShowApp { get; set; }
public Action<string, string, string> ShowMsg { get; set; }
public Action OpenSettingDialog { get; set; }
public Action<string> ShowCurrentResultItemTooltip { get; set; }
/// <summary>
/// reload all plugins
/// Public APIs for plugin invocation
/// </summary>
public Action ReloadPlugins { get; set; }
public IPublicAPI API { get; set; }
public Action<string> InstallPlugin { get; set; }
#region Legacy APIs
public Action StartLoadingBar { get; set; }
public Action StopLoadingBar { get; set; }
[Obsolete("This method has been obsoleted, use API.ShellRun instead")]
public bool ShellRun(string cmd)
{
return API.ShellRun(cmd);
}
public Func<string, bool> ShellRun { get; set; }
[Obsolete("This method has been obsoleted, use API.OpenSettingDialog instead")]
public void ChangeQuery(string query, bool requery = false)
{
API.ChangeQuery(query, requery);
}
public Action<Query, List<Result>> PushResults { get; set; }
[Obsolete("This method has been obsoleted, use API.CloseApp instead")]
public void CloseApp()
{
API.CloseApp();
}
[Obsolete("This method has been obsoleted, use API.HideApp instead")]
public void HideApp()
{
API.HideApp();
}
[Obsolete("This method has been obsoleted, use API.ShowApp instead")]
public void ShowApp()
{
API.ShowApp();
}
[Obsolete("This method has been obsoleted, use API.OpenSettingDialog instead")]
public void ShowMsg(string title, string subTitle, string iconPath)
{
API.ShowMsg(title, subTitle, iconPath);
}
[Obsolete("This method has been obsoleted, use API.OpenSettingDialog instead")]
public void OpenSettingDialog()
{
API.OpenSettingDialog();
}
[Obsolete("This method has been obsoleted, use API.ShowCurrentResultItemTooltip instead")]
public void ShowCurrentResultItemTooltip(string tooltip)
{
API.ShowCurrentResultItemTooltip(tooltip);
}
[Obsolete("This method has been obsoleted, use API.StartLoadingBar instead")]
public void StartLoadingBar()
{
API.StartLoadingBar();
}
[Obsolete("This method has been obsoleted, use API.StopLoadingBar instead")]
public void StopLoadingBar()
{
API.StopLoadingBar();
}
[Obsolete("This method has been obsoleted, use API.InstallPlugin instead")]
public void InstallPlugin(string path)
{
API.InstallPlugin(path);
}
[Obsolete("This method has been obsoleted, use API.ReloadPlugins instead")]
public void ReloadPlugins()
{
API.ReloadPlugins();
}
#endregion
}
}

View file

@ -3,6 +3,7 @@ using System.Collections;
using System.Collections.Generic;
namespace Wox.Plugin {
public class Result {
public string Title { get; set; }
public string SubTitle { get; set; }
@ -41,7 +42,6 @@ namespace Wox.Plugin {
return Title + SubTitle;
}
public Result() {
}
@ -52,6 +52,5 @@ namespace Wox.Plugin {
this.SubTitle = SubTitle;
}
}
}

View file

@ -46,6 +46,7 @@
<ItemGroup>
<Compile Include="AllowedLanguage.cs" />
<Compile Include="IPlugin.cs" />
<Compile Include="IPublicAPI.cs" />
<Compile Include="ISettingProvider.cs" />
<Compile Include="PluginPair.cs" />
<Compile Include="PluginInitContext.cs" />

View file

@ -15,40 +15,29 @@ namespace Wox.Commands
private string currentPythonModulePath = string.Empty;
private IntPtr GIL;
public override void Dispatch(Query q)
public override void Dispatch(Query query)
{
PluginPair thirdPlugin = Plugins.AllPlugins.FirstOrDefault(o => o.Metadata.ActionKeyword == q.ActionName);
PluginPair thirdPlugin = Plugins.AllPlugins.FirstOrDefault(o => o.Metadata.ActionKeyword == query.ActionName);
if (thirdPlugin != null && !string.IsNullOrEmpty(thirdPlugin.Metadata.ActionKeyword))
{
var customizedPluginConfig = UserSettingStorage.Instance.CustomizedPluginConfigs.FirstOrDefault(o => o.ID == thirdPlugin.Metadata.ID);
if (customizedPluginConfig != null && customizedPluginConfig.Disabled)
{
//need to stop the loading animation
UpdateResultView(null);
return;
}
if (thirdPlugin.Metadata.Language == AllowedLanguage.Python)
{
SwitchPythonEnv(thirdPlugin);
}
//if (thirdPlugin.Metadata.Language == AllowedLanguage.Python)
//{
// SwitchPythonEnv(thirdPlugin);
//}
ThreadPool.QueueUserWorkItem(t =>
{
try
{
thirdPlugin.InitContext.PushResults = (qu, r) =>
{
if (r != null)
{
r.ForEach(o =>
{
o.PluginDirectory = thirdPlugin.Metadata.PluginDirecotry;
o.OriginQuery = qu;
});
UpdateResultView(r);
}
};
List<Result> results = thirdPlugin.Plugin.Query(q) ?? new List<Result>();
thirdPlugin.InitContext.PushResults(q, results);
List<Result> results = thirdPlugin.Plugin.Query(query) ?? new List<Result>();
App.Window.PushResults(query,thirdPlugin.Metadata,results);
}
catch (Exception queryException)
{

View file

@ -19,19 +19,10 @@ namespace Wox.Commands
ThreadPool.QueueUserWorkItem(state =>
{
pair1.InitContext.PushResults = (q, r) =>
{
foreach (Result result in r)
{
result.PluginDirectory = pair1.Metadata.PluginDirecotry;
result.OriginQuery = q;
result.AutoAjustScore = true;
}
UpdateResultView(r);
};
List<Result> results = pair1.Plugin.Query(query);
pair1.InitContext.PushResults(query, results);
results.ForEach(o=> { o.AutoAjustScore = true; });
App.Window.PushResults(query,pair1.Metadata,results);
});
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Wox.Plugin;
namespace Wox.PluginLoader
{
public class ExecutablePluginLoader : BasePluginLoader
{
public override List<PluginPair> LoadPlugin()
{
List<PluginPair> plugins = new List<PluginPair>();
List<PluginMetadata> metadatas = pluginMetadatas.Where(o => o.Language.ToUpper() == AllowedLanguage.ExecutableFile.ToUpper()).ToList();
foreach (PluginMetadata metadata in metadatas)
{
ExecutablePluginWrapper executer = new ExecutablePluginWrapper();
PluginPair pair = new PluginPair()
{
Plugin = executer,
Metadata = metadata
};
plugins.Add(pair);
}
return plugins;
}
}
}

View file

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
using Wox.Plugin;
using Wox.RPC;
namespace Wox.PluginLoader
{
public class ExecutablePluginWrapper : IPlugin
{
private PluginInitContext context;
private static string executeDirectory = Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath);
public List<Result> Query(Query query)
{
List<Result> results = new List<Result>();
try
{
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = Path.Combine(executeDirectory, "PYTHONTHOME\\Scripts\\python.exe");
start.Arguments = string.Format("{0} \"{1}\"",
context.CurrentPluginMetadata.ExecuteFilePath,
RPC.JsonRPC.GetRPC("query", query.GetAllRemainingParameter()));
start.UseShellExecute = false;
start.CreateNoWindow = true;
start.RedirectStandardOutput = true;
using (Process process = Process.Start(start))
{
if (process != null)
{
using (StreamReader reader = process.StandardOutput)
{
string output = reader.ReadToEnd();
if (!string.IsNullOrEmpty(output))
{
JsonPRCModel rpc = JsonConvert.DeserializeObject<JsonPRCModel>(output);
var rpcresults = JsonConvert.DeserializeObject<List<ActionJsonRPCResult>>(rpc.result);
List<Result> r = new List<Result>();
foreach (ActionJsonRPCResult result in rpcresults)
{
if (!string.IsNullOrEmpty(result.ActionJSONRPC))
{
result.Action = (context) =>
{
return true;
};
}
r.Add(result);
}
return r;
}
}
}
}
}
catch
{
}
return results;
}
public void Init(PluginInitContext context)
{
this.context = context;
}
}
}

View file

@ -1,96 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Microsoft.CSharp;
using Wox.Helper;
using Wox.Infrastructure;
using Wox.Infrastructure.Storage;
using Wox.Infrastructure.Storage.UserSettings;
using Wox.Plugin;
namespace Wox.PluginLoader {
public static class Plugins {
//private static string debuggerMode = null;
public static String DebuggerMode { get; private set; }
namespace Wox.PluginLoader
{
public static class Plugins
{
public static String DebuggerMode { get; private set; }
private static List<PluginPair> plugins = new List<PluginPair>();
private static List<PluginPair> plugins = new List<PluginPair>();
private static ManualResetEvent initializing = null;
public static void Init()
{
plugins.Clear();
BasePluginLoader.ParsePluginsConfig();
public static void Init() {
if (initializing != null) return;
plugins.AddRange(new PythonPluginLoader().LoadPlugin());
plugins.AddRange(new CSharpPluginLoader().LoadPlugin());
plugins.AddRange(new ExecutablePluginLoader().LoadPlugin());
initializing = new ManualResetEvent(false);
plugins.Clear();
BasePluginLoader.ParsePluginsConfig();
plugins.AddRange(new PythonPluginLoader().LoadPlugin());
plugins.AddRange(new CSharpPluginLoader().LoadPlugin());
Forker forker = new Forker();
foreach (IPlugin plugin in plugins.Select(pluginPair => pluginPair.Plugin)) {
IPlugin plugin1 = plugin;
PluginPair pluginPair = plugins.FirstOrDefault(o => o.Plugin == plugin1);
if (pluginPair != null) {
PluginMetadata metadata = pluginPair.Metadata;
pluginPair.InitContext = new PluginInitContext() {
Plugins = plugins,
CurrentPluginMetadata = metadata,
ChangeQuery = s => App.Window.Dispatcher.Invoke(new Action(() => App.Window.ChangeQuery(s))),
CloseApp = () => App.Window.Dispatcher.Invoke(new Action(() => App.Window.CloseApp())),
HideApp = () => App.Window.Dispatcher.Invoke(new Action(() => App.Window.HideApp())),
ShowApp = () => App.Window.Dispatcher.Invoke(new Action(() => App.Window.ShowApp())),
ShowMsg = (title, subTitle, iconPath) => App.Window.Dispatcher.Invoke(new Action(() =>
App.Window.ShowMsg(title, subTitle, iconPath))),
OpenSettingDialog = () => App.Window.Dispatcher.Invoke(new Action(() => App.Window.OpenSettingDialog())),
ShowCurrentResultItemTooltip = (msg) => App.Window.Dispatcher.Invoke(new Action(() => App.Window.ShowCurrentResultItemTooltip(msg))),
ReloadPlugins = () => App.Window.Dispatcher.Invoke(new Action(() => Init())),
InstallPlugin = (filePath) => App.Window.Dispatcher.Invoke(new Action(() => {
PluginInstaller.Install(filePath);
})),
StartLoadingBar = () => App.Window.Dispatcher.Invoke(new Action(() => App.Window.StartLoadingBar())),
StopLoadingBar = () => App.Window.Dispatcher.Invoke(new Action(() => App.Window.StopLoadingBar())),
//ShellRun = (cmd) => (bool)App.Window.Dispatcher.Invoke(new Func<bool>(() => App.Window.ShellRun(cmd)))
};
Forker forker = new Forker();
foreach (IPlugin plugin in plugins.Select(pluginPair => pluginPair.Plugin))
{
IPlugin plugin1 = plugin;
PluginPair pluginPair = plugins.FirstOrDefault(o => o.Plugin == plugin1);
if (pluginPair != null)
{
PluginMetadata metadata = pluginPair.Metadata;
pluginPair.InitContext = new PluginInitContext()
{
CurrentPluginMetadata = metadata,
API = App.Window
};
pluginPair.InitContext.ShellRun = (cmd) => {
try {
return (bool)App.Window.Dispatcher.Invoke(new Func<bool>(() => App.Window.ShellRun(cmd)));
}
catch (Exception) {
return false;
}
};
forker.Fork(() => plugin1.Init(pluginPair.InitContext));
}
}
forker.Fork(() => plugin1.Init(pluginPair.InitContext));
}
}
forker.Join();
}
ThreadPool.QueueUserWorkItem(o => {
forker.Join();
initializing.Set();
initializing = null;
});
}
public static List<PluginPair> AllPlugins
{
get
{
return plugins;
}
}
public static List<PluginPair> AllPlugins {
get {
var init = initializing;
if (init != null) {
init.WaitOne();
}
return plugins;
}
}
public static bool HitThirdpartyKeyword(Query query)
{
if (string.IsNullOrEmpty(query.ActionName)) return false;
public static bool HitThirdpartyKeyword(Query query) {
if (string.IsNullOrEmpty(query.ActionName)) return false;
return plugins.Any(o => o.Metadata.PluginType == PluginType.ThirdParty && o.Metadata.ActionKeyword == query.ActionName);
}
return plugins.Any(o => o.Metadata.PluginType == PluginType.ThirdParty && o.Metadata.ActionKeyword == query.ActionName);
}
public static void ActivatePluginDebugger(string path) {
DebuggerMode = path;
}
}
public static void ActivatePluginDebugger(string path)
{
DebuggerMode = path;
}
}
}

17
Wox/RPC/JsonPRCModel.cs Normal file
View file

@ -0,0 +1,17 @@
using Wox.Plugin;
namespace Wox.RPC
{
public class JsonPRCModel
{
public int id { get; set; }
public string jsonrpc { get; set; }
public string result { get; set; }
}
public class ActionJsonRPCResult : Result
{
public string ActionJSONRPC { get; set; }
}
}

20
Wox/RPC/JsonRPC.cs Normal file
View file

@ -0,0 +1,20 @@
using System.Collections.Generic;
using System.Linq;
namespace Wox.RPC
{
public class JsonRPC
{
public static string GetRPC(string method, List<string> paras)
{
var list = paras.Select(s => string.Format(@"\""{0}\""", s));
return string.Format(@"{{\""jsonrpc\"": \""2.0\"",\""method\"": \""{0}\"", \""params\"": [{1}], \""id\"": 1}}",
method, string.Join(",", list.ToArray()));
}
public static string GetRPC(string method, string para)
{
return GetRPC(method, new List<string>() { para });
}
}
}

View file

@ -138,11 +138,15 @@
<DependentUpon>HotkeyControl.xaml</DependentUpon>
</Compile>
<Compile Include="Converters\ImagePathConverter.cs" />
<Compile Include="RPC\JsonRPC.cs" />
<Compile Include="Msg.xaml.cs">
<DependentUpon>Msg.xaml</DependentUpon>
</Compile>
<Compile Include="PluginLoader\BasePluginLoader.cs" />
<Compile Include="PluginLoader\CSharpPluginLoader.cs" />
<Compile Include="PluginLoader\ExecutablePluginLoader.cs" />
<Compile Include="PluginLoader\ExecutablePluginWrapper.cs" />
<Compile Include="RPC\JsonPRCModel.cs" />
<Compile Include="PluginLoader\Plugins.cs" />
<Compile Include="PluginLoader\PythonPluginLoader.cs" />
<Compile Include="PluginLoader\PythonPluginWrapper.cs" />