If statement makes debugger skip backwards - vb.net

I have a Visual Studio 2015 Windows Forms program with a menu form and several others. The code for the menu button in question looks like this:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Me.Visible = False
Form1.Show()
End Sub
When this button is pressed, Form 1 is loaded. Within the Form 1 load event is the following For loop:
For i As Integer = 1 To 10
If x_DataTable.Rows(0)(i.ToString()) <> "" Then
Me.Controls(("txt" & (i)).ToString()).Text = x_DataTable.Rows(0)(i.ToString())
Dim s As String = Me.Controls(("txt" & (i)).ToString()).Text.Trim()
If Convert.ToInt32(s.Substring(s.Length - 2)) < (m_DataTable.Rows(0)("Limit")) Then
Me.Controls(("txt" & (i)).ToString()).BackColor = Color.IndianRed
End If
End If
Next
Every time that the debugger hits the line that starts with "If Convert.ToInt32", it exits the for loop and the load event sub, and skips backwards to the Form1.Show() statement in the menu code above. Any idea what might be causing this or how to make it execute the code normally?

The Form.Load event has some odd exception handling behavior on x64 systems compared to x86 when a debugger is attached. When an exception is unhandled in a x86 process running on an x64 version of Windows the function is basically aborted, and the exception is eaten by the wow64 sub-system. Execution resumes at the last .net code on the stack before Load was called.
See this answer for a very thorough explanation.

Please Check Convert.ToInt32(s.Substring(s.Length - 2)) , (m_DataTable.Rows(0) ("Limit")) are valid Integers

Ok, I think I figured it out. The "Limit" column at row 0 was null, so it was not able to execute that line. Still not sure why it didn't show any error messages and just skipped backwards though.

Sounds like the assemblies and the source code are out of sync. Have you changed the config (Release|Debug) or maybe not built, or build to the wrong location?

Related

VB .net Clickonce - check for update but dont update

ok so ive got an updating program via clickonce, I want it to notify the user there is an update but don't actually update the program until an admin logs on and requests the update to go ahead.
I'm checking for updates like this via code
If My.Application.IsNetworkDeployed() Then
If My.Application.Deployment.CheckForUpdate() Then
MsgBox("Updates are available", vbInformation, "Updates available")
Note I haven't called the
My.Application.Deployment.Update()
to actually update.
When my application checks for updates it displays ok, but when no one does anything else when it is shut down again and then started up - it seems to revert back to automatically downloading the update on program startup. I have update automatically turned off in the project properties
I tried not checking for updates and the program starts and doesn't update so I'm thinking that just the act of checking and finding an update automatically sets the program to download it next time its started. which id rather it didn't
has anyone come across this issue before?
thanks
Modify this code. It is with an "updating" action but you'll be able to change this
Imports System.Deployment.Application
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Try
Dim updateCheck = ApplicationDeployment.CurrentDeployment
Dim info = updateCheck.CheckForDetailedUpdate()
If (info.UpdateAvailable) Then
MsgBox("Update wird geladen.")
updateCheck.Update()
MessageBox.Show("The application has been upgraded, and will now restart.")
Application.Restart()
End If
Catch : End Try
Form1.Show()
Me.Close()
End Sub
Do you by any chance have these options selected??
If you're handling the updates programmatically, you need to untick the "The application should check for updates" box.
Doing this will grey-out the "After" and "Before" radio buttons below it.

Optionally launch form in VB.Net console application

