【问题标题】:VSTO Async/Await - unable to cancel a long running operationVSTO 异步/等待 - 无法取消长时间运行的操作
【发布时间】:2022-07-12 01:07:35
【问题描述】:

我正在使用 VSTO 和 WPF (MVVM) 为 C# 中的 Word 开发一个搜索工具。

我正在使用 Microsoft.Office.Interop.Word.Find() 方法并遍历文档以查找匹配项。我需要处理的一些文档超过 300,000 个字符,因此搜索可能需要 10 多秒。我想为我的用户提供取消操作的选项。

我面临的问题是,取消长时间运行的操作的按钮无法访问,因为 UI/主线程由于 Find 操作触发编组回主线程而保持忙碌 - 中继命令是从未触发。我的数据绑定是正确的,并且已经使用不使用 UI/主线程的长时间运行的操作测试了按钮。

public class SmartFindViewModel : BindableBase
{
   ctor()
   {
            FindCommand = new RelayCommand(o => Find(), o => CanFindExecute());
   }

   private async void Find()
   {
            var token = cancellationTokenSource.Token;
            **Update user here and show progress view**
            
            try
            {
                await System.Threading.Tasks.Task.Run(async() => { 
                        var searchResults = await SearchRange(token);
                        System.Windows.Application.Current.Dispatcher.Invoke(() =>
                        {
                            **Update results on UI Thread**
                        });
                        return;
                    }
                });
            }
            catch (OperationCanceledException)
            {
                ...
            }
            catch(Exception ex)
            {
                ...
            }
            finally
            {
                **Hide progress view**
            }
            
    }

    public async Task<List<SmartFindResultViewModel>> SearchRange(CancellationToken cancellationToken)
    {
            ** Get Word range**
            await System.Threading.Tasks.Task.Run(() =>
            {
                do
                {
                    range.Find.Execute();
                    if (!range.Find.Found) return;
                    **
                } while (range.Find.Found && !cancellationToken.IsCancellationRequested);
            });

            return Results;

    }
}

我的问题很简单,如果 UI 线程通过互操作方法保持忙碌,如何允许按钮保持操作?还是只是 VSTO 的限制或我的代码有问题?

【问题讨论】:

    标签: c# wpf vsto office-interop office-addins


    【解决方案1】:

    每当您在主线程上运行代码时,请确保该线程正在发送 Windows 消息,await 操作员依赖它。但真正的解决方案是避免在辅助线程上使用 Word 对象。

            public static void DoEvents(bool OnlyOnce = false)
            {
                MSG msg;
                while (PeekMessage(out msg, IntPtr.Zero, 0, 0, 1/*PM_REMOVE*/))
                {
                    TranslateMessage(ref msg);
                    DispatchMessage(ref msg);
                    if (OnlyOnce) break;
                }
            }
    
           [StructLayout(LayoutKind.Sequential)]
            public struct POINT
            {
                public int X;
                public int Y;
                public POINT(int x, int y)
                {
                    this.X = x;
                    this.Y = y;
                }
                public static implicit operator System.Drawing.Point(POINT p)
                {
                    return new System.Drawing.Point(p.X, p.Y);
                }
                public static implicit operator POINT(System.Drawing.Point p)
                {
                    return new POINT(p.X, p.Y);
                }
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public struct MSG
            {
                public IntPtr hwnd;
                public uint message;
                public UIntPtr wParam;
                public IntPtr lParam;
                public int time;
                public POINT pt;
                public int lPrivate;
            }
            [DllImport("user32.dll")]
            static extern sbyte GetMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax);
            [DllImport("user32.dll")]
            static extern bool TranslateMessage([In] ref MSG lpMsg);
            [DllImport("user32.dll")]
            static extern IntPtr DispatchMessage([In] ref MSG lpmsg);
            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            static extern bool PeekMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-27
      • 2022-01-03
      • 1970-01-01
      • 2020-04-23
      相关资源
      最近更新 更多