Populate result of another query into combo box - sql

I have an Excel file for colleagues to extract reports from SQL server.
We created separate user and password for their department.
I have the module which shows the result of SQL query in an Excel file.
Here is working code:
Sub Button3_Click()
ActiveSheet.Cells.Clear
Dim qt As QueryTable
sqlstring1 = "SELECT * FROM dbo.ReportDataAdded ORDER BY ProductID, CountryCodeID"
With ActiveSheet.QueryTables.Add(Connection:=getConnectionStr2, Destination:=Range("A3"), Sql:=sqlstring1)
.Refresh
End With
End Sub
Private Function getConnectionStr2()
getConnectionStr2 = "ODBC;DRIVER={SQL Server};" & _
"DATABASE=em_CountryConsumer;" & _
"SERVER=192.192.192.192;" & _
"UID=UserName;" & _
"PWD=passwordd;"
End Function
I need to populate the result of another query into combo box. For that I need to get result of query into variable with dataset datatype.
How can I change my VBA code to do that?

Here is an example of how I have handled a similar problem in the past:
First here is a function to Query the database based with a given connection_string and query.
Function GetQuery(SQL As String, connect_string As String, Optional HasFields As Long = 0) As Variant
'''
' Returns: A Variant() Array with results from query.
'
' HasFields is an optional field to include the field names in the array
' Any integer in this field will include fields, leave it blank for just data
'''
Dim Conn As New ADODB.Connection
Dim RS As New ADODB.Recordset
Dim data_sheet As Worksheet
Dim R As Long, C As Long
Dim dbArr() As Variant
'''''''''''''''''''''''''''''
' Setting Up DB connection
'''''''''''''''''''''''''''''
conReTry:
On Error GoTo ConnectErr:
With Conn
.ConnectionString = connect_string
.Open
End With
ConnectErr:
If Err.Number <> 0 Then
MsgBox "There was an issue connecting to the Central DB."
Resume subexit
End If
On Error GoTo 0
''''''''''''''''''''''
' Starting the connection to DB
''''''''''''''''''''''
On Error GoTo QueryErr:
RS.Open SQL, Conn, adOpenStatic
QueryErr:
If Err.Number <> 0 Then
MsgBox "There was a problem with the Query. Could not get results from the statement:" & vbCrLf & Err.Description
dbArr = Array(" Failed Q ", " Failed Q ")
Resume subexit
End If
On Error GoTo 0
'''''''''''''''''''''
' Parse Data and fill array: DBarr
'''''''''''''''''''''
R = 0
#If VBA7 Then
Dim tmp_rowNum As LongPtr, tmp_colNum As LongPtr
Dim rowNum As Integer, colNum As Integer
tmp_rowNum = RS.RecordCount
tmp_colNum = RS.Fields.Count
rowNum = CLng(tmp_rowNum)
colNum = CLng(tmp_colNum)
#Else
Dim rowNum As Long, colNum As Long
rowNum = RS.RecordCount
colNum = RS.Fields.Count
#End If
If HasFields = 0 Then
ReDim dbArr(1 To rowNum + 1, 1 To colNum)
Else
ReDim dbArr(1 To rowNum + 2, 1 To colNum)
End If
Do While Not RS.EOF
R = R + 1
For C = 1 To RS.Fields.Count
If R = 1 And HasFields = 1 Then
dbArr(R, C) = RS.Fields(C - 1).Name
ElseIf Not R = 1 Then
dbArr(R, C) = RS.Fields(C - 1).Value
End If
Next
If Not R = 1 Then RS.MoveNext
Loop
subexit:
GetQuery = dbArr
Set Conn = Nothing
Set RS = Nothing
End Function
Next use the result of the Query (A [multi-dimensional] Array) to set the range in a worksheet:
Sub SetInitData()
Application.ScreenUpdating = False
Application.EnableEvents = False
Dim sql_string As String, connect_String as String
Dim ws as Worksheet
Set ws = ThisWorkbook.Sheets(config.DATA_SHEET_NAME)
sql_string = config.GET_INITIAL_PACKTYPE_QUERY
connect_string = config.MAIN_CONNECTION_STRING
Debug.Print sql_string
Dim packtypedata As Variant
packtypedata = GetQuery(sql_string)
ws.Range(ws.Cells(1, 1), ws.Cells(UBound(packtypedata), UBound(packtypedata, 2))).Value = packtypedata
'' Keep Total Rows for next routine explained below
Dim total_rows as Integer
total_rows = UBound(packtypedata)
SetComboBoxValues(total_rows)
''' Turn on events and screen updating again
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
Finally you want to set the ComboBox:
Sub SetComboBoxValues(total_rows As Integer)
Dim ws as Worksheet, data_ws as Worksheet
Dim data_arr as Variant
Dim pack_dd as DropDown
Set ws = ThisWorkbook.Sheets(config.INPUT_SHEET_NAME)
Set data_ws = ThisWorkbook.Sheets(config.DATA_SHEET_NAME)
data_arr = data_ws.Range(data_ws.Cells(1,config.DATA_COL_DROPDOWN_INDEX),
data_ws.Cells(total_rows,config.DATA_COL_DROPDOWN_INDEX)).Value
Set pack_dd = ws.Shapes(config.MAIN_DATA_DROPDOWN_NAME).OLEFormat.Object
pack_dd.List = pack_dd
''' To set the index
pack_dd.ListIndex = 1
End Sub
** Note -- The GetQuery function has some kinks that I haven't had time to work out, namely I don't think the HasFields option to include headers actually works.
Also I'm using DropDowns, so I'm not sure if you are using the same type of object.
Good Luck

