Search code examples
c#asynchronousasync-awaitinvoke

Invoke or BeginInvoke cannot be called Error when I never use Invoke or BeginInvoke


I'm new to C# and async programming so the solution to the problem I have might be simple (or not?!).

Here is the issue:

  • I have an onClick function that create a new UIControl and add it to the current Form as a new tab.
  • I had it run synchronously first and had no crash but the UI was blocked for a few second as the data provided to this Tab require a lots of calculations.
  • I moved the onclick to async and added an await on the Control's datprocessing function but I now get the infamous:

"Invoke or BeginInvoke cannot be called on a control until the window handle has been created".

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);
            }
        }

The crash always happen while I call the function "processData" but not alway at the same location. I have a few debug lines and they are not always the same processed.

The crash tells me the issue in on the line: "Application.Run(new FormMain());"

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

Here is the full stack trace:

System.Reflection.TargetInvocationException HResult=0x80131604
Message=Exception has been thrown by the target of an invocation.
Source=System.Private.CoreLib StackTrace: at 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[] parameters, CultureInfo culture)
at System.Delegate.DynamicInvokeImpl(Object[] args) at System.Delegate.DynamicInvoke(Object[] args) at System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry tme) at System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(Object obj) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) --- End of stack trace from previous location --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry tme) at System.Windows.Forms.Control.InvokeMarshaledCallbacks()
at System.Windows.Forms.Control.WndProc(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, WM msg, IntPtr wparam, IntPtr lparam) at Interop.User32.DispatchMessageW(MSG& msg) at System.Windows.Forms.Application.ComponentManager.Interop.Mso.IMsoComponentManager.FPushMessageLoop(UIntPtr dwComponentID, msoloop uReason, Void* pvLoopData) at 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() in Program.cs:line 22

This exception was originally thrown at this call stack: [External Code]

Inner Exception 1: InvalidOperationException: Invoke or BeginInvoke cannot be called on a control until the window handle has been created.

What on earth am I doing wrong with all this?


Solution

  • The heart of the issue I had is that creating a Control will not automatically ensure the handler connected to it is created.

    I changed my code to display the panel first (with a loading message) while the data is processed and displayed asynchronously.

    As pointed by @Henk Holterman, the architecture can/should be changed and improved to avoid the need for this solution.