VB.NET - System.AccessViolationException: 'Attempted to read or write protected memory...' Occurs sporadically when using autoCompleteSource - vb.net

System.AccessViolationException: 'Attempted to read or write protected memory. This is often an indication that other memory is corrupt.'
I started getting this exception after trying to implement AutoComplete into one of my text boxes. It seems to occur somewhat sporadically? It often occurs when trying to use AutoComplete within a couple seconds of loading it's panel but has occured upwards of 5/6 seconds afterwards too. Additionally if it doesn't occur, the AutoComplete can be used endlessly without crashing.
I've had the same error occur on two different machines so I'm really unsure of what could be causing this and would be endlessly grateful if someone could point me in the right direction!
I've tried to strip out all the parts that definitely aren't causing it to save space.
This code runs first:
Private Sub btnManagerEditItem_Click(sender As Object, e As EventArgs) Handles btnManagerEditItem.Click
refreshEditItemsScreen(False) ' clears all on screen data and repopulates DataGrid
txtManagerEditItemsSearchName.AutoCompleteMode = AutoCompleteMode.SuggestAppend
txtManagerEditItemsSearchName.AutoCompleteSource = AutoCompleteSource.CustomSource
End Sub
Private Sub refreshEditItemsScreen(refreshDatabase As Boolean)
dtgrdManagerEditItems.Rows.Clear() ' DataGridView displays items to user.
If refreshDatabase Then
updateItemDatabase() ' reads the database from file and updates _itemDatabase with it
End If
For Each entry In _itemDatabase ' Global variable holding the database
dtgrdManagerEditItems.Rows.Add(entry.Name, entry.Price.ToString("c"), entry.UniqueID, getStaffDictionary(entry.StaffID))
Next
End Sub
Private Sub updateItemDatabase()
_itemDatabase = readItemDatabaseFromFile()
End Sub
Private Function readItemDatabaseFromFile() As List(Of Item)
Dim pathtofile As String = IO.Path.Combine(My.Application.Info.DirectoryPath.Substring(0, My.Application.Info.DirectoryPath.Length - 21), "Item Database.xml")
Dim itemDatabaseFromFile As New List(Of Item)
If System.IO.File.Exists(pathtofile) Then
Dim objStreamReader As New StreamReader(pathtofile)
Dim xsSerialize As New XmlSerializer(itemDatabaseFromFile.GetType)
itemDatabaseFromFile = xsSerialize.Deserialize(objStreamReader)
objStreamReader.Close()
End If
Return itemDatabaseFromFile
End Function
Then this code runs when you start typing in the AutoCorrect box:
Private Sub txtManagerEditItemsName_TextChanged(sender As Object, e As EventArgs) Handles txtManagerEditItemsSearchName.TextChanged
txtManagerEditItemsSearchName.AutoCompleteCustomSource = _autoCompleteSource
End Sub
_autoCompleteSource gets it's value when this code is ran on form load (updateItemDatabase() is also ran on form load):
Private Sub updateAutoCompleteSource()
' reads the id dictionary from file and adds it to the auto complete source
For Each Itm In readItemIDDictionaryFromFile().Keys
_autoCompleteSource.Add(Itm)
Next
End Sub
Private Function readItemIDDictionaryFromFile() As Dictionary(Of String, String)
Dim pathtofile As String = IO.Path.Combine(My.Application.Info.DirectoryPath.Substring(0, My.Application.Info.DirectoryPath.Length - 21), "ID Dictionary.txt")
Dim output As New Dictionary(Of String, String)
If File.Exists(pathtofile) Then
For Each line In IO.File.ReadAllLines(pathtofile)
Dim parts() As String = line.Split(",")
output.Add(parts(1), parts(0))
Next
Return output
End If
End Function
Sorry if some of this code was unnecessary to post, I'm pretty lost as to what's causing the error so I just posted everything that runs before it occurs that I think could be related to it.
Thank you

Related

