【问题标题】:Invoking a Multi-Threaded DLL at Run-Time在运行时调用多线程 DLL
【发布时间】:2013-05-03 18:30:28
【问题描述】:

所有,我在运行时从 WinForm C# 应用程序调用包含 WinForm 的 .NET DLL。为此,我使用以下内容:

DLL = Assembly.LoadFrom(strDllPath);
classType = DLL.GetType(String.Format("{0}.{1}", strNamespaceName, strClassName));
if (classType != null)
{
    if (bDllIsWinForm)
    {
        classInst = Activator.CreateInstance(classType);
        Form dllWinForm = (Form)classInst;
        dllWinForm.Show();

        // Invoke required method.
        MethodInfo methodInfo = classType.GetMethod(strMethodName);
        if (methodInfo != null)
        {
            object result = null;
            result = methodInfo.Invoke(classInst, new object[] { dllParams });
            return result == null ? String.Empty : result.ToString();
        }
    }
}

这是调用 WinForm DLL 和 DLL 中串行方法所需的方法。但是,我现在正在调用一个多线程 DLL,并调用以下方法:

public async void ExecuteTest(object[] args)
{
    Result result = new Result();
    if (!BuildParameterObjects(args[0].ToString(), args[1].ToString()))
        return;
    IProgress<ProgressInfo> progressIndicator = new Progress<ProgressInfo>(ReportProgress);
    List<Enum> enumList = new List<Enum>()
    {
        Method.TestSqlConnection, 
        Method.ImportReferenceTables
    };
    Task task = Task.Factory.StartNew(() =>
    {
        foreach (Method method in enumList)
        {
            result = Process.ProcessStrategyFactory.Execute(Parameters.Instance, progressIndicator,
            Process.ProcessStrategyFactory.GetProcessType(method));
            if (!result.Succeeded)
            {
                // Display error.
                return;
            }
        }
    });
    await task;
    Utilities.InfoMsg("VCDC run executed successfully.");
}

但是由于await(这是预期的),这会立即将控制权返回给调用者。但是,返回会导致调用方法退出,从而关闭 DLL WinForm。

保持 DLL WinForm 活动/打开的最佳方法是什么?

感谢您的宝贵时间。


编辑。按照下面 Stephen 的建议,我决定将我的 DLL intery 方法类型转换为 Task&lt;object&gt; 并设置如下延续

if (classType != null)
{
    if (bDllIsWinForm)
    {   
        // To pass object array to constructor use the following.
        // classInst = Activator.CreateInstance(classType, new object[] {dllParams});
        classInst = Activator.CreateInstance(classType);
        dllWinForm = (Form)classInst;
        dllWinForm.Show();

        // Invoke required method.
        MethodInfo methodInfo = classType.GetMethod(strMethodName);
        if (methodInfo != null)
        {
            object result = null;
            result = methodInfo.Invoke(classInst, new object[] { dllParams });
            if (result != null)
            {
                if (result.GetType() == typeof(Task<object>))
                {
                    Task<object> task = (Task<object>)result;
                    task.ContinueWith(ant =>
                        {
                            object innerResult = task.Result;
                            return innerResult == null ? String.Empty : innerResult.ToString();
                        });
                }
                return result.ToString();
            }
            return String.Empty;
        }
    }
}

我决定设置延续而不是 await 以避免与 await 关键字发生的链接 - 即创建调用方法(调用 Task&lt;String&gt; 等类型的 DLL . 向上调用堆栈。

DLL 入口方法现在变为:

public Task<object> ExecuteTest(object[] args)
{
    Task<object> task = null;
    Result result = new Result();
    if (!BuildParameterObjects(args[0].ToString(), args[1].ToString()))
        return task;
    IProgress<ProgressInfo> progressIndicator = new Progress<ProgressInfo>(ReportProgress);
    List<Enum> enumList = new List<Enum>()
    {
        Method.TestSqlConnection, 
        Method.ImportReferenceTables
    };
    task = Task.Factory.StartNew<object>(() =>
    {
        foreach (Method method in enumList)
        {
            result = Process.ProcessStrategyFactory.Execute(Parameters.Instance, progressIndicator,
            Process.ProcessStrategyFactory.GetProcessType(method));
            if (!result.Succeeded)
            {
                // Display error.
            }
            task.Wait(5000); // Wait to prevent the method returning too quickly for testing only.
        }
        return null;
    });
    return task;
}

但这会导致 DLL WinForm 显示片刻然后消失。我什至试图使Form dllWinForm 全局保持对对象的引用处于活动状态,但这也没有奏效。我想指出对 DLL 的调用(注意调用方法已经在后台线程池线程上运行)。

感谢任何进一步的帮助。

【问题讨论】:

  • 尽量不要踢开的门:不要关门。 Boilerplate 是 BackgroundWorker.RunWorkerCompleted 和 TaskScheduler.FromCurrentSynchronizationContext,用于在工作人员完成后在 UI 线程上运行代码。
  • 对不起,我不明白你的意思@HansPassant。感谢您的宝贵时间...
  • @HansPassant BackgroundWorker 或多或少已经过时并被认为是遗留问题。它除了对 Task 和 Progress 的简单调用之外没有任何作用,不能异步提供详细的进度,不能用于链接多个异步调用,不能使用 ThreadPool,不能利用异步方法,不能……
  • @PanagiotisKanavos - BackgroundWorker 通过 SynchronizationContext 类使用 ThreadPool。它的进度也使用 ThreadPool 报告。

标签: winforms dll task-parallel-library async-await c#-5.0


【解决方案1】:

Execute的返回类型改为Taskawait吧。

【讨论】:

  • +1 好主意,但我怎样才能从调用者那里做到这一点而不使调用代码非通用?被调用的是用户调用的函数来调用他们自己的 DLL。对不起,我应该提到这一点!感谢您的宝贵时间。
  • 我看不出像if (typeof(result) == GetType(Task)) await result as Task; 这样的东西在这里怎么用?
  • 如果结果的类型是(或派生自)Task,那么您可以将其转换为Taskawait
  • 嗨斯蒂芬我已经尝试过你的建议,如果你有任何进一步的建议,我将不胜感激。我已经编辑了问题以显示我尝试过的内容。 DLL WinForm 仍然显示,然后在调用方法返回时迅速消失。非常感谢您的宝贵时间...
  • 在这种情况下,我想说第一步是找出表单关闭的原因。尝试添加一个关闭处理程序,在其中插入一个断点,然后检查您的堆栈。
【解决方案2】:

很难猜出你在 dll 中有什么,但最终你的 dll 代码 + 暴露在你的问题中:

dllWinForm.Show();  

最终应该是在并列之后:

new Thread
      (   
         () => new Form().ShowDialog()
       )
       .Start();  

可能,您应该将dllWinForm.Show(); 更改为dllWinForm.ShowDialog().Start()

ShowDialog(),与Show()starts its own message pumping and returns only when explicitly closed 形成对比。

更新(回应评论):
从 UI 启动表单并非绝对必要。
由于您使用的是 .NET 4.5,因此使用 WPF(而不是 Windows 窗体)表单可能更简单。
这里是the code for WPF form,为了适应 Windows 窗体,您应该通过初始化 WindowsFormsSynchronizationContext 来更改 Dispatcher 部分

虽然,IMO,WinForms 代码会复杂得多。

【讨论】:

  • +1 我最终的排序是为了确保从 UI 线程调用 DLL。然后像我一样调用多线程 DLL。非常感谢您的宝贵时间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-05
  • 2019-12-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多