...新手在这里:D
我正在尝试研究插件机制
我在Winform中有一个exe和dll,并且主应用程序都有一个文本框,其中包含两个用于设置和获取文本框文本的函数,例如,这两个函数就是API。
public void SetDataX(string data)
{
textboxx.Text = data;
}
public string GetDataX()
{
return textboxx.Text;
}
好的,然后我创建了一个类来保存函数并将其添加到dll和exe中:
plugin_interface.cs
namespace Plugin_Mech_Study
{
public class app_api
{
public Action<string> SetData { get; set; }
public Func<string> GetData { get; set; }
}
}
在dll中,我做了一个接受app_api
的函数,并将其命名为Load(app_api apibridge)
现在,当我尝试反射来调用并将app_api传递给dll时,出现此错误:
System.ArgumentException:'类型为'Plugin_Mech_Study.app_api'的对象 无法转换为'pluginTest.app_api'类型。'
这是我调用dll的方式:
private void load_plugin(string pluginadd)
{
var loadplugin = Assembly.LoadFile(pluginadd);
Type t = loadplugin.GetType("pluginTest.plugin");
app_api newapi = new app_api();
newapi.SetData = SetDataX;
newapi.GetData = GetDataX;
var apimethod = t.GetMethod("Load");
if (apimethod == null)
{
MessageBox.Show("Can't Generate API!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
Environment.Exit(501);
}
var o2 = Activator.CreateInstance(t);
var result2 = apimethod.Invoke(o2, new object[] { newapi }); /// Error Happens Here
}
如何解决此问题? 如果问题不清楚,我可以上传源代码 谢谢
此处是最小代码
Plugin_Mech_Study [Winform exe] => Program.cs
using System;
using System.Windows.Forms;
namespace Plugin_Mech_Study
{
static class Program
{
[STAThread]
static void Main()
{
Application.Run(new mainapp());
}
}
}
Plugin_Mech_Study [Winform exe] => mainapp.cs
using System;
using System.Windows.Forms;
using System.Reflection;
namespace Plugin_Mech_Study
{
public partial class mainapp : Form
{
public mainapp()
{
InitializeComponent();
}
public void SetDataX(string data)
{
textboxx.Text = data;
}
public string GetDataX()
{
return textboxx.Text;
}
private void load_plugin(string pluginadd)
{
var loadplugin = Assembly.LoadFile(pluginadd);
Type t = loadplugin.GetType("pluginTest.plugin");
var guimethod = t.GetMethod("GetControl");
if (guimethod == null)
{
MessageBox.Show("Can't Load GUI!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
var o = Activator.CreateInstance(t);
var result = guimethod.Invoke(o, null);
plug_ui.Controls.Add((UserControl)result);
app_api newapi = new app_api();
newapi.SetData = SetDataX;
newapi.GetData = GetDataX;
var apimethod = t.GetMethod("Load");
if (apimethod == null)
{
MessageBox.Show("Can't Generate API!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
var o2 = Activator.CreateInstance(t);
var result2 = apimethod.Invoke(o2, new object[] { newapi });
}
private void button1_Click(object sender, EventArgs e)
{
load_plugin(Environment.CurrentDirectory + @"\tzplugins\pluginTest.dll");
}
}
}
Plugin_Mech_Study [Winform exe] => mainapp.Designer.cs
namespace Plugin_Mech_Study
{
partial class mainapp
{
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.textboxx = new System.Windows.Forms.TextBox();
this.plug_ui = new System.Windows.Forms.Panel();
this.SuspendLayout();
this.button1.BackColor = System.Drawing.Color.SteelBlue;
this.button1.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.button1.ForeColor = System.Drawing.Color.AntiqueWhite;
this.button1.Location = new System.Drawing.Point(253, 24);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(147, 52);
this.button1.TabIndex = 0;
this.button1.Text = "Load Plugin";
this.button1.UseVisualStyleBackColor = false;
this.button1.Click += new System.EventHandler(this.button1_Click);
this.textboxx.BackColor = System.Drawing.SystemColors.Info;
this.textboxx.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.textboxx.Location = new System.Drawing.Point(12, 24);
this.textboxx.Multiline = true;
this.textboxx.Name = "textboxx";
this.textboxx.Size = new System.Drawing.Size(235, 170);
this.textboxx.TabIndex = 2;
this.textboxx.Text = "This is a Test";
this.plug_ui.BackColor = System.Drawing.SystemColors.Info;
this.plug_ui.Location = new System.Drawing.Point(253, 82);
this.plug_ui.Name = "plug_ui";
this.plug_ui.Size = new System.Drawing.Size(147, 112);
this.plug_ui.TabIndex = 3;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.RoyalBlue;
this.ClientSize = new System.Drawing.Size(417, 210);
this.Controls.Add(this.plug_ui);
this.Controls.Add(this.textboxx);
this.Controls.Add(this.button1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
this.Name = "mainapp";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Plugin Loader";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button button1;
private System.Windows.Forms.TextBox textboxx;
private System.Windows.Forms.Panel plug_ui;
}
}
Plugin_Mech_Study [Winform exe] => plugin_interface.cs
using System;
namespace Plugin_Mech_Study
{
public interface api_interface
{
Action<string> SetData { get; set; }
Func<string> GetData { get; set; }
}
public class app_api : api_interface
{
public Action<string> SetData { get; set; }
public Func<string> GetData { get; set; }
}
}
pluginTest [类库] => plugin.cs
using System.Windows.Forms;
namespace pluginTest
{
public class plugin : plugin_interface
{
private plugin_UI pluginUI;
public UserControl GetControl() {
var new_gui = new plugin_UI();
pluginUI = new_gui;
return new_gui;
}
public void Load(api_interface apibridge) {
pluginUI.LoadPlugin(apibridge);
}
}
}
pluginTest [类库] => plugin_interface.cs
using System;
using System.Windows.Forms;
namespace pluginTest
{
public interface plugin_interface
{
UserControl GetControl();
void Load(api_interface apibridge);
}
public interface api_interface
{
Action<string> SetData { get; set; }
Func<string> GetData { get; set; }
}
public class app_api : api_interface
{
public Action<string> SetData { get; set; }
public Func<string> GetData { get; set; }
}
}
pluginTest [类库] => plugin_UI.cs
using System;
using System.Windows.Forms;
namespace pluginTest
{
public partial class plugin_UI : UserControl
{
api_interface bridgedAPI;
public void LoadPlugin(api_interface apibridge)
{
bridgedAPI = apibridge;
}
public plugin_UI()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = bridgedAPI.GetData();
}
private void button2_Click(object sender, EventArgs e)
{
bridgedAPI.SetData(textBox1.Text);
}
}
}
pluginTest [类库] => plugin_UI.Designer.cs
namespace pluginTest
{
partial class plugin_UI
{
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.button2 = new System.Windows.Forms.Button();
this.textBox1 = new System.Windows.Forms.TextBox();
this.SuspendLayout();
this.button1.BackColor = System.Drawing.SystemColors.HotTrack;
this.button1.Font = new System.Drawing.Font("Microsoft Sans Serif", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.button1.ForeColor = System.Drawing.SystemColors.Control;
this.button1.Location = new System.Drawing.Point(15, 14);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(118, 25);
this.button1.TabIndex = 0;
this.button1.Text = "Get Data";
this.button1.UseVisualStyleBackColor = false;
this.button1.Click += new System.EventHandler(this.button1_Click);
this.button2.BackColor = System.Drawing.SystemColors.HotTrack;
this.button2.Font = new System.Drawing.Font("Microsoft Sans Serif", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.button2.ForeColor = System.Drawing.SystemColors.Control;
this.button2.Location = new System.Drawing.Point(15, 47);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(118, 25);
this.button2.TabIndex = 1;
this.button2.Text = "Set Data";
this.button2.UseVisualStyleBackColor = false;
this.button2.Click += new System.EventHandler(this.button2_Click);
this.textBox1.BackColor = System.Drawing.SystemColors.Info;
this.textBox1.ForeColor = System.Drawing.Color.Red;
this.textBox1.Location = new System.Drawing.Point(15, 79);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(118, 20);
this.textBox1.TabIndex = 2;
this.textBox1.Text = "Test Data";
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.SystemColors.Info;
this.Controls.Add(this.textBox1);
this.Controls.Add(this.button2);
this.Controls.Add(this.button1);
this.Name = "plugin_UI";
this.Size = new System.Drawing.Size(147, 112);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button2;
private System.Windows.Forms.TextBox textBox1;
}
}
答案 0 :(得分:1)
想象一下这样的事情:
// in assembly Data
interface IAppAPI
{
void SetData(string data);
string GetData();
}
// wherever you want. You only need a reference to the Data assembly
class AppAPI : IAppAPI
{
public string GetData()
{
// get and return data
}
public void SetData(string data)
{
// set data
}
}
// wherever you want. You only need a reference to the Data assembly
void LoadPlugin(string pluginPath, string pluginTypeIdentifier)
{
Assembly pluginAssembly = Assembly.LoadFile(pluginPath);
Type pluginType = pluginAssembly.GetType(pluginTypeIdentifier);
IAppAPI plugin = (IAppAPI)Activator.CreateInstance(pluginType);
plugin.SetData("whatever");
string whatever = plugin.GetData();
}
您有一些程序集(我们称之为数据)。在此,您具有用于实现插件的接口。然后,您可以在任何位置实现它。您只需添加对该程序集的引用,即可知道该接口是什么。
现在,您可以使用此LoadPlugin
方法,该方法获取程序集的路径和插件类型的全名(例如pluginTest.plugin
)。
然后,您可以将该插件的新实例强制转换为接口(因为您需要这些方法)。
现在调用函数或使用它做任何您想做的事情。
请注意,我没有添加任何错误检查。我强烈建议您检查
File.Exists
)type == null
还是使用异常重载)pluginType.IsAssignableFrom(typeof(IAppAPI))
)分配类型pluginType.IsClass && !pluginType.IsAbstract
)。 IsAbstract还清除静态部分(请参见this answer)。有关无参数的构造函数,请参见this question。也许您可以/应该做更多的检查。
正如与Jamiec讨论的那样,这可能无法回答您的直接问题。 Jamiec和我都认为让插件直接使用TextBox在许多方面都是一个坏主意。查看我们的对话内容:
您为如何编写插件提供了一个很好的通用答案,但没有解决实际问题。
我的意思是,为插件添加对文本框的引用并不难,但这确实很糟糕,因为那样您就有了UI-Element意大利面。
我同意100%
因此,请确保插件解决方案适合您的用例,并严格将插件与UI分开(UI插件更难,我什至不想谈论它们)。
祝你好运!
答案 1 :(得分:1)
撇开设计考虑,实际上您的代码只有两个非常简单的问题
您在2个地方定义了接口api_interface
,然后尝试将其中一个视为另一个。尽管您知道它们在编译器方面是相同的(相同的属性/方法/任何内容),但它们是2个完全不同的接口。
在load_plugin
内创建插件的2个单独实例,并尝试在一个实例上调用GetControl
,在另一个实例上调用Load
-这是因为您拥有插件实例(private plugin_UI pluginUI;
上的内部状态,因此第二次调用会丢失此状态。
修复这两个问题非常简单。
对于1.创建一个第三类库并将api_interface
移动到该程序集。然后从winforms和插件中引用此新程序集。 (当然,还要从2个地方中删除该定义/修正using
参考)
对于2。只需使用相同的实例:
private void load_plugin(string pluginadd)
{
var loadplugin = Assembly.LoadFile(pluginadd);
Type t = loadplugin.GetType("pluginTest.plugin");
var guimethod = t.GetMethod("GetControl");
if (guimethod == null)
{
MessageBox.Show("Can't Load GUI!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
var o = Activator.CreateInstance(t);
var result = guimethod.Invoke(o, null);
plug_ui.Controls.Add((UserControl)result);
app_api newapi = new app_api();
newapi.SetData = SetDataX;
newapi.GetData = GetDataX;
var apimethod = t.GetMethod("Load");
if (apimethod == null)
{
MessageBox.Show("Can't Generate API!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
//var o2 = Activator.CreateInstance(t); <-- DONT DO THIS
var result2 = apimethod.Invoke(o, new object[] { newapi });
}
通过这两个更改,我使您的代码正常运行,我怀疑您是期望的。