Related

Block if without end if? What am I missing (very simple statement)?

Here is the code:
numLoansSoldPrev = Range("LoansSold:NewHedges").Cells.Count
If numLoansSoldPrev > 3 Then
Set rngLoansSoldStart = ActiveWorkbook.Sheets("Email").Range("LoansSold").Offset(1, 1)
Let strLoansSoldStart = rngLoansSoldStart.Address
Set rngLoansSoldEnd = ActiveWorkbook.Sheets("Email").Range("NewHedges").Offset(-2, 5)
Let strLoansSoldEnd = rngLoansSoldEnd.Address
Range(strLoansSoldStart & ":" & strLoansSoldEnd).Select
Selection.ClearContents
End If
The commands below the beginning of the if statement work just fine on their own, but every time I try to execute this, I get "block if without end if" despite clearly having one at the bottom.
I have numerous of these if statements in the file but they are all in the same format, so it's not like one if statement is missing an end if.
Any idea?
Expanded Code:
' DECLARE NEW LONGS VARIABLES
Dim numNewLoansPrev As Integer
Dim rngLoansStart As Range
Dim rngLoansEnd As Range
Dim strLoansStart As String
Dim strLoansEnd As String
' DECLARE NEW LOANS SOLD VARIABLES
Dim numLoansSoldPrev As Integer
Dim rngLoansSoldStart As Range
Dim rngLoansSoldEnd As Range
Dim strLoansSoldStart As String
Dim strLoansSoldEnd As String
' DECLARE NEW HEDGES VARIABLES
Dim numNewHedges As Integer
Dim rngNewHedgesStart As Range
Dim rngNewHedgesEnd As Range
Dim strNewHedgesStart As String
Dim strNewHedgesEnd As String
Dim xcess As Integer
' Active E-mail Tab
Worksheets("Email").Activate
' CLEAR EXCESS NEW LONG POSITIONS
numNewLoansPrev = Range("NewLongs:LoansSold").Cells.Count
If numNewLoansPrev > 3 Then
Set rngLoansStart = ActiveWorkbook.Sheets("Email").Range("NewLongs").Offset(1, 1)
Set strLoansStart = rngLoansStart.Address
Set rngLoansEnd = ActiveWorkbook.Sheets("Email").Range("LoansSold").Offset(-2, 5)
Set strLoansEnd = rngLoansEnd.Address
Range(strLoansStart & ":" & strLoansEnd).Select
Selection.ClearContents
End If
' CLEAR EXCESS SOLD LONG POSITIONS
numLoansSoldPrev = Range("LoansSold:NewHedges").Cells.Count
If numLoansSoldPrev > 3 Then
Set rngLoansSoldStart = ActiveWorkbook.Sheets("Email").Range("LoansSold").Offset(1, 1)
Set strLoansSoldStart = rngLoansSoldStart.Address
Set rngLoansSoldEnd = ActiveWorkbook.Sheets("Email").Range("NewHedges").Offset(-2, 5)
Set strLoansSoldEnd = rngLoansSoldEnd.Address
Range(strLoansSoldStart & ":" & strLoansSoldEnd).Select
Selection.ClearContents
End If
' CLEAR EXCESS NEW HEDGES POSITIONS
numNewHedges = Range("NewHedges:Pnl").Cells.Count
If numNewHedges > 3 Then
Set rngNewHedgesStart = ActiveWorkbook.Sheets("Email").Range("NewHedges").Offset(1, 1)
Set strNewHedgesStart = rngNewHedgesStart.Address
Set rngNewHedgesEnd = ActiveWorkbook.Sheets("Email").Range("PnL").Offset(-2, 5)
Set strNewHedgesEnd = rngNewHedgesEnd.Address
Range(strNewHedgesStart & ":" & strNewHedgesEnd).Select
Selection.ClearContents
End If

