从 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
        });
    }

我必须承认,这不是处理消息的理想方式。那里有更好的(不幸的是,实施起来更复杂)解决方案。这个虽然很简单。只是不要忘记您在发送消息的同一个线程上收到消息,避免死锁。