Excel VBA Reading XML Errors (Blank attributes?) - excel

I have some code (subroutine macro) that's meant to search through a list of US state, get their name and abbreviation and then search a sheet for that state to get a corresponding integer value.The code largely works, but at the end of the loop through the results, there is always a blank result (even if I check for it, which I've done in a variety of different ways). This seems to be the cause of errors if I try to apply a function to the result.
Dim xmlDoc As DOMDocument30
Set xmlDoc = New DOMDocument30
xmlDoc.Load ("C:\xampp\htdocs\election\states.xml")
Dim list As IXMLDOMNodeList
Set list = xmlDoc.SelectNodes("//states/state")
Dim abbrNode As IXMLDOMAttribute
Dim abbr As String
Dim stateNode As IXMLDOMAttribute
Dim state As String
Dim resultNode As IXMLDOMAttribute
Dim result As String
Dim node As IXMLDOMNode
Dim childNode As IXMLDOMNode
Dim NumberOfVotes As Integer
For Each node In list
Set abbrNode = node.Attributes.getNamedItem("abbreviation")
Set stateNode = node.Attributes.getNamedItem("name")
Set resultNode = node.Attributes.getNamedItem("result")
If (Trim(abbrNode.Text) <> "") Then
MsgBox stateNode.Text
End If
Even with the check for an empty string (also checked for if abbrNode was nothing), I always get an empty MsgBox at the end, when I'd rather it end at Wyoming.
If I run the lookup function in the loop:
Set NumberOfVotes = lookupElectoralVotes(stateNode.Text)
I get a compile error (object required), I assume because it can't perform that function on a blank result.
I also get an error if I try to set a variable to equal stateNode.Text or similar:
Set state = stateNode.Text
Also generates a compile error, I'm not sure why, but again possibly due to the final result being blank. But I can use stateNode.Text by itself so maybe I'm just missing a core concept of VBA.
The XML file is nothing complex:
<?xml version="1.0" encoding="utf-8"?>
<states>
<state name="ALABAMA" abbreviation="AL" result="U"/>
<state name="ALASKA" abbreviation="AK" result="U"/>
<state name="ARIZONA" abbreviation="AZ" result="U"/>
</states>
Thanks for any help.

Related

VBA Index Match function

I am new to vba and totally lost in writing the above mentioned function in vba. Actually, I want to do the same thing I would do with the usual excel formula.
Update:
Based on the answer of Scott I have adjusted my code. Now I have a Type mismatch error. The Type of Mname by definition is a string. The values in the lookup Range (B18:B38) are (not exclusive) integer. Should I tell Excel to take them as a string? If yes, how?
Summary:
I have a range (D18:D38) where I want to chose a value from based on the row number determined by a match between a string Variable (Mname) and another range (B18:B38). The string Variable is determined by the name of a file in a folder.
My Problem is that I get the error message: 'Unable to get the match property of the worksheetfunction class'
Thanks for your help!
Sub Test()
Application.ScreenUpdating = False
Dim Mname As String
Set WSCockpit = ThisWorkbook.ActiveSheet
Dim strFileName As String
Dim strFolder As String: strFolder = WSCockpit.Range("D9").Value
Dim strFileSpec As String: strFileSpec = strFolder & "*.xls*"
strFileName = Dir(strFileSpec)
Do While Len(strFileName) > 0
Dim strFilePath As String: strFilePath = strFolder & strFileName
Mname = Mid(strFileName, 13, Len(strFileName) - 17)
Dim rw As Long
rw = Application.Match(Mname, WSCockpit.Range("B18:B38"), 0)
Dim VarImp As Boolean
VarImp = Application.WorksheetFunction.Index(WSCockpit.Range("D18:D38"), rw)
'some other task'
Loop
Application.DisplayAlerts = True
End Sub
Backup:
Sorry for the code being messy. I have no clue about the general rules for writing vba. Like mentioned before, my goal is to get the lookup running. The looked up value will be "TRUE" or "FALSE". Afterwards I will use this in order to determine whether the file found in the folder needs to be imported or not. If you have some other suggestions for my coding or for the task I want to perform, I would be glad to hear.
Break it into steps and also ensure the data you expect to find in the lookup is in indeed there. Meaning, check the value of Mname at run-time (via debugging code) and verify you can find it manually.
Lastly, use Application.Match instead.
Dim rw as Long
rw = Application.Match(Mname, WSCockpit.Range("B18:B38"),0)
Dim import as Boolean
import = Application.WorksheetFunction.Index(WSCockpit.Range("D18:D38"),rw)

Open two existing word files from excel