Regex vba script throwing error : runtime error 9 ... Subscript out of range

I have a Word doc with some numbers referred in the foot notes. and I am exporting these references as a csv file.
Sub FindNumber()
Dim exp, exp1 As RegExp
Set exp = New RegExp
exp.Pattern = "\b[A-Za-z]{3}[0-9]{7}\b"
exp.Global = True
Dim splits(1000) As String
Dim x As Long
Dim results As MatchCollection
Set results = exp.Execute(ActiveDocument.StoryRanges(wdFootnotesStory))
x = 1
For Each res In results
splits(x) = res
x = x + 1
Next res
Dim Filename As String, line As String
Dim i As Integer
Filename = "C:\VBA Export" & "\Numbers.csv"
Open Filename For Output As #2
Print #2, "Control Numbers"
For i = LBound(splits) To UBound(splits)
Print #2, splits(i)
Next i
Close #2
MsgBox "Numbers were exported to " & Filename, vbInformation
End Sub
The code above was working fine and just suddenly starting throwing error at 'splits(x) = res'
I have tried checking my regex and I can see that it works fine. If I change splits(x) to splits(6) or something similar it works like a charm .
Can someone please help ?
EDIT - changed code to write matches directly to Excel.
Sub Tester()
Dim oXl As Excel.Application 'add reference to MS Excel object library...
Dim oWb As Excel.Workbook, c As Excel.Range, i As Long, col As Collection
Set oXl = New Excel.Application
oXl.Visible = True
Set oWb = oXl.Workbooks.Add()
Set c = oWb.Worksheets(1).Range("A1")
ListMatchesInExcel ActiveDocument.StoryRanges(wdFootnotesStory), _
"\b[A-Za-z]{3}[0-9]{7}\b", _
"Id Numbers", c
Set c = c.Offset(0, 1)
ListMatchesInExcel ActiveDocument.StoryRanges(wdFootnotesStory), _
"\b[A-Za-z]{2}[0-9]{9}\b", _
"Other Numbers", c
Set c = c.Offset(0, 1)
'etc etc
End Sub
'Search through `SearchText` for text matching `patt` and export all
' matches to Excel with a header `HeaderText`, starting at range `c`
Sub ListMatchesInExcel(SearchText As String, patt As String, _
headerText As String, c As Excel.Range)
'add reference to MicroSoft VBscript regular expressions
Dim exp, exp1 As RegExp, col As New Collection
Dim results As MatchCollection, res As Match, i As Long
Set exp = New RegExp
exp.Pattern = patt
exp.Global = True
Set results = exp.Execute(SearchText)
'log to Immediate pane
Debug.Print (col.Count - 1) & " matche(s) for '" & patt & "'"
c.Value = headerText
i = 1
For Each res In results
c.Offset(i).Value = res
i = i + 1
Next res
c.EntireColumn.AutoFit
End Sub

DDE - Mail Merge From Excel to Word 2016 using OpenDataSource