Bound datatgridview not saving changes to tableadapter, and thus not to database

I have a datagridview bound to a table that seems to work fine, but will not save changes to the database. I am using the same code as I used in another project, which did save changes, so I am flummoxed. Also, I have debugged and the save code is being called, just not working. The code in the form calls a separate class with business logic.
Code in the form below:
Private Sub frmAdminAssign_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' Below is generated by Visual Studio when I select my data source for my datagridview
Credit_adminTableAdapter.Fill(DataSet1.credit_admin) ' Foreign key relationship to table being updated (lookup table).
LoanTableAdapter.Fill(DataSet1.loan) ' Table being updated
admin_assign = New AdminAssign()
admin_assign.FilterUnassigned(dgvAssign, LoanTableAdapter)
End Sub
Private Sub dgvAssign_CellEndEdit(sender As Object, e As DataGridViewCellEventArgs) Handles dgvAssign.CellEndEdit
admin_assign.SaveAssignmentChanges(DataSet1, LoanTableAdapter)
End Sub
Below is code in business logic class called from above:
Public Sub SaveAssignmentChanges(ByRef data_set As DataSet1, ByRef loan_table_adapter As DataSet1TableAdapters.loanTableAdapter)
' Saves changes to DB.
Dim cmdBuilder As SqlCommandBuilder
cmdBuilder = New SqlCommandBuilder(loan_table_adapter.Adapter)
loan_table_adapter.Adapter.UpdateCommand = cmdBuilder.GetUpdateCommand(True)
Try
loan_table_adapter.Adapter.Update(data_set)
Catch unknown_ex As Exception
Dim error_title As String = "Database Save Error"
Dim unknown_error As String = $"There was an error saving to the database.{vbNewLine}Error: {unknown_ex.ToString}"
MessageBox.Show(unknown_error, error_title, MessageBoxButtons.OK, MessageBoxIcon.Warning)
End Try
End Sub
The data from the datagridview is not saving to the tableadapter, which I found out by adding the lines below in the start of the second procedure:
Dim x As String = loan_table_adapter.GetData()(0)("note_number") ' Unchanged, check was right row
Dim y As String = loan_table_adapter.GetData()(0)("credit_admin_id") ' Changed in datagridview but still null (get null error)
I figured it out, it was related to a filter routine that is not above where I passed a tableadapter byref when I should have passed a datatable byval. So, related to one of jmcilhinney's byref vs byval comments above. See problem code below:
Public Sub FilterUnassigned(ByRef dgv_assigned As DataGridView, loan_table_adapter As DataSet1TableAdapters.loanTableAdapter)
' Should have passed in DataSet1.loan DataTable ByVal and used it below:
Dim unassigned_loans As DataView = loan_table_adapter.GetData().DefaultView()
unassigned_loans.RowFilter = "request_date Is Not Null AND numerated_assigned_user Is Null"
dgv_assigned.DataSource = unassigned_loans
End Sub
Also, skipping the SQLCommandBuilder commands and my code still works. So, all I need is one line as per jmcilhinney instead of 4 (excl error handling) as per below:
Try
loan_table_adapter.Update(data_set)
Catch unknown_ex As Exception
Dim error_title As String = "Database Save Error"
Dim unknown_error As String = $"There was an error saving to the database. Please contact Kevin Strickler to resolve.{vbNewLine}Error: {unknown_ex.ToString}"
MessageBox.Show(unknown_error, error_title, MessageBoxButtons.OK, MessageBoxIcon.Warning)
End Try

Need a fix for my deadlocking issue with my parallel.foreach