So I've set my application to a console type application and pointed it to a module containing just Sub Main, i.e.
Module mdlConsole
Sub Main(ByVal cmdArgs() As String)
If cmdArgs.Length = 0 Then
Dim frm As New frmMain
frm.Show()
End If
End Sub
End Module
Ideally if no arguments are supplied then the program would simply launch the primary form. The goal is to make this program (optionally) script-able from the command line. If arguments are supplied then the application form is not loaded and processes its features based off the command line arguments supplied.
As it is now, the program runs, briefly launches the form (frmMain) and then closes. What am I doing wrong or missing?
If you're not keen on giving me the answer, I'd be happy to be pointed in the right direction also. I don't expect anyone to just supply answers. I need to learn also.
Thanks!
For Winforms, you need to 'run' the App object, passing a form to use:
Sub Main(ByVal cmdArgs() As String)
If cmdArgs.Length = 0 Then
Dim frm As New frmMain
Application.Run(frm)
Else
' cmd line version
End If
End Sub
I see in your comment that you'd like to remove the console window that appears when running the form version of the program with the solution currently proposed. I cannot comment due to lack of reputation, so I will make this a full-fledged answer.
Consider approaching this from an inverse perspective: if you write the program as a forms application, opening it by default will bring up the form. But in the Form1_Load event, check the command line arguments; if they are greater than 0, simply run your (abbreviated) code logic here. At the end of the code, simply run Application.Exit(), like so:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
If My.Application.CommandLineArgs.Count > 0 Then
' Execute (abbreviated) code logic
' When finished, exit the program
Application.Exit()
End If
End Sub
This can also make your code cleaner and more practical if you're relying on a user-interface, because you can still access the values of form elements that the user would otherwise be modifying - but without the form showing on the screen (unless you prompt it to with a MsgBox or such).
This also works very nicely for Scheduled Tasks, as the user can run them manually with a user-interface, while the program executes without being visible via a scheduled task.
Kind of a follow-on to Chad's solution above I used the steps defined in How to have an invisible start up form? to avoid showing my form.
In short, create an Overrides subroutine that gets launched before Form1_Load:
This worked for me:
Protected Overrides Sub SetVisibleCore(ByVal value As Boolean)
If Not Me.IsHandleCreated Then
Me.CreateHandle()
value = False
MyBase.SetVisibleCore(value)
Else
Exit Sub
End If
If My.Application.CommandLineArgs.Count > 0 Then
MsgBox("Argument Sensed!")
' Execute (abbreviated) code logic
' When finished, exit the program
Me.Close()
Application.Exit()
Else
MyBase.SetVisibleCore(True)
End If
End Sub

Winforms RadTextBox control validating event triggered twice

I am doing a database validation on the TextBox validating event. I am also using the e.Cancel = True if the data is invalid. The problem is that the validating event is triggered twice ultimately causing the SQL also to run twice, and I don't want that to happen (coz sometime the query is resource intensive).
Steps:
Drag & drop a RadTextBox & a RadLabel to the Form.
For the RadTextBox validating event use the below code.
Run the application, focus the RadTextBox & then click on the label. Then if you check the output window of visual studio you will notice that the console has logged that the validating event was actually triggered twice. (The event runs twice only when I try to click a RadButton or a RadLabel)
I noticed this bug when I was checking my queries in SQL Server Profiler & the query gets executed twice, which is unnecessary. I also checked with actual wincontrols & this issue doesn't exist in them.
How do I fix this issue ?
Here a sample code to replicate the behavior
Private Sub RadTextBox1_Validating(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles RadTextBox1.Validating
Console.WriteLine("VALIDATING EVENT TRIGGERED")
e.Cancel = True
End Sub
It seems as a known issue with RadTextBox: issue link
Perhaps you can try RadTextBoxControl for your needs?

Code getting skipped after 'For' loop

I'm seeing some strange behavior during my form load event. Everything works as expected until I run a For loop. Any code below the Next line won't fire. I get no error the form just loads like every things good but it ignores those lines. I have placed a msgbox("test") above and below the loop to confirm this behavior.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Do some form loading stuff
msgbox("test1") 'This will Fire
For i = 0 To DataGridView1.Columns.Count
DataGridView1.Columns(i).SortMode = DataGridViewColumnSortMode.NotSortable
Next
msgbox("test2") 'This wont fire
End Sub
I could fix this by just putting the loop at the bottom of the form load but it bugs to me to not understand why this is happening.
EDIT: After further testing I've found out that if I just run the FOR loop without changing the sort mode then the test2 messagebox will fire. If I comment out the sortmode line everything works fine. Something about setting the sort mode in a loop is preventing the rest of the code from running.
P.S. If anyone knows of a better way to make a databound datagridview with extra columns not sort-able i'm all ears.
The problem is the loop that executes once too often
Arrays in NET starts at index zero and the maximum index is the length of the array minus one
For i = 0 To DataGridView1.Columns.Count - 1
DataGridView1.Columns(i).SortMode = DataGridViewColumnSortMode.NotSortable
Next
Your loop causes an exception that is probably trapped in the caller code and silently suppressed or, being in the load method and if you execute this code inside VS debugger, it is simply not catched for a well know problem with the load event in 64bit code with a debugger attached.
There's an error in your code: you can't enumerate untill DataGridView1.Columns.Count
Fix it like this:
For i = 0 To DataGridView1.Columns.Count -1
DataGridView1.Columns(i).SortMode = DataGridViewColumnSortMode.NotSortable
Next
By the way, you don't see the MsgBox because DataGridView1.Columns(i) is going to raise an exception, but this exception will be ignored if it's in the Load method.

vb.net - redirect output to form textbox

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.