How can I get the value from the expression in my Select Case statement?
When we write select case statements, we are told that good practice is to have a case else at the end. I understand the purpose of this to be if we don't think of all possible cases, there is a way to inform us instead of just moving on. Usually I just use debug.assert in that case, which works great for personal debugging and is sometimes sufficient for some end users after delivery.
That doesn't work well when I am writing to the VBE, as breaking is not supported anytime after the VBE has been called. I realize that I could probably unload the VBE object and then debug.assert, but that kind of defeats the purpose of pausing my code, if the case has to do with what I am reading/writing to the VBE.
To my mind, the easiest solution would be to msgbox TheUnexpectedResultFromMyExpression, but I have no idea how to call that. The second easiest solution appears to be to have full and complete prescience of what my users may or may not do, as well as when and how they may do it. I have been working on that too, so if you don't know how to return the value, then maybe you have some tips on omniscience.
I know that I, in the vase majority of cases, could simply copy the expression itself to a msgbox ... inside case else, but I happen to be working with a case of a decision tree based upon the return to setting an object, and I am not interested in doing that twice. Another option might be to just myVariable=<expression> and select case myVariable instead of select case <expression>, and then always debug.print myVariable before every select case, but my log is already so busy, doing that in a larger project would mean I have to buy another monitor, and I am struggling with groceries right now.
Asking here seems easier. Thanks.
EDIT:
For those that seemed to have a hard time understanding what I am trying to ask, I boiled the code down as simply as I could. Obviously the below isn't super useful, but you get the idea.
Select Case Forms("Form1").Module.CreateEventProc("Click", Forms("Form1").Controls("label0").Name)
Case 1
Debug.Print "line1"
Case 2
Debug.Print "line2"
Case Else
Debug.Print Forms("Form1").Module.CreateEventProc("Click", Forms("Form1").Controls("label0").Name)
End Select
When my Case Else statement runs, it may print the value of the expression (the workaround I mentioned) in the immediate window, and I know I could do the same with a msgbox or variant or whatever, but it runs the code again. And yes, as I mentioned above already, I could (in this case) just assign the value to a long and then run the Select Case on the long, but that option doesn't solve the problem in my application. And lest this become a conversation where we debate the merits of using VBE objects, or tries to get me to ask a different question of why I get unexpected values (I am not, I am trying to plan for clean debugging during runtime), or someone asking me why I can't just make the code simpler and easier to use instead of tens of thousands of lines of code that write another thousand+ lines of code, it is because I have a client. And they pay for what they want.
So, back to original question, all I want is to know how to return the value from the select case expression. If you are a superhero, and can get code to pause after the VBE is called, then by all means answer that question instead or in addition to.
The answer is: you can't.
Sorry, no source, except multiple descriptions of Select Case, e.g. MSDN, which would mention it, if this functionality existed.
It is a rather unusual question, that's one reason for the confusion in the comments. You are looking for a "meta" variable or method, like ##IDENTITY in T-SQL. But this doesn't exist for the VBA Select Case.
And the reason why it isn't needed: you have complete control over the testexpression in the Select part. The usual way, and I actually consider this good programming practice, is to always assign any sort of complex expression or method call (like CreateEventProc) to a variable, and then use this variable for Select Case.
So
LineNr = Forms("Form1").Module.CreateEventProc("Click", Forms("Form1").Controls("label0").Name)
Select Case LineNr
Case <expected values>
' do something useful
Case Else
Debug.Print "Whoa, unexpected LineNr: " & LineNr
End Select
is really the and the only solution.
I just wrote this maybe itll help?
Private Sub mystuff()
Dim stuff As String
Select Case stuff
Case Is = "Mine"
stuff = "yours "
Case Is = "Not yours"
stuff = "his "
Case Else
stuff = "Hers"
End Select
debug.print "stuff"; stuff
End Sub
Related
I've just started getting into a standalone VB programming and I see that there's a lot of differences in code from what I am kind of used to in excel VBA. I have a code in my excel VBA:
Dim UserName As String
UserName = Split(Application.UserName, " ")(0)
If Right$(UserName, 1) = "s" Then UserName = Left$(UserName, Len(UserName) - 1)
Which is extracting first word of a username and finding if the last letter of that is "s" in which case it trims it off.
I need to do the same thing in VB.NET. WHat I have got so far is
Dim UserName As String
UserName = Split(Environment.UserName, " ")(0)
If Str.Last(UserName) = "s" Then
UserName = UserName.Substring(0, UserName.Length - 1)
End If
The if statement is not correct and I wonder if someone here could, please, help me with the right if statement for that function
If you want to know whether a String ends with a particular substring, you call the String.EndsWith method:
If UserName.EndsWith("s") Then
This is an example of why you ought to spend time reading documentation. If you want to do something with a String, you make a quick perusal of the documentation for the String class. If you at least look down the member listing, which would take a few minutes at most, then there's a good chance that you see this method and Bob's your uncle. Even if you don't see what you need, either because it doesn't exist in that place or because it's not obvious enough that it is what you need, you haven't lost a lot of time and you might see something else that could be of use use now or later. I've learned a lot this way over the years, so I'm speaking from experience.
Function Right$ is still available, only it is moved to Strings.Right. Left$ and Right$ are moved, so that Left$ doesn't get confused with Form.Left
I'm comparing values of numbers from 2 data sheets, and I've dropped the relevant data from both into their own arrays. I need to find matching values to run other steps of analysis.
For i = Lbound(Array1) to UBound(Array1)
For j = LBound(Array2) to UBound(Array2)
If (criteria for Array2) then
variable = 11111
Else
variable = 22222
End if
If variable = Array1(i,1) Or variable = Array1(i,2) or variable = Array1(i,3) then
more steps
Else
more steps
End if
next j
next i
The first if statement sets the variable correctly, but the variable doesn't match any of the criteria. It doesn't go to the else like it should. Now I only know this because I walked through the code step by step. If I just F5 and run the thing, "Excel is not responding". I don't know what the hang up it. All of my variables are declared and assigned a type, I'm not missing any closing statements. And I have no idea what I'm doing wrong.
What do I need to check for in my code?
EDIT
Sorry, but in this instance I'm not allowed to upload any code here. It's work related, NDA kind of stuff. Hence the pseudo code. What I need to show wouldn't be a big deal(at least I don't think it would), but I'm not risking it.
My apologies.
The solution, as it turns out, has to do with a poorly named array(not me) and a simple typo(definitely me). I'm certain that would have been an easy solve for the good citizens of Stack Overflow if I would have been allowed to post actual code.
For what's it worth, I think it's dumb that I couldn't in this case. Thanks #ScottCraner and #SuperSymmertry for trying to be helpful even without much to go on.
Super, I'm still curious about Val. If you've got a minute, I would appreciate more knowledge on that. Anything from an actual person is better than Microsoft documentation.
I have a spreadsheet, with the following two subroutines in it (there's a lot more to them, but I've stripped out all the code not directly relevant to the question):
Sub HF_Reset()
Feats_Reset
End Sub
Sub Feats_Reset()
Range("TblAllFeatsSelected").Value = CVErr(xlErrNA)
Range("Test").Value = "Success"
Range("Test2").Value = 1
End Sub
Test is a single cell, Test2 is a two-cell range, TblAllFeatsSelected is a large range.
If I call Feats_Reset, it executes absolutely fine, does what it's intended to do. If I call HF_Reset, then Testgets "Success" put into it, and Test2 is filled with 1s, but TblAllFeatsSelected doesn't change. I have absolutely no clue what's going on - any ideas?
For debugging purposes, I've also tried setting Range("TblAllFeatsSelected").Value = 1 and Range("TblAllFeatsSelected").Value = 0, and again it works fine when calling Feats_Reset but not when calling HF_Reset.
EDIT: I've played some more, and traced the problem to another subroutine called in Feats_Reset. I suspect I'm not going to be able to provide enough information here to get a useful answer - it's a complicated sheet, and there's a lot of interactions that could be the problem. Bother.
EDIT2: Found the problem. The subroutine was setting TblAllFeatsSelected to the value of another range, which when calling from HF_Reset needed to have an Application.Calculate or it would justset it back to what it used to be.
Is there any way I can delete this question as not useful? It's such a specific thing, I doubt it could help anyone else.
Problem Exists Between Keyboard and Chair. I was missing an Application.Calculate in a completely different part of the code.
I'm creating a macro that opens a file that everyone has on their computer and in order to do so must know the person's username / work ID.
To get the person's work ID I've tried using the following:
sso = IIf(InStr(Application.OperatingSystem, "Windows") = 1, Environ("UserName"), _
'MacScript("(user name as string)"))
Running this on windows returns an error because of the Macscript (I think) and I'd assume the same would happen vice versa, even though the error part of the IIF is never actually accessed I'm guessing seeing as the whole line is executed this is why there is a problem, thus On Error Resume Next would not really help here.
I know this can be easily overcome by just using an if and else statement but I just want to know if I'm right / why this problem occurs and if there are any other more sophisticated ways of achieving what I want.
Thanks
The IIF function evaluates both the true and false parts, or rather it attempts to do so. There is no short-circuit. Your assumption about why it's failing (and also that you can't use an OERN) is correct. You may take a look at conditional compilation logic, if certain parts of your code will not compile on Windows (or Mac, respectively).
http://msdn.microsoft.com/en-us/library/aa240847(v=vs.60).aspx
I am attempting to create a VBScript function (within the context of QuickTest Pro 10) that is able to take in a line of code as a parameter, such as: JavaWindow("Export Control Notice").JavaButton("OK").Click
Problem is, when I try passing that line as a string, VBScript understandably chokes on the quotes. Here is the catch: I have many lines of code similar to the one above, so I would like to take the line of code and pass it as is. I don't want to go around doubling the quotes for these lines of code, let along for every new line that QTP records. (Find/Replace All can easily go out of control)
How can I pass the above line of code?
If turning it into a string is the way to go, how can I encode that line so VBscript doesn't choke on the quote marks?
Additionally, I haven't been able to find any way to change the delimiter to something other than quote marks to assign the line of code as a string. Finding a way to change the delimiter would probably solve this issue.
Alternately, I've also tried passing JavaWindow("Export Control Notice").JavaButton("OK") as an object into the function and that has worked. Unfortunately, there doesn't seem to be a way to turn that object back into a string in order to append ".Click" (or some other action stored as a string) back onto the end of it. Am I overlooking something?
Depending on how exactly you 'have' those lines of code (in your head, in a text document, ...) and whether you prefer to type or to program you can
put them into VBScript code using the proper escape (""):
Dim sCodeLine : sCodeLine = "JavaWindow(""Export Control Notice"").JavaButton(""OK"").Click"
WScript.Echo sCodeLine
JavaWindow("Export Control Notice").JavaButton("OK").Click
put them into VBScript code using a delimiter of your choice and Replace():
Dim sCodeLine : sCodeLine = "JavaWindow('Export Control Notice').JavaButton('OK').Click"
WScript.Echo Replace( sCodeLine, "'", """" )
JavaWindow("Export Control Notice").JavaButton("OK").Click
put them in an external file (.txt, .xls, .xml, ...) (resp. use the given document); load and parse the file into a suitable data structure (array, dictionary, ...). Use that collection to feed your function.
The way we eventually passed in QTP objects & actions into the function was to keep the QTP object as an object, then pass in the action as a string that a Select Case would sort through. We don't have that many actions, so a Select Case works well in this situation.
For example, here is the code for performing an action:
Before:
If existCheck JavaWindow("Certificate Management").JavaWindow("Import Key Store").JavaEdit("* Certificate Name").Exist(2) Then
existCheck JavaWindow("Certificate Management").JavaWindow("Import Key Store").JavaEdit("* Certificate Name").Set "Personal"
Reporter.ReportEvent micPass,"OK button clicked on Export Control Notice","Ok"
Else Reporter.ReportEvent micFail,"Export Control Notice is not found","Step Fail" End If
After:
existCheck JavaWindow("Certificate Management").JavaWindow("Import Key Store").JavaEdit("* Certificate Name"), "Set", "Personal", Null, 2, Null, Null
We are now have the action occupying only one line, which helps immensely for code readability, but also allows us to improve the error-checking / reporting function all in one place.
Here is the some of the error-checking function, minus the reporting portion.
The Function:
'existCheck provides error-checking by determining if the object exists right before we interact with it.
'Afterwards, it reports on the results. This function has parameters for:
'object: an object (such as a button or field)
'strAction: the action to perform on that object (such as "Click")
'strParameter1 (and two): the parameter that is passed when the action is performed
'intWait: the time in seconds to wait for the object to appear
'strPass: the string to report for a success. If not string is provided, a default one will be used.
'strFail: the string to report for a failure. If not string is provided, a default one will be used.
Function existCheck(object, strAction, strParameter1, strParameter2, intWait, strPass, strFail)
'Before we can perform any action, we must see if the object exists. This determines much of what we do next.
If object.Exist(intWait) Then
'Chooses the action to be performed on the object (and then performs it)
Select Case strAction
Case "Click"
object.Click
Case "Select"
object.Select strParameter1
Case "Press"
object.Press strParameter1
Case "Set"
object.Set strParameter1
Case "SetSecure"
object.SetSecure strParameter1
Case "Type"
object.Type strParameter1
Case "SelectCell"
object.SelectCell strParameter1, strParameter2
End Select
... and so on.