Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/cocoa/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# DoEvents vs其他任何东西-->;用于长时间COM操作_C#_Wpf_Multithreading_Com_Doevents - Fatal编程技术网

C# DoEvents vs其他任何东西-->;用于长时间COM操作

C# DoEvents vs其他任何东西-->;用于长时间COM操作,c#,wpf,multithreading,com,doevents,C#,Wpf,Multithreading,Com,Doevents,我有一个WPF程序,在这个程序中,我的模型需要加载一个“outproc”(.exe)COM组件,以便在用户对UI执行操作时实现一些验证。我想通知用户,将进行一个长时间的操作,让他知道应用程序正忙,而不仅仅是冻结。但是UI上的任何操作都会在COM操作完成后发生 我认为任何COM通信都应该在主UI线程上完成。它消除了在主(UI)线程以外的其他线程上运行的任何解决方案 我尝试了很多选择,但都没有成功: 我看不出如何从需要刷新UI的模型中实现同步操作。 我的操作有一个属性“IsLoading”

我有一个WPF程序,在这个程序中,我的模型需要加载一个“outproc”(.exe)COM组件,以便在用户对UI执行操作时实现一些验证。我想通知用户,将进行一个长时间的操作,让他知道应用程序正忙,而不仅仅是冻结。但是UI上的任何操作都会在COM操作完成后发生

我认为任何COM通信都应该在主UI线程上完成。它消除了在主(UI)线程以外的其他线程上运行的任何解决方案

我尝试了很多选择,但都没有成功:

我看不出如何从需要刷新UI的模型中实现同步操作。 我的操作有一个属性“IsLoading”,我从我的视图中订阅它,并尝试根据它的状态更新UI,但这在WPF中似乎是不可能的

还有其他建议吗

我是否可以使用async/await并从运行另一个dispatcher的另一个线程执行COM操作(有点复杂),这样会失去所需的同步性(用户需要COM操作的结果才能继续工作)

主要针对盲人。。。 一些更清晰的解释(关于所需同步性的更多细节):

当用户单击TreeView项时,我加载一个网格,然后需要验证在网格中输入的数据是否仍然有效。要进行验证,我需要通过COM加载一个应用程序,并自动加载文档,然后解析它并验证网格中的数据(在视图中的网格模型中)。这需要10秒钟。
如果我在另一个线程上这样做,那么用户可以执行一个操作来选择在网格中添加一个新行,这仍然依赖于与上一个文档一起加载的同一个COM应用程序。我仍然需要等待应用程序加载。这是一个同步动作。我的应用程序依赖于COM应用程序及其加载的文档处于有效状态,以便用户执行更多操作。但我需要给用户一些关于我正在做什么的反馈(启动COM应用程序并加载文档)。在另一个线程上执行COM操作只是稍后报告问题,但不能解决用户需要等待操作完成的事实。我想我需要(强制)更新我的WPF应用程序,但找不到任何(扭曲的)方法来更新它。

我曾经在WPF中使用过这个,以便强制屏幕重新绘制: 我使用了VB的自动翻译,所以我希望它是正确的

private Action EmptyDelegate = () => { };
[System.Runtime.CompilerServices.Extension()]
public void Refresh(UIElement uiElement)
{
    uiElement.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Render, EmptyDelegate);
}

您可以在任何线程上创建和使用COM对象,如果您的应用程序使用STA线程模型,封送器将负责在后台线程上运行COM对象。没有必要通过主UI线程来实现每次调用

至于您在评论中的最后一个问题,由于您将在后台线程上运行此程序,因此您当然需要像往常一样在主线程上同步
lock
Invoke
结果。这与COM无关,只是Windows线程的工作方式


简而言之,停止将UI线程用于繁重的、与UI无关的工作。

[UPDATE]由于OP已更新问题并指定他使用的是进程外对象,下面描述的自定义STA线程管道没有意义。现在,一个简单的
wait Task.Run(()=>{/*调用out-of-proc-COM*/})
就足以让UI保持响应。澄清这一点值得称赞。

最近我回答了一个相关的问题:

其目的是在专用的后台STA线程上创建和使用STA COM对象,该线程为这些COM对象提供消息泵送和线程关联

我想展示如何在您的案例中使用
async/await

dynamic _comObject = null;

ThreadWithAffinityContext _staThread = null;

// Start the long-running task
Task NewCommandHandlerAsync()
{
    // create the ThreadWithAffinityContext if haven't done this yet
    if (_staThread == null)
        _staThread = new ThreadWithAffinityContext(
            staThread: true,
            pumpMessages: true);

    // create the COM Object if haven't done this yet
    if (_comObject == null)
    {
        await _staThread.Run(() =>
        {
            // _comObject will live on a dedicated STA thread,
            // run by ThreadWithAffinityContext
            _comObject = new ComObject();
        }, CancellationToken.None);
    }

    // use the COM object
    await _staThread.Run(() =>
    {
        // run a lengthy process
        _comObject.DoWork();
    }, CancellationToken.None);
}

// keep track of pending NewCommandHandlerAsync
Task _newCommandHandler = null;

// handle a WPF command
private async void NewCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
    try
    {
        // avoid re-entrancy (i.e., running two NewCommandHandlerAsync in parallel)
        if (_newCommandHandler != null)
            throw new InvalidOperationException("One NewCommandHandlerAsync at a time!");
        try
        {
            await _newCommandHandler = NewCommandHandlerAsync();
        }
        finally
        {
            _newCommandHandler = null;
        }
    }
    catch (Exception ex)
    {
        // handle all exceptions possibly thrown inside "async void" method
        MessageBox.Show(ex.Message);
    }
}
我们将冗长的进程
\u comObject.DoWork()
卸载到一个单独的线程中,这一事实并不能自动解决其他与UI相关的常见问题:

当冗长的后台操作挂起时,如何处理UI?

有很多选择。例如,您可以禁用触发
NewCommand\u Executed
事件的UI元素,以避免重新进入,并启用另一个UI元素以允许用户取消挂起的工作(一个
停止
按钮)。如果COM对象支持,您还应该提供一些进度反馈


或者,您可以在启动长时间运行的任务之前显示一个模式对话框,并在任务完成后将其隐藏。就用户界面的可用性而言,模态不太理想,但它很容易实现()。

出于好奇,为什么要从主用户界面线程调用COM?为什么你认为它需要在用户界面线程中完成?据我所知,几乎所有的COM组件都是STA(单线程单元),应该从第一个线程(UI)调用一个应用程序的。。。因为CoInitialize(),我认为?当从托管线程使用COM对象时,CLR将自动创建一个单线程单元,并封送来自该单元的所有调用/返回。如果使用STA COM对象,请确保将背景线程标记为STA。@Lukazoid,如果我理解正确,我可以将工作线程标记为STA,然后在该线程上创建COM对象。但是objet可以直接从我的主线程访问,或者我必须通过创建它的线程。(还有,是否有一种方法可以将其与我的主线程同步?…否则可能会发生预期COM操作在未完成时完成的操作)?我想有人会给你-1。不是我。很抱歉谢谢你的尝试。我可以使用具有后台优先级的BeginInvoke,但它会阻止我的代码同步(用户可以在我的操作发生之前在屏幕上执行一些操作),而且,它也会发生在我的模型中。当我点击屏幕时,它第一次做长动作。之后,无需采取任何行动。我知道我可以在调用模型之前检查模型状态