从 C# 中的外部 DLL 访问 windows 窗体控件

Posted

技术标签:

【中文标题】从 C# 中的外部 DLL 访问 windows 窗体控件【英文标题】:Access windows forms controls from external DLLs in C# 【发布时间】:2019-02-11 11:18:33 【问题描述】:

这是我在这里的第一个主题,我没有找到任何类似的主题,所以我尽量描述我的问题:

我的公司命令我创建一个模块化 C# 程序,以协助我们的软件开发人员完成后台任务。该程序由具有用户界面的 Windows 窗体应用程序组成,该用户界面调用执行实际工作的外部 DLL。所有这些 DLL 也是由我编写的,并遵循某些规则以使其与主应用程序兼容。这样,我只需将 DLL 放入预定义的文件夹中,就可以轻松地向 Programm 添加新功能。所以说即插即用

主程序包含一个显示所有可用插件的列表框,如果选择一个 get 并单击“开始”按钮,则主程序调用相应的 DLL 并调用启动 DLL 实际功能的方法“程序”。此外,Main 包含一个方法“Output”,它应该将每个插件的结果写入我的 TabControl 的选项卡中。这样就可以独立查看在不同线程中运行的每个插件的结果。对选项卡的访问已经有一个委托以使其成为线程安全的。信息是通过调用插件自己的“returnOutput”方法收集的,该方法只是将包含结果的字符串列表返回到 Main。

我现在的问题是:如何在我的插件 DLL 中实现一种回调,以便他们可以命令主程序随时收集结果?

我的第一个想法是简单地将结果作为返回值添加到“程序”方法本身,但这会使信息仅在程序结束时可用,并且某些任务需要在运行时“实时更新”。

我的第二个想法是使用控件的委托作为参数并将其传递给插件,以便插件 DLL 可以自己访问控件。这个想法失败了,因为 DLL 不“知道”主程序并且无法访问它的方法或委托实例,所以我总是缺少参考。

有没有办法解决我的问题?如果需要我可以提供 Code sn-ps 但是程序已经有大约 800 行 Code 并且每个 PlugIn 增加了几百行..

提前感谢您的每一个回答,并为我的非母语英语感到抱歉:D

最好的问候

Gerrit "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 文件

【问题讨论】:

这似乎是问题中最重要的部分,我对此有点困惑How can i implement a Kind of a callback into my PlugIn DLLs so they can order the Main Program to gather the results at any time 你到底是什么意思? 所以你基本上有一个方法(“程序”)在一个单独的线程上运行并返回一个字符串列表。您希望它一个一个地生成这些字符串并将它们从这个方法发送回它的调用者吗?这是你想要做的吗? 某种解耦的消息传递可能是最好的方法,或者事件聚合器。如果这是 MEF,只需将某种操作或委托系统放入插件使用的界面中。这里有很多方法 不要在您的 cmets 中添加代码,将其添加到您的问题中 好的,我在问题中添加了对 DLL 的调用 【参考方案1】:

有许多不同的方法可以做到这一点。 cmets 中的一些建议非常好,实施它们将成为一个强大且可扩展的解决方案。

如果您正在寻找一种从插件获取消息的快速简便的方法,那么您可以将回调作为操作直接传递给插件:

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# 中的外部 DLL 访问 windows 窗体控件的主要内容,如果未能解决你的问题,请参考以下文章

C# webservice 引用 dll 无法访问外部 xml 文件

从 C# 程序访问 DLL 中的方法

从 C# 程序访问 C++ DLL 中的多个函数

MFC dll 中的访问冲突(用 C++/CLI 包装)从 C# 程序开始

怎样在C#中调用DLL中的函数,最好有代码和详细说明

使用 user32.dll 从外部应用程序的文本框(Unicode)中提取文本到 C# 应用程序中