I have a old legacy code which is programmed for mail merge. I have a add-in code to populate xls file which in turn should merge the data to word template defined. Code snippet :
Public Sub ProcessForSharePoint(DataSource As Object, MainDoc As Document)
Dim tempPath As String
Dim i As Integer
Dim recordCount As Integer
Dim ActualCount As Integer
Dim ws As Variant
Dim tempName As String
Dim rowEmpty As Boolean
On Error GoTo tempFileError
tempName = Left(MainDoc.name, InStrRev(MainDoc.name, ".") - 1)
tempPath = Environ("TEMP") + "\" + tempName + ".xls"
If (Dir(tempPath) <> "") Then
SetAttr tempPath, vbNormal
Kill tempPath
End If
DataSource.SaveAs (tempPath)
On Error GoTo openDataSourceError
MainDoc.MailMerge.OpenDataSource tempPath, ConfirmConversions:=False, ReadOnly:=False, LinkToSource:=True,
AddToRecentFiles:=False, Revert:=False, Connection:="Entire Spreadsheet", SubType:=wdMergeSubTypeWord2000
recordCount = 0
On Error GoTo wsError
Set ws = DataSource.WorkSheets(1)
Dim r As Integer
Dim c As Integer
' Work out how many rows we have to process
For r = 2 To ws.UsedRange.Rows.Count
rowEmpty = True
For c = 1 To ws.UsedRange.Columns.Count
If Not IsEmpty(ws.Cells(r, c)) Then
rowEmpty = False
Exit For
End If
Next c
If rowEmpty Then
Exit For
End If
recordCount = recordCount + 1
Next r
GoTo DoMerge
wsError:
GoTo CloseMerge
DoMerge:
ActualCount = 0
If (recordCount = 0) Then
OutputDebugString "PSLWordDV: ProcessForSharePoint: No records to process"
GoTo CloseMerge
End If
On Error GoTo mergeError
For i = 1 To recordCount
' .Destination 0 = DOCUMENT, 1 = PRINTER
MainDoc.MailMerge.Destination = 0 'wdSendToNewDocument
MainDoc.MailMerge.SuppressBlankLines = True
With MainDoc.MailMerge.DataSource
.FirstRecord = i 'wdDefaultFirstRecord
.LastRecord = i 'wdDefaultLastRecord
.ActiveRecord = i
End With
MainDoc.MailMerge.Execute Pause:=False
Populate MainDoc, ActiveDocument
ActualCount = ActualCount + 1
Next i
GoTo CloseMerge
When I call this function, my xls files gets open and populate data (I want data from Sheet 1 only). Then my WORD opens (OpenDataSource) and on selection of "Yes" for population on Word --> My code fails and catches the error "462".
On further analysis (not sure correct or not), it seems, there is a problem in :
Set ws = DataSource.WorkSheets(1)
NOTE: If I hard code my recordCount variable to 1 (say) -> merging process works absolutely fine.
Can anyone please help on priority to sort my client issue please.

Fill array formula for different cells by entering formula in one cell

