Indirecet on Closed files

Omer_K

Board Regular
Joined
Apr 9, 2017
Messages
124
Office Version
  1. 365
Hey everyone,
In Google searches there seem to be a number of solutions but I didn't find anything creative ..
I have a file with an indirect function linked to different files,
When the external file is open everything works perfectly but when the external files are closed (there are more than one external file) the INDIRECT function returns a ref error.
anything can help :)

Omer.
 

Excel Facts

Test for Multiple Conditions in IF?
Use AND(test, test, test, test) or OR(test, test, test, ...) as the logical_test argument of IF.
Make one that auto opens needed child files from connections list??
 
Upvote 0
Try using Harlan Grove's Pull function or the IndirectEx by Wilson.So (you need the GetNames Sub with IndirectEx)

VBA Code:
Function pull(xref As String) As Variant
    'inspired by Bob Phillips and Laurent Longre
    'but written by Harlan Grove
    '-----------------------------------------------------------------
    'Copyright (c) 2003 Harlan Grove.
    '
    'This code is free software; you can redistribute it and/or modify
    'it under the terms of the GNU General Public License as published
    'by the Free Software Foundation; either version 2 of the License,
    'or (at your option) any later version.
    '-----------------------------------------------------------------
    '2004-05-28
    'fixed the previous fix - replaced all instances of 'expr' with 'xref'
    'also now checking for initial single quote in xref, and if found
    'advancing past it to get the full pathname [dumb, really dumb!]
    '-----------------------------------------------------------------
    '2004-03-25
    'revised to check if filename in xref exists - if it does, proceed;
    'otherwise, return a #REF! error immediately - this avoids Excel
    'displaying dialogs when the referenced file doesn't exist
    '-----------------------------------------------------------------

    Dim xlapp As Object, xlwb As Workbook
    Dim b As String, r As Range, C As Range, n As Long

    '** begin 2004-05-28 changes **
    '** begin 2004-03-25 changes **
    n = InStrRev(Len(xref), xref, "\")

    If n > 0 Then
        If Mid(xref, n, 2) = "\[" Then
            b = Left(xref, n)
            n = InStr(n + 2, xref, "]") - n - 2
            If n > 0 Then b = b & Mid(xref, Len(b) + 2, n)

        Else
            n = InStrRev(Len(xref), xref, "!")
            If n > 0 Then b = Left(xref, n - 1)

        End If

        '** key 2004-05-28 addition **
        If Left(b, 1) = "'" Then b = Mid(b, 2)

        On Error Resume Next
        If n > 0 Then If Dir(b) = "" Then n = 0
        Err.Clear
        On Error GoTo 0

    End If

    If n <= 0 Then
        pull = CVErr(xlErrRef)
        Exit Function
    End If
    '** end 2004-03-25 changes **
    '** end 2004-05-28 changes **

    pull = Evaluate(xref)

    If CStr(pull) = CStr(CVErr(xlErrRef)) Then
        On Error GoTo CleanUp                    'immediate clean-up at this point

        Set xlapp = CreateObject("Excel.Application")
        Set xlwb = xlapp.Workbooks.Add           'needed by .ExecuteExcel4Macro

        On Error Resume Next                     'now clean-up can wait

        n = InStr(InStr(1, xref, "]") + 1, xref, "!")
        b = Mid(xref, 1, n)

        Set r = xlwb.Sheets(1).Range(Mid(xref, n + 1))

        If r Is Nothing Then
            pull = xlapp.ExecuteExcel4Macro(xref)

        Else
            For Each C In r
                C.Value = xlapp.ExecuteExcel4Macro(b & C.Address(1, 1, xlR1C1))
            Next C

            pull = r.Value

        End If

CleanUp:
        If Not xlwb Is Nothing Then xlwb.Close 0
        If Not xlapp Is Nothing Then xlapp.Quit
        Set xlapp = Nothing

    End If

End Function

'------------------------------------
'Extended INDIRECT Function v1.0
'------------------------------------
'Copyright (c) 2009 Wilson So.
'E-mail: shwskm@yahoo.com.hk
'------------------------------------
'Credits:
'- Designed and written by Wilson So.
'- The 'CreateObject("Excel.Application")' trick was inspired by Harlan Grove's PULL function source code.
'------------------------------------
'This is an open source. You can freely redistribute and modify it, but please kindly give credit to the contributers.
'Please also kindly report any bugs/suggestions through e-mail or in the forums where I posted it.
'------------------------------------
'How to use:
'- Basically same as INDIRECT() in Excel - the same concept for the ref_text parameter.
'- To update the static memory for a particular reference,
'  type TRUE in the second parameter (just one of the IndirectEx() containing that reference)
'  and calculate it once.
'------------------------------------
'Features:
'- You can refer to the closed workbook data.
'- The retrieved closed workbook data will be stored in the static memory,
'  so in the next time, the closed workbook will not be opened again for fast retrieve.
'- A range instead of an array will be returned if the path is omitted in the ref_text,
'  so it still works fine if the user refers to an enormous array, e.g. "Sheet1!1:65536".
'- You can use it inside INDEX(), VLOOKUP(), MATCH() etc.
'- You can use it with OFFSET(), but only for opened workbook data.
'- The procedure will not blindly retrieve all the data as requested;
'  it will not retrieve data beyond the "Ctrl + End" cell, in order to keep the memory as small as possible.
'- #NUM! will be returned in case of lack of memory.
'- #REF! will be returned in case of a wrong path.
'- #VALUE! will be returned in case of other errors.
'------------------------------------
'Known issues:
'- Due to the use of SpecialCells(), #VALUE! will be returned if the worksheet for a closed workbook is protected.
'------------------------------------

Function IndirectEx(ref_text As String, Optional refresh_memory As Boolean = False) As Variant
    On Error GoTo ClearObject

    Dim RefName As String
    Dim SheetName As String
    Dim WBName As String
    Dim FolderName As String
    
    Dim vExcel As Object
    Dim vWB As Workbook
    
    Static dbOutput() As Variant
    Static dbKey() As String
    Static dbTotalOutput As Integer
    Dim dbIndex As Integer
    
    Dim UserEndRow As Long, UserEndCol As Integer
    Dim RealEndRow As Long, RealEndCol As Integer
    Dim EndRow As Long, EndCol As Integer
    Dim RangeHeight As Long, RangeWidth As Integer
    
    GetNames ref_text, RefName, SheetName, WBName, FolderName
    
    If dbTotalOutput = 0 Then
        ReDim dbOutput(1 To 1) As Variant
        ReDim dbKey(1 To 1) As String
    End If
    
    For i = 1 To dbTotalOutput
        If dbKey(i) = FolderName & WBName & "!" & SheetName & "!" & RefName Then
            dbIndex = i
        End If
    Next
    
    If dbIndex = 0 Or refresh_memory Then
        If dbIndex = 0 Then
            dbTotalOutput = dbTotalOutput + 1
            dbIndex = dbTotalOutput
            ReDim Preserve dbOutput(1 To dbTotalOutput) As Variant
            ReDim Preserve dbKey(1 To dbTotalOutput) As String
            dbKey(dbIndex) = FolderName & WBName & "!" & SheetName & "!" & RefName
        End If
        If FolderName = "" Then
            Set dbOutput(dbIndex) = Workbooks(WBName).Worksheets(SheetName).Range(RefName)
        ElseIf Dir(FolderName & WBName) <> "" Then
            Set vExcel = CreateObject("Excel.Application")
            Set vWB = vExcel.Workbooks.Open(FolderName & WBName)
            With vWB.Sheets(SheetName)
                On Error GoTo ClearObject
                UserEndRow = .Range(RefName).Row + .Range(RefName).Rows.Count - 1
                UserEndCol = .Range(RefName).Column + .Range(RefName).Columns.Count - 1
                RealEndRow = .Range("A1").SpecialCells(xlCellTypeLastCell).Row
                RealEndCol = .Range("A1").SpecialCells(xlCellTypeLastCell).Column
                EndRow = IIf(UserEndRow < RealEndRow, UserEndRow, RealEndRow)
                EndCol = IIf(UserEndCol < RealEndCol, UserEndCol, RealEndCol)
                RangeHeight = EndRow - .Range(RefName).Row + 1
                RangeWidth = EndCol - .Range(RefName).Column + 1
                On Error Resume Next
                dbOutput(dbIndex) = .Range(RefName).Resize(RangeHeight, RangeWidth).Value
                If Err.Number <> 0 Then
                    IndirectEx = CVErr(xlErrNum)
                    GoTo ClearObject
                End If
            End With
            On Error GoTo ClearObject
            vWB.Close False
            vExcel.Quit
            Set vExcel = Nothing
        Else
            IndirectEx = CVErr(xlErrRef)
            Exit Function
        End If
    End If
    
    If TypeOf dbOutput(dbIndex) Is Range Then
        Set IndirectEx = dbOutput(dbIndex)
    Else
        IndirectEx = dbOutput(dbIndex)
    End If

    Exit Function
    
ClearObject:
    On Error Resume Next
    If Not (vExcel Is Nothing) Then
        vWB.Close False
        vExcel.Quit
        Set vExcel = Nothing
    End If
End Function
Private Sub GetNames(ByVal ref_text As String, ByRef RefName As String, ByRef SheetName As String, ByRef WBName As String, ByRef FolderName As String)
    Dim P_e As Integer
    Dim P_b1 As Integer
    Dim P_b2 As Integer
    Dim P_s As Integer
    
    P_e = InStr(1, ref_text, "!")
    P_b1 = InStr(1, ref_text, "[")
    P_b2 = InStr(1, ref_text, "]")
    P_s = InStr(1, ref_text, ":\")
    
    If P_e = 0 Then
        RefName = ref_text
    Else
        RefName = Right$(ref_text, Len(ref_text) - P_e)
    End If
    RefName = Replace$(RefName, "$", "")
    
    If P_e = 0 Then
        SheetName = Application.Caller.Parent.Name
    ElseIf P_b1 = 0 Then
        SheetName = Left$(ref_text, P_e - 1)
    Else
        SheetName = Mid$(ref_text, P_b2 + 1, P_e - P_b2 - 1)
    End If
    SheetName = Replace$(SheetName, "'", "")
    
    If P_b1 = 0 Then
        WBName = Application.Caller.Parent.Parent.Name
    Else
        WBName = Mid$(ref_text, P_b1 + 1, P_b2 - P_b1 - 1)
    End If
    
    If P_s = 0 Then
        FolderName = ""
    Else
        FolderName = Left$(ref_text, P_b1 - 1)
    End If
    If Left$(FolderName, 1) = "'" Then FolderName = Right$(FolderName, Len(FolderName) - 1)
End Sub
 
Upvote 0
Try using Harlan Grove's Pull function or the IndirectEx by Wilson.So (you need the GetNames Sub with IndirectEx)

VBA Code:
Function pull(xref As String) As Variant
    'inspired by Bob Phillips and Laurent Longre
    'but written by Harlan Grove
    '-----------------------------------------------------------------
    'Copyright (c) 2003 Harlan Grove.
    '
    'This code is free software; you can redistribute it and/or modify
    'it under the terms of the GNU General Public License as published
    'by the Free Software Foundation; either version 2 of the License,
    'or (at your option) any later version.
    '-----------------------------------------------------------------
    '2004-05-28
    'fixed the previous fix - replaced all instances of 'expr' with 'xref'
    'also now checking for initial single quote in xref, and if found
    'advancing past it to get the full pathname [dumb, really dumb!]
    '-----------------------------------------------------------------
    '2004-03-25
    'revised to check if filename in xref exists - if it does, proceed;
    'otherwise, return a #REF! error immediately - this avoids Excel
    'displaying dialogs when the referenced file doesn't exist
    '-----------------------------------------------------------------

    Dim xlapp As Object, xlwb As Workbook
    Dim b As String, r As Range, C As Range, n As Long

    '** begin 2004-05-28 changes **
    '** begin 2004-03-25 changes **
    n = InStrRev(Len(xref), xref, "\")

    If n > 0 Then
        If Mid(xref, n, 2) = "\[" Then
            b = Left(xref, n)
            n = InStr(n + 2, xref, "]") - n - 2
            If n > 0 Then b = b & Mid(xref, Len(b) + 2, n)

        Else
            n = InStrRev(Len(xref), xref, "!")
            If n > 0 Then b = Left(xref, n - 1)

        End If

        '** key 2004-05-28 addition **
        If Left(b, 1) = "'" Then b = Mid(b, 2)

        On Error Resume Next
        If n > 0 Then If Dir(b) = "" Then n = 0
        Err.Clear
        On Error GoTo 0

    End If

    If n <= 0 Then
        pull = CVErr(xlErrRef)
        Exit Function
    End If
    '** end 2004-03-25 changes **
    '** end 2004-05-28 changes **

    pull = Evaluate(xref)

    If CStr(pull) = CStr(CVErr(xlErrRef)) Then
        On Error GoTo CleanUp                    'immediate clean-up at this point

        Set xlapp = CreateObject("Excel.Application")
        Set xlwb = xlapp.Workbooks.Add           'needed by .ExecuteExcel4Macro

        On Error Resume Next                     'now clean-up can wait

        n = InStr(InStr(1, xref, "]") + 1, xref, "!")
        b = Mid(xref, 1, n)

        Set r = xlwb.Sheets(1).Range(Mid(xref, n + 1))

        If r Is Nothing Then
            pull = xlapp.ExecuteExcel4Macro(xref)

        Else
            For Each C In r
                C.Value = xlapp.ExecuteExcel4Macro(b & C.Address(1, 1, xlR1C1))
            Next C

            pull = r.Value

        End If

CleanUp:
        If Not xlwb Is Nothing Then xlwb.Close 0
        If Not xlapp Is Nothing Then xlapp.Quit
        Set xlapp = Nothing

    End If

End Function

'------------------------------------
'Extended INDIRECT Function v1.0
'------------------------------------
'Copyright (c) 2009 Wilson So.
'E-mail: shwskm@yahoo.com.hk
'------------------------------------
'Credits:
'- Designed and written by Wilson So.
'- The 'CreateObject("Excel.Application")' trick was inspired by Harlan Grove's PULL function source code.
'------------------------------------
'This is an open source. You can freely redistribute and modify it, but please kindly give credit to the contributers.
'Please also kindly report any bugs/suggestions through e-mail or in the forums where I posted it.
'------------------------------------
'How to use:
'- Basically same as INDIRECT() in Excel - the same concept for the ref_text parameter.
'- To update the static memory for a particular reference,
'  type TRUE in the second parameter (just one of the IndirectEx() containing that reference)
'  and calculate it once.
'------------------------------------
'Features:
'- You can refer to the closed workbook data.
'- The retrieved closed workbook data will be stored in the static memory,
'  so in the next time, the closed workbook will not be opened again for fast retrieve.
'- A range instead of an array will be returned if the path is omitted in the ref_text,
'  so it still works fine if the user refers to an enormous array, e.g. "Sheet1!1:65536".
'- You can use it inside INDEX(), VLOOKUP(), MATCH() etc.
'- You can use it with OFFSET(), but only for opened workbook data.
'- The procedure will not blindly retrieve all the data as requested;
'  it will not retrieve data beyond the "Ctrl + End" cell, in order to keep the memory as small as possible.
'- #NUM! will be returned in case of lack of memory.
'- #REF! will be returned in case of a wrong path.
'- #VALUE! will be returned in case of other errors.
'------------------------------------
'Known issues:
'- Due to the use of SpecialCells(), #VALUE! will be returned if the worksheet for a closed workbook is protected.
'------------------------------------

Function IndirectEx(ref_text As String, Optional refresh_memory As Boolean = False) As Variant
    On Error GoTo ClearObject

    Dim RefName As String
    Dim SheetName As String
    Dim WBName As String
    Dim FolderName As String
   
    Dim vExcel As Object
    Dim vWB As Workbook
   
    Static dbOutput() As Variant
    Static dbKey() As String
    Static dbTotalOutput As Integer
    Dim dbIndex As Integer
   
    Dim UserEndRow As Long, UserEndCol As Integer
    Dim RealEndRow As Long, RealEndCol As Integer
    Dim EndRow As Long, EndCol As Integer
    Dim RangeHeight As Long, RangeWidth As Integer
   
    GetNames ref_text, RefName, SheetName, WBName, FolderName
   
    If dbTotalOutput = 0 Then
        ReDim dbOutput(1 To 1) As Variant
        ReDim dbKey(1 To 1) As String
    End If
   
    For i = 1 To dbTotalOutput
        If dbKey(i) = FolderName & WBName & "!" & SheetName & "!" & RefName Then
            dbIndex = i
        End If
    Next
   
    If dbIndex = 0 Or refresh_memory Then
        If dbIndex = 0 Then
            dbTotalOutput = dbTotalOutput + 1
            dbIndex = dbTotalOutput
            ReDim Preserve dbOutput(1 To dbTotalOutput) As Variant
            ReDim Preserve dbKey(1 To dbTotalOutput) As String
            dbKey(dbIndex) = FolderName & WBName & "!" & SheetName & "!" & RefName
        End If
        If FolderName = "" Then
            Set dbOutput(dbIndex) = Workbooks(WBName).Worksheets(SheetName).Range(RefName)
        ElseIf Dir(FolderName & WBName) <> "" Then
            Set vExcel = CreateObject("Excel.Application")
            Set vWB = vExcel.Workbooks.Open(FolderName & WBName)
            With vWB.Sheets(SheetName)
                On Error GoTo ClearObject
                UserEndRow = .Range(RefName).Row + .Range(RefName).Rows.Count - 1
                UserEndCol = .Range(RefName).Column + .Range(RefName).Columns.Count - 1
                RealEndRow = .Range("A1").SpecialCells(xlCellTypeLastCell).Row
                RealEndCol = .Range("A1").SpecialCells(xlCellTypeLastCell).Column
                EndRow = IIf(UserEndRow < RealEndRow, UserEndRow, RealEndRow)
                EndCol = IIf(UserEndCol < RealEndCol, UserEndCol, RealEndCol)
                RangeHeight = EndRow - .Range(RefName).Row + 1
                RangeWidth = EndCol - .Range(RefName).Column + 1
                On Error Resume Next
                dbOutput(dbIndex) = .Range(RefName).Resize(RangeHeight, RangeWidth).Value
                If Err.Number <> 0 Then
                    IndirectEx = CVErr(xlErrNum)
                    GoTo ClearObject
                End If
            End With
            On Error GoTo ClearObject
            vWB.Close False
            vExcel.Quit
            Set vExcel = Nothing
        Else
            IndirectEx = CVErr(xlErrRef)
            Exit Function
        End If
    End If
   
    If TypeOf dbOutput(dbIndex) Is Range Then
        Set IndirectEx = dbOutput(dbIndex)
    Else
        IndirectEx = dbOutput(dbIndex)
    End If

    Exit Function
   
ClearObject:
    On Error Resume Next
    If Not (vExcel Is Nothing) Then
        vWB.Close False
        vExcel.Quit
        Set vExcel = Nothing
    End If
End Function
Private Sub GetNames(ByVal ref_text As String, ByRef RefName As String, ByRef SheetName As String, ByRef WBName As String, ByRef FolderName As String)
    Dim P_e As Integer
    Dim P_b1 As Integer
    Dim P_b2 As Integer
    Dim P_s As Integer
   
    P_e = InStr(1, ref_text, "!")
    P_b1 = InStr(1, ref_text, "[")
    P_b2 = InStr(1, ref_text, "]")
    P_s = InStr(1, ref_text, ":\")
   
    If P_e = 0 Then
        RefName = ref_text
    Else
        RefName = Right$(ref_text, Len(ref_text) - P_e)
    End If
    RefName = Replace$(RefName, "$", "")
   
    If P_e = 0 Then
        SheetName = Application.Caller.Parent.Name
    ElseIf P_b1 = 0 Then
        SheetName = Left$(ref_text, P_e - 1)
    Else
        SheetName = Mid$(ref_text, P_b2 + 1, P_e - P_b2 - 1)
    End If
    SheetName = Replace$(SheetName, "'", "")
   
    If P_b1 = 0 Then
        WBName = Application.Caller.Parent.Parent.Name
    Else
        WBName = Mid$(ref_text, P_b1 + 1, P_b2 - P_b1 - 1)
    End If
   
    If P_s = 0 Then
        FolderName = ""
    Else
        FolderName = Left$(ref_text, P_b1 - 1)
    End If
    If Left$(FolderName, 1) = "'" Then FolderName = Right$(FolderName, Len(FolderName) - 1)
End Sub

Hey,
I tried to use 2 positions,
In both, the function returns the value VALUE :(
 
Upvote 0
I tried to use 2 positions,
In both, the function returns the value VALUE
Post a sample of both the sheet that you are pulling from and you are pulling to (use the boards BB2XL app to do this so that we can copy the data into Excel) with a description of what you are trying to pull and an example of the formula you are trying to use (the one that works when both workbooks are open).

And make sure that there is no protection on the workbook/worksheets that you are pulling from as per the comment in the code....
VBA Code:
'------------------------------------
'Known issues:
'- Due to the use of SpecialCells(), #VALUE! will be returned if the worksheet for a closed workbook is protected.
'------------------------------------
 
Last edited:
Upvote 0
Here The exapmle:

MonthsBudget
jan4869
C:\Users\Omer\Desktop\All The Files\[​
feb9429
Version​
mar32661
<== Data Validation​
apr4939
may8220
jun7372
jul8709
aug4980
sep9353
oct6361
nov4884
dec5395


The function In B2 (the number is 4869):
=INDIRECT("'"&$E$2&$E$3&$E$4&".xlsx]Costs'!"&ADDRESS(ROW(B2),COLUMN(B2)),TRUE)

The external files called Version1 and Version 2,
The Path for both:
C:\Users\Omer\Desktop\All The Files\[
The sheets name: Costs

The Append File call: Append

Thanks :)
 
Upvote 0
Please use the boards app so I can see the cell references and I take it that is just the sheet being pulled from (I need examples of both).
Post a sample of both the sheet that you are pulling from and you are pulling to (use the boards BB2XL app to do this so that we can copy the data into Excel)
 
Upvote 0

Forum statistics

Threads
1,215,483
Messages
6,125,063
Members
449,206
Latest member
Healthydogs

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top