vb.net process class with process.exited event not reached - vb.net

I have created a class based on this msdn page the only problem my process.exited event is never reached.
The .exe for the process is a console application that I have created. And it closes it self when it's done. But my event does not get reached and I have to wait 30s for the code to resume.
Is there something that i am doing wrong? For my test case the .exe only takes +- 5 seconds to complete.
I assume when a console application closes after the code is done the process is exited. My this assumption is wrong?
Imports System
Imports System.Diagnostics
Imports System.Threading
Public Class CopyDesignProcessClass
Private WithEvents oProcess As New Process()
Private elapsedTime As Integer
Private eventHandled As Boolean
Public Event Exited As EventHandler
Shared Sub Start(ByVal oArgument As String)
' Verify that an argument has been entered.
If oArgument Is Nothing Or oArgument = "" Then
Exit Sub
End If
' Create the process and copy the design.
Dim myCopyDesignProcess As New CopyDesignProcessClass
myCopyDesignProcess.CopyDesign(oArgument)
End Sub
' Print a file with any known extension.
Sub CopyDesign(ByVal oArgument As String)
elapsedTime = 0
eventHandled = False
Try
' Start a process to copy a design and raise an event when done.
oProcess.StartInfo.FileName = "C:\Program Files\Autodesk\CopyDesign Tool\CopyDesignTool.exe"
oProcess.StartInfo.Arguments = oArgument
oProcess.Start()
Catch ex As Exception
MsgBox("An error occurred trying to copydesign " & oArgument & " :" &
vbCrLf & ex.Message, MsgBoxStyle.Exclamation, "CopyDesign Failed")
Return
End Try
' Wait for Exited event, but not more than 30 seconds.
Const SLEEP_AMOUNT As Integer = 100
Do While Not eventHandled
elapsedTime += SLEEP_AMOUNT
If elapsedTime > 30000 Then
Exit Do
End If
Thread.Sleep(SLEEP_AMOUNT)
Loop
End Sub
' Handle Exited event and display process information.
Private Sub myProcess_Exited(ByVal sender As Object,
ByVal e As System.EventArgs) Handles oProcess.Exited
eventHandled = True
Debug.Print("Exit time: {0}" & vbCrLf &
"Exit code: {1}" & vbCrLf &
"Elapsed time: {2}",
oProcess.ExitTime, oProcess.ExitCode, elapsedTime)
End Sub
End Class
Additional:
The answer may be given in this post but it's in c# and this I don't understand.
c# possible answer
Corrected code, but used another solution posted as answer.
Imports System
Imports System.Diagnostics
Imports System.Threading
Public Class CopyDesignProcessClass
Private WithEvents oProcess As New Process()
Private elapsedTime As Integer
Private eventHandled As Boolean
Public Event Exited As EventHandler
Shared Sub Start(ByVal oArgument As String)
' Verify that an argument has been entered.
If oArgument Is Nothing Or oArgument = "" Then
Exit Sub
End If
' Create the process and copy the design.
Dim myCopyDesignProcess As New CopyDesignProcessClass
myCopyDesignProcess.CopyDesign(oArgument)
End Sub
' Print a file with any known extension.
Sub CopyDesign(ByVal oArgument As String)
elapsedTime = 0
eventHandled = False
Try
' Start a process to copy a design and raise an event when done.
oProcess.StartInfo.FileName = "C:\Program Files\Autodesk\CopyDesign Tool\CopyDesignTool.exe"
oProcess.StartInfo.Arguments = oArgument
oProcess.EnableRaisingEvents = True
oProcess.Start()
Catch ex As Exception
MsgBox("An error occurred trying to copydesign " & oArgument & " :" &
vbCrLf & ex.Message, MsgBoxStyle.Exclamation, "CopyDesign Failed")
Return
End Try
' Wait for Exited event, but not more than 30 seconds.
Const SLEEP_AMOUNT As Integer = 100
Do While Not eventHandled
elapsedTime += SLEEP_AMOUNT
If elapsedTime > 30000 Then
Exit Do
End If
Thread.Sleep(SLEEP_AMOUNT)
Loop
End Sub
' Handle Exited event and display process information.
Private Sub myProcess_Exited(ByVal sender As Object,
ByVal e As System.EventArgs) Handles oProcess.Exited
eventHandled = True
Debug.Print("Exit time: {0}" & vbCrLf &
"Exit code: {1}" & vbCrLf &
"Elapsed time: {2}",
oProcess.ExitTime, oProcess.ExitCode, elapsedTime)
End Sub
End Class