When running my code I seem to encounter deadlocks while trying to update a GUI element from within one of the parallel tasks.
I've tried surrounding the Output function with "Synclock me" to try to ensure that only one task is trying to update the control at a time.
Private Sub RunParallel(records as list(of DataRecord), ou as String)
Dim ParallelOptions As New ParallelOptions
ParallelOptions.MaxDegreeOfParallelism = 10
Parallel.ForEach(records, ParallelOptions, Sub(myrecord)
ProcessRecord(myrecord, ou)
End Sub)
Output("Done...." & vbCrLf)
End Sub
Private Sub ProcessRecord(ByVal record As DataRecord, ByVal ou As String)
'Output($"BromcomID = {record("ID")}, Forename = {record("Forename")}{vbCrLf}")
Dim ud As New UserDetails With {
.EmployeeID = record("ID"),
.SamAccountName = record("SamAccountName"),
.GivenName = record("Forename"),
.Surname = record("Surname")
}
If Not CreateUser(ou, ud) Then
'Threading.Thread.Sleep(2000)
' Output($"Error creating {ud.EmployeeID}{vbCrLf}")
End If
End Sub
Private Sub Output(ByVal s As String)
SyncLock Me
If Me.InvokeRequired Then
Invoke(Sub()
Outbox.AppendText(s)
Outbox.SelectionStart = Len(Outbox.Text)
Outbox.ScrollToCaret()
Outbox.Select()
End Sub)
Else
Outbox.AppendText(s)
Outbox.SelectionStart = Len(Outbox.Text)
Outbox.ScrollToCaret()
Outbox.Select()
End If
End SyncLock
End Sub
The code as supplied seems to run, but if I uncomment out the Output calls in the ProcessRecord() function, it hangs and never gets exits the Parallel.foreach
--- Update
After playing around with suggestions and comments on here I still can't get it to work correctly.
If I take out all of the output from ProcessRecord it seems to work correctly. However with the following code, it now seems to run each ProcessRecord sequentially (not 10 at a time as I intended), and then hangs after the last one.
Output("Dispatching" & vbCrLf)
Dim ParallelOptions As New ParallelOptions With {
.MaxDegreeOfParallelism = 10
}
Parallel.ForEach(recordList, ParallelOptions, Sub(myrecord)
ProcessRecord(myrecord, ou)
End Sub)
'For Each myrecord As DataRecord In recordList
' Task.Factory.StartNew(Sub() ProcessRecord(myrecord, ou))
'Next
Output("Done...." & vbCrLf)
End Sub
Private Sub ProcessRecord(ByVal record As DataRecord, ByVal ou As String)
Dim ud As New UserDetails With {
.EmployeeID = record("ID"),
.SamAccountName = record("SamAccountName"),
.GivenName = record("Forename"),
.Surname = record("Surname"),
.DisplayName = $"{record("Forename")} {record("Surname")} (Student)"}
If Not CreateUser(ou, ud) Then
' Output($"Error creating {ud.EmployeeID}{vbCrLf}")
End If
Output($"BromcomID = {record("ID")}, Forename = {record("Forename")}{vbCrLf}")
End Sub
Private Sub Output(ByVal s As String)
If Me.InvokeRequired Then
Invoke(Sub()
Output(s)
End Sub)
Else
Outbox.AppendText(s)
Outbox.SelectionStart = Outbox.TextLength
Outbox.ScrollToCaret()
Outbox.Select()
Outbox.Refresh()
End If
End Sub
If I use the commented out Task.Factory code everything seems to work perfectly, except I cant control how many tasks at a time are launched, and I can't wait till all of them have finished, the for loop just launches all the tasks, and then carries on with the Output("Done....) line.
The synclock statements didn't seem to affect anything either way.
Give this a try
Private Sub Output(ByVal s As String)
If Me.InvokeRequired Then
Me.Invoke(Sub() Output(s))
'Me.BeginInvoke(Sub() Output(s))
Else
Outbox.AppendText(s)
Outbox.SelectionStart = Outbox.TextLength
Outbox.ScrollToCaret()
Outbox.Select()
Outbox.Refresh()
End If
End Sub
There may be an issue if you have events tied to Outbox, like text changed. Tested Output method with
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim nums As New List(Of Integer)
For x As Integer = 1 To 500
nums.Add(x)
Next
'because it is in a button, run from a task
Dim t As Task
t = Task.Run(Sub()
Parallel.ForEach(nums, Sub(num)
Output(num.ToString & Environment.NewLine)
End Sub)
End Sub)
End Sub
If you want to go ahead with using a Task-based approach, then you certainly can control how many are launched at a time, and wait for all of them to finish. It requires some additional code for the manual management. This is discussed in some detail in Microsoft documentation: https://learn.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/consuming-the-task-based-asynchronous-pattern
It's not necessarily a bad thing to initiate all of the tasks immediately, then you'll be leaving it to the thread pool to take care of how many to run at a time.
If you want greater control, you can use the "throttling" design from the link. In your "pending" queue, store delegates/lambdas that will themselves kick off Task.Run. Then, as you dequeue from the "pending" queue into the "active" list, you can Invoke on the delegate/lambda to get the Task and Await Task.WhenAny on the "active" list.
One potential benefit of doing things this way is that the work in each top-level Task can be split between UI work running on the UI thread and processor-limited work running on the thread pool.
(I'm not suggesting that this is necessarily the best option for you, just trying to expand on what you should be looking at doing if you really want to pursue using Task instead of Parallel.)

How to write a List(of) to a text file and retrieve it (vb.net)

Title. I need to write my ListOf, which just contains values such as Zero, Zero, One, One, Two, etc to a text file, and then load back up again. Any help appreciated!
Hello & Welcome to Stack Overflow!. In the future please show some effort when asking a question and at least google or even bing your question first, there are a stack of tutorials regarding your question.With that being said, I am going to give you a lifeline.From what I can tell, you want to write your list to a text file and then read from that text file.
Module Module1
Dim mylist As List(Of String) = New List(Of String)
Dim desktopPath As String = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
Dim newfile As String = "myTextFile.txt"
Dim newPath As String = System.IO.Path.Combine(desktopPath, newfile)
Sub Main()
mylist.Add("Zero")
mylist.Add("One")
mylist.Add("Two")
mylist.Add("Three")
writer()
End Sub
Sub writer()
Using sw As New System.IO.StreamWriter(newPath)
For Each item As String In mylist
sw.WriteLine(item)
Next
sw.Flush() ''yeap I'm the sort of person that flushes it then closes it
sw.Close()
End Using
reader()
End Sub
Sub reader()
Using sr As New System.IO.StreamReader(newPath)
Console.WriteLine(sr.ReadToEnd)
sr.Close()
End Using
Console.ReadKey()
End Sub
End Module
I didn't put too much effort into this, I will leave the rest up to you, however this should get your well and truly on your way.This was done with a Console applicationAlso if you have any problems or even a question or two regarding my answer, leave a comment and I will do my best to answer you and help you out as I know learning something for the first time can be difficult and you will have lots of questions.EDIT: If you need to load each value separately eg skip the first 4 lines and only read the 5th line, you should look into learning how to do a loop.
EDIT - Here is what I think you are trying to achieve just from reading your comments.
''Reads whatever is in the newPath Textfile and addes the words to a listbox or wherever is needed.
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
ListBox1.Items.Clear() ''This is stop double ups.
Dim myTextFile = System.IO.File.ReadAllLines(newPath)
For Each word As String In myTextFile
ListBox1.Items.Add(word) '' change this to mylist if need be
''mylist.Add(word)
Next
End Sub
This should fix your problem, although you may need to clear the mylist first or even create another array.
Here's some sandbox code:
Imports System.Xml.Serialization
Imports System.IO
Public Class frmTest
Dim l1 As New List(Of String)
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
l1.AddRange({"1", "b", "7"})
Dim mySerializer As XmlSerializer = New XmlSerializer(GetType(List(Of String)))
' To write to a file, create a StreamWriter object.
Dim myWriter As StreamWriter = New StreamWriter("C:\temp\list.xml")
mySerializer.Serialize(myWriter, l1)
myWriter.Close()
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim myFileStream As FileStream = New FileStream("C:\temp\list.xml", FileMode.Open)
Dim mySerializer As XmlSerializer = New XmlSerializer(GetType(List(Of String)))
' Call the Deserialize method and cast to the object type.
l1 = New List(Of String) ' refresh for test
l1 = CType(mySerializer.Deserialize(myFileStream), List(Of String))
Stop ' l1 is populated
End Sub
End Class

Find and Replace using another form

I have my frmMainwhich has RichTextBox1 and I have a button btnfind&Replacewhich whose click event pops out another minute form frmFindandReplace which has two textboxes: TextBoxSearch and TextBoxReplace with two buttons: replaceButton and findButton. I cannot seem to get my code for instances of finding a word in textbox and an instance of replacing it. Here is my code:
Public Class frmFindandReplace
Dim txtClientArea As RichTextBox
Private Sub TextBoxSearch_TextChanged(sender As Object, e As EventArgs) Handles TextBoxSearch.TextChanged
End Sub
Private Sub frmFindandReplace_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Private Sub replaceButton_Click(sender As Object, e As EventArgs) Handles replaceButton.Click
End Sub
Protected Friend Sub findButton_Click(sender As Object, e As EventArgs) Handles findButton.Click
Dim a As String
Dim b As String
a = TextBoxSearch.Text
b = InStr(StartPosition, a, txtClientArea)
If b Then txtClientArea.Focus()
txtClientArea.SelectionStart = b - 1
txtClientArea.SelectionLength = Len(a)
txtClientArea.ScrollToCaret()
End Sub
The findButton code doesnot even work. Throws an error!
Error 3: Overload resolution failed because no accessible 'InStr' can be called with these arguments:
'Public Function InStr(Start As Integer, String1 As String, String2 As String, [Compare As Microsoft.VisualBasic.CompareMethod = Microsoft.VisualBasic.CompareMethod.Binary]) As Integer': Value of type 'System.Windows.Forms.TextBox' cannot be converted to 'String'.
'Public Function InStr(String1 As String, String2 As String, [Compare As Microsoft.VisualBasic.CompareMethod = Microsoft.VisualBasic.CompareMethod.Binary]) As Integer': Value of type 'System.Windows.Forms.RichTextBox' cannot be converted to 'Microsoft.VisualBasic.CompareMethod'. C:\Users\Joseph GodwinKE\Documents\Visual Studio 2013\Projects\simpleapp\frmFindandReplace.VB 25 13 Simple app
I know I have not done much but am new and all my efforts of searching a solution over the internet have failed! Thank you I hope someone will help me pls.
A few pointers:
InStr returns an integer.
Check the documentation as it'll show you have the search values the wrong way around.
Turn Option Explicit on to help find your issues.
This should work better.
Private Sub findButton_Click(sender As Object, e As EventArgs) Handles findButton.Click
Dim searchString As String
Dim findPos As Integer
Try
searchString = TextBoxSearch.Text
findPos = InStr(txtClientArea.Text, searchString)
If findPos > 0 Then txtClientArea.Focus()
txtClientArea.SelectionStart = findPos - 1
txtClientArea.SelectionLength = searchString.Length
txtClientArea.ScrollToCaret()
Catch ex As Exception
MessageBox.Show(String.Concat("An error occurred: ", ex.Message))
End Try
End Sub
If you want your code work you need to pass the reference to the RichTextBox present in the first form to the findandReplace form.
Otherwise you will not be able to work with that instance of the RichTextBox.
Usually, this means that when you create and open an instance of the findandReplace form you pass the reference to the RichTextBox to work with in the call to the constructor. Something like this
Dim fReplace As frmFindandReplace = New frmFindandReplace(Me.txtClientArea)
fReplace.Show()
Here the New call reaches the constructor of frmfindandReplace. This call is usually hidden by VB.NET but you could add it writing explicit code for it
Public Class frmFindandReplace
Dim txtClientArea As RichTextBox
Public Sub New (ByVal txt as RichTextBox)
txtClientArea = txt
End Sub
Now the global variable txtClientArea inside the findandReplace class is assigned to the existing reference of the RichTextBox present in the first form and you could happily work with it
Protected Friend Sub findButton_Click(sender As Object, e As EventArgs) Handles findButton.Click
Dim a As String
a = TextBoxSearch.Text
Dim position = txtClientArea.Find(a, 0, RichTextBoxFinds.MatchCase)
.....
End Sub
And please make yourself a favor and start using the more complete methods available from the NET Framework library and stop using the old fashioned VBA methods.
For example the RichTextBox has a method that does exactly what you are trying to do in code. Find, search the content of the textbox and if it founds a match it highlight the text and return the starting position of the text.
There is no replace builtin method but having the position and the length is really simple to implement your own replacing code.
You Have defined b as a string. Change it to an integer. Also Instr doesn't allow you to set a start position, just a string to search and the string to search for and optionally the type of search - Binary or Text.
Finally rather than type If b then, use If b>0 then rather than turning off Option Strict. It's always better to write code with Option Strict on as it makes you write better code and in the long run is easier to chase down errors

Hyperlinks inside ListBox

Hello, can someone help me with my project that I'm working on? Anything is appreciated!
So, what do I need? Its simple, I need link and name bars to actually create a hyperlink into the LSB listbox. Can someone please help me??
PS: Could you please keep it simple for me, I just started with this language.
OK This isn't as simple as I would like it to be for you but here goes. I'll try to explain as I go along
OK all the bits code below goes in your Form1 class as one lump - without my explanations that is. What I've done here is create a new class called Hyperlink
It has a couple of properties called Name and Link and a constructor called New
Class HyperLink
Private friendlyName As String
Private link As String
Public Property Name As String
Get
Return friendlyName
End Get
Set(ByVal value As String)
friendlyName = value
End Set
End Property
Public Property URL As String
Get
Return link
End Get
Set(value As String)
link = URL
End Set
End Property
Public Sub New(nm As String, ln As String)
friendlyName = nm
link = ln
End Sub
End Class
Here I'm creating list called linkList - this will hold the list of hyperlinks - I'm doing this so that later on the listbox will be set to use this as it's source of list items
Dim linklist As New List(Of HyperLink)
This sub called addLinkToListbox actually does several things. The important bits are that it tells you program to temporarily stop responding when the index of the listbox changes(which happens even when you're changing the contents off the listbox)
Then it adds a new **hyperlink* to the list of hyperlinks taking data from the link textbox and the name textbox.
To actually refresh the data shown in the textbox, I have to change the listbox datasource to nothing and then back to to the linkList
The next two lines tell the listbox to display the Name property inn the list box and when the item is actually clicked, to return the URL property.
Finally, I tell the program to start responding again when the index of the listbox index changes.
Private Sub addLinkToListbox(linkName As String, linkURL As String)
RemoveHandler ListBox1.SelectedIndexChanged, AddressOf ListBox1_SelectedIndexChanged
linklist.Add(New HyperLink(linkName, linkURL))
ListBox1.DataSource = Nothing
ListBox1.DataSource = linklist
ListBox1.DisplayMember = "Name"
ListBox1.ValueMember = "URL"
AddHandler ListBox1.SelectedIndexChanged, AddressOf ListBox1_SelectedIndexChanged
End Sub
These last two bits of code are hopefully self-explanatory
Private Sub btnAddLink_Click(sender As Object, e As EventArgs) Handles btnAddLink.Click
addLinkToListbox(txtName.Text, txtLink.Text)
End Sub
Private Sub ListBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ListBox1.SelectedIndexChanged
MessageBox.Show(ListBox1.SelectedValue.ToString)
End Sub