Object reference not set to an instance of an object (Completely broken?) in vb.net - vb.net

I know, I know, I could have used a for loop, dont tell me anything about that. Please, help!
Private Function LoadSaved() ''//Loads saved clippings if the user wants us to
Dim ZomgSavedClips As StringCollection
If IsDBNull(My.Settings.SavedClips) = False Then ''//If it is null this would return a rather ugly error. Dont want that do we?
ZomgSavedClips = My.Settings.SavedClips ''//ZomgSavedClips name was a joke, I just felt like it.
ZomgSavedClips.Add(" ") ''//This line ought to fix the error, but doesnt
i = 0
While i < ZomgSavedClips.Count ''//This is where the error occurs
ClipListings.Rows.Add(ZomgSavedClips(i))
i = i + 1 ''//First time I wrote this function I forgot this line. Crashed mah comp. Fail.
End While
End If
End Function
The line While i < ZomgSavedClips.Count is bugging, I know that the .count should return null but I even added a blank piece of text just to stop that. Whats up with this? Should I add actual text?

SavedClips is null no? If it is null it could pass the test IsDBNull beacuse the both are not the same

Obviously, My.Settings.SavedClips is still set to Nothing.

SavedClips is regular 'ole null (nothing in VB). Include a check for "My.Settings.SavedClips is nothing". If that evaluates to true then just leave the function.

