So I have this form that loads a page, and after the page is loaded I want to insert the source into RichTextBox1.Text
However after the page load the program crashes(?) and gives me this error
"An unhandled exception of type 'System.AccessViolationException' occurred in Awesomium.Windows.Forms.dll
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
This is my code and it is worth mentioning that I am using awesomium for this!
Public Class Form1
Dim Thread As System.Threading.Thread
Dim html_source_code As String = ""
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Control.CheckForIllegalCrossThreadCalls = False
Thread = New System.Threading.Thread(AddressOf Load_Page)
Thread.Start()
End Sub
Private Sub Load_Page()
While html_source_code = ""
If WebControl1.IsDocumentReady Then
html_source_code = WebControl1.ExecuteJavascriptWithResult("document.documentElement.outerHTML").ToString()
RichTextBox1.Text = html_source_code
End If
End While
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Thread = New System.Threading.Thread(AddressOf Load_Page)
Thread.Start()
End Sub
End Class
Thanks in advance!
Here is your first clue...
Control.CheckForIllegalCrossThreadCalls = False
This is a pretty obvious red flag. You should never use this except perhaps for very specific debugging purposes, and for very good reason. Given that the remainder of your code is full of illegal cross thread calls, it's no wonder your program is crashing.
Every single line of code you have written that is executed in a worker thread is touching UI components. You cannot do this. See this post for examples : Cross-thread operation not valid
UI controls are continuously operated on by the main thread of your application. The main thread must be able to access them to handle their associated events, draw them to the screen, etc. If another thread is modifying their contents while the main thread is attempting to read or write to them then bad things happen.
Consider a text box that the main thread is attempting to draw to the screen. It obtains a reference to the location of the string that contains the textbox's text and, in the middle of reading the string, another thread modifies the string, invalidating the reference that the main thread is using. When the main thread tries to read, its existing reference now points to an invalid memory location and, voila, access violation.
In almost all circumstances you should never, ever set CheckForIllegalCrossThreadCalls to False. Leaving it set True will raise an exception, but it gives you the opportunity to see where the problematic code is and allows you to correct it. Setting it False simply hides the illegal call, which may or may not fail, and your application will be vulnerable to random, buggy failures when those illegal calls end up causing problems. Suggest you do some reading about how to write multithreaded .NET applications. A good start would be here : StackOverflow Search.
See also : How to: Make Thread-Safe Calls to Windows Forms Controls
Related
My code is designed to be a control system for a 2-axis motion system. I have 2 drives that each output a count of their steps. I can read the device, update a property, and update the text field of a label. However, it does not update the form. When I use a message box, I can display the text value being correct, but nothing updates the label.
I'm happy to try any suggestions, but I've been fighting this for about 16 hours and I'm at my wits end - as evidenced by the clear overkill/terrible coding that is shown in the code. I can't understand why it's not updating.
Additionally, a manual button with all versions seen below to refresh a form doesn't update the control.
Direction, recommendations?
Private Sub PositionChanged(ByVal sender As Object, ByVal e As EventArgs)
If TraverseController.InvokeRequired Then
TraverseController.Invoke(
New EventHandler(Of EventArgs)(AddressOf PositionChanged), sender, e)
Return
End If
'RaiseEvent PropertyChanged(TraverseController, New System.ComponentModel.PropertyChangedEventArgs("Position"))
MessageBox.Show(TraverseController.lblLinearDrivePosDisp.Text)
TraverseController.lblLinearDrivePosDisp.Text = CStr(_position)
Application.DoEvents()
TraverseController.lblLinearDrivePosDisp.ResetBindings()
TraverseController.GBDrivePositionDisp.Refresh()
TraverseController.lblLinearDrivePosDisp.Refresh()
TraverseController.Refresh()
TraverseController.Invalidate()
TraverseController.Update()
Application.DoEvents()
MessageBox.Show(TraverseController.lblLinearDrivePosDisp.Text)
End Sub
Assumption: TraverseController is form's class name.
This looks like a VB default form instance issue. It is apparent that you are trying to properly marshal control interaction back to the UI thread by using checking TraverseController.InvokeRequired. However, due to the way these default instance are created, TraverseController.InvokeRequired is creating a new instance of TraverseController on the secondary thread and all subsequent code is modifying that instance and not the one created on the UI thread.
One way to deal with this is to pass a synchronizing control instance to the class where PositionChanged changed method is defined and check that control's InvokeRequired method instead of TraverseController.InvokeRequired. If the containing class is itself a UI control, then use that class instance (Me.InvokeRequired).
I am trying to read/use the output from a python program in my vb.net project so far I'm not getting any results. What I'd like to see is the python program run (just by itself first) and all of the output get redirected into a textbox.
I've looked at some other posts about this, but I'm either missing something or not understanding something, as all I'm getting is blank output.
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim PythonPath = "C:\Python27\"
Dim strPath As String = Application.StartupPath
MessageBox.Show(PythonPath & "python.exe """ & strPath & "\Resources\import_logs.py"" ")
Dim start_info As New ProcessStartInfo(TextBox1.Text)
' Make the process and set its start information.
Dim process As New Process()
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
process.StartInfo.FileName = PythonPath & "\python.exe"
process.StartInfo.Arguments = """" & strPath & "\resources\import_logs.py"""""
process.StartInfo.UseShellExecute = False
process.StartInfo.CreateNoWindow = True
process.StartInfo.RedirectStandardOutput = True
'process.StartInfo.RedirectStandardError = True
AddHandler process.OutputDataReceived, AddressOf proccess_OutputDataReceived
process.Start()
process.BeginOutputReadLine()
End Sub
Public Sub proccess_OutputDataReceived(ByVal sender As Object, ByVal e As DataReceivedEventArgs)
On Error Resume Next
' output will be in string e.Data
' modify TextBox.Text here
'Server_Logs.Text = e.Data ` Does not display anything in textbox
MsgBox(e.Data) 'It works but I want output in text box field
End Sub
End Class
Eventually I'm going to pass arguments to the python script and I'd like to get feedback that I can then use (insert error into a database, email when it's done, etc), so I'd like it to capture the process while running and not just a data dump at the end.
Any help would be much appreciated.
First things first—it's no wonder you aren't sure what's wrong with your code, you're silencing all errors that could possibly help you to diagnose it. That's the only purpose of On Error Resume Next in VB.NET. That unstructured error handling was included only for backwards compatibility with the pre-.NET versions of VB and it's time to forget that it ever existed. You certainly don't want to use it in code. (I would say "in code that you're debugging", but all code is a potential candidate for debugging and ignoring errors is just dumb.)
Anyway, on to the specific problem. We know that the call to MsgBox works, but it doesn't work right when you start interacting with controls on your form. So something is falling apart there.
It turns out that the OutputDataReceived event is raised on an entirely different thread, a different one than was used to create the process and a different one than is running your application's UI. It actually just retrieves a thread from the system thread pool.
And that's where the problem lies: you cannot manipulate UI objects on a thread other than the one that created those objects (at least not without jumping through some hoops), which is precisely what your code tries to do here. In fact, you're probably swallowing an exception that would have rather obtusely informed you of this situation.
The simple fix is to set the SynchronizingObject property of the Process class to one of your UI components (like the form, or the specific control you want to output to). This forces all event handlers to execute on the same thread that created that component. At that point, your code should work fine, because you're not trying to do any cross-thread UI access. (Message boxes are not vulnerable to this because any thread can display a message box. You're not trying to access an existing UI object that is bound to another thread.)
Alternatively, you could handle the marshalling yourself in the event handler method through the use of delegates and the BeginInvoke method, but this seems like unnecessary work to me.
I am using DownloadFileAsync to download a larger file (1.3 GB), but i'd like to add a simple percentage indicator (ex. 64%). I'm new to Visual Basic I have no idea how to do this.
Any help would be appreciated.
The WebClient class has a DownloadProgressChanged event that you can listen to if you want to update a progresss display. For instance, if you’ve got a console application, it’s as simple as:
Dim client As New WebClient()
AddHandler client.DownloadProgressChanged, AddressOf ProgressUpdate
client.DownloadFileAsync(yourURI, yourFile)
Sub ProgressUpdate(sender As Object, e As DownloadProgressChangedEventArgs)
' Reset cursor position …
Console.CursorTop -= 1
Console.CursorLeft = 0
Console.WriteLine("{0}% completed", e.ProgressPercentage)
End Sub
If, on the other hand, you are on a Form in a WinForms project and you’ve got a label ProgressLabel that you want to update, the following code will do that:
Sub ProgressUpdate(sender As Object, e As DownloadProgressChangedEventArgs)
Dim s = String.Format("{0}% completed", e.ProgressPercentage)
Me.Invoke(New Action(Sub()
ProgressLabel.Text = s
End Sub))
End Sub
The ProgressUpdate method is a bit complicated due to multithreading:
The WebClient is running the asynchronous file download in a background thread. However, form controls can only be updated from the foreground thread that the form is running in. For that reason, we cannot update the label directly inside the ProgressUpdate event (because that, too, is being invoked, and running, in the background thread1).
So what we do instead is use the Form.Invoke method which guarantees that whatever we want to execute is execute in the form’s own thread. We pass an Action delegate to the Invoke method which contains the code that we want to execute. And that code is just updating the label.
1 At least I couldn’t find anything in the documentation saying otherwise – the event might actually execute in the foreground thread but in that case the above code still works.
I just have a simple vb.net website that need to call a Sub that performs a very long task that works with syncing up some directories in the filesystem (details not important).
When I call the method, it eventually times out on the website waiting for the sub routine to complete. However, even though the website times out, the routine eventually completes it's task and all the directories end up as they should.
I want to just prevent the timeout so I'd like to just call the Sub asynchronously. I do not need (or even want) and callback/confirmation that it ran successfully.
So, how can I call my method asynchronously inside a website using VB.net?
If you need to some code:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Call DoAsyncWork()
End Sub
Protected Sub DoAsyncWork()
Dim ID As String = ParentAccountID
Dim ParentDirectory As String = ConfigurationManager.AppSettings("AcctDataDirectory")
Dim account As New Account()
Dim accts As IEnumerable(Of Account) = account.GetAccounts(ID)
For Each f As String In My.Computer.FileSystem.GetFiles(ParentDirectory)
If f.EndsWith(".txt") Then
Dim LastSlashIndex As Integer = f.LastIndexOf("\")
Dim newFilePath As String = f.Insert(LastSlashIndex, "\Templates")
My.Computer.FileSystem.CopyFile(f, newFilePath)
End If
Next
For Each acct As Account In accts
If acct.ID <> ID Then
Dim ChildDirectory As String = ConfigurationManager.AppSettings("AcctDataDirectory") & acct.ID
If My.Computer.FileSystem.DirectoryExists(ChildDirectory) = False Then
IO.Directory.CreateDirectory(ChildDirectory)
End If
My.Computer.FileSystem.DeleteDirectory(ChildDirectory, FileIO.DeleteDirectoryOption.DeleteAllContents)
My.Computer.FileSystem.CopyDirectory(ParentDirectory, ChildDirectory, True)
Else
End If
Next
End Sub
I wouldn't recommend using the Thread class unless you need a lot more control over the thread, as creating and tearing down threads is expensive. Instead, I would recommend using a ThreadPool thread. See this for a good read.
You can execute your method on a ThreadPool thread like this:
System.Threading.ThreadPool.QueueUserWorkItem(AddressOf DoAsyncWork)
You'll also need to change your method signature to...
Protected Sub DoAsyncWork(state As Object) 'even if you don't use the state object
Finally, also be aware that unhandled exceptions in other threads will kill IIS. See this article (old but still relevant; not sure about the solutions though since I don't reaslly use ASP.NET).
You could do this with a simple thread:
Add :
Imports System.Threading
And wherever you want it to run :
Dim t As New Thread(New ThreadStart(AddressOf DoAsyncWork))
t.Priority = Threading.ThreadPriority.Normal
t.Start()
The call to t.Start() returns immediately and the new thread runs DoAsyncWork in the background until it completes. You would have to make sure that everything in that call was thread-safe but at first glance it generally seems to be so already.
I also was looking for information on Asynchronous programming in VB. In addition to this thread, I also found the following: beginning with Visual Studio 2012 and .Net Framework 4.5, VB was given two new keywords to make a method asynchronous right in the declaration, without using Thread or Threadpool. The new keywords are "Async" and "Await". You may refer to the following links if you wish:
http://msdn.microsoft.com/library/hh191443%28vs.110%29.aspx
https://msdn.microsoft.com/en-us/library/hh191564%28v=vs.110%29.aspx
This is an older thread, but I figured I'd add to it anyway as I recently needed to address this. If you want to use the ThreadPool to call a method with parameters, you can modify #Timiz0r's example as such:
System.Threading.ThreadPool.QueueUserWorkItem(Sub() MethodName( param1, param2, ...))
Team,
I have build a VB.Net windows application which does uploads data into database and basically updates two controls:
1. A textbox which is constantly updated with one line per database record upload.
2. A label which keeps track of the count of database record uploaded.
I have used BackgroundWorker thread concept, where the thread's bgwWorker_DoWork() method contains the business logic for upload and bgwWorker_ProgressChanged() updates the 2 UI controls based on uploads.
But the issue I am facing is that I do not get complete updates on both the UI controls. Sometimes the thread bypasses update of textbox and sometimes of label. I could resolve this issue by adding System.Threading.Thread.Sleep(25) before each UI control update code. Is this correct way of solving the issue? OR is there something I am missing?
Kindly suggest.
Below is the code in both these methods:
Private Sub bgwWorker_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bgwWorker.DoWork
.................
.................
'Updates database record related update in textbox
System.Threading.Thread.Sleep(25)
updater.eventName = "UpdateStatusBox"
updater.errorMessageToLog = String.Empty
updater.errorMessageToLog += GetErrorMessage(dataTable(rowNumber)("Name").ToString(), ExceptionData)
bgwWorker.ReportProgress(1, updater)
.................
.................
'Updates Status Count in LABEL
System.Threading.Thread.Sleep(25)
updater.eventName = "UpdateStatusBar"
updater.successCount = successCount.ToString()
updater.failureCount = failureCount.ToString()
bgwWorker.ReportProgress(2, updater)
End Sub
Private Sub bgwWorker_ProgressChanged(ByVal sender As System.Object, ByVal e As ProgressChangedEventArgs) Handles bgwWorker.ProgressChanged
Dim updater As UIUpdater = TryCast(e.UserState, UIUpdater)
..........................................
If updater.eventName = "UpdateStatusBar" Then
UpdateStatusBar(updater.successCount, updater.failureCount)
ElseIf updater.eventName = "UpdateStatusBox" Then
txtUpdates.Text = txtUpdates.Text & updater.errorMessageToLog
End If
.....................................
End Sub
I'm almost positive that your problem is your instance of the UIUpdater object called updater. This object appears to be declared globally and is thus shared between calls.
Omitting a little bit of code this is what you have:
updater.eventName = "UpdateStatusBox"
bgwWorker.ReportProgress(1, updater)
updater.eventName = "UpdateStatusBar"
bgwWorker.ReportProgress(2, updater)
Although you call ReportProgress() linearly, it doesn't fire your ProgressChanged event immediately nor does it block until that method completed. To do so would defeat the purpose of threading if you think about it.
To put it another way, you have a global object that you are setting a property on. You then say "when someone gets a chance, do something with this". You then change a property on that global object and sometimes this happens before "someone has done something" happens.
The solution is either to create two global variables, one for each possible event or to just create an instance variable when needed. I'm not sure that its thread safe to use a global variable the way you are so I would recommend just creating an instance variable. In fact, the state object you pass to ReportProgress could just be a string.
I would NOT use a sleep in your DoWork event.
Have you tried refreshing the control after you update it? Each control has a Refresh method which forces a redraw. This may result in flickering though.
Another option is to include the information needed for both controls (textbox and label) in a single call to ReportProgress rather than trying to make two calls.