you can replace this part of the code:
Try
' Start a process to copy a design and raise an event when done.
oProcess.StartInfo.FileName = "C:\Program Files\Autodesk\CopyDesign Tool\CopyDesignTool.exe"
oProcess.StartInfo.Arguments = oArgument
oProcess.Start()
Catch ex As Exception
MsgBox("An error occurred trying to copydesign " & oArgument & " :" &
vbCrLf & ex.Message, MsgBoxStyle.Exclamation, "CopyDesign Failed")
Return
End Try
' Wait for Exited event, but not more than 30 seconds.
Const SLEEP_AMOUNT As Integer = 100
Do While Not eventHandled
elapsedTime += SLEEP_AMOUNT
If elapsedTime > 30000 Then
Exit Do
End If
Thread.Sleep(SLEEP_AMOUNT)
Loop
with:
Try
' Start a process to copy a design and raise an event when done.
oProcess.StartInfo.FileName = "C:\Program Files\Autodesk\CopyDesign Tool\CopyDesignTool.exe"
oProcess.StartInfo.Arguments = oArgument
oProcess.Start()
oProcess.WaitForExit()
Catch ex As Exception
MsgBox("An error occurred trying to copydesign " & oArgument & " :" &
vbCrLf & ex.Message, MsgBoxStyle.Exclamation, "CopyDesign Failed")
Return
End Try
waitforexit method is very useful, it will freeze your code until given software is done. If running longer processes background worker may be necessary.

Related

Getting values by EvaluateScriptAsync in Event CefSharp is not working

I'm trying to get my code to run when the page is finished loading in CefSharp, but when I plug my code into the CefSharp event in any way it won't work and the program will not responding.
This is my code:
Private Sub Web_FrameLoadEnd(sender As Object, e As FrameLoadEndEventArgs) Handles Web.FrameLoadEnd
If Login = False Then
Try
Web.ExecuteScriptAsync("document.getElementById('txt_Username').value='user'")
Web.ExecuteScriptAsync("document.getElementById('txt_Password').value='pass'")
Web.ExecuteScriptAsync("document.getElementById('loginbutton').click()")
Login = True
Catch ex As Exception
Login = False
End Try
Else
If Not Web.Address.Contains("bbsp") Then
Web.Load("http://192.168.1.1/html/bbsp/userdevinfo/userdevinfosmart.asp?type=wifidev")
Else
GetNames()
End If
End If
End Sub
Private Sub GetNames()
Try
For B As Int32 = List1.Items.Count To 0 Step -1
List1.Items.RemoveAt(B)
Next
Catch ex As Exception
End Try
Dim i = 0
Do
Try
Dim IDname = "divDevName_" & i
Dim name = Web.EvaluateScriptAsync("document.getElementById('" & IDname & "').innerHTML")
Dim response = name.Result
If response.Success = True Then
List1.Items.Add(i & "- " & response.Result.ToString)
Else
Exit Do
End If
Catch ex As Exception
Exit Do
End Try
i += 1
Loop
End Sub
But when I run GetNames() with a button it works perfectly,
where did I go wrong?

Error handling forms which call sub procedures - MS Access VBA

