首页 > 解决方案 > 当我从不使用 Invoke 或 BeginInvoke 时,不能调用 Invoke 或 BeginInvoke 错误

问题描述

我是 C# 和异步编程的新手,所以我遇到的问题的解决方案可能很简单(或者不是?!)。

这是问题:

“在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke”。

private async void btnNewTab_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Multiselect = true;
            openFileDialog.Filter = "*.test";
            DialogResult dialogResult = openFileDialog.ShowDialog();

            if (dialogResult == DialogResult.OK)
            {
                //Display a basic loading Control while the other control is being prepared
                TabPage myTabPage = new TabPage("loading");
                LoadingControlPanel loadingPanel = new LoadingControlPanel();
                loadingPanel.Dock = DockStyle.Fill;
                myTabPage.Controls.Add(loadingPanel);
                tabControl.TabPages.Add(myTabPage);
                tabControl.SelectTab(myTabPage);

                //Load Data from files
                List<DataFile> result = await Task.Run(() => loadFiles(openFileDialog.FileNames));
                
                // create Control to display the data loaded
                MyTabControl newPanel = new MyTabControl();
                newPanel.Dock = DockStyle.Fill;

                // provide Data to new control (long processing)
                await Task.Run(() => newPanel.processData(result));

                // rename Tab, remove loading Control and display the tab with the processed Data
                myTabPage.Text = "Loaded";
                myTabPage.Controls.Remove(loadingPanel);
                myTabPage.Controls.Add(newPanel);
            }
        }

当我调用函数“processData”但并不总是在同一个位置时,崩溃总是发生。我有几条调试线,它们的处理方式并不总是相同的。

崩溃告诉我问题就行了:“Application.Run(new FormMain());”

static void Main()
        {
            Application.SetHighDpiMode(HighDpiMode.SystemAware);
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new FormMain());
        } 

这是完整的堆栈跟踪:

System.Reflection.TargetInvocationException HResult=0x80131604
Message=异常已被调用的目标抛出。
Source=System.Private.CoreLib StackTrace: 在 System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder , Object[] 参数, CultureInfo 文化)
在 System.Delegate.DynamicInvokeImpl(Object[] args) 在 System.Delegate.DynamicInvoke(Object[] args) 在 System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry tme) 在 System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(Object obj) 在 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) --- 从先前位置结束堆栈跟踪 --- 在 System.Threading 的 System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()。 System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry tme) 在 System.Windows.Forms.Control.InvokeMarshaledCallbacks() 的 ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
在 System.Windows.Forms.Control.WndProc(Message& m) 在 System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
在 System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, WM msg, IntPtr wparam, IntPtr lparam) 在 Interop.User32.DispatchMessageW(MSG& msg) 在 System.Windows.Forms.Application.ComponentManager.Interop.Mso.IMsoComponentManager.FPushMessageLoop(UIntPtr dwComponentID, msoloop uReason, Void* pvLoopData) 在 System.Windows.Forms.Application .ThreadContext.RunMessageLoopInner(msoloop reason, ApplicationContext context) at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(msoloop reason, ApplicationContext context) at System.Windows.Forms.Application.Run(Form mainForm) at Program.Main()在 Program.cs:第 22 行

此异常最初是在此调用堆栈中引发的:[外部代码]

内部异常 1:InvalidOperationException:在创建窗口句柄之前,无法在控件上调用 Invoke 或 BeginInvoke。

我到底做错了什么?

标签: c#asynchronousasync-awaitinvoke

解决方案


问题是Task.Run(() => newPanel.processData(result)),这只能在 processData() 没有触及任何 UI 内容时起作用(但为什么它会成为面板的一部分?)

您只能使用 Task.Run() 处理内容,然后在主线程的 UI 中加载结果。

看看你能不能让它看起来像:

//await Task.Run(() => newPanel.processData(result));
var data = await Task.Run(() => ProcessData(result));
newPanel.AcceptData(data);

LoadData 和 ProcessData() 最好是某些服务类的一部分,而不是您的表单。但这与架构有关,而不是直接问题。


推荐阅读