vb.net system.threading.thread ask the thread to cleanup and stop - vb.net

I am new to vb.net multithreading, and I am facing a issue.
For threading I am using system.threading.thread function.
I know I can stop a thread using thread.abort call. But that's not a good choice (I have searched a bit and came to know that). This also will not work because my running threads needs some cleanup before cancellation.
So, I'm trying to do some kind of action that set a variable to UI thread like
dim bolStop as Boolean
So, if my UI thread set that Boolean value as true then all running thread can access/notice that change and can start cleanup process before stop/cancel the thread itself.
hope that makes sense...
best regards
Edit:1 (Source Is Pasted Here as Suggested By #DrunkenCodeMonkey
This is the code for Main Form (Form1.vb) (I think i need something here and also on the thread module (class) too) so, below i will paste module code too
Public Class Form1
Private objThreadList As ArrayList
Dim bolStop As Boolean
Private Sub cmd1_Click(sender As System.Object, e As System.EventArgs) Handles cmd1.Click
Dim intTLoop As Integer
sPrepareThreading()
For intTLoop = 1 To 5
sSendThread("", "Zakir", "Biplob")
Next
'sFinishThreading()
MessageBox.Show("Done!")
End Sub
Sub sPrepareThreading()
Dim intTLoop As Integer
objThreadList = New ArrayList
For intTLoop = 1 To 3
objThreadList.Add(Nothing)
Next intTLoop
End Sub
Sub sSendThread(strDraft As String, ByVal strTitle As String, ByVal strSubHeader As String)
Dim bolFalied As Boolean
Dim intTLoop As Integer
Dim objItem As System.Windows.Forms.ListViewItem
Dim objTData As objThreadCrossData
Dim objThreadClass As clsThread
Dim objNewThread As System.Threading.Thread
Zakir_RecheckThread:
My.Application.DoEvents()
If bolStop = True Then
Exit Sub
End If
bolFalied = True
For intTLoop = 1 To 3
bolFalied = True
If objThreadList(intTLoop - 1) Is Nothing Then
bolFalied = False
Else
If CType(objThreadList(intTLoop - 1), System.Threading.Thread).IsAlive = False Then
bolFalied = False
Else
bolFalied = True
End If
End If
If bolFalied = False Then
objTData = New objThreadCrossData
objTData.ID = ListView1.Items.Count
objTData.bolStop = False
objTData.strTime = DateTime.Now.ToString
objItem = ListView1.Items.Add(strTitle)
objItem.SubItems.Add(strSubHeader)
objItem = Nothing
objThreadClass = New clsThread(objTData, "", Me)
objNewThread = New System.Threading.Thread(AddressOf objThreadClass.StartThread)
objNewThread.IsBackground = True
objNewThread.Start()
objThreadList(intTLoop - 1) = objNewThread
Exit For
End If
Next
If bolFalied = True Then
GoTo Zakir_RecheckThread
Exit Sub
End If
End Sub
Public Sub ReceiveThreadMessage(ByVal objCrosData As Object)
Dim objTDataV1 As objThreadCrossData
objTDataV1 = CType(objCrosData, objThreadCrossData)
ListView1.Items(objTDataV1.ID).SubItems(1).Text = objTDataV1.strProgress
End Sub
Private Sub cmd2_Click(sender As System.Object, e As System.EventArgs) Handles cmd2.Click
REM This is where i like to stop the running thred either by setting bolStop = True and all running thead check that variable.
REM or by chate the thread (Thread Data Object Property naemd "bolStop" value"
REM please help me out...
End Sub
End Class
Code For Thread Module (clsThread.vb):
Public Class clsThread
Private m_Counter As Integer = 0
Private m_MainWindow As Form
Dim m_ThreadCrorssData As objThreadCrossData
Private Delegate Sub NotifyMainWindow(ByVal objCrosData As objThreadCrossData)
'We need an object of this deletegate
Private m_NotifyMainWindow As NotifyMainWindow
Public Sub New(ByVal objCrossData As Object, ByVal strXML As String, ByRef MainWindow As Form1)
m_MainWindow = MainWindow
m_ThreadCrorssData = CType(objCrossData, objThreadCrossData)
'We need to point our delegate to the Method, which we want to call from this thread
m_NotifyMainWindow = AddressOf MainWindow.ReceiveThreadMessage
End Sub
Public Sub StartThread()
While m_Counter < 100
m_Counter = m_Counter + 1
rem for example here i like to check for the value that whether user asked to stop
m_ThreadCrorssData.strProgress = CStr(m_Counter)
m_MainWindow.Invoke(m_NotifyMainWindow, m_ThreadCrorssData)
rem for example also here i like to check for the value that whether user asked to stop
'wait for some time before continuing loop
System.Threading.Thread.Sleep(1000)
End While
End Sub
End Class
Here is the Code For Module (Thread Object Data)
Module mdlCMM
Public Class objThreadCrossData
Property strProgress As String
Property bolStop As Boolean = False
Property strTime As String
Property ID As Integer
Property intStatusNum As Integer
Property strStatusValue As String
End Class
End Module

Related

System.Diagnostics.Process not properly closing after the application is closed

I have been trying to find a solution to why the ExitTool I am using is not properly closing out after I close the main application. What is happening is that when I close my application, the ExifTool stays running in the background and I have to manually kill it.
Here is the code snippet of the process startup.
Public Shared Sub ExecuteExifTool()
If ExifToolStarted Then Exit Sub
ExifToolStarted = True
Dim NowString As String = Date.Now.ToString("yyyyMMddHHmmss")
If Not IO.Directory.Exists(".\Runtime") Then IO.Directory.CreateDirectory(".\Runtime")
Dim GetDirectory As New IO.DirectoryInfo(".\Runtime")
If GetDirectory.GetFiles.Count > 0 Then
For Each i As IO.FileInfo In GetDirectory.GetFiles
If i.FullName.Contains("exif.Yv") AndAlso i.FullName.Contains("-cL") AndAlso i.FullName.Contains(".exe") Then : Try : i.Delete() : Catch : End Try : End If
Next
End If
HostName = ".\Runtime\exif.Yv" & NowString.Substring(0, 8) & "-cL" & NowString.Substring(8, 6) & ".exe"
IO.File.Copy(".\exiftool.exe", HostName)
Using ExifToolProcess As New Process
With ExifToolProcess
.StartInfo.RedirectStandardInput = True
.StartInfo.FileName = HostName
.StartInfo.UseShellExecute = False
.StartInfo.Arguments = "-stay_open" & " True -# " & "-"
.StartInfo.RedirectStandardOutput = True
.StartInfo.RedirectStandardError = True
.StartInfo.CreateNoWindow = True
.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
.Start()
.BeginOutputReadLine()
.BeginErrorReadLine()
End With
End Using
End Sub
The latest attempt to solve this issue was to try and launch another executable application; which essentially, is another Windows.Forms.Form that waits for the main application to close and then it will attempt to kill the process immediately afterwards, then dispose of itself. Here is the snippet.
Public Class KillProcess
Private _ProcessName As String
Public Property ProcessName As String
Get
Return _ProcessName
End Get
Set(value As String)
_ProcessName = value
End Set
End Property
Private _MainApp As Form
Public Property MainApplication As Form
Get
Return _MainApp
End Get
Set(value As Form)
_MainApp = value
End Set
End Property
Private Sub KillProcess_Load(sender As Object, e As EventArgs) Handles MyBase.Load
CleanupProcess()
End Sub
Public Sub CleanupProcess()
While Not MainApplication.IsDisposed
Dim FilesToDelete As New List(Of String)
Dim ProcessesToKill As New List(Of Process)
For Each p As Process In Process.GetProcesses
If p.ProcessName = ProcessName Then
FilesToDelete.Add(p.MainModule.FileName)
ProcessesToKill.Add(p)
End If
Next
For Each p As Process In ProcessesToKill
Try
p.Kill()
p.WaitForExit(10000)
p.Close()
Catch winException As System.ComponentModel.Win32Exception
Catch invalidException As InvalidOperationException
End Try
Next
End While
Me.Dispose()
End Sub
End Class
And here is the code snippet of the startup.
Public Sub CleanupTask()
Dim Handler As New Custodian.KillProcess With {.ProcessName = ExifToolHooker.HostName, .MainApplication = Me}
Windows.Forms.Application.Run(Handler)
End Sub
Private Sub CloseApplication(sender As Object, e As FormClosingEventArgs) Handles Me.Closing
Dim TaskHandler As Thread = New Thread(AddressOf CleanupTask)
TaskHandler.SetApartmentState(ApartmentState.STA)
TaskHandler.Start()
...
End Sub

Fire method on Main Thread from other Threads

Issue
I am using multi-threading inside my application and the way it works is that i have an array that contains 22 string which stand for some file names:
Public ThreadList As String() = {"FSANO1P", "FJBJB1P", "COPOR1P", "FFBIVDP", "FFCHLDP", "FFDBKDP", "FFDREQP", "FFINVHP", "FFJMNEP", "FFPIVHP", "FFUNTTP", "FJBJM1P", "FJBJM2P", "FJBNT2P", "FPPBE9P", "FTPCP1P", "FTTEO1P", "FTTRQ1P", "FJBJU1P", "FTTEG1P", "FFJACPP", "XATXTDP"}
I then loop through the array and create a new thread for each file:
For Each mThreadName As String In ThreadList
Dim mFileImportThread = New FileImportThreadHandling(mThreadName, mImportGuid, mImportDate, Directory_Location, mCurrentProcessingDate, mRegion)
Next
So inside the new thread 'FileImportThreadHandling' it will call a method by starting a new thread:
mThread = New Thread(AddressOf DoWork)
mThread.Name = "FileImportThreadHandling"
mThread.Start()
Then in 'DoWork' it will determine what file is current in question and will run the code related to the file.
After the code has ran for the file I want to report this back to the main thread. Can somebody give me a solution please.
You will need to use Delegates
EDIT:
Take a look at this snippet:
Sub Main()
mThread = New Thread(AddressOf doWork)
mThread.Name = "FileImportThreadHandling"
mThread.Start()
End Sub
Sub doWork()
'do a lot of hard work
workDone(result)
End Sub
Delegate Sub workDoneDelegate(result As Integer)
Sub workDone(abilita As Boolean, Optional src As Control = Nothing)
If Me.InvokeRequired Then
Me.Invoke(New workDoneDelegate(AddressOf workDone), {result})
Else
'here you're on the main thread
End If
End Sub
Here is an example that might give you some ideas. Note the use of Async and Task. You will need a form with two buttons, and a label.
Public Class Form1
Public ThreadList As String() = {"FSANO1P", "FJBJB1P", "COPOR1P", "FFBIVDP", "*******", "FFJACPP", "XATXTDP"}
'note Async keyword on handler
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Button1.Enabled = False
Dim running As New List(Of Task)
For Each tskName As String In ThreadList
'start new task
Dim tsk As Task
tsk = Task.Run(Sub()
For x As Integer = 1 To 10
Dim ct As Integer = x
'simulate code related to the file
Threading.Thread.Sleep(500)
'report to the UI
Me.Invoke(Sub()
Label1.Text = String.Format("{0} {1}", tskName, ct)
End Sub)
Next
End Sub)
Threading.Thread.Sleep(100) 'for testing delay between each start
running.Add(tsk)
Next
'async wait for all to complete
For Each wtsk As Task In running
Await wtsk
Next
Button1.Enabled = True
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
'test UI responsive during test
Label1.Text = DateTime.Now.ToString
End Sub
End Class
Using Actions and a callback to track remaining operations. (Simplified your class for my example)
Public ThreadList As String() = {"FSANO1P", "FJBJB1P", "COPOR1P", "FFBIVDP", "FFCHLDP"}
Private actionCounter As Integer = 0
Private lockActionCounter As New Object()
Sub Main()
Console.WriteLine("Starting...")
For Each mThreadName As String In ThreadList
Dim mFileImportThread = New FileImportThreadHandling(mThreadName)
actionCounter += 1
Call New Action(AddressOf mFileImportThread.DoWork).
BeginInvoke(AddressOf callback, mThreadName)
Next
Console.Read()
End Sub
Private Sub callback(name As IAsyncResult)
Dim remainingCount As Integer
SyncLock lockActionCounter
actionCounter -= 1
remainingCount = actionCounter
End SyncLock
Console.WriteLine("Finished {0}, {1} remaining", name.AsyncState, actionCounter)
If remainingCount = 0 Then Console.WriteLine("All done")
End Sub
Private Class FileImportThreadHandling
Shared r = New Random()
Private _name As String
Public Sub New(name As String)
_name = name
End Sub
Public Sub DoWork()
Dim delayTime = (r).Next(500, 5000)
Console.WriteLine("Doing {0} for {1:0}ms.", _name, delayTime)
Thread.Sleep(delayTime)
End Sub
End Class

Async and Await, why two return values

I'm trying to get my head around Async and Await. It's going well but one thing I would like clarification on is why there are two return statements in my method. I'm really looking for an explanation of what is actually happening behind the scenes.
I'll post the full code below as it only amounts to around 80 lines. I'm talking about the central method AllSubfolderFiles, which has both Return counter and Return dirsFraction. What's actually happening with these?
Basically, it is a WinForm application that iterates all the files of subfolders, updating a ProgressBar for each iterated subfolder.
Imports System.IO
Public Class frmAsyncProgress
Private Sub frmAsyncProgress_Load(sender As Object, e As EventArgs) Handles MyBase.Load
barFileProgress.Minimum = 0
barFileProgress.Maximum = 100
btnCancel.Enabled = False
End Sub
Private Async Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click
If String.IsNullOrWhiteSpace(txtPath.Text) Then
MessageBox.Show("Provide a location first.", "Location")
Exit Sub
End If
Dim sLocation As String = txtPath.Text.Trim()
If Not Directory.Exists(sLocation) Then
MessageBox.Show("Directory doesn't exist.", "Location")
Exit Sub
End If
Dim progressIndicator = New Progress(Of Integer)(AddressOf UpdateProgress)
btnStart.Enabled = False
btnCancel.Enabled = True
lblPercent.Text = "0%"
Dim allFiles As Integer = Await AllSubfolderFiles(sLocation, progressIndicator)
Debug.WriteLine(allFiles.ToString()) 'the number of subfolders iterated
btnStart.Enabled = True
btnCancel.Enabled = False
End Sub
Private Async Function AllSubfolderFiles(location As String, progress As IProgress(Of Integer)) As Task(Of Integer)
Dim dirsTotal As Integer = Directory.GetDirectories(location).Length
Dim dirsFraction As Integer = Await Task(Of Integer).Run(Function()
Dim counter As Integer = 0
For Each subDir As String In Directory.GetDirectories(location)
SubfolderFiles(subDir)
counter += 1
If progress IsNot Nothing Then
progress.Report(counter * 100 / dirsTotal)
End If
Next
Return counter
End Function)
Return dirsFraction
End Function
Private Sub UpdateProgress(value As Integer)
barFileProgress.Value = value
lblPercent.Text = (value / 100).ToString("#0.##%")
End Sub
Private Sub SubfolderFiles(location As String)
'source: http://stackoverflow.com/questions/16237291/visual-basic-2010-continue-on-error-unauthorizedaccessexception#answer-16237749
Dim paths = New Queue(Of String)()
Dim fileNames = New List(Of String)()
paths.Enqueue(location)
While paths.Count > 0
Dim sDir = paths.Dequeue()
Try
Dim files = Directory.GetFiles(sDir)
For Each file As String In Directory.GetFiles(sDir)
fileNames.Add(file)
Next
For Each subDir As String In Directory.GetDirectories(sDir)
paths.Enqueue(subDir)
Next
Catch ex As UnauthorizedAccessException
' log the exception or ignore it
Debug.WriteLine("Directory {0} could not be accessed!", sDir)
Catch ex As Exception
' log the exception or ...
Throw
End Try
End While
'could return fileNames collection
End Sub
End Class
My assessment is that counter is returned and then marshalled back onto the UI thread as dirsFraction, but I'm not convinced by my attempted explanation.
Inside your AllSubfolderFiles function you call Task.Run and pass in an anonymous function that returns with Return counter. AllSubfolderFiles awaits the result of that call and then returns with Return dirsFraction.
So, you have 2 returns in the same function because you have an anonymous function inside your original function. You can move that function out to its own named function which will make it clearer that there are 2 different functions here.

vb.net autocad 2007 selection set has no Items

In OS WIn 7 using Autocad 2007 I try to select items then do stuff
Problem is that there are no ITEMS in the selection set ssetObj - not sure why!
Code: works in vba but not standalone vb.net
Private Sub CommandButton1_Click()
Dim myapp As AcadApplication
Dim mydoc As AcadDocument
Dim ssetObj As AcadSelectionSet
Dim ent As AcadObject
Dim numVertices As Long
On Error GoTo err:
Set myapp = GetObject(, "AutoCAD.Application.17")
Set mydoc = myapp.ActiveDocument
If mydoc.SelectionSets.Count > 0 Then
mydoc.SelectionSets(0).Delete
End If
Set ssetObj = mydoc.SelectionSets.Add("ss")
list1.Clear
Me.Hide
AppActivate ("Autocad")
ssetObj.SelectOnScreen:'WORKS TO SELECT
Dim numpls As Integer
numpls = ssetObj.Count:'WORKS TO GET COUNT
Dim i As Integer
For i = 0 To numpls - 1
Set ent = ssetObj.Item(i)':PROBLEM HERE**THERE ARE NO ITEMS THOUGH COUNT IS CORRECT
If ent.ObjectName = "AcDbLWPolyline" Or ent.ObjectName = "AcDbPolyline" Then
numVertices = (UBound(ent.Coordinates) + 1) / 2
list1.AddItem Str(ent.ObjectID) + "\" + Str(numVertices) + " Vertices"
End If
Next i
Me.Show
Exit Sub
err:
MsgBox err.Description
End Sub
Edit: Further investigation shows that you should be calling ssetObj(i) if you want to get indexed items of your selection set.
I'd not worry about trying to get the count of the selection set anyway if you plan on iterating through it. A For Each should suffice to walk though it. One of the problems with going from VBA/VB6 to VB.NET is the temptation to use the same methodology, when it can sometimes be invalid (at times it can be excellent, but .NET is very capable). Here's my entire class that I tested your problem with, just to show how I'm connecting to AutoCAD and interfacing with it.
Public Class frmMain
Private acApp As AcadApplication
Private polyList As List(Of String)
Const acProgId As String = "AutoCAD.Application.17"
<DllImport("User32.dll")> _
Private Shared Function SetForegroundWindow(ByVal hWnd As IntPtr) As Boolean
End Function
Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click
Try
acApp = DirectCast(Marshal.GetActiveObject(acProgId), AcadApplication)
Catch
Try
Dim acType = Type.GetTypeFromProgID(acProgId)
acApp = DirectCast(Activator.CreateInstance(acType), AcadApplication)
Catch ex As Exception
MsgBox("Unable to create AutoCAD application of type: " & acProgId)
End Try
End Try
End Sub
Private Sub btnSelect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSelect.Click
If acApp Is Nothing Then Return
acApp.Visible = True
Dim acDoc As AcadDocument = acApp.ActiveDocument
' Kill all existing selection sets
While (acDoc.SelectionSets.Count > 0)
acDoc.SelectionSets(0).Delete()
End While
Dim mySS As AcadSelectionSet = acDoc.SelectionSets.Add("ss")
SetForegroundWindow(acApp.HWND)
mySS.SelectOnScreen()
polyList = New List(Of String)
Dim numVertices As Integer
For Each ent As AcadEntity In mySS
If ent.ObjectName = "AcDbLWPolyline" Or
ent.ObjectName = "AcDbPolyline" Then
numVertices = (ent.Coordinates.Length) / 2
polyList.Add(String.Format("{0} \ {1} Vertices", ent.ObjectID, numVertices))
End If
Next
End Sub
End Class
External COM methods like this are going to be slower than you're used to seeing via VBA. Therefore it's definitely worth diving into the in-process AutoCAD .NET stuff to see great performance.

VB.NET accessing class objects from launched thread

I am writing an application in VB.NET that allows users to schedule submissions (emails) to be sent at a later date. I use threads to wait until the time is right to send a particular submission, but for some reason I can't access one of the class objects from the listener threads (or something else is happening, that's what I'm trying to figure out). Here is the relevant code:
Public Class AppContext
Inherits ApplicationContext
Private submsnMngr As SubmissionManager
Public Sub New()
submsnMgr = New SubmissionManager()
menuAddEdit = New ToolStripMenuItem("Add/Edit Submissions")
...
End Sub
Private Sub menuAddEdit_Click(ByVal sender As Object, ByVal e As System.EventArgs)
Handles menuAddEdit.Click
' The user clicking this tray button is the ONLY way that the form can be shown
submsnMngr.ShowWelcome()
End Sub
...
End Class
Class SubmissionManager
Public currentSubmissions As SubmissionList
Public WelcomeForm As Welcome
Public Sub ShowWelcome()
If WelcomeForm Is Nothing Then
' Welcome is the form that needs to be refreshed down in the MailSender subroutine
WelcomeForm = New Welcome(Me)
End If
WelcomeForm.Show()
End Sub
Public Sub CheckDates()
For Each submsn In currentSubmissions.Submissions
SyncLock submsn
If Today.Date >= submsn.EffDate.AddDays(-90).Date And Not submsn.Sent90 And Not submsn.Denied90 And submsn.Thread Is Nothing Then
submsn.Send(1)
submsn.Sent90 = True
currentSubmissions.Save()
ElseIf Today.Date = submsn.EffDate.AddDays(-91).Date And submsn.Thread Is Nothing Then
Dim thd As New Thread(AddressOf MailSender)
thd.IsBackground = True
submsn.Thread = thd
Dim args As New ThreadArgs(submsn.Insured, 1)
thd.Start(args)
End If
If Today.Date >= submsn.EffDate.AddDays(-60).Date And submsn.Thread Is Nothing Then
submsn.Send(2)
currentSubmissions.RemoveSubmission(submsn)
If WelcomeForm IsNot Nothing Then
WelcomeForm.RefreshSubmissions()
End If
ElseIf Today.Date = submsn.EffDate.AddDays(-61).Date And submsn.Thread Is Nothing Then
Dim thd As New Thread(AddressOf MailSender)
thd.IsBackground = True
submsn.Thread = thd
Dim args As New ThreadArgs(submsn.Insured, 2)
thd.Start(args)
End If
End SyncLock
Next
End Sub
Private Sub DateListener()
Do
CheckDates()
Thread.Sleep(3600000)
Loop
End Sub
Private Sub MailSender(args As ThreadArgs)
Dim wait As New TimeSpan(14 - DateTime.Now.Hour, 23 - DateTime.Now.Minute, 0)
Thread.Sleep(wait.TotalMilliseconds)
Dim submsn As Submission = currentSubmissions.GetSubmission(args.insured)
SyncLock submsn
submsn.Send(args.mode)
If args.mode = 1 Then
submsn.Sent90 = True
submsn.Thread = Nothing
currentSubmissions.Save()
Else
currentSubmissions.RemoveSubmission(submsn)
End If
End SyncLock
If WelcomeForm IsNot Nothing Then
' Here is the issue, this code is not being run, even though WelcomeForm is set
' in New() above
WelcomeForm.RefreshSubmissions()
End If
End Sub
End Class
Paying special attention to the few comment lines in the code above, why is WelcomeForm Nothing when I clearly set it to reference the form created in the New() subroutine? I tried alternatively sending the reference to the MailSender thread as an argument, but the same thing happened. Note that I need the If statement there because the user may have closed the form before the thread gets to that point. But it is essential that RefreshSubmissions() be called on it if it is still open.
Sorry guys, realized my thread was being aborted elsewhere in my application's code. No problems with the actual code I posted above.