0

I have a parallel thread requiring to open a form using Form.Show() or, worst case, Form.ShowDialog(owner). I use the classic WinForm pattern as:

if (owner != null && owner.InvokeRequired)
    owner.Invoke(new Action(() => { xForm.Show(owner); }));      // <<< INVOKE ACTION >>>

The code normally works except for very few sporadic cases and the bug seems to be unreproducible.

It seems that the called xForm.Show(owner) blocks just before returning and the callee owner.Invoke() still wait.

Could you help me in finding some definitive solution?

An interesting clue is that, attaching to the blocked executable using Visual Studio Debugger.... pressing [Pause] and after a while [Continue] the code unblocks and proceed correctly: this seems that using a different parallel thread may change some form semaphores.

Also note that my application uses a number of Thread and also Tasks for async/await pattern: sometimes in the explicit Thread there may be loops calling Application.DoEvents().

Could be the case that different thread calling Application.DoEvents() will consume different events so the form lifecycle could be corrupted?

To debug the situation, I replaced the classic Invoke() pattern using my own method App.Invoke(): this starts a parallel System.Timer.Timers.Timer: if the Invoke() is not resolved, when the timer elapses all stacktraces are dumped by using Microsoft.Diagnostics.Runtime.DataTarget.CreateSnapshotAndAttach() .

Here below, the stack traces taken for an example: the PARALLEL Thred requires to open a form containg a ProgressBar using Form.Show().


Thread: managedId='40', name='PARALLEL Thread'
\[HelperMethodFrame_1OBJ\] (System.Threading.WaitHandle.WaitOneNative)
System.Threading.WaitHandle.InternalWaitOne(System.Runtime.InteropServices.SafeHandle, Int64, Boolean, Boolean)
System.Threading.WaitHandle.WaitOne(Int32, Boolean)
System.Windows.Forms.Control.WaitForWaitHandle(System.Threading.WaitHandle)
System.Windows.Forms.Control.MarshaledInvoke(System.Windows.Forms.Control, System.Delegate, System.Object\[\], Boolean)
System.Windows.Forms.Control.Invoke(System.Delegate, System.Object\[\])     \<\<\<\< INVOKE ACTION \>\>\>\>
MY.App.InvokeInternal(System.Windows.Forms.Control, System.Action, System.String)
MY.App.Invoke(System.Windows.Forms.Control, System.Action, System.String)
MY.UI.ShowProgressForm.ProgressReportingObject_ProgressChanged(System.Object, TITP.Automation.Abstraction.Utility.Progress.ProgressChangedEventArgs)
MY.UI.ProgressContext.RaiseProgressChanged(TITP.Automation.Abstraction.Utility.Progress.ProgressChangedEventArgs)
MY.UI.ProgressContext.set_LongOperationInProgress(Boolean)
MY.UI.ProgressContext.BeginLongOperation(System.String, System.String)
MY.UI.frmMain.AutoWorkCycle()
System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
System.Threading.ThreadHelper.ThreadStart()
\[GCFrame\]
\[DebuggerU2MCatchHandlerFrame\]

