Can't return a value from a VBA function - vba

I'm trying to return a value from my code. It's much easier to just show the code:
Function writeHeaderData() As IXMLDOMNode
Dim xmlDoc As New MSXML2.DOMDocument30
xmlDoc.async = False
xmlDoc.LoadXML "<Foo></Foo>"
Dim Foo As IXMLDOMNode
Set Foo = xmlDoc.DocumentElement
'code snip; includes appending lots of things to Foo
'the error is on this line:
writeHeaderData = Foo
Exit Function
End Function
I've already Google searched, but it's been of no avail. This function is being called from the main subroutine, and I'm trying to append the returned IXMLDOMNode to a bigger one, but I keep getting an "Object variable or With block variable not set" error on the writeHeaderData = Foo line. What's up here?

In VB(A), when you want to assign to an object variable, including assigning the return value of a function, you need to use Set, so:
'the error is on this line:
writeHeaderData = Foo
should be
Set writeHeaderData = Foo

Related

VBA Run-time error 450 from function returning a collection

Context: I am writing a function which returns words/numbers present in a string which are enclosed by parenthesis.
Example: Calling ExtractParenthesis("This {should} work. But {doesnt}.") should return a collection containing two items, should and doesnt.
Error: The error I receive from the code below is
Run-time error '450': Wrong number of arguments or invalid property
assignment
It doesn't appear on a particular line and I just receive an error message with "OK" and "Help" as options.
Code:
Public Function ExtractParenthesis(strText As String) As Collection
Dim i As Long
Dim RegExp As Object
Dim Matches As Object
Dim Output As New Collection
Set Output = Nothing
Set RegExp = CreateObject("vbscript.regexp")
RegExp.Pattern = "{(.*?)}"
RegExp.Global = True
Set Matches = RegExp.Execute(strText)
For i = 0 To (Matches.count - 1)
Output.Add Matches(i).submatches(0)
Next i
Set ExtractParenthesis = Output
End Function
It works exactly the way you want it for me:
Option Explicit
Public Sub TestMe()
Dim myColl As New Collection
Set myColl = ExtractParenthesis("This {should} work. But {doesnt}.")
Debug.Print myColl(1)
Debug.Print myColl(2)
End Sub
Public Function ExtractParenthesis(strText As String) As Collection
Dim i As Long
Dim RegExp As Object
Dim Matches As Object
Dim Output As New Collection
Set Output = Nothing
Set RegExp = CreateObject("vbscript.regexp")
RegExp.Pattern = "{(.*?)}"
RegExp.Global = True
Set Matches = RegExp.Execute(strText)
For i = 0 To (Matches.Count - 1)
Output.Add Matches(i).submatches(0)
Next i
Set ExtractParenthesis = Output
End Function
I receive "should" and "doesnt" on the immediate window (Ctrl+G). Probably you are not aware that you are returning a collection. It should be used with the Set keyword.
To run it from the immediate window, try like this:
?ExtractParenthesis("This {should} work. But {doesnt}.")(1)
?ExtractParenthesis("This {should} work. But {doesnt}.")(2)
From what I understand of your comment to Vityatas answer you mean you want to run it as a worksheet function - running it directly
The changes I've made to your code will let you use it as a function:
In A1:B1 this will work: {=ExtractParenthesis("This {should} work. But {doesnt}.")}
In A1:A2 use it like this: {=TRANSPOSE(ExtractParenthesis("This {should} work. But {doesnt}."))}
NB: The curly brackets are added by Excel when you enter the formula using Ctrl+Shift+Enter rather than Enter on its own.
The one problem with the code is that you must select the correct number of cells first - if it should return three words, but you've only selected two then you'll only see the first two.
Public Function ExtractParenthesis(strText As String) As Variant
Dim i As Long
Dim RegExp As Object
Dim Matches As Object
Dim Output As Variant
Set Output = Nothing
Set RegExp = CreateObject("vbscript.regexp")
RegExp.Pattern = "{(.*?)}"
RegExp.Global = True
Set Matches = RegExp.Execute(strText)
ReDim Output(1 To Matches.Count)
For i = 1 To (Matches.Count)
Output(i) = Matches(i - 1).submatches(0)
Next i
ExtractParenthesis = Output
End Function

ListView.Items.Find throwing error; The object reference is not set to an instance of an object

This has been bugging me for a few hours now. On each new ListViewItem I add, I associate a Name value to it, so it doesn't add the same item twice. The value is a URL, I'm not sure if perhaps that's the issue?
The error I am getting is:
The object reference is not set to an instance of an object.
Full error description:
System.NullReferenceException: The object reference is not set to an instance of an object.
    By System.Windows.Forms.ListView.ListViewItemCollection.FindInternal (String key, Boolean searchAllSubItems, ListViewItemCollection listViewItems, ArrayList foundItems)
    At System.Windows.Forms.ListView.ListViewItemCollection.Find (String key, Boolean searchAllSubItems)