Yes, Access is used and it cannot be changed.
I have a form class object, let's say:
Option Compare Database
Private Sub cmdCalculate_Click()
Dim Employee As String
Employee = InputBox("Enter Name of Employee")
If InStr(Employee, "Eka") > 0 Then
Call Hello
Else
Call Hello2
End If
End Sub
And I have one module. As you can see, I call each procedure from my form.
Option Compare Database
Option Explicit
Public Sub Hello()
MsgBox "Hello Eka"
End Sub
Public Sub Hello2()
MsgBox "Hello stranger"
End Sub
The issue I have is with the error handling implementation as here we have the subsequent procedures which I call. I tried to add a simple On Error GoTo - see below to an individual sub-procedure to display a nice message and break the entire script execution but yes, the sub procedure will show a nice message, you click OK to close it and the main script just continues running. Can you, please, direct me to a source where I can read more on the potential solution or assist with it? I found something about global error handling, but not sure if it is relevant.
Private Sub cmdCalculate_Click()
On Error GoTo errormessage
#TO-DO. VBA Code
Exit Sub
errormessage:
MsgBox "An error has occured. Please check your work."
End Sub
Here is a small sample.
MainProcedure calls SubProcedure1 and this calls SubProcedure2.
In SubProcedure2 there will be a division by zero error.
SubProcedure2 handles this error and reraise it to the upper procedure in the call stack: SubProcedure1.
SubProcedure1 also handles it and also reraises it, now to MainProcedure.
MainProcedure now shows the error. It can stop execution now if you want that.
Remark1: VBA unfortunately has no call stack information you could read at runtime. So in my example I always add the current procedurename as a new line to the top of the source property of the error.
So finally you can see where the error happened and how the call stack was.
But that is just an example.
Remark2: If you, for example, wouldn't place an active error handler in SubProcedure1 the error would bubble up itself to MainProcedure, but then you couldn't add your call stack information.
Public Sub MainProcedure()
On Error GoTo Catch
SubProcedure1
Finally:
Exit Sub
Catch:
MsgBox Err.Number & " : " & Err.Description & vbNewLine & vbNewLine & "- MainProcedure" & vbNewLine & Err.Source, vbCritical
Resume Finally
End Sub
Public Sub SubProcedure1()
On Error GoTo Catch
SubProcedure2
Finally:
Exit Sub
Catch:
Err.Raise Err.Number, "- SubProcedure2" & vbNewLine & Err.Source, Err.Description
Resume Finally
End Sub
Public Sub SubProcedure2()
On Error GoTo Catch
Dim value As Long
value = 0
Dim value2 As Long
value2 = 1 / value
Finally:
Exit Sub
Catch:
Err.Raise Err.Number, "- SubProcedure2" & vbNewLine & Err.Source, Err.Description
Resume Finally
End Sub

Making VBA Form specific TextBox accept Numbers only and also "."

