如何在界面显示进度条时对COM对象进行长时间调用?

时间:2014-09-12 15:28:39

标签: c# wpf asynchronous com

我在C#(WPF)中创建一个调用COM对象中的函数的程序。其中一些功能需要一些时间,比如返回5到10秒。如果这些调用是由UI线程进行的,那么界面就会没有响应,因此我想从辅助线程进行调用,并使用UI显示包含不确定进度条的对话框窗口。

好吧,要从辅助线程进行调用,我必须初始化其中的COM对象。麻烦的是:函数返回后,辅助线程丢失。当我需要进行另一个调用时,我必须创建一个新的(第三个)线程,COM对象不属于该线程,这会引发异常。我无法实例化一个新的COM对象,因为我依赖于先前调用所导致的状态。

我看到可能的解决方案:

  • 初始化COM对象并从UI线程进行调用,并使用辅助线程显示进度条窗口。然而,似乎并不正确;
  • 初始化辅助线程中的COM对象并使其空闲,等待UI线程调用。我一直在努力做到这一点,因为到目前为止我还无法弄清楚如何将辅助线程置于空闲状态以及如何通过UI线程调用这些函数(无法解决)创建一个调度员。)。
  • 还有其他想法吗?

提前谢谢你们!

代码示例:

private ARK.ICore ark;  // COM object
private ProgressWindow progressWindow = new ProgressWindow();

public MainWindow()
{
    InitializeComponent();
}

// Example of use of the COM object (assume this will be used first)
private void OpenButton_Click(object sender, RoutedEventArgs e)
{
    Thread thread = new Thread(() =>
    {
        ark = new ARK.Core2();  // COM object initialization
        ark.OpenDevice(0);
        progressWindow.Dispatcher.Invoke(progressWindow.Close);
        // Here the thread is lost, so I can't call the functions of ark in its own thread
    });

    thread.Start();
    progressWindow.ShowDialog();
}

// Another example of use of the COM object (assume this will be used later)
private void InitButton_Click(object sender, RoutedEventArgs e)
{
    Thread thread = new Thread(() =>
    {
        ark.Init(ARK.EarConstants.fLeft, null); // ark object initialized in another thread, exception thrown
        progressWindow.Dispatcher.Invoke(progressWindow.Close);
    });

    thread.Start();
    progressWindow.ShowDialog();
}

2 个答案:

答案 0 :(得分:1)

正如你所说,解决方案是:

  

初始化辅助线程中的COM对象并使其空闲,等待UI线程调用。

您确实需要以您的方式创建线程,并在该线程中实例化COM对象。但是你不应该让线程死掉,你应该等待来自GUI线程的命令并处理它们。假设您使用的是.NET 4.0或更高版本,这应该可以帮助您入门:

// make this a field
BlockingCollection<Action<ARK.Core2>> queue = 
    new BlockingCollection<Action<ARK.Core2>>();

// initialization code, execute e.g. when application starts or main window is loaded
Thread thread = new Thread(() =>
{
    var ark = new ARK.Core2();  // COM object initialization

    foreach (var action in queue.GetConsumingEnumerable())
    {
        action(ark);
    }

    // cleanup ark here
});

thread.Start();

然后,要从UI线程中对COM对象执行某些操作,请执行

queue.Add(ark => ark.SomeMethod());

如果您需要更新UI,请务必致电调度员,例如:

queue.Add(ark =>
{
    ark.SomeMethod();
    progressWindow.Dispatcher.Invoke(progressWindow.Close);
});

完成后,想要释放COM对象并关闭线程,请调用

queue.CompleteAdding();

有关详细信息,请参阅http://msdn.microsoft.com/en-us/library/dd267312.aspx

答案 1 :(得分:0)

使用可以将对象返回到UI线程的任务

 private void MethodThatWillCallComObject()
        {
            System.Threading.Tasks.Task.Factory.StartNew(() =>
            {
                //this will call in background thread
                return this.MethodThatTakesTimeToReturn();
            }).ContinueWith(t =>
            {
                //t.Result is the return value and MessageBox will show in ui thread
                MessageBox.Show(t.Result);
            }, System.Threading.Tasks.TaskScheduler.FromCurrentSynchronizationContext());
        }

        private string MethodThatTakesTimeToReturn()
        {
            System.Threading.Thread.Sleep(5000);
            return "end of 5 seconds";
        }