getting vales from different file locations contained in cells Excel - vba

So I'm trying to create a tracking file in Excel and in cell M5, I have a file location which links to a supporting document and as such, contains some duplicated information so I want to try and pull certain fields (B8 in this example) from the external files referenced in M5.
I have tried =([M5]Sheet1!B8) which works to a degree but this brings up a dialogue box and I have to select the file location manually which is too manual for the purpose.
I have also looked into using the INDIRECT function but can't guarantee that both files will be open at the same time, so would prefer another option if possible.
Any suggestions would be hugely appreciated!!

Here is formula you can use to get individual cell values from a closed workbook.
It is a User Defined Function (UDF) and you call it like this:
=GetClosedCell(A3,B3,C3)
The 1st parameter is the workbook path and name.
The 2nd parameter is the worksheet name.
The 3rd parameter is the address of the cell.
Place this function in a standard code module:
Public Function GetClosedCell(ByVal FileSpec$, ByVal SheetName$, ByVal RangeAddress$)
Const CNX = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=[];Extended Properties=""Excel 12.0;imex=1;hdr=no;"""
Const QRY = "SELECT * FROM [.$|:|]"
On Error GoTo errorh
RangeAddress = Range(RangeAddress)(1, 1).Address(0, 0)
With CreateObject("adodb.recordset")
.Open Replace(Replace(QRY, "|", RangeAddress), ".", SheetName), Replace(CNX, "[]", FileSpec)
GetClosedCell = .Fields(0)
End With
Exit Function
errorh:
GetClosedCell = "ERROR: " & Err & " " & Err.Description
End Function
Here is how it would look on a worksheet:

It is possible to grab data from an external file without opening it. For Excel to get a data item, it needs to know:
the file location
the name of the file within that location
the name of the worksheet within that file
the address of the cell within that worksheet
We enter the required info into some cell, say cell C3 in a very specific format:
'C:\TestFolder\[ABC.xls]xxx'!R9C2
note the single quotes!
then running this short macro:
Public Sub GrabData()
Dim r1 As Range, r2 As Range
Set r1 = Range("C3")
Set r2 = Range("C4")
r2.Value = ExecuteExcel4Macro(r1.Value)
End Sub
will retrieve the data and place it in cell C4
Macros are very easy to install and use:
ALT-F11 brings up the VBE window
ALT-I
ALT-M opens a fresh module
paste the stuff in and close the VBE window
If you save the workbook, the macro will be saved with it.
If you are using a version of Excel later then 2003, you must save
the file as .xlsm rather than .xlsx
To remove the macro:
bring up the VBE window as above
clear the code out
close the VBE window
To use the macro from Excel:
ALT-F8
Select the macro
Touch RUN
To learn more about macros in general, see:
http://www.mvps.org/dmcritchie/excel/getstarted.htm
and
http://msdn.microsoft.com/en-us/library/ee814735(v=office.14).aspx
Macros must be enabled for this to work!

Related

How to open and activate another workbook in VBA?

I'm creating a macro where I will need to run it in 1 file (called "Masterfile"), it will open and execute the macro on another file ("SurveyReport") and then give me a message box saying "done!".
The code I have to execute on the SurveyReport file works fine when I open that file manually and execute it. The code I need to open SurveyReport from MasterFile is also working it seems, I ran the below with no issues:
Sub PivotTable()
'
' PivotTable Macro
Dim MasterFile As String
MasterFile = ActiveWorkbook.Name
Dim SurveyReport As String
SurveyReport = Application.GetOpenFilename("Excel files (*.xlsx), *xlsx", 1, "Please select the Survey Create Report file", , False)
Workbooks.Open (SurveyReport)
End Sub
But, when I try to activate the SurveyReport file so I can begin executing the macro in it, I get a "Subscript out of range" error. I've tried using the following code after the above block and before the code to execute in the SurveyReport file:
Windows(SurveyReport).Activate
This didn't work, not did:
ThisWorkbook.Activate
...which only had the effect of activating the MasterFile.
SurveyReport file is a .xlsx file. I tried saving it as a .xls file and amending the code, but no joy.
I also tried passing it the file name directly (i.e. Windows("filename.xlsx").Activate), same issue.
ActiveWorkbook is as it says on the tin - whichever workbook happens to be active when the code runs.
ThisWorkbook is always the workbook that the code is sitting in.
You can SET references to specific workbooks rather than just using their names each time. A name can change, or reference the wrong object.... imagine you have a friend called Darren. Each time you mention him you mention him by name. Someone that doesn't know Darren hasn't a clue which Darren out of all the ones available in the world you're talking about. Now imagine you have a little replica of Darren in your pocket... nah, that's a terrible anology - it wouldn't be a replica, it would be a reference to the real Darren... anyway, I digress.
This code sets a reference to the workbook, you can then use that reference any time you want to refer to the correct workbook:
Sub PivotTable()
Dim MasterFile As Workbook
Dim SurveyRptName As String
Dim SurveyReport As Workbook
Set MasterFile = ThisWorkbook '
SurveyRptName = Application.GetOpenFilename("Excel files (*.xlsx), *xlsx", 1, _
"Please select the Survey Create Report file", , False)
If SurveyRptName <> "False" Then
Set SurveyReport = Workbooks.Open(SurveyRptName)
End If
SurveyReport.Activate 'You don't need this line. It doesn't matter if
'the workbook is active, the code knows which one
'you're talking about in the next line.
MsgBox "This is " & SurveyReport.Name & _
" containing " & SurveyReport.Worksheets.Count & " sheets." & vbCr & _
"The value in cell A1 of the first sheet is " & _
SurveyReport.Worksheets(1).Range("A1")
End Sub
Edit: Of course, if you press Cancel when selecting a file then the lines following the IF...THEN code won't have a reference to work on and you'll get a Object Variable or With block variable not set - best not to run the bottom bit of code if you haven't successfully opened the Survey Report file.
The part of the answer that is missing - is that he tried to call a method of an object when his variable was STRING - the open command and subsequent commands will give you an OBJECT - which has properties and methods like .Activate. The STRING type has no such method or property (to be sure - it may have others).
the solution provided by Darren solves this by declaring his SurveyReport as type Workbook - an object of Excel.

Excel VBA macro - Change link name

I have a excel macro routine that I need to prep a file for another routine which is run daily. This prepping involves changes the links of t-1 file to a t0 file.
The code I usually do is:
ActiveWorkbook.ChangeLink Name:= _
"file path", NewName:=new_file_path, Type:=xlExcelLinks
My trouble now is that for this particular routine the file path to be changed to a new routine is not always the same, thus I would need a way to automatize finding out what are the current links to replace them all. The new file path I now because it is the worksheet that is calling this routine and opening this file, so first thing I do Is
new_file_path = "C:\...."& ActiveWorkbook.Name & ".xlsm"
What would help me is if there is a trick to replace all links for a new one, without the need to say the name/path of the old links. Does any one know?
Thanks
To change all the excel links in a workbook try this procedure:
Sub WbThs_ChangeLink_Excel()
Dim wbTrg As Workbook
Dim sLinkNew As String
Dim aLinks As Variant, vLink As Variant
sLinkNew = "##VBA Links Replace - Target 3.xlsb" 'Change as required
Set wbTrg = ThisWorkbook 'Change as required
Rem Set array with all excel links
aLinks = ActiveWorkbook.LinkSources(xlExcelLinks)
Rem Replace each excel Link
If Not IsEmpty(aLinks) Then
For Each vLink In aLinks
wbTrg.ChangeLink Name:=vLink, NewName:=sLinkNew, Type:=xlExcelLinks
Next: End If
End Sub
See the following pages for additional information on the resources used:
Workbook.ChangeLink Method (Excel)
Workbook.LinkSources Method (Excel)
XlLink Enumeration (Excel)

How to get File Name of External File for an Existing Connections - Excel

I import an External text file (.csv) to my excel worksheet named "SourceData".
The connection is called "Data". Every time I refresh my workbook, excel will ask me to choose my source file. My source files same data structure, and they are automatically generated by other database:
REPL_STATS_010314130000.CSV
REPL_STATS_030314060001.CSV
....
My question is:
How can I get the file name of my external csv file that currently imported to my worksheet "SourceData" so when I refresh the connection, This name is displayed in a cell (for example A1) in another sheet named "Summary"
For example: after I click refresh all, choosing file "REPL_STATS_010314130000.CSV" to update my data source, then cell A1 will display "REPL_STATS_010314130000.CSV"
I try to search for a solution a few days already, but I can't get it work.
Can you suggest form VBA code that can get this information?
Thank you in advance!
Thanks to Tim Williams suggestion I write the following code, and put this code in Sheet1(SourceData):
Private Sub Worksheet_Change(ByVal Target As Range)
Dim ConSource As String
ConSource = Worksheets("SourceData").QueryTables("Data").Connection
Worksheets("Summary").Range("A1").Value = "Source: " & Right(ConSource, Len(ConSource) - InStrRev(ConSource, "\"))
ConSource = ""
End Sub

Excel VBA - Code to move or copy worksheets without warnings

I am trying to create a macro that would act the same as right clicking a workbook tab, selecting move or copy, checking the copy option, selecting another open workbook and clicking ok but without the warnings. I found the code to disable warning and I was able to record a macro that does what I want but I don't know how to make it request which open workbook to copy to.
In short how do I make the following code work where WorksheetIWantToCopy is the one the user currently has selected and OpenWorkbookIWantToCopyToo.xlsx is a workbook to be selected by the user out of a list of open workbooks.
Application.DisplayAlerts = False
Sheets("**WorksheetIWantToCopy**").Select
Sheets("**WorksheetIWantToCopy**").Copy Before:=Workbooks( _
"**OpenWorkbookIWantToCopyToo.xlsx**").Sheets(1)
I appreciate any information anyone can provide. My team greatly appreciates your support (we currently have to hit ok on 25 warnings due to conflicts we don't really care about). Thx!
If the worksheet you want to copy will always be the active sheet then you can use ActiveSheet.
As for letting the user select a workbook, it can be as simple as using the InputBox.
Public Function getWorkbookName() As String
Dim i As Integer, sListOfWbks As String, sRsp As String
' build list of workbooks
For i = 1 To Workbooks.Count
sListOfWbks = sWbkList & vbCrLf & i & " - " & Workbooks(i).Name
Next i
sRsp = InputBox("Select workbook." & vbCrLf & sListOfWbks)
If IsNumeric(sRsp) Then
getWorkbookName = Workbooks(CInt(sRsp)).Name
Else
' user pressed cancel or entered invalid text
getWorkbookName = ""
End If
End Function
This basic example will of course list all workbooks, including hidden add-ins and the workbook you are moving away from.
This needs to be said before anything else: always, always, ALWAYS make use of .Copy instead of .Move when automatically shuffling excel workbooks with VBA. Move has inherent risks because it is a modification of the other file, and if your code misbehaves then you could lose all of the data you're working with.
First of all, know which workbook is which, with no ambiguity:
Dim wkbkDestination, wkbkTemporary As Workbook
Set wkbkDestination = Workbooks("OpenWorkbookIWantToCopyTo.xlsx")
Set wkbkTemporary = Workbooks.Open("WorkbookIWantToCopy.xlsx")
Next, Copy your desired tab to your destination workbook, rename the new tab to prevent errors, and close the second workbook, without saving.
wkbkTemporary.Worksheets("WorksheetIWantToCopy").Copy Before:=wkbkDestination.Worksheets(1)
wkbkDestination.Worksheets(1).Name = "WorkbookIWantToCopy"
wkbkTemporary.Close SaveChanges = False
Naturally, depending on the exact controls you intend to use, there are lots of ways this code could be implemented. From your description it is somewhat unclear what exact problem you're trying to solve and whether this is a one-off event you're trying to accomplish for a given list of files, or whether this function is to be used on an ongoing basis.

Cancel External Query in Excel VBA

I have created an Excel Spreadsheet which helps with data analysis from an Oracle database.
The user enters then clicks the "Refresh Query" button which generates a query for Oracle to execute. The query takes a minute or so to complete. Although the VBA code does not hang on ".Refresh", all Excel windows remain frozen until the query completes.
Sub refreshQuery_click()
Dim queryStr as String
' Validate parameters and generate query
' ** Code not included **
'
' Refresh Query
With ActiveWorkbook.Connections("Connection").OLEDBConnection
.CommandText = queryStr
.Refresh
End With
End Sub
Is there a way for the user to manually cancel the query (calling .CancelRefresh) while the Excel user-interface is frozen?
EDIT I don't know if the following is worth noting or regular behavior. While the query is executing, all open Excel windows (including the VBA Editor) become "Not Responding" in Task Manager. Neither pressing Esc nor Ctrl+Break will cancel the script. Also, calling DoEvents (either before or after .Refresh) does not change this behavior.
Here's a method that I know will work. However, there are some complications.
Here's how it's done:
Put the spreadsheet with the data in a separate workbook. This worksheet should execute the refresh query when it's opened and then close once the data is updated.
Create a batch file to call the "Data" Excel file.
Within a different workbook, create a procedure (macro) for the user to call. This procedure will call the batch file, which subsequently calls the Excel file. Since you are calling a batch file and not Excel directly, the Excel procedure will continue because the command shell is released so quickly and opens the other Excel file in a different thread. This allows you to continue working within the main Excel file.
Here are some complications:
I included a method to alert the user that the data has been udpated. There are timing issues where it's possible to try to check if the data has been update when the workbook is not accessible, which forces the user to try to update values. I included a method called my time which pauses the execution of the code so it only checks every so many seconds.
The updated worksheet will pop up in a new window, so the user will need to click on their original worksheet and keep working. You could learn to hide this if you're comfortable with Windows scripting (I haven't learned that yet).
Here are some files and code. Be sure to read the comments in the code for why some things are there.
FILE: C:\DataUpdate.xls
We'll make a workbook called "DataUpdate.xls" and put it in our C:\ folder. In cell A1 of Sheet1, we'll add our QueryTable which grabs external data.
Option Explicit
Sub UpdateTable()
Dim ws As Worksheet
Dim qt As QueryTable
Set ws = Worksheets("Sheet1")
Set qt = ws.Range("A1").QueryTable
qt.Refresh BackgroundQuery:=False
End Sub
Sub OnWorkbookOpen()
Dim wb As Workbook
Set wb = ActiveWorkbook
'I put this If statement in so I can change the file's
'name and then edit the file without code
'running. You may find a better way to do this.
If ActiveWorkbook.Name = "DataUpdate.xls" Then
UpdateTable
'I update a cell in a different sheet once the update is completed.
'I'll check this cell from the "user workbook" to see when the data's been updated.
Sheets("Sheet2").Range("A1").Value = "Update Table Completed " & Now()
wb.Save
Application.Quit
End If
End Sub
In the ThisWorkbook object in Excel, there's a procedure called Workbook_Open(). It should look like the following so it executes the update code when it is opened.
Private Sub Workbook_Open()
OnWorkbookOpen
End Sub
NOTE: I found a bug when this file closed if 1) you accessed the file from the command line or shell and 2) you have the Office Live Add-in installed. If you have the Office Live Add-in installed, it will throw an exception on exit.
FILE: C:\RunExcel.bat
Next, we're going to create a batch file that will open the Excel file we just made. The reason that call the Excel file from within the batch file and not directly from the other Excel file using Shell is because Shell will not continue until the other application closes (at least when using Excel.exe "c:\File.xls"). The batch file, however, runs its code and then immediately closes, thus allowing the original code that called it to continue. This is what will let your uses continue working in Excel.
All this file needs is:
cd "C:\Program Files\Microsoft Office\Office10\"
Excel.exe "C:\DataUpdate.xls"
If you're handy with Windows Scripting, you do fancy things like open the window in a hidden mode or pass a parameter of the file name or Excel location. I kept it simple with a batch file.
FILE: C:\UserWorkbook.xls
This is the file that the user will open to "do their work in." They'll call the code to update the other workbook from within this workbook and they'll still be able to work in this workbook while this one is updating.
You need a cell in this workbook where you'll check the "Update Table Completed" cell from the DataUpdate workbook. I chose cell G1 in Sheet1 for my example.
Add the following code to a VBA module in this workbook:
Option Explicit
Sub UpdateOtherWorkbook()
Dim strFilePath As String
Dim intOpenMode As Integer
Dim strCallPath As String
Dim strCellValue As String
Dim strCellFormula As String
Dim ws As Worksheet
Dim rng As Range
Set ws = Worksheets("Sheet1")
Set rng = ws.Range("G1")
strCellFormula = "='C:\[DataUpdate.xls]Sheet2'!A1"
'This makes sure the formula has the most recent "Updated" value
'from the data file.
rng.Formula = strCellFormula
strFilePath = "C:\RunExcel.bat"
intOpenMode = vbHide
'This will call the batch file that calls the Excel file.
'Since the batch file executes it's code and then closes,
'the Excel file will be able to keep running.
Shell strFilePath, intOpenMode
'This method, defined below, will alert the user with a
'message box once the update is complete. We know that
'the update is complete because the "Updated" value will
'have changed in the Data workbook.
AlertWhenChanged
End Sub
'
Sub AlertWhenChanged()
Dim strCellValue As String
Dim strUpdatedCellValue As String
Dim strCellFormula As String
Dim ws As Worksheet
Dim rng As Range
Set ws = Worksheets("Sheet1")
Set rng = ws.Range("G1")
strCellFormula = "='C:\[DataUpdate.xls]Sheet2'!A1"
strCellValue = rng.Value
strUpdatedCellValue = strCellValue
'This will check every 4 seconds to see if the Update value of the
'Data workbook has been changed. MyWait is included to make sure
'we don't try to access the Data file while it is inaccessible.
'During this entire process, the user is still able to work.
Do While strCellValue = strUpdatedCellValue
MyWait 2
rng.Formula = strCellFormula
MyWait 2
strUpdatedCellValue = rng.Value
DoEvents
Loop
MsgBox "Data Has Been Updated!"
End Sub
'
Sub MyWait(lngSeconds As Long)
Dim dtmNewTime As Date
dtmNewTime = DateAdd("s", lngSeconds, Now)
Do While Now < dtmNewTime
DoEvents
Loop
End Sub
As you can see, I constantly updated the formula in the "Listening Cell" to see when the other cell was updated. Once the data workbook has been updated, I'm not sure how you'd force an update in code without rewriting all the cells. Closing the workbook and reopening it should refresh the values, but I'm not sure of the best way to do it in code.
This whole process works because you're using a batch file to call Excel into a different thread from the original file. This allows you to work in the original file and still be alerted when the other file has been updated.
Good luck!
EDIT: Rather than include a more complete answer in this same answer, I've created a separate answer dedicated entirely to that solution. Check it out below (or above if it gets voted up)
Your users can break the VBA function by pressing Ctrl+Break on the keyboard. However, I've found that this can cause your functions to randomly break until each time any function is run. It goes away when the computer is restarted.
If you open this file in a new instance of Excel (meaning, go to Start > Programs and open Excel from there), I think that the only workbook that will be frozen will be the one executing the code. Other intances of Excel shouldn't be affected.
Lastly, you might research the DoEvents functions, which yields execution back to the Operating System so that it can process other events. I'm not sure if it would work in your case, but you could look into it. That way you can do other things while the process is being completed (It's kind of dangerous because the user can then change the state of your application while the process is working).
I believe I know a way that actually will work, but it's complicated and I don't have the code in front of me. It involves creating a separate instance of the Excel application in code and attaching a handler to the execution of that instance. You include the DoEvents part of the code in a loop that releases once the application closes. The other instantiated Excel application has the sole purpose of opening a file to execute a script and then close itself. I've done something like this before so I know that it works. I'll see if I can find the code tomorrow and add it.
Well, you could consider the old-fashion way -- split the query into smaller batches and use Do Events in between batches.
You could try XLLoop. This lets you write excel functions (UDfs) on an external server. It includes server implementations in many languages (eg. Java, Ruby, Python, PHP).
You could then connect to your oracle database (and potentially add a caching layer) and serve up the data to your spreadsheet that way.
The XLL also has a feature to popup a "busy" GUI that lets the user cancel the function call (which disconnects from the server).
BTW, I work on the project so let me know if you have any questions.