Hi I'm trying to write code to use excel to work with two existing word documents but I keep getting OLE errors. This is just the start but it keeps crashing. What am I doing wrong?
Sub BoQtoWord()
Dim Word As Object
Dim WordDoc As Object
Dim WordDoc1 As Object
Dim StdSpec As String
Dim NewSpec As String
StdSpec = Application.GetOpenFilename()
Set Word = CreateObject("Word.Application")
Set WordDoc = Word.Documents.Open(StdSpec)
Sheet1.Range ("A1").Value = StdSpec
NewSpec = Application.GetOpenFilename()
Set WordDoc1 = Word.Documents.Open(NewSpec)
Sheet1.Range("A2").Value = NewSpec
End Sub
It seems to works fine on my end, albeit a bit slow, at least as far as writing the file paths to cells A1 and A2 is concerned. Beyond that we'd need to see more of your code.
Depending on what you're trying to accomplish with the Word objects the OLE problems might stem from conflicts in your references, make sure you check what libraries you are referencing (Tools/References...) for any possible conflict.
Another possible conflict might be the use of the word "Word" as your variable name for the object. Word is also a key name when using the Microsoft Word library, try using a different name see if that helps at all.
Speaking of which, by adding the Microsoft Word library to your references you can skip the step to create the word object, you can directly create a word.document object instead, like this:
Sub BoQtoWord()
Dim WordDoc As Word.Document
Dim WordDoc1 As Word.Document
Dim StdSpec As String
Dim NewSpec As String
'Get first doc
StdSpec = Application.GetOpenFilename()
Set WordDoc = Documents.Open(StdSpec)
Sheet1.Range("A1").Value = StdSpec
'Get second doc
NewSpec = Application.GetOpenFilename()
Set WordDoc1 = Documents.Open(NewSpec)
Sheet1.Range("A2").Value = NewSpec
'Do something with the documents opened
End Sub
Maybe that would solve your OLE problems.
Hope this points you in the right direction, if anything, a bit more information might help narrow down the issue!

Replace string with string variable - Error 91