I even added a blank piece of text just to stop that.
All you did was move where the error happens. You can't call .Add() on a null/Nothing object.
'''<summary>Loads saved clippings if the user wants us to</summary>'
Private Sub LoadSaved() ''//Loads saved clippings if the user wants us to
''//Load saved clips into memory
Dim ZomgSavedClips As StringCollection = My.Settings.SavedClips
If ZomgSavedClips Is Nothing Then ZomgSavedClips = New StringCollection()
''//Apply loaded clips to visible listings
Dim i As Integer
While i < ZomgSavedClips.Count ''
ClipListings.Rows.Add(ZomgSavedClips(i))
i += 1
End While
End Sub
Some notes on this code:
Don't use Function when you mean Sub
Since you'll be selling this code to others, you want to use xml comments at the top so that Visual Studio can give better intellisense helps.
IsDBNull() doesn't do what you think it does.
Yes, you should use a for loop, but since you already commented on that I left the while loop alone with the assumption that there's more code you didn't show us.

Related

Error HRESULT E_FAIL when iterating through for loop only

I'm seeing the good old "System.Runtime.InteropServices.COMException HResult=0x80004005 Message=Error HRESULT E_FAIL has been returned from a call to a COM component" error when attempting to find an item via a for loop as shown below:
For i = 1 to itemList.Count
oObject = itemList.Item(i)
Next
But not if I hardcode the index, this finds item 1 without issue:
oObject = itemList.Item(1)
Obviously I don't want to do that and need to search through all the objects in my "itemList" to find the one I'm looking for.
I'm being intentionally vague because the software I'm working in is Dassault 3D Experience but am writing macros for it through Visual Studio 2017. I'm not sure where to even start debugging this sort of issue so any suggestions would be appreciated. Thanks.
Edit: adding full code of what I'm trying to do here (find an object, display its name, also select it on screen to double check. I will later add a check to make sure the object found in each loop is really what I'm looking for). All variables have been declared before this section.
selactive = CATIA.ActiveEditor.Selection
selactive.Clear()
product1Service = CATIA.ActiveEditor.GetService("PLMProductService")
oRootOcc = product1Service.RootOccurrence
cVPMOccurrences = oRootOcc.Occurrences
For i = 1 to cVPMOccurrences.Count
oVPMOccurrence = cVPMOccurrences.Item(i)
selactive.Add(oVPMOccurrence)
MsgBox(oVPMOccurrence.Name)
Next
The line that fails is oVPMOccurrence = cVPMOccurrences.Item(i)
Can you do something like this with a For Each loop?
For each oVPMOccurrence as oRootOcc.Occurrence in cVPMOccurrences.Items
selactive.Add(oVPMOccurrence)
MsgBox(oVPMOccurrence.Name)
Next
Using a For Each means you don't have to worry at all about the index
Not sure what the type of oVPMOccurrence is as you haven't specified
Most indexes in .net are zero base. I don't know what itemList is but I suspect the index of the first item is 0.
For i = 0 to itemList.Count - 1
oObject = itemList.Item(i)
Next
Not sure why you want to overwrite the value of oObject on every iteration.

Check if a property of an object has value or not

I couldn't find a solution for this on SO or other source. I apologize in advance if this is a tired question:
I have a macro that iterates through placeholders in shapes. Some of these placeholders have text, some don't. I'm trying to figure out if
.Shapes.Placeholder.TextFrame.TextRange has any value or not (In the 'watches' section of the VBA editor, this property has <The specified value is out of range.>). If it does, do something. Otherwise, continue the For Each statement. However, everything I tried has thrown out of bounds errors or something of that type.
I have tried:
If Not (placeholder.TextFrame.TextRange.Length = 0) Then: Immediately throws out of bounds error
Try/catch, which doesn't seem to exist in VBA (Gives me Sub or Function not registered)
If Not CStr(placeholder.TextFrame.TextRange) = "0" then
I'm quite out of my depth as I don't have experience with any Visual Basic iteration. I just need to, if that property is empty or null, skip over the placeholder.
EDIT: Following suggestions, I tried this:
For Each placeholder In sld.NotesPage.Shapes.Placeholders
If Not (placeholder.TextFrame Is Nothing) Then
If Not (placeholder.TextFrame.TextRange Is Nothing) Then
If Not (placeholder.TextFrame.TextRange.LanguageID Is Nothing) Then
placeholder.TextFrame.TextRange.LanguageID = lng
End If
End If
End If
Next
I still get an out of bounds error on placeholder.TextFrame.TextRange Is Nothing. Did I make a syntax error?
FINAL EDIT: Turns out there was an answer in SO after all. For this specific problem, it was enough to add On Error Resume Next right after theFor Each.

Input box getting a compile error in VBA

I am learning how to create input boxes and I keep getting the same error. I have tried two different computers and have received the same error. The error I get is a "Compile Error: Wrong number of arguments or invalid property assignment"
Here is my code:
Option Explicit
Sub InputBox()
Dim ss As Worksheet
Dim Link As String
Set ss = Worksheets("ss")
Link = InputBox("give me some input")
ss.Range("A1").Value = Link
With ss
If Link <> "" Then
MsgBox Link
End If
End With
End Sub
When I run the code, it highlights the word "inputbox"
And help would be greatly appreciated.
Thanks,
G
Three things
1) Call your sub something other than the reserved word InputBox as this may confuse things. *Edit... and this alone will resolve your error. See quote from #Mat's Mug.
2) A̶p̶p̶l̶i̶c̶a̶t̶i̶o̶n̶.̶I̶n̶p̶u̶t̶B̶o̶x̶(̶"̶g̶i̶v̶e̶ ̶m̶e̶ ̶s̶o̶m̶e̶ ̶i̶n̶p̶u̶t̶"̶)̶ Use VBA.Interaction.InputBox("give me some input"). You can do this in addition to the first point. Documentation here.
3) Compare with vbNullString rather than "" . See here. Essentially, you will generally want to do this as vbNullString is, as described in that link, faster to assign and process and it takes less memory.
Sub GetInput()
Dim ss As Worksheet
Dim Link As String
Set ss = Worksheets("ss")
Link = VBA.Interaction.InputBox("give me some input")
ss.Range("A1").Value = Link
' With ss ''commented out as not sure how this was being used. It currently serves no purpose.
If Link <> vbNullString Then
MsgBox Link
End If
' End With
End Sub
EDIT: To quote #Mat's Mug:
[In the OP's code, what is actually being called is] VBA.Interaction.InputBox, but the call is shadowed by the procedure's identifier "InputBox", which is causing the error. Changing it to Application.InputBox "fixes" the problem, but doesn't invoke the same function at all. The solution is to either fully-qualify the call (i.e. VBA.Interaction.InputBox), or to rename the procedure (e.g. Sub DoSomething(), or both.
Sub InputBox()
That procedure is implicitly Public. Presumably being written in a standard module, that makes it globally scoped.
Link = InputBox("give me some input")
This means to invoke the VBA.Interaction.InputBox function, and would normally succeed. Except by naming your procedure InputBox, you've changed how VBA resolves this identifier: it no longer resolves to the global-scope VBA.Interaction.InputBox function; it resolves to your InputBox procedure, because VBAProject1.Module1.InputBox (assuming your VBA project and module name are respectively VBAProject1 and Module1) are always going to have priority over any other function defined in any other referenced type library - including the VBA standard library.
When VBA resolves member calls, it only looks at the identifier. If the parameters mismatch, it's not going to say "hmm ok then, not that one" and continue searching the global scope for more matches with a different signature - instead it blows up and says "I've found the procedure you're looking for, but I don't know what to do with these parameters".
If you change your signature to accept a String parameter, you get a recursive call:
Sub InputBox(ByVal msg As String)
That would compile and run... and soon blow up the call stack, because there's a hard limit on how deep the runtime call stack can go.
So one solution could be to properly qualify the InputBox call, so that the compiler knows exactly where to look for that member:
Link = VBA.Interaction.InputBox("give me some input")
Another solution could be to properly name your procedure so that its name starts with a verb, roughly describes what's going on, and doesn't collide with anything else in global scope:
Sub TestInputBox()
Another solution/work-around could be to use a similar function that happens to be available in the Excel object model, as QHarr suggested:
Link = Application.InputBox("give me some input")
This isn't the function you were calling before though, and that will only work in a VBA host that has an InputBox member on its Application class, whereas the VBA.Interaction.InputBox global function is defined in the VBA standard library and works in any VBA host.
A note about this:
If Link <> "" Then
This condition will be False, regardless of whether the user clicked OK or cancelled the dialog by "X-ing out". The InputBox function returns a null string pointer when it's cancelled, and an actual empty string when it's okayed with, well, an empty string input.
So if an empty string needs to be considered a valid input and you need to be able to tell it apart from a cancelled inputbox, you need to compare the string pointers:
If StrPtr(Link) <> 0 Then
This condition will only be False when the user explicitly cancelled, and will still evaluate to True if the user provided a legit empty string.

why would a boolean revert to an original value?

I know this might be a more of a general question, but I've looked at my code a 100 times and am not sure what's wrong. I set a global flag so that it can be used in different forms of my application.
Dim OverPopulated as Boolean
I have a function where I first set it to FALSE, and after it goes through some validating it gets set to TRUE.
Now i do a bunch of stuff, go through different forms, and the value stays correct the entire time. Basically I need this value for another form, so when I check
if OverPopulated = false then
MsgBox "You Can't do this and that"
exit sub
End if
Then all of the sudden it gets set to FALSE. I've went through every instance of OVERPOPULATED in my application and put a break point to it to make sure that nowhere do i set it to FALSE, except for the one time I set it to FALSE before my validation.
I only have it in 2 other forms in my applications. There is one place where the flag is being run twice through the same statement (just like the one above). The first time it goes through, the value is correct, then it goes through again and the value is set to FALSE.
Any ideas? if this is too vague, please let me know, I'll try to edit it.
EDIT: I deleted a lot of the code but here's how it looks.....
Dim OverPopulated as Boolean
' modular level
Private Sub ValidatePopulation()
Dim admDate as date
OverPopulated = False
admDate = Format(Now(), "mm/dd/yyyy")
Select Case revPURP
Case 0, 1, 2
'Check make sure these fields
'some if statements, checking, validation
Case Else ' no need to do a
End Select
OverPopulated = True 'I MAKE SURE IT GETS HERE and is set to TRUE!!!
End sub
Now, in other functions I used it like this...
If OverPopulated = False Then
If optStat (0).Value = True Or optStat (1).Value = True Then
MsgBox "You are not able to do this”, vbCritical, "Incorrect review status...."
TabPop.Tab = 6
End If
Exit sub
Else
'Proceed with SAVE
End If
So I have this. and I have another statement just like this somewhere else. This one gets hit twice. The first time I go through it line by line and it's FALSE, then the 2nd time around the value is TRUE.
It looks like for some reason the value was getting reset to FALSE. I took out the Dim OverPopulated as Boolean from my form code, and placed it in a GLOBAL module where all the other globals are stored. This seems to have fixed the issue of the value remaining the same while I'm manuevering in the application. Thank you for all your comments.
Global OverPopulated as Boolean

What are a good No OP operation for vb.net?

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.