In the following block of code, does VB.NET gracefully exit the With block if Var1 = 2?
With MyObject
.Property1 = "test"
If Var1 = 2 Then
Return True
End If
.Property2 = "Test2"
End With
Return False
I remember this being an issue in VB6 and causing headaches with unpredicable behaviour - is the same true of VB.NET?
According to MSDN, this still isn't possible:
If you need to exit before all the statements have been executed, put a label on the End With statement and use the GoTo Statement to branch to it. (...) You cannot transfer control either from outside a With block to inside it, or from inside it to the outside. You can call a procedure from inside the block, but control returns to the following statement.
Had to add another answer here, because I was mainly curious. Never used WITH much, and I can't recall ever exiting the block prematurely, but I just tested this under VB2010 and it appears to work just fine (ie as I would expect it to, in other words...
If Var1 =2, the function returns TRUE, and the value of MyObject.Property1 is "Test" but MyObject.Property2 has not be set.
It's possible that it worked this way in a test, but in a real app of significant size, with debugging turned off etc, etc, it could work differently, so, there's that to consider....
Related
Using VBA (Excel 365 (16.0.12527.20880), German) I'm trying to set the strikethrough property of a checkbox (Checkbox1) caption to False in accordance to the Office VBA reference this should work.
The following code is placed in a Module1 (simplified) and changes the strikethrough-value of UserForm1.Checkbox1 (UserForm1 and Checkbox1 is static, created via the VBA-Editor, not via code during runtime).
Sub ChangeCheckBox()
UserForm1.CheckBox1.Caption = "Test" 'this triggers the Init-Procedure, which only sets the Height and Width of the UserForm. This shouldn't effect the problem, but I'm mentioning it here so it's clear that the form has been initialized. But if I leave it out, it's the same problem.
'Pre-Test - works fine
'Check initial value
Debug.Print UserForm1.CheckBox1.Font.Strikethrough 'returns false (as it should)
'Write value and check it
UserForm1.CheckBox1.Font.Strikethrough = False
Debug.Print UserForm1.CheckBox1.Font.Strikethrough 'returns false (as it should)
'Write value and check it again
UserForm1.CheckBox1.Font.Strikethrough = False
Debug.Print UserForm1.CheckBox1.Font.Strikethrough 'returns false (as it should)
'This next line seeems to cause the post-test failure
tmpString = ThisWorkbook.Worksheets("Sheet1").Cells(tmpIndex, tmpColumn).Value
Debug.Print tmpString 'returns the correct value
'Post-Test - fails!!!
'Check initial value
Debug.Print UserForm1.CheckBox1.Font.Strikethrough 'returns false (as it should)
'Write value and check it
UserForm1.CheckBox1.Font.Strikethrough = False
Debug.Print UserForm1.CheckBox1.Font.Strikethrough 'returns true (should still be false)
'Write value and check it again
UserForm1.CheckBox1.Font.Strikethrough = False
Debug.Print UserForm1.CheckBox1.Font.Strikethrough 'returns true (definitely should return false now)
End Sub
In my case, for some reason, the command sets the box to True instead of False. So far I pinpointed the problem to a specific line of code. The "Pre-Test" succeeds, the "Post-Test" fails (everything else works fine). Note, I'm using debug mode via step by step, no other code is executed "in-between".
The problem persists if I create a different Form and try the same thing there. The original procedures are several hundred lines of code, but I created the Testprocedure above and the problem persists. I can also reproduce it by creating a new Excel file from scratch with just one Sheet, one Userform/Checkbox, and one Module containing the test procedure.
So, why the hell does tmpString = ThisWorkbook.Worksheets("Sheetname").Cells(tmpIndex, tmpColumn).Value cause the "Post-Test" to fail?
Note: This error can not be reproduced on all machines, I tried it on a different machine. There, I can not reproduce the error.
I cannot reproduce the issue and I've tried on 3 different machines.
However, I remember seeing a similar issue about 7 years ago and I managed to work around it by first checking if I need to change the value of the font property.
Try replacing all occurences of this:
UserForm1.CheckBox1.Font.Strikethrough = False
with this:
If UserForm1.CheckBox1.Font.Strikethrough Then UserForm1.CheckBox1.Font.Strikethrough = False
This basically sets the .Font.Strikethrough property to False only if it is already True because otherwise it is False (obviously) and if there is any bug in the setter of that property then this skips the bug.
Unrelated to your question but it's not a good idea to use the default instance of a Userform like you're doing here with Userform1.Checkbox.... I recommend you read this article written by #MathieuGuindon back in 2017.
Try using index for your worksheet instead of the name.
Its a funny thing to happen in a programming language.
tmpString = ThisWorkbook.Worksheets(1).Cells(tmpIndex, tmpColumn).Value
Debug.Print tmpString
It worked for me and I think because the polymorphic functions in VBA made without debugging! because nothing else could cause this kind of behavior!
I've managed to reproduce the issue the same way it occured to you.
The root cause here is that you are not explicitly UNLOADING the form after you close it, which means that all modifications that you made to it while open get retained in memory.
For example using your code, shows the checkbox with strikethrough even when running it 2nd time. Which means it's retained in memory.
So to fix this, simply add Unload UserForm1 in your procedure after running the code.
While this approach works in resetting the checkbox (which I assume is your ultimate goal here), it still returns true for those last 2 checks for some reason. To workaround that as well (though it could be unnecessary if you achieve your goal), then simply use #Cristian Buse's if/then solution.
I would like to know if there is an equivalent of Python's pass statement in VBA.
I am using Excel 2016.
The use of Stop (see this answer) seems to be the best thing to do if you are looking for some "non-statement" that you can use to insert a breakpoint, because the Stop command causes the code to break when it is reached, i.e. you don't even need to mark it as a breakpoint because it is one.
You might also like to consider using Debug.Assert some_logical_expression, which will break automatically whenever the logical expression evaluates to False. So Debug.Assert False would be equivalent to Stop, and Debug.Assert x = 3 would be equivalent to If x <> 3 Then Stop.
In Python you need the Pass, because otherwise the methods will not run.
In VBA, its perfectly ok if you leave an empty method like this:
Public Function Foo() As String()
End Function
Maby you are looking for the "Stop" statement.
The good thing about it is that it doesn't clear your variables.
It depends what are you trying to achieve.
You may declare a Label and then use GoTo Label e.g. declare a label (like Skip:)in your code where you want to jump if a condition is met and then use GoTo Skip
Below is the small demo code to give you an idea about this...
Dim i As Long
For i = 1 To 10
If i = 5 Then GoTo Skip
MsgBox i
Next i
Skip:
The Problem
I am trying to debug some code, and somewhere in the middle I stopped at a breakpoint. Now I want to change some variables and run a certain loop several times.
How far did I get?
I know how to change the variables, but somehow I get stuck when trying to run the loop in the immediate window. Here is an example:
Dim i As Integer
Dim j As Integer
For i = 0 To 6
j=i ' Do something
Next i
I tried several variations of the code, but each time I get the following error:
Compile error: Next without for
Other relevant information
I tried searching but mostly found information about problems with loops, whilst I am quite sure the loop itself is fine. (Especially as I reached it before arriving at the breakpoint).
The only place I saw someone addres this situation, he reduced the loop to a single line, however to do this every time would be very impractical in my case.
I realize that I could call a function containing the loop, and then the function call would probably work, but again this feels quite impractical. So I guess it boils down to the following question.
The question
What is a practical way to run a loop whilst debugging VBA code in Excel?
There is actually a way for using loops or other multi-line statements in the Immediate Window - using a colon : to separate statements instead of a new line.
Full solution is described here.
Note that in the Immediate Window you also don't have to declare the variables using a Dim statement.
To summarize, your snippet would look something like this:
For i = 0 To 6: j=i: debug.Print i+j: Next i
I think I understand your question. You want to run a multi-line code block (i.e. the loop) in the Immediate Window. This throws errors because the Immediate Window is only intended for single lines of code.
I don't have any suggestions other than those you already mentioned. I'd recommend putting your test loop into a separate function and calling that from the Immediate Window:
Sub Test()
Dim i As Integer
Dim j As Integer
For i = 0 To 6
j=i ' Do something
Next i
End
Another option is to set several breakpoints. You can also run one line of code at a time with F8.
What is likely the preferred method (i.e., what most people actually do) is use the full power of the IDE, which includes the Immediate, Locals and Watch panes. You can change the value of most variables at runtime by direct assignment in the Immediate Pane (i=6 will do exactly what you think it should do). The IDE also allows you to set breakpoints, add watch conditions, step through code line-by-line using the F8, step through function or procedure calls using Shift+F8, stepping over (and back) through code using the mouse/cursor, and with a few exceptions, you can even add new variables during runtime.
I would like to test if a procedure is called directly by the user (so it is on top of the call stack, see ctrl+L in debug mode) or called from another procedure.
Does someone know a way to do this without tracking the call stack in a parameter passed in each procedure?
I tried to do this using a public parameter topOfCallStackFound and then adding to each procedure some code like:
select case topOfCallStackFound
case false
currentProcedureIsTopOfCallStack = true
topOfCallStackFound = true
case true
currentProcedureIsTopOfCallStack = false
end select
But this doesn't work because VBA remembers the value of topOfCallStackFound after code execution is finished! The lifetime of topOfCallStackFound only ends when the workbook is closed, an end command is given or in a few other not useful circumstances. If it is possible to end the lifetime of topOfCallStackFound when code execution is finished and/or reinitializing topOfCallStackFound to false when the user starts new code execution, I would be done.
Thanks!
in VBA there is no normal way to see the callstack.
This is because the accessing this callstack via api is part of the low level compiler that is not part of the VBA.
You can view the callstack view the windows, as the VBE compiles the VBA and runs it and therefore has access to the VBA callstack but it does not expose it to the VBA.
You can however use try to use something like this:
http://www.everythingaccess.com/vbwatchdog.htm
Something we can just put break point on while making sure it doesn't do anything else.
In c, that would be while(false);
What to do in vb.net?
If you always need it to break there you can put
Stop or Debugger.Break()
If you really want a No-Op for some reason (could this turn into a contest for the most ineffectual single line of code?!), how about these?
System.Threading.Thread.Sleep(1) - 1ms is unlikely to have a huge impact outside of a loop
Debug.Write("") - doesn't appear to output anything.
There is a legitimate use-case for this.
When a temporary breakpoint is required after the statement of interest and this is the last line inside an if statement, an extra no-op type statement is required to place the temporary breakpoint on.
In this case I use:
If someCondition >0 Then
doSomething
Space (1) 'Dummy line to place breakpoint
End If
This returns a string containing one space, but does not assign it to anything. I use it in VBA, but it's also supported in .net
My two cents...
You can combine any series of commands onto one line with colons:
If False Then : End If
Select Case False : Case Else : End Select
I've also made it into a sub. Then it gets a recognizable name of own:
'Definition...
Public Sub noop () 'Or Private, Protected, etc.
End Sub
'Usage...
Sub Main()
If sometest Then
noop
Else
MsgBox "test is false"
End If
End Sub
Very strange question, you could place a BreakPoint about anywhere in the code. But here are some useless lines :
Do While False
Loop
While False
End While
Even the following :
Dim hello = Nothing
Or this :
Format("", "")
A no-op statement is also useful as an aid to document code nicely and make it more easily understandable. You could for example put in a statement like A = A.
For example:
If MyNumber => 100 then A = A
Else:
I know this is an old query, but for what it is worth, my preferred solution to the original question is
Debug.Assert (vbTrue)
If you wanted, you could use a variable instead of vbTrue and then enable/disable all breakpoints in your code by changing one variable
Dim bDisableBreakpoints as Boolean: bDisableBreakpoints = vbTrue
'your code here
Debug.Assert (bDisableBreakpoints)
'rest of your code
Simply change bDisableBreakpoints to vbFalse and the breakpoints will be set wherever you have used Debug.Assert
My personal favorite is
dim b=1
Then I put a breakpoint there.