I am now trying to achieve something like the query function in Google Sheets. Obviously in this GIF, someone has already done that. I wonder how they could do that in Excel / VBA.
My specific question is: in VBA, how to fill other cells' formulas by entering a formula in a specific cell? (replicate the function used in this GIF and not using VBA + advanced filter)
Enter a formula in cell A3
Press CTRL + SHIFT + ENTER
Receive results
This is what I got so far:
The code in a standard module:
Sub run_sql_sub(sql)
On Error Resume Next
Set cn = CreateObject("ADODB.Connection")
Set rs = CreateObject("ADODB.Recordset")
With cn
.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & _
This Workbook.FullName _
& ";Extended Properties=""Excel 12.0 Xml;HDR=YES;IMEX=1"";"
.Open
End With
rs.Open sql, cn
Application.ScreenUpdating = False
ActiveSheet.Range("A1:XFD1048576").ClearContents
For intColIndex = 0 To rs.Fields.Count - 1
Range("A1").Offset(0, intColIndex).Value = rs.Fields(intColIndex).Name
Next
Range("A2").CopyFromRecordset rs
Application.ScreenUpdating = True
rs.Close: cn.Close: Set rs = Nothing: Set cn = Nothing
End Sub
And this code is in activesheet's module:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim KeyCells As Range
Set KeyCells = ActiveSheet.Range("A1")
If Not Application.Intersect(KeyCells, Range(Target.Address)) _
Is Nothing Then
If InStr(KeyCells.Value2, "mi_sql") > 0 Then
sql = Right(KeyCells.Value2, Len(KeyCells.Value2) - Len("mi_sql "))
run_sql_sub sql
End If
End If
End Sub
Update 08.04.2019: found a solution
' Code in standard Module
Public collectCal As Collection
Private ccal As CallerCal
Sub subResizeKQ(caller As CallerInfo)
On Error Resume Next
Application.EnableEvents = False
If caller.Id <> "" Then
Application.Range(caller.Id).ClearContents
Application.Range(caller.Id).Resize(caller.rows, caller.cols).FormulaArray = caller.FomulaText
End If
Application.EnableEvents = True
End Sub
Function ResizeKQ(value As Variant) As Variant
If ccal Is Nothing Then Set ccal = New CallerCal
If collectCal Is Nothing Then Set collectCal = New Collection
Dim caller As New CallerInfo
Dim rows As Long, cols As Long
Dim arr As Variant
arr = value
rows = UBound(arr, 1) - LBound(arr, 1) + 1
cols = UBound(arr, 2) - LBound(arr, 2) + 1
Dim rgcaller As Range
Set rgcaller = Application.caller
caller.Id = rgcaller.Address(True, True, xlA1, True, True)
caller.rows = rgcaller.rows.Count
caller.cols = rgcaller.Columns.Count
caller.FomulaText = rgcaller.Resize(1, 1).Formula
If caller.rows <> rows Or caller.cols <> cols Then
caller.rows = rows
caller.cols = cols
collectCal.Add caller, caller.Id
End If
ResizeKQ = arr
End Function
Function fRandArray(numRow As Long, numCol As Long) As Variant
Application.Volatile True
ReDim arr(1 To numRow, 1 To numCol)
For i = 1 To numRow
For j = 1 To numCol
arr(i, j) = Rnd
Next
Next
fRandArray = ResizeKQ(arr)
End Function
'--------------------------------------------------------------------------
' code in Class Module name CallerCal
Private WithEvents AppEx As Application
Private Sub AppEx_SheetCalculate(ByVal Sh As Object)
Dim caller As CallerInfo
If collectCal Is Nothing Then Exit Sub
For Each caller In collectCal
subResizeKQ caller
collectCal.Remove caller.Id
Set caller = Nothing
Next
Set collectCal = Nothing
End Sub
Private Sub Class_Initialize()
Set AppEx = Application
End Sub
Private Sub Class_Terminate()
Set AppEx = Nothing
End Sub
'--------------------------------------------------------------------------
' code in Class Module name CallerInfo
Public rows As Long
Public cols As Long
Public Id As String
Public FomulaText As String
To test it, go to Excel Sheet, enter the following test formula in A1:
=fRandArray(10,10)
P.S: If anyone is using Excel 365 Insider Program, Microsoft has published this kind of formula called Dynamic Array Function:
https://support.office.com/en-ie/article/dynamic-arrays-and-spilled-array-behavior-205c6b06-03ba-4151-89a1-87a7eb36e531
I agree with the other comments - MS doesn't seem to provide a way to do this natively, and any way of doing it directly would probably involve some Excel-breaking memory manipulation.
However...
I suggest taking your method one step further and generalizing it
Copy and paste this class into a text file, then import it into VBA (which allows Attribute VB_PreDeclaredID = True and Attribute Item.VB_UserMemId = 0):
RangeEdit
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "RangeEdit"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Private colRanges As Collection
Private colValues As Collection
Private Sub Class_Initialize()
Set colRanges = New Collection
Set colValues = New Collection
End Sub
Public Property Let Item(rng_or_address As Variant, value As Variant)
Attribute Item.VB_UserMemId = 0
colRanges.Add rng_or_address
colValues.Add value
End Property
Public Sub flush(sh As Worksheet)
Application.EnableEvents = False
While colRanges.Count > 0
If TypeName(colRanges(1)) = "Range" Then
colRanges(1).value = colValues(1)
ElseIf TypeName(colRanges(1)) = "String" Then
sh.Range(colRanges(1)).value = colValues(1)
End If
colRanges.Remove 1
colValues.Remove 1
Wend
Application.EnableEvents = True
End Sub
Make your Workbook_SheetChange method the following:
Private Sub Workbook_SheetChange(ByVal sh As Object, ByVal Target As Range)
RangeEdit.flush sh
End Sub
Now you can create a UDF that modifies other cells. The way it works is it queues up all the modifications you make and only runs them after the cell loses focus. The syntax allows you to treat it almost like the regular Range function. You can run it either with an address string or with an actual range (though you might want to add an error if it's not either one of those).
Here is a quick example UDF that can be run from an Excel cell formula:
Public Function MyUDF()
RangeEdit("A1") = 4
RangeEdit("B1") = 6
RangeEdit("C4") = "Hello everyone!"
Dim r As Range
Set r = Range("B12")
RangeEdit(r) = "This is a test of using a range variable"
End Function
For your specific case, I would replace
For intColIndex = 0 To rs.Fields.Count - 1
Range("A1").Offset(0, intColIndex).Value = rs.Fields(intColIndex).Name
Next
with
For intColIndex = 0 To rs.Fields.Count - 1
RangeEdit(Range("A1").Offset(0, intColIndex)) = rs.Fields(intColIndex).Name
Next
And to copy the recordset I would define the following function (it assumes that the recordset cursor is set to the first record. if you Move it previously you might want to have rs.MoveFirst in there):
Public Sub FormulaSafeRecordsetCopy(rs As ADODB.Recordset, rng As Range)
Dim intColIndex As Long
Dim intRowIndex As Long
While Not rs.EOF
For intColIndex = 0 To rs.Fields.Count - 1
RangeEdit(rng.Offset(intRowIndex, intColIndex)) = rs.Fields(intColIndex).value
Next
rs.MoveNext
intRowIndex = intRowIndex + 1
Wend
End Sub

Book list - getting book details from amazon using Excel VBA barcode lookups

I have a barcode reader and bunch of books. For each of the books, I want to list the book name and the author in an Excel spreadsheet.
My view is that some VBA code connecting to an Amazon web service would make this easier.
My questions is - hasn't anyone done this before? Could you point me to the best example.
I thought it was an easy one googling, but turned out more difficult than I expected.
In fact, I was unable to find a VBA ISBN based program to get book data from the web, so decided to do one.
Here is a VBA macro using the services from xisbn.worldcat.org. Examples here.. The services are free and don't need authentication.
To be able to run it you should check at Tools-> References (in the VBE window) the "Microsoft xml 6.0" library.
This macro takes the ISBN (10 digits) from the current cell and fills the following two columns with the author and title. You should be able to loop through a full column easily.
The code has been tested (well, a bit) but there is no error checking in there.
Sub xmlbook()
Dim xmlDoc As DOMDocument60
Dim xWords As IXMLDOMNode
Dim xType As IXMLDOMNode
Dim xword As IXMLDOMNodeList
Dim xWordChild As IXMLDOMNode
Dim oAttributes As IXMLDOMNamedNodeMap
Dim oTitle As IXMLDOMNode
Dim oAuthor As IXMLDOMNode
Set xmlDoc = New DOMDocument60
Set xWords = New DOMDocument60
xmlDoc.async = False
xmlDoc.validateOnParse = False
r = CStr(ActiveCell.Value)
xmlDoc.Load ("http://xisbn.worldcat.org/webservices/xid/isbn/" _
+ r + "?method=getMetadata&format=xml&fl=author,title")
Set xWords = xmlDoc
For Each xType In xWords.ChildNodes
Set xword = xType.ChildNodes
For Each xWordChild In xword
Set oAttributes = xWordChild.Attributes
On Error Resume Next
Set oTitle = oAttributes.getNamedItem("title")
Set oAuthor = oAttributes.getNamedItem("author")
On Error GoTo 0
Next xWordChild
Next xType
ActiveCell.Offset(0, 1).Value = oTitle.Text
ActiveCell.Offset(0, 2).Value = oAuthor.Text
End Sub
I did not go through Amazon because of their new "straightforward" authentication protocol ...
This is has been enormously helpful for me!
I have updated the macro to allow it to cycle through a column of ISBN numbers until it reaches an empty cell.
It also search for publisher, year and edition.
I have added some basic error checking if certain fields are not available.
Sub ISBN()
Do
Dim xmlDoc As DOMDocument60
Dim xWords As IXMLDOMNode
Dim xType As IXMLDOMNode
Dim xword As IXMLDOMNodeList
Dim xWordChild As IXMLDOMNode
Dim oAttributes As IXMLDOMNamedNodeMap
Dim oTitle As IXMLDOMNode
Dim oAuthor As IXMLDOMNode
Set xmlDoc = New DOMDocument60
Set xWords = New DOMDocument60
xmlDoc.async = False
xmlDoc.validateOnParse = False
r = CStr(ActiveCell.Value)
xmlDoc.Load ("http://xisbn.worldcat.org/webservices/xid/isbn/" _
+ r + "?method=getMetadata&format=xml&fl=author,title,year,publisher,ed")
Set xWords = xmlDoc
For Each xType In xWords.ChildNodes
Set xword = xType.ChildNodes
For Each xWordChild In xword
Set oAttributes = xWordChild.Attributes
On Error Resume Next
Set oTitle = oAttributes.getNamedItem("title")
Set oAuthor = oAttributes.getNamedItem("author")
Set oPublisher = oAttributes.getNamedItem("publisher")
Set oEd = oAttributes.getNamedItem("ed")
Set oYear = oAttributes.getNamedItem("year")
On Error GoTo 0
Next xWordChild
Next xType
On Error Resume Next
ActiveCell.Offset(0, 1).Value = oTitle.Text
On Error Resume Next
ActiveCell.Offset(0, 2).Value = oAuthor.Text
On Error Resume Next
ActiveCell.Offset(0, 3).Value = oPublisher.Text
On Error Resume Next
ActiveCell.Offset(0, 4).Value = oYear.Text
On Error Resume Next
ActiveCell.Offset(0, 5).Value = oEd.Text
ActiveCell.Offset(1, 0).Select
Loop Until IsEmpty(ActiveCell.Value)
End Sub
I just found this thread as I was attempting to do the same thing. Unfortunately I'm on a MAC, so these answers don't help. With a bit of research I was able to do get it to work in MAC Excel with this module:
Option Explicit
' execShell() function courtesy of Robert Knight via StackOverflow
' http://stackoverflow.com/questions/6136798/vba-shell-function-in-office- 2011-for-mac
Private Declare Function popen Lib "libc.dylib" (ByVal command As String, ByVal mode As String) As Long
Private Declare Function pclose Lib "libc.dylib" (ByVal file As Long) As Long
Private Declare Function fread Lib "libc.dylib" (ByVal outStr As String, ByVal size As Long, ByVal items As Long, ByVal stream As Long) As Long
Private Declare Function feof Lib "libc.dylib" (ByVal file As Long) As Long
Function execShell(command As String, Optional ByRef exitCode As Long) As String
Dim file As Long
file = popen(command, "r")
If file = 0 Then
Exit Function
End If
While feof(file) = 0
Dim chunk As String
Dim read As Long
chunk = Space(50)
read = fread(chunk, 1, Len(chunk) - 1, file)
If read > 0 Then
chunk = Left$(chunk, read)
execShell = execShell & chunk
End If
Wend
exitCode = pclose(file)
End Function
Function HTTPGet(sUrl As String) As String
Dim sCmd As String
Dim sResult As String
Dim lExitCode As Long
Dim sQuery As String
sQuery = "method=getMetadata&format=xml&fl=*"
sCmd = "curl --get -d """ & sQuery & """" & " " & sUrl
sCmd = "curl --get -d """ & sQuery & """" & " " & sUrl
sResult = execShell(sCmd, lExitCode)
' ToDo check lExitCode
HTTPGet = sResult
End Function
Function getISBNData(isbn As String) As String
Dim sUrl As String
sUrl = "http://xisbn.worldcat.org/webservices/xid/isbn/" & isbn
getISBNData = HTTPGet(sUrl)
End Function
Function getAttributeForISBN(isbn As String, info As String) As String
Dim data As String
Dim start As Integer
Dim finish As Integer
data = getISBNData(isbn)
start = InStr(data, info) + Len(info) + 2
finish = InStr(start, data, """")
getAttributeForISBN = Mid(data, start, finish - start)
End Function
This is not all my original work, I pasted it together from another site, then did my own work. Now you can do things like:
getAttributeForISBN("1568812019","title")
This will return the title of that book. Of course you can apply this formula to all of the ISBNs in column A to look up multiple titles, or authors, or whatever.
Hopefully this helps someone else out there!
If the barcode is the ISBN, which seems likely, perhaps you can use: amazon.com/Advanced-Search-Books/b?ie=UTF8&node=241582011