I want to block some specific textboxes has numeric values only and accept ".". However, it blocks almost all my textboxes in my userform. I don't understand why. What I forgot in my code?
Private Sub tbxHour_Exit(ByVal Cancel As MSForms.ReturnBoolean)
'Making TextBox accept Numbers only
If Not IsNumeric(tbxHour.Value) Then
MsgBox "only numbers allowed"
Cancel = True
End If
End Sub
Private Sub tbxHour_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
Select Case KeyAscii
Case 46
If InStr(1, tbxHour, ".") > 0 Then KeyAscii = 0
Case 48 To 57
Case Else
KeyAscii = 0
End Select
End Sub
This one worked for me:
Private Sub tbxHour_AfterUpdate()
'Make sure the item is Numeric or has a "." in it
If Not IsNumeric(Me.tbxHour.Text) And Not Me.tbxHour.Text = "." Then
MsgBox "This is illegal!"
Me.tbxHour.Text = ""
End If
End Sub
Short. Simple. Effective and looks like what you're trying to do anyway.
I use this simply NumKeyValidator class for that, to simply prevent invalid input to be supplied by the user:
Option Explicit
Private Const vbKeyDot As Integer = 46
Public Function IsValidKeyAscii(ByVal keyAscii As Integer, ByVal value As String) As Boolean
'returns true if specified keyAscii is a number, or if it's a dot and value doesn't already contain one
IsValidKeyAscii = (keyAscii = vbKeyDot And InStr(1, value, Chr$(vbKeyDot)) = 0) Or (keyAscii >= vbKey0 And keyAscii <= vbKey9)
End Function
You can use it by simply declaring an instance field for it:
Private validator As New NumKeyValidator
And then you use it in each textbox' KeyPress handler, like this:
Private Sub tbxHour_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
If Not validator.IsValidKeyAscii(keyAscii, tbxHour.Value) Then keyAscii = 0
End Sub
There's no need to handle Exit and pop a MsgBox then - either the box is empty, or it contains a valid number; you could have an IsValidForm property that returns True if all required textboxes contain numbers, and false otherwise - and then decide that the form's Ok button is disabled until the form is valid.
FWIW that validator class is quite thoroughly tested (using Rubberduck unit tests [disclaimer: I own that open-source VBE add-in project]):
Option Explicit
Option Private Module
'#TestModule
'' uncomment for late-binding:
Private Assert As Object
'' early-binding requires reference to Rubberduck.UnitTesting.tlb:
'Private Assert As New Rubberduck.AssertClass
'#TestMethod
Public Sub DotIsValidForEmptyValue()
On Error GoTo TestFail
'Arrange:
Dim actual As Boolean
Dim sut As New NumKeyValidator
'Act:
actual = sut.IsValidKeyAscii(Asc("."), vbNullString)
'Assert:
Assert.IsTrue actual
TestExit:
Exit Sub
TestFail:
Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub
'#TestMethod
Public Sub DotIsValidForNonEmptyValueWithoutAnyDots()
On Error GoTo TestFail
'Arrange:
Dim actual As Boolean
Dim sut As New NumKeyValidator
'Act:
actual = sut.IsValidKeyAscii(Asc("."), "123")
'Assert:
Assert.IsTrue actual
TestExit:
Exit Sub
TestFail:
Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub
'#TestMethod
Public Sub DotIsInvalidWhenValueHasDot()
On Error GoTo TestFail
'Arrange:
Dim actual As Boolean
Dim sut As New NumKeyValidator
'Act:
actual = sut.IsValidKeyAscii(Asc("."), "123.45")
'Assert:
Assert.IsFalse actual
TestExit:
Exit Sub
TestFail:
Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub
'#TestMethod
Public Sub AllDigitsAreValid()
On Error GoTo TestFail
Dim sut As New NumKeyValidator
Assert.IsTrue sut.IsValidKeyAscii(Asc("0"), vbNullString)
Assert.IsTrue sut.IsValidKeyAscii(Asc("1"), vbNullString)
Assert.IsTrue sut.IsValidKeyAscii(Asc("2"), vbNullString)
Assert.IsTrue sut.IsValidKeyAscii(Asc("3"), vbNullString)
Assert.IsTrue sut.IsValidKeyAscii(Asc("4"), vbNullString)
Assert.IsTrue sut.IsValidKeyAscii(Asc("5"), vbNullString)
Assert.IsTrue sut.IsValidKeyAscii(Asc("6"), vbNullString)
Assert.IsTrue sut.IsValidKeyAscii(Asc("7"), vbNullString)
Assert.IsTrue sut.IsValidKeyAscii(Asc("8"), vbNullString)
Assert.IsTrue sut.IsValidKeyAscii(Asc("9"), vbNullString)
TestExit:
Exit Sub
TestFail:
Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub
'#TestMethod
Public Sub AlphaIsInvalid()
On Error GoTo TestFail
'Arrange:
Dim actual As Boolean
Dim sut As New NumKeyValidator
'Act:
actual = sut.IsValidKeyAscii(Asc("a"), vbNullString)
'Assert:
Assert.IsFalse actual
TestExit:
Exit Sub
TestFail:
Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub
'#TestMethod
Public Sub DollarSignIsInvalid()
On Error GoTo TestFail
'Arrange:
Dim actual As Boolean
Dim sut As New NumKeyValidator
'Act:
actual = sut.IsValidKeyAscii(Asc("$"), vbNullString)
'Assert:
Assert.IsFalse actual
TestExit:
Exit Sub
TestFail:
Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub
'#TestMethod
Public Sub NegativeSignIsInvalid()
On Error GoTo TestFail
'Arrange:
Dim actual As Boolean
Dim sut As New NumKeyValidator
'Act:
actual = sut.IsValidKeyAscii(Asc("-"), vbNullString)
'Assert:
Assert.IsFalse actual
TestExit:
Exit Sub
TestFail:
Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub
That said I don't see how the code you've shown could ever "block almost all textboxes in your userform".

Vb.net Run Script inside Textbox

