从 C# 中的外部 DLL 访问 windows 表单控件
Access windows forms controls from external DLLs in C#
这是我的第一个主题,我没有找到任何类似的主题,所以我尽量描述我的问题:
我的公司命令我创建一个模块化的 C# 程序来协助我们的软件开发人员处理后台任务。该程序由一个 Windows 表单应用程序组成,该应用程序具有一个用户界面,该界面调用执行实际工作的外部 DLL。所有这些 DLL 也是由我编写的,并遵循一定的规则以使其与主应用程序兼容。这样我就可以通过将 DLL 放入预定义的文件夹中轻松地向 Programm 添加新功能。所以说即插即用-运行
主程序包含一个显示所有可用插件的列表框,如果选择一个并单击 "start" 按钮,主程序将调用相应的 DLL 并调用启动方法 "program" DLL 的实际功能。此外,Main 包含一个方法 "Output",该方法应该将每个插件的结果写入我的 TabControl 的选项卡中。这样,每个插件 运行 在单独线程中的结果都可以独立查看。对选项卡的访问已经有一个委托使其成为线程安全的。信息是通过调用插件自己的 "returnOutput" 方法收集的,该方法只是 Returns 一个包含 Main.
结果的字符串列表
我现在的问题是:如何在我的插件 DLL 中实现一种回调,以便它们可以命令主程序随时收集结果?
我的第一个想法是简单地将结果作为 return 值添加到 "program" 方法本身,但这会使信息仅在程序结束时可用,并且某些任务需要a "live update" 在运行时。
我的第二个想法是使用控件的委托作为参数并将其传递给插件,这样插件 DLL 就可以自己访问控件。这个想法失败了,因为 DLL 没有 "know" 主程序并且无法访问它的方法或委托实例,所以我总是缺少引用。
有办法解决我的问题吗?如有必要,我可以提供代码片段,但该程序已经有大约 800 行代码,每个插件又增加了几百行代码。
预先感谢您的每一个回答,并为我的非母语英语感到抱歉:D
此致
格瑞特 "Raketenmaulwurf" M.
编辑:我正在使用 SharpDevelop 5.1
DLL 调用的代码片段:
PlugIn = PlugIns.SelectedItem.ToString();
Assembly PlugInDLL = Assembly.LoadFile(@PlugInOrdner+"\"+PlugIn+".dll");
Object Objekt = PlugInDLL.CreateInstance("DLL.PlugIn");
MethodInfo Info1 = Objekt.GetType().GetMethod("Programm");
Info1.Invoke(Objekt, new Object[]{Projekt, TIAInstanz});
它基本上是查找与列表框中突出显示的项目具有相同名称的 DLL 文件
有很多不同的方法可以做到这一点。评论中的一些建议非常好,实施它们将成为一个健壮且可扩展的解决方案。
不过,如果您正在寻找一种快速简便的方法来从您的插件中获取消息,那么您可以将回调作为 Action 直接传递给插件:
public class PluginRunner
{
public class PluginMessageEventArgs
{
public string Text { get; set; }
}
public event EventHandler<PluginMessageEventArgs> PluginMessage;
public void Run( string pluginPath )
{
Assembly PlugInDLL = Assembly.LoadFile(pluginPath);
Object Objekt = PlugInDLL.CreateInstance("DLL.PlugIn");
MethodInfo Info1 = Objekt.GetType().GetMethod("Programm");
Info1.Invoke(Objekt, new Object[] { Projekt, TIAInstanz, new Action<string>(Log) });
}
private void Log(string s)
{
PluginMessage?.Invoke(this, new PluginMessageEventArgs { Text = s });
}
}
因此您可以像这样使用它:
var path =
Path.Combine(
Path.GetDirectoryName(Assembly.GetEntryAssembly().Location),
"Plugins",
"MyAwesomePlugin.dll");
var pr = new PluginRunner();
// be aware that your event delegate might be invoked on a plugin's thread, not the application's UI thread!
pr.PluginMessage += (s,e) => Console.WriteLine("LOG: " + e.Text);
pr.Run(path);
然后您的插件的 Programm 方法写入其日志:
public void Programm( ProjektClass p0, TIAClass p1, Action<string> log )
{
Task.Run(() =>
{
// do something
log.Invoke("here am I!");
// do something else
log.Invoke("here am I again!");
// do something more
});
}
我必须承认,这不是处理消息的理想方式。那里有更好的(不幸的是,实施起来更复杂)解决方案。这个虽然很简单。只是不要忘记您在发送消息的同一个线程上收到消息,避免死锁。
这是我的第一个主题,我没有找到任何类似的主题,所以我尽量描述我的问题:
我的公司命令我创建一个模块化的 C# 程序来协助我们的软件开发人员处理后台任务。该程序由一个 Windows 表单应用程序组成,该应用程序具有一个用户界面,该界面调用执行实际工作的外部 DLL。所有这些 DLL 也是由我编写的,并遵循一定的规则以使其与主应用程序兼容。这样我就可以通过将 DLL 放入预定义的文件夹中轻松地向 Programm 添加新功能。所以说即插即用-运行
主程序包含一个显示所有可用插件的列表框,如果选择一个并单击 "start" 按钮,主程序将调用相应的 DLL 并调用启动方法 "program" DLL 的实际功能。此外,Main 包含一个方法 "Output",该方法应该将每个插件的结果写入我的 TabControl 的选项卡中。这样,每个插件 运行 在单独线程中的结果都可以独立查看。对选项卡的访问已经有一个委托使其成为线程安全的。信息是通过调用插件自己的 "returnOutput" 方法收集的,该方法只是 Returns 一个包含 Main.
结果的字符串列表我现在的问题是:如何在我的插件 DLL 中实现一种回调,以便它们可以命令主程序随时收集结果?
我的第一个想法是简单地将结果作为 return 值添加到 "program" 方法本身,但这会使信息仅在程序结束时可用,并且某些任务需要a "live update" 在运行时。
我的第二个想法是使用控件的委托作为参数并将其传递给插件,这样插件 DLL 就可以自己访问控件。这个想法失败了,因为 DLL 没有 "know" 主程序并且无法访问它的方法或委托实例,所以我总是缺少引用。
有办法解决我的问题吗?如有必要,我可以提供代码片段,但该程序已经有大约 800 行代码,每个插件又增加了几百行代码。
预先感谢您的每一个回答,并为我的非母语英语感到抱歉:D
此致
格瑞特 "Raketenmaulwurf" M.
编辑:我正在使用 SharpDevelop 5.1
DLL 调用的代码片段:
PlugIn = PlugIns.SelectedItem.ToString();
Assembly PlugInDLL = Assembly.LoadFile(@PlugInOrdner+"\"+PlugIn+".dll");
Object Objekt = PlugInDLL.CreateInstance("DLL.PlugIn");
MethodInfo Info1 = Objekt.GetType().GetMethod("Programm");
Info1.Invoke(Objekt, new Object[]{Projekt, TIAInstanz});
它基本上是查找与列表框中突出显示的项目具有相同名称的 DLL 文件
有很多不同的方法可以做到这一点。评论中的一些建议非常好,实施它们将成为一个健壮且可扩展的解决方案。
不过,如果您正在寻找一种快速简便的方法来从您的插件中获取消息,那么您可以将回调作为 Action 直接传递给插件:
public class PluginRunner
{
public class PluginMessageEventArgs
{
public string Text { get; set; }
}
public event EventHandler<PluginMessageEventArgs> PluginMessage;
public void Run( string pluginPath )
{
Assembly PlugInDLL = Assembly.LoadFile(pluginPath);
Object Objekt = PlugInDLL.CreateInstance("DLL.PlugIn");
MethodInfo Info1 = Objekt.GetType().GetMethod("Programm");
Info1.Invoke(Objekt, new Object[] { Projekt, TIAInstanz, new Action<string>(Log) });
}
private void Log(string s)
{
PluginMessage?.Invoke(this, new PluginMessageEventArgs { Text = s });
}
}
因此您可以像这样使用它:
var path =
Path.Combine(
Path.GetDirectoryName(Assembly.GetEntryAssembly().Location),
"Plugins",
"MyAwesomePlugin.dll");
var pr = new PluginRunner();
// be aware that your event delegate might be invoked on a plugin's thread, not the application's UI thread!
pr.PluginMessage += (s,e) => Console.WriteLine("LOG: " + e.Text);
pr.Run(path);
然后您的插件的 Programm 方法写入其日志:
public void Programm( ProjektClass p0, TIAClass p1, Action<string> log )
{
Task.Run(() =>
{
// do something
log.Invoke("here am I!");
// do something else
log.Invoke("here am I again!");
// do something more
});
}
我必须承认,这不是处理消息的理想方式。那里有更好的(不幸的是,实施起来更复杂)解决方案。这个虽然很简单。只是不要忘记您在发送消息的同一个线程上收到消息,避免死锁。