Here's my code:
I get the error at If Not ListView2.Items.Find(lv.Name, False).Count >= 1 Then
Dim m_url As String = HttpUtility.HtmlDecode(matchUrl)
m_url = m_url.Replace(" ", "")
If Not m_url.Contains(rootdomain) Then
Dim lv As New ListViewItem
lv.Name = m_url
If Not ListView2.Items.Find(lv.Name, False).Count >= 1 Then
lv.Text = m_url
lv.SubItems.Add(StripTags(match.Groups(2).Value))
lv.SubItems.Add("")
ListView2.Items.Add(lv)
End If
e_links_c += 1
End If
I tried to not use the Name at all, but then I got a few errors which stated something like the key was already added, I have no idea why that happended since I didn't associate any Name value to the items.
Updated code:
Dim lv As New ListViewItem(m_url)
lv.Name = m_url
If ListView2.Items.IndexOfKey(lv.Name) = -1 Then
lv.SubItems.Add(StripTags(match.Groups(2).Value))
lv.SubItems.Add("")
ListView2.Items.Add(lv)
End If
I'm not sure why the exception is thrown, but you can try the IndexOfKey() method instead. It will only iterate until it finds one match instead of many.
If ListView2.Items.IndexOfKey(lv.Name) = -1 Then '-1 = no item found.

Set statement, return empty string if error

From the following link there is an example at the bottom of the page which I have recreated in vb.net.
Before the following function runs, I save some data from a textfile into a dictionary called T.
For example:
Name - T0962
Value - 5.89
Public Shared Function initialization()
'Variables initialization
Dim parts As New List(Of Intialization)
'Add parts to the list.
parts.Add(New Intialization() With {
.PartName = "T0962",
.PartId = T.Item(.PartName))
})
If parts.Exists(Function(p) p.PartName = "T0962") Then
Dim value = parts.Where(Function(p) p.PartName = variable_type).FirstOrDefault()
Msgbox(value.PartId)
End If
End Function
The program works perfectly when I have "T0962" variable. When that variable does not exist in the textfile, it does not exist in the dictionary aswell. Thus, I get an error in the code, because the .PartId fails to be initialized. This is because in that textfile sometimes I have that value sometimes I do not.
After I have analized carefully I have noticed that the error happens in the Property statement, at Set(value As String) to be more exactly.
Public Property PartId() As String
Get
Return m_PartId
End Get
'here the error happens
Set(value As String)
m_PartId = value
End Set
End Property
Is there a way to avoid this in the Set statement? For example when there is an error then return an empty string?
Please let me know if there is something you do not understand.
Ok. Try Below. It works for me.
Dim partName As String
partName = "T0962"
parts.Add(New Intialization() With {
.PartName = partName,
.PartId = T.FirstOrDefault(Function(f) f.Key = partName).Value
})

Searching Strings from a split list

Attempting to split and store strings from a listbox and search then search the contents of the text file I have stored them to, hopefully sorting them in different categories,
firstly I am getting an "Object reference not set to an instance of an object." error with this
Dim variable As String = Nothing
If listArray.SelectedIndex > 0 Then
variable = listArray.Items(listArray.SelectedIndex)
End If
Dim part As String() = variable.Split(New Char() {","c})
Dim line As String
For Each line In part
MessageBox.Show(line)
and secondly, would this be the right code to use for searching those separated strings?
For count As Integer = 0 To Logbook.listArray.Items.Count - 1
Dim searchIndex As String = Logbook.listArray.Items(count).ToString
If searchIndex.Contains(indexSearch.Text) Then
Logbook.listArray.SetSelected(0, True)
End If
Next
I'm pretty new to StackOverflow, my apologies if i'm not up to date with the website etiquette.
am getting an "Object reference not set to an instance of an object."
I guess you don't know that the first item is at index 0 and that SelectedIndex returns -1 if there is no item selected. That's why following code throws that exception:
Dim variable As String = Nothing
If listArray.SelectedIndex > 0 Then
variable = listArray.Items(listArray.SelectedIndex)
End If
Dim part As String() = variable.Split(New Char() {","c}) ' <--- variable Nothing if first item selected
Then you just have to use <> -1 or >= 0:
Dim variable As String = Nothing
If listArray.SelectedIndex >= 0 Then
variable = listArray.Items(listArray.SelectedIndex)
End If
According to the second part of the question(always ask only once), you haven't provided enough informations to understand what you want and what doesn't work with your code.

Why won't this list of struct allow me to assign values to the field?

Public Structure testStruct
Dim blah as integer
Dim foo as string
Dim bar as double
End Structure
'in another file ....
Public Function blahFooBar() as Boolean
Dim tStrList as List (Of testStruct) = new List (Of testStruct)
For i as integer = 0 To 10
tStrList.Add(new testStruct)
tStrList.Item(i).blah = 1
tStrList.Item(i).foo = "Why won't I work?"
tStrList.Item(i).bar = 100.100
'last 3 lines give me error below
Next
return True
End Function
The error I get is: Expression is a value and therefore cannot be the target of an assignment.
Why?
I second the opinion to use a class rather than a struct.
The reason you are having difficulty is that your struct is a value type. When you access the instance of the value type in the list, you get a copy of the value. You are then attempting to change the value of the copy, which results in the error. If you had used a class, then your code would have worked as written.
try the following in your For loop:
Dim tmp As New testStruct()
tmp.blah = 1
tmp.foo = "Why won't I work?"
tmp.bar = 100.100
tStrList.Add(tmp)
Looking into this I think it has something to do with the way .NET copies the struct when you access it via the List(of t).
More information is available here.
Try creating the object first as
Dim X = New testStruct
and setting the properties on THAT as in
testStruct.blah = "fiddlesticks"
BEFORE adding it to the list.