none
Writing to a control form another thread. RRS feed

  • Question

  • I have an application that uses a multi-line textbox to show output. It also uses Titanium Proxy which writes to the textbox from one of the handlers. This is achieved via telling the log subroutine that it is on another thread and using invoke. It works well.

        Sub Log_Message(Message As String, Optional Message_Is_From_Another_Thread As Boolean = False)
    
            Debug.WriteLine(Message)
    
            Message &= Environment.NewLine
    
            If Message_Is_From_Another_Thread Then
                ' Ask the textbox to call for an update.
                Log.Invoke(Sub() Log.AppendText(Message))
            Else
                Log.AppendText(Message)
            End If
    
        End Sub

    I am now trying to add additional functionality to the code so that it can download another file. The same code in the same handler calls this same function, and hangs on the invoke (until, after a minute, Selenium times out). So i am trying to figure out where the new functionality differs from the existing functionality, and why this causes a hang on invoke.

    There is an obvious difference in how they work. In the existing functionality, a button is clicked and a generated pdf is downloaded in a new tab (which Titanium usurps). In the new functionality, a button is clicked, but it reloads the same url with different html which itself includes an embedded pdf (which Titanium usurps).

    If i comment out the invoke, all works fine. Please help me: what am i doing wrong?



    Friday, September 20, 2019 3:57 PM

All replies

  • Hi,

    Is there any error returned by the Visual Studio editor?

    Best Regards,

    Julie


    MSDN Community Support Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Wednesday, September 25, 2019 1:24 AM
    Moderator
  • Yes.

    Exception:

    OpenQA.Selenium.WebDriverException: 'The HTTP request to the remote WebDriver server for URL http://localhost:53053/session/2a43933c6da603e545ebabbadc5c48f6/url timed out after 60 seconds.'

    Inner Exception:

    WebException: The operation has timed out

    Stacktrace:

       at OpenQA.Selenium.Remote.HttpCommandExecutor.MakeHttpRequest(HttpRequestInfo requestInfo)
       at OpenQA.Selenium.Remote.HttpCommandExecutor.Execute(Command commandToExecute)
       at OpenQA.Selenium.Remote.DriverServiceCommandExecutor.Execute(Command commandToExecute)
       at OpenQA.Selenium.Remote.RemoteWebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
       at OpenQA.Selenium.Remote.RemoteWebDriver.set_Url(String value)
       at function in file at line xxx
       at function in file at line xxx
       at System.Windows.Forms.Control.OnClick(EventArgs e)
       at System.Windows.Forms.Button.OnClick(EventArgs e)
       at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
       at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
       at System.Windows.Forms.Control.WndProc(Message& m)
       at System.Windows.Forms.ButtonBase.WndProc(Message& m)
       at System.Windows.Forms.Button.WndProc(Message& m)
       at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
       at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
       at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
       at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
       at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
       at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
       at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
       at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.OnRun()
       at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.DoApplicationModel()
       at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.Run(String[] commandLine)
       at project.My.MyApplication.Main(String[] Args) in :line 81

    After a moment a popup shows up: 




    • Edited by Brian Tkatch Wednesday, September 25, 2019 3:18 PM
    Wednesday, September 25, 2019 3:17 PM
  • I am bumping into this error again in a similar project. I have found the issue is not specific to invoke(), rather, it will hang on any reference to the form from a second thread.

    In my case, i am using Titanium Proxy's BeforeResponse handler, and in some cases referring to the form fails, in others it does not. I cannot seem to tell what the difference between the fails and successes are. (The problem occurs whether or not the handler is declared Async.)

    In the original program that i experienced the error, it seems to happen when i download one type of report but not on the other. (I got around the issue by not calling .invoke on that report type with a simple If statement.) I do not know what specifically is causing it.

    Wrapping the reference in a Try does not help. When the statement that refers to the form is hit it simply hangs the entire program, and will stay there until something times out (such as Selenium, in my case).

    Tuesday, November 12, 2019 11:59 PM
  • Spent a little more time on, didn't fix it, but made some progress, kind of.

    I was passing a parameter to tell the logger that it was from another thread. There's a better way to do it, a way that is easier and more direct. Controls have a .InvokeRequired property, which will tell you if invoke is required. It's not in the smaller "common" set of properties, so it won't come up automatically, but the editor will understand it once it is (fully) typed in.

    So, the new version of the function is:

        Sub Log_Message(Message As String)
    
            Debug.WriteLine(Message)
    
            Message &= Environment.NewLine
    
            If Log.InvokeRequired Then
                ' Ask the textbox to call for an update.
                Log.Invoke(Sub() Log.AppendText(Message))
            Else
                Log.AppendText(Message)
            End If
    
        End Sub

    I also tried the two solutions from How to: Make thread-safe calls to Windows Forms controls. The first method, Use the Invoke method with a delegate, is what is being done here. A Delegate is implicit when using AddressOf, so there's no need to declare one. However, this function uses an actualy sub instead of AddressOf. Even so, i tried the Delegate/AddressOf explicity, and it did not work either.

    The second solution, Use a BackgroundWorker event handler, also did not work. I had the function set a Class variable and call the task to update the log from the variable. It too froze on reference to the Log.

    So, at least for the meanwhile, i implemented a crude queue via a timer and that seems to work. I also added a new line parameter, because of cases where the log will not be using a new line.

    Class Message
            Public Text As String
            Public New_Line As Boolean
        End Class
    
        Private ReadOnly Message_Queue As New List(Of Message)
    
        Dim WithEvents Read_Message_Timer As New Timer With {.Enabled = True, .Interval = 1000}
        Sub Read_Message() Handles Read_Message_Timer.Tick
    
            Dim Queue As New List(Of Message)(Message_Queue)
            Message_Queue.Clear()
    
            For Each Message As Message In Queue
                Log_Message(Message.Text, Message.New_Line)
            Next
        End Sub
    

    Wednesday, November 13, 2019 11:42 PM