Background
Recently I answered a question which involved looking at a file's properties. Eventually the code I put up worked fine, but there is one thing about it that got me puzzled.
Problem
There are two specific lines where I wanted to replace a (to me what looks like) a string, with a variable, more specifically, try the following:
Sub TestForSO()
Dim oDir As Object: Set oDir = CreateObject("Shell.Application").Namespace("C:\Users\...\")
Debug.Print oDir.GetDetailsOf(oDir.Items, 1)
End Sub
Replace the pathname to a directory which includes an excel file, and it should return the property value just fine.
Now when I try to replace the full path with a variable the following throws an "Runtime Error 91: Object variable or with block variable not set" on the debug.print line:
Sub TestForSO()
Dim MainPath As String: MainPath = "C:\Users\...\"
Dim oDir As Object: Set oDir = CreateObject("Shell.Application").Namespace(MainPath)
Debug.Print oDir.GetDetailsOf(oDir.Items, 1)
End Sub
Solution
A bit peculiar to me that the following did work:
Sub TestForSO()
Dim MainPath As String: MainPath = "C:\Users\...\"
Dim oDir As Object: Set oDir = CreateObject("Shell.Application").Namespace(CStr(MainPath))
Debug.Print oDir.GetDetailsOf(oDir.Items, 1)
End Sub
I do not understand the difference per se as the code below will give the same result through "Watches":
Sub test()
Dim check1 As String, check2 As String
check1 = "Hello"
check2 = CStr("Hello")
End Sub
Question
Does somebody understand why the string variable on itself was not enough and would throw an error? Why would adding Cstr() make the code work when seemingly it's the same data type?
According to documentation about Namespace, it needs a parameter that must be a Variant or can be a string that specifies the path of the folder.
That explains why these 2 methods work with no problems:
Set oDir = CreateObject("Shell.Application").Namespace("C:\Users\...\ 'string path
Or defining a Variant variable:
Dim MainPath As Variant: MainPath = "C:\Users\...\"
Dim oDir As Object: Set oDir = CreateObject("Shell.Application").Namespace(CStr(MainPath))
But defining MainPath as string causes error Runtime Error 91: Object variable or with block variable not set
OP found a solution. If MainPath declared as string, and combined with Cstr, the code works.
It's just a theory, but some unnoficial sources (not directly related to VBA) mention that Cstr converts the value to a variant with a subtype.
http://www.csidata.com/custserv/onlinehelp/vbsdocs/vbs89.htm
https://docs.oracle.com/cd/E57185_01/HFMAD/ch10s06s04s03.html
Actually, the official documentation it's kind of confusing, because at first lines it says:
Each function coerces an expression to a specific data type.
and later on it says
The function name determines the return type
But if we read carefully, there is also some important information like this:
"...In general, you can document your code using the data-type
conversion functions to show that the result of some operation
should be expressed as a particular data type rather than the
default data type..."
And also:
"...This technique is consistent with the conversion of all other
intrinsic types to their equivalent Variant subtypes..."
So after doing some research and thinking about it in the last 24 hours, and reading a lot of times the previous paragraphs I've posted, I would dare to say that all conversion functions returns a Variant with a subtype. In this case, CStr does return a Variant that is being coerced to be expressed as string being string the subtype, but the data is Variant.
That would explain why doing Cstr(MainPath) makes the code works.

Excel VBA Shell.Namespace returns Nothing

I'm trying to extract a .CAB file using Excel VBA, but I'm getting the following error:
Run-time error '91': Object variable or With block variable not set
I usually get this when I forget to use Set with an Object, but I've checked for that.
All examples I can find are variations on this theme:
Private Function DeCab(vSource, vDest) As Long
Dim objShell, objFileSource, objFileDest As Object
Set objShell = CreateObject("Shell.Application")
Set objFileSource = objShell.Namespace(vSource)
Set objFileDest = objShell.Namespace(vDest)
Call objFileDest.MoveHere(objFileSource.Items, 4 Or 16) 'Fails here
Decab = objFileDest.Items.Count
End Function
It's not failing on the Set line, but it's setting both objFileSource and objFileDest to Nothing even though I've confirmed vSource and vDest exist.
To confirm it has nothing to do with the .CAB file, I've also tried it without setting objFileSource and checking the value of objFileDest after it's set. It still returns Nothing. Why would that be? I'm on Windows 7, 64-bit, running Office 2010.
Your parameters must be submitted as Variant, not String
Sub Tester()
Dim src, dest '<< works
'Dim src As String, dest As String '<< gives the error you see
src = "D:\temp\test.zip"
dest = "D:\temp\unzip"
DeCab src, dest
End Sub
https://msdn.microsoft.com/en-us/library/windows/desktop/bb774085(v=vs.85).aspx
Tim's answer is correct. I found an alternative, as well:
Private Function DeCab(vSource, vDest) As Long
Dim objShell, objFileSource, objFileDest As Object
Set objShell = CreateObject("Shell.Application")
Set objFileSource = objShell.Namespace((vSource)) '<-extra parentheses
Set objFileDest = objShell.Namespace((vDest)) '<-extra parentheses
Call objFileDest.MoveHere(objFileSource.Items, 4 Or 16) 'Fails here
Decab = objFileDest.Items.Count
End Function
When you place an object in parentheses in VBA, it returns the default value of the object. Apparently, objShell.Namespace can't handle a pointer. It can only handle a string literal. Changing the signature to the following also works if you're passing in Strings:
Private Function DeCab(ByVal vSource, ByVal vDest) As Long
for my case. i used the shell
Set oShell = CreateObject("Shell.Application")
x= oShell.xxxx
y= oShell.Namespace(x)
in two different lines. seems like i'll need to re initialized to able to use on second line. eg
Set oShell = CreateObject("Shell.Application")
x= oShell.xxxx
y= CreateObject("Shell.Application").Namespace(x)
then only it works.

How can I extract the distance from Google Directions API via Excel web query?

I have a long list of origins and destinations in Excel, using webquery I can fill in the cities and postal code to give a webquery like:
http://maps.googleapis.com/maps/api/directions/xml?origin=Scoresby&destination=Melborne&sensor=false
This returns me a long XML file, but all I need is just the distance. Is there a way to extract only the distance value?
Or should I just run a macro script to extract distance one by one? (Since the format remains roughly the same each time I ask the server)
The short answer is XPath - well worth learning if you are going to work with any kind of XML
In the macro editor in Excel, go to Tools > References and add a reference to "Microsoft XML, v6.0" Now Insert > Module and add this code:
Sub getDistances()
Dim xhrRequest As XMLHTTP60
Dim domDoc As DOMDocument60
Dim ixnlDistanceNodes As IXMLDOMNodeList
Dim ixnNode As IXMLDOMNode
Dim lOutputRow As Long
' Read the data from the website
Set xhrRequest = New XMLHTTP60
xhrRequest.Open "GET", "http://maps.googleapis.com/maps/api/directions/xml?origin=Scoresby&destination=Melborne&sensor=false", False
xhrRequest.send
' Copy the results into a format we can manipulate with XPath
Set domDoc = New DOMDocument60
domDoc.loadXML xhrRequest.responseText
' The important bit: select every node called "value" which is the child of a node called "distance" which is
' in turn the child of a node called "step"
Set ixnlDistanceNodes = domDoc.selectNodes("//step/distance/value")
' Basic stuff to output the distances
lOutputRow = 1
With Worksheets("Sheet1")
.UsedRange.ClearContents
For Each ixnNode In ixnlDistanceNodes
.Cells(lOutputRow, 1).Value = ixnNode.Text
lOutputRow = lOutputRow + 1
Next ixnNode
End With
Set ixnNode = Nothing
Set ixnlDistanceNodes = Nothing
Set domDoc = Nothing
Set xhrRequest = Nothing
End Sub
To extend this to cover multiple trips you would just loop through the required origins and destinations, pass each pair as parameters to this procedure and then output the results in whichever format you need

Resources