I was wondering if there is a way to excute a Script that is in Textbox1
Like you write this code inside textbox1
msgbox("Hello World")
and when you click on the button or press enter it will run the command/script you wrote in Textbox1
Yes, you can. This is a bit messy and was cobbled together from various articles on the web, but you get the general idea...
Imports System.IO
Imports System.Reflection
Imports System.CodeDom
Imports System.CodeDom.Compiler
Imports Microsoft.VisualBasic
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
' read code from textbox
Dim Code As String = TextBox1.Text
' clear output textbox
TextBox2.Clear()
' create fully functional assembly string
Code = ("Imports System" & vbCrLf &
"Imports System.Windows.Forms" & vbCrLf &
"Imports Microsoft.Visualbasic" & vbCrLf &
"Public Class TempClass" & vbCrLf &
"Public Sub MyCode(ByVal Textbox2 As TextBox)" & vbCrLf &
Code & vbCrLf &
"End Sub" & vbCrLf &
"End Class")
' create the compiler
Dim vbProv = New VBCodeProvider()
' create parameters to pass to the compiler
Dim vbParams = New CompilerParameters()
' add referenced assemblies.
vbParams.ReferencedAssemblies.Add("System.dll")
vbParams.ReferencedAssemblies.Add("System.Windows.Forms.dll")
vbParams.ReferencedAssemblies.Add("Microsoft.VisualBasic.dll")
' generate an assembly in memory
vbParams.GenerateExecutable = False
vbParams.GenerateInMemory = True
' give it a name
vbParams.OutputAssembly = "MyCode"
' compile the code and get the compiler results
Dim compResults = vbProv.CompileAssemblyFromSource(vbParams, Code)
' check for compile errors
If compResults.Errors.HasErrors Then
Dim ErrorMsg As String = compResults.Errors.Count.ToString & " Errors:"
For x As Integer = 0 To compResults.Errors.Count - 1
ErrorMsg = ErrorMsg & vbCrLf & "Line: " & compResults.Errors(x).Line.ToString & " - " + compResults.Errors(x).ErrorText
Next
TextBox2.Text = ErrorMsg & vbCrLf & vbCrLf + Code
Else
' create instance of the temporary compiled class
Dim obj As Object = compResults.CompiledAssembly.CreateInstance("TempClass")
' use textbox 2 for output
Dim args() As Object = {Me.TextBox2}
Try
' execute the code
Dim result As Object = obj.GetType().InvokeMember("MyCode", BindingFlags.InvokeMethod, Nothing, obj, args)
Catch Oops As Exception
' oops
MessageBox.Show(Oops.Message)
End Try
End If
End Sub
End Class
You're looking for CodeDOM. This basically lets you run the compiler from within your program. Be careful, the user could type anything into the box and compromise your program with it.

Cannot cancel a BackgroundWorker procss

I have a BackgroundWorker that includes a class ExcelOutput, used to output various data to a workbook, and I should mention straight away that bw.WorkerSupportsCancellation = True is set.
At each stage of the output I'm checking for errors in ExcelOutput using Try/Catch, and if necessary displaying an error (using a function called ErroReport().
In conjunction with the error message, I want to cancel the BackgroundWorker to avoid further errors. To that end I have added the OutputWorker property to the ExcelOutput class and I set that to be a copy of my BackgroundWorker in the bw_DoWork() method.
However, the cancellation carried out in ExcelOutput.ErroReport() is not working, and I don't know why.
Note that I've tested the value of bw.CancellationPending and it is set to True after an error. I've also tested that the If condition following is working by showing a message box, and that also works. For some reason it seems as though the Exit Sub command is ignored though.
Can anyone suggest what I am doing wrong? Thanks.
Here is how the bw_DoWork() function from the BackgroundWorker class is set up -
Private Sub bw_DoWork(ByVal sender As Object,
ByVal e As DoWorkEventArgs)
Dim Excel As New ExcelOutput ' Create a new instance of the ExcelOutput class
Dim CurrentRow As Integer = 4 ' Set the first output row
'** Include a copy of the OutputWorker in the ExcelOutput (so that the OutputWorker can be cancelled)
Excel.OutputWorker = Me
If bw.CancellationPending = True Then
e.Cancel = True
Exit Sub
Else
Excel.Prepare()
End If
If bw.CancellationPending = True Then
e.Cancel = True
Exit Sub
Else
CurrentRow = Excel.OutputGroup("General", Headers, Data, 4)
End If
' More stuff here...
End Sub
Here is how the ErrorReport() function from the ExcelOutput class is set up -
Private Sub ErrorReport(ByVal Ex As Exception,
Optional ByVal CustomMessage As String = "")
Call Me.ResetRange() ' Destroy the 'Range' object
Dim ErrorMessage As String = "Message: " & Ex.Message ' Set the default message
If CustomMessage <> "" Then ErrorMessage = CustomMessage & vbCrLf & vbCrLf & Ex.Message
Dim Result As Integer = MessageBox.Show(ErrorMessage,
"An Error Has Occured",
MessageBoxButtons.OK,
MessageBoxIcon.Stop)
'** Close the workbook (if it's open) and stop the OutputWorker *'
Try
Call Me.WB.Close(SaveChanges:=False)
If Me.OutputWorker.WorkerSupportsCancellation = True Then
Me.OutputWorker.CancelAsync()
End If
Catch
End Try
End Sub
You should try to add the DoWorkEventsArgs as parameter to your ErrorReport function.
Private Sub ErrorReport(ByVal Ex As Exception,
Optional ByVal CustomMessage As String = "",
ByVal e As DoWorkEventsArgs)
Call Me.WB.Close(SaveChanges:=False)
If e.WorkerSupportsCancellation = True Then
e.CancelAsync()
End If
You'll be able to cancel the Backgroundworker.