Thread: managedId='1', name='UIThread'

    [InlinedCallFrame] (System.Windows.Forms.SafeNativeMethods.ShowWindow)
    [InlinedCallFrame] (System.Windows.Forms.SafeNativeMethods.ShowWindow)
    DomainBoundILStubClass.IL_STUB_PInvoke(System.Runtime.InteropServices.HandleRef, Int32)
    System.Windows.Forms.Control.SetVisibleCore(Boolean)
    System.Windows.Forms.Form.SetVisibleCore(Boolean)
    *System.Windows.Forms.Form.Show(System.Windows.Forms.IWin32Window)*
    MY.UI.ShowProgressForm.<__tryShow>b__55_0()              <<<< INVOKE ACTION >>>>
    MY.App.InvokeInternal(System.Windows.Forms.Control, System.Action, System.String)
    MY.App.Invoke(System.Windows.Forms.Control, System.Action, System.String)
    MY.UI.ShowProgressForm.__tryShow()
    MY.UI.ShowProgressForm.__addProgressBar(System.Object, System.Object)
    MY.UI.ShowProgressForm+<>c__DisplayClass42_0.<AddProgressBar>b__0()
    MY.App.InvokeInternal(System.Windows.Forms.Control, System.Action, System.String)
    MY.App.Invoke(System.Windows.Forms.Control, System.Action, System.String)
    MY.UI.ShowProgressForm.AddProgressBar(System.Object)
    MY.UI.ShowProgressForm+<>c__DisplayClass70_0.<Context_Show>b__0()
    MY.App.InvokeInternal(System.Windows.Forms.Control, System.Action, System.String)
    MY.App.Invoke(System.Windows.Forms.Control, System.Action, System.String)
    MY.UI.ShowProgressForm.Context_Show(System.Object)
    MY.UI.ShowProgressForm.ProgressChangedInternal(System.Object, TITP.Automation.Abstraction.Utility.Progress.ProgressChangedEventArgs)
    MY.UI.ShowProgressForm+<>c__DisplayClass38_0.<ProgressReportingObject_ProgressChanged>b__0()
    [DebuggerU2MCatchHandlerFrame]
    [HelperMethodFrame_PROTECTOBJ] (System.RuntimeMethodHandle.InvokeMethod)
    System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(System.Object, System.Object[], System.Object[])
    System.Delegate.DynamicInvokeImpl(System.Object[])
    System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry)
    System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(System.Object)
    System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
    System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
    System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
    System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry)
    System.Windows.Forms.Control.InvokeMarshaledCallbacks()
    System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)
    System.Windows.Forms.Form.WndProc(System.Windows.Forms.Message ByRef)
    System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)
    DomainBoundILStubClass.IL_STUB_ReversePInvoke(Int64, Int32, Int64, Int64)
    [InlinedCallFrame] (System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW)
    [InlinedCallFrame] (System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW)
    DomainBoundILStubClass.IL_STUB_PInvoke(MSG ByRef)
    System.Windows.Forms.Application+ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr, Int32, Int32)
    System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(Int32, System.Windows.Forms.ApplicationContext)
    System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext)
    MY.UI.Program.Main()
    [GCFrame]

The lines marked with <<<< INVOKE ACTION >>>> corresponds to the blocked lines in my code.

At the end it seems that SetVisibleCore() in UiThread and InternalWaitOne() in PARALLEL thread are locking toghether.

=== UPDATE ===

Let me add a further episode: while "PARALLEL Thread" is doing

while (flagExit))
    Thread.Sleep(50); 

the UiThread may receive a click on textbox to open a popup dialog

((TextBox)control4).Click += delegate(object s, EventArgs e)
{
    ...
    frmNumericPad.OpenNumpad() => 
    { 
        using (frmNumericPad xPad = new frmNumericPad())               
        {
            if (xPad.ShowDialog() == DialogResult.OK)
                return true;
            else
                return false;
        }
    }
}

This new case result in a freeze where the process dump returns:

Not Flagged     5924    1   Main Thread UITHREAD    
    System.Windows.Forms.dll!System.Windows.Forms.Control.SetVisibleCore
    [External Code]
    MYControls.dll!MYControls.frmNumericPad.OpenNumpad(System.Windows.Forms.TextBox control, string numberFormat, double? minValue, double? maxValue, bool canBeIncremental, bool canBeNull) Line 140
    MYControls.dll!MYControls.__internal_SetTouchKeypadBehaviour.AnonymousMethod__2(object s, System.EventArgs e) Line 253
    [External Code]
    MY.exe!MY.Main()
  • What does is waiting SetVisibleCore() ?
  • What happen on the native code? Shall it be affected by GarbageCollector()?
  • Task for async/await may hit on the UiThread ?

0 Answers0