How to make Excel VBA variables available to multiple macros? - vba

I have a string of macros that call upon each other and refer to workbooks A and B. I want the first macro to prompt the user to select document A and B and these Selections to become the workbook A and B variables I refer to in the various macros.
How do I make the selected documents the referred to variable throughout all the macros?
Thanks in advance!

Declare them outside the subroutines, like this:
Public wbA as Workbook
Public wbB as Workbook
Sub MySubRoutine()
Set wbA = Workbooks.Open("C:\file.xlsx")
Set wbB = Workbooks.Open("C:\file2.xlsx")
OtherSubRoutine
End Sub
Sub OtherSubRoutine()
MsgBox wbA.Name, vbInformation
End Sub
Alternately, you can pass variables between subroutines:
Sub MySubRoutine()
Dim wbA as Workbook
Dim wbB as Workbook
Set wbA = Workbooks.Open("C:\file.xlsx")
Set wbB = Workbooks.Open("C:\file2.xlsx")
OtherSubRoutine wbA, wbB
End Sub
Sub OtherSubRoutine(wb1 as Workbook, wb2 as Workbook)
MsgBox wb1.Name, vbInformation
MsgBox wb2.Name, vbInformation
End Sub
Or use Functions to return values:
Sub MySubroutine()
Dim i as Long
i = MyFunction()
MsgBox i
End Sub
Function MyFunction()
'Lots of code that does something
Dim x As Integer, y as Double
For x = 1 to 1000
'Lots of code that does something
Next
MyFunction = y
End Function
In the second method, within the scope of OtherSubRoutine you refer to them by their parameter names wb1 and wb2. Passed variables do not need to use the same names, just the same variable types. This allows you some freedom, for example you have a loop over several workbooks, and you can send each workbook to a subroutine to perform some action on that Workbook, without making all (or any) of the variables public in scope.
A Note About User Forms
Personally I would recommend keeping Option Explicit in all of your modules and forms (this prevents you from instantiating variables with typos in their names, like lCoutn when you meant lCount etc., among other reasons).
If you're using Option Explicit (which you should), then you should qualify module-scoped variables for style and to avoid ambiguity, and you must qualify user-form Public scoped variables, as these are not "public" in the same sense. For instance, i is undefined, though it's Public in the scope of UserForm1:
You can refer to it as UserForm1.i to avoid the compile error, or since forms are New-able, you can create a variable object to contain reference to your form, and refer to it that way:
NB: In the above screenshots x is declared Public x as Long in another standard code module, and will not raise the compilation error. It may be preferable to refer to this as Module2.x to avoid ambiguity and possible shadowing in case you re-use variable names...

You may consider declaring the variables with moudule level scope.
Module-level variable is available to all of the procedures in that module, but it is not available to procedures in other modules
For details on Scope of variables refer this link
Please copy the below code into any module, save the workbook and then run the code.
Here is what code does
The sample subroutine sets the folder path & later the file path. Kindly set them accordingly before you run the code.
I have added a function IsWorkBookOpen to check if workbook is already then set the workbook variable the workbook name
else open the workbook which will be assigned to workbook variable accordingly.
Dim wbA As Workbook
Dim wbB As Workbook
Sub MySubRoutine()
Dim folderPath As String, fileNm1 As String, fileNm2 As String, filePath1 As String, filePath2 As String
folderPath = ThisWorkbook.Path & "\"
fileNm1 = "file1.xlsx"
fileNm2 = "file2.xlsx"
filePath1 = folderPath & fileNm1
filePath2 = folderPath & fileNm2
If IsWorkBookOpen(filePath1) Then
Set wbA = Workbooks(fileNm1)
Else
Set wbA = Workbooks.Open(filePath1)
End If
If IsWorkBookOpen(filePath2) Then
Set wbB = Workbooks.Open(fileNm2)
Else
Set wbB = Workbooks.Open(filePath2)
End If
' your code here
End Sub
Function IsWorkBookOpen(FileName As String)
Dim ff As Long, ErrNo As Long
On Error Resume Next
ff = FreeFile()
Open FileName For Input Lock Read As #ff
Close ff
ErrNo = Err
On Error GoTo 0
Select Case ErrNo
Case 0: IsWorkBookOpen = False
Case 70: IsWorkBookOpen = True
Case Else: Error ErrNo
End Select
End Function
Using Prompt to select the file use below code.
Dim wbA As Workbook
Dim wbB As Workbook
Sub MySubRoutine()
Dim folderPath As String, fileNm1 As String, fileNm2 As String, filePath1 As String, filePath2 As String
Dim filePath As String
cmdBrowse_Click filePath, 1
filePath1 = filePath
'reset the variable
filePath = vbNullString
cmdBrowse_Click filePath, 2
filePath2 = filePath
fileNm1 = GetFileName(filePath1, "\")
fileNm2 = GetFileName(filePath2, "\")
If IsWorkBookOpen(filePath1) Then
Set wbA = Workbooks(fileNm1)
Else
Set wbA = Workbooks.Open(filePath1)
End If
If IsWorkBookOpen(filePath2) Then
Set wbB = Workbooks.Open(fileNm2)
Else
Set wbB = Workbooks.Open(filePath2)
End If
' your code here
End Sub
Function IsWorkBookOpen(FileName As String)
Dim ff As Long, ErrNo As Long
On Error Resume Next
ff = FreeFile()
Open FileName For Input Lock Read As #ff
Close ff
ErrNo = Err
On Error GoTo 0
Select Case ErrNo
Case 0: IsWorkBookOpen = False
Case 70: IsWorkBookOpen = True
Case Else: Error ErrNo
End Select
End Function
Private Sub cmdBrowse_Click(ByRef filePath As String, num As Integer)
Dim fd As FileDialog
Set fd = Application.FileDialog(msoFileDialogFilePicker)
fd.AllowMultiSelect = False
fd.Title = "Select workbook " & num
fd.InitialView = msoFileDialogViewSmallIcons
Dim FileChosen As Integer
FileChosen = fd.Show
fd.Filters.Clear
fd.Filters.Add "Excel macros", "*.xlsx"
fd.FilterIndex = 1
If FileChosen <> -1 Then
MsgBox "You chose cancel"
filePath = ""
Else
filePath = fd.SelectedItems(1)
End If
End Sub
Function GetFileName(fullName As String, pathSeparator As String) As String
Dim i As Integer
Dim iFNLenght As Integer
iFNLenght = Len(fullName)
For i = iFNLenght To 1 Step -1
If Mid(fullName, i, 1) = pathSeparator Then Exit For
Next
GetFileName = Right(fullName, iFNLenght - i)
End Function

Create a "module" object and declare variables in there. Unlike class-objects that have to be instantiated each time, the module objects are always available. Therefore, a public variable, function, or property in a "module" will be available to all the other objects in the VBA project, macro, Excel formula, or even within a MS Access JET-SQL query def.

Related

Check which file is open VBA

All,
I have a large module which in the earlier part checks whether a files is in use (Readonly) format and if it is in use to open the next file. I.e. if file one is in use open file two etc..
In a later part of the module I wish to use the file which has been opened. However I am struggling to identify the file which is opened in the earlier part of the automation and set is as WB.
The code I am currently using is;
Dim wb As Object
On Error Resume Next
Workbooks("\\Csdatg04\psproject\Robot\Project Preload\Transactions\Transactions1.csv").Activate
If Err.Number = 0 Then
wb = GetObject("\\Csdatg04\psproject\Robot\Project Preload\Transactions\Transactions1.csv")
GoTo skipline
End If
On Error GoTo 0
On Error Resume Next
Workbooks("\\Csdatg04\psproject\Robot\Project Preload\Transactions\Transactions2.csv").Activate
If Err.Number = 0 Then
wb = GetObject("\\Csdatg04\psproject\Robot\Project Preload\Transactions\Transactions2.csv")
GoTo skipline
End If
On Error GoTo 0
On Error Resume Next
Workbooks("\\Csdatg04\psproject\Robot\Project Preload\Transactions\Transactions3.csv").Activate
If Err.Number = 0 Then
wb = GetObject("\\Csdatg04\psproject\Robot\Project Preload\Transactions\Transactions3.csv")
GoTo skipline
End If
On Error GoTo 0
On Error Resume Next
Workbooks("\\Csdatg04\psproject\Robot\Project Preload\Transactions\Transactions4.csv").Activate
If Err.Number = 0 Then
wb = GetObject("\\Csdatg04\psproject\Robot\Project Preload\Transactions\Transactions4.csv")
GoTo skipline
End If
skipline:
On Error GoTo 0
Can anyone recommend how I can identify which file is open and set is as WB
Any help would be much appreciated.
Thanks
Don't try to match the path: mapped drives and aliases will spoof your matches.
Your match term is the file name, with the extension, and you can iterate the Excel workbooks collection to see if there's a matching name:
Option Explicit
Public Function WorkbookIsOpen(WorkBookName As String) As Boolean
' Returns TRUE if a workbook (or csv file open in Excel) is open
Dim wbk As Excel.Workbook
WorkbookIsOpen = False
If IsError(WorkBookName) Then
WorkbookIsOpen = False
ElseIf WorkBookName = "" Then
WorkbookIsOpen = False
Else
For Each wbk In Application.Workbooks
If wbk.Name = WorkBookName Then
WorkbookIsOpen = True
Exit For
End If
Next wbk
End If
End Function
Public Function FileName(FilePath As String) As String
' Returns the last element of a network path
' This is usually the file name, but it mat be a folder name if FilePath is a folder path:
' FileName("C:\Temp\Readme.txt") returns "ReadMe.txt"
' ?FileName("C:\Temp") returns "Temp"
' FileName("C:\Temp\") returns ""
' This function does not perform any file checking - the file need not exist, the path
' can be invali or inaccessible. All we're doing is String-handling.
Dim arr() As String
Dim i As Integer
If IsError(FilePath) Then
FileName = "#ERROR"
ElseIf FilePath = "" Then
FileName = ""
Else
arr = Split(Trim(FilePath), "\")
i = UBound(arr)
FileName = arr(i)
Erase arr
End If
End Function
Then it's just a matter of checking if the open workbook is open read-only:
Dim bReadOnly As Boolean
If WorkbookIsOpen("C:Temp\Brian.csv") Then
bReadOnly = Application.WorkBooks(FileName("C:Temp\Brian.csv")).ReadOnly
End If
Things get a lot more interesting if you need to check that the file isn't open in another session of Excel, or another application: this code won't test that for you.
I need to answer the other point in your question: opening the file in Excel if it isn't already open in this session.
I would recommend using Application.Workbooks.Open(FileName) for that, as it's smarter than GetObject() and will open the file - csv, xml, xls, xlsx - in Excel, as a workbook, with Excel guessing the necessary format parameters. Also,the native 'open' function allows you to specify additional parameters, like Read-Only.

VBA kill crashes after being called by auto_open

I'm experiencing some trouble with my VBA code. I have created an application in Excel and its copies have been distributed to users. To be able to correct bugs or add some new functions, every copy stores information what version it is. I have written procedure, that opens (read-only) a central file, that is providing some data a and information, which version is current. If the file, that opened this central file is older, it gets updated.
So the auto_open calls a procedure discovers that it has to be updated, saves the current file AS FileName_old.xlsm (to have some backup), kills the FileName.xlsm and copies a new file from a template. The problem is that the procedure crashes when it tries to kill the old file (to be more precise, it just ends without any error message). What confuses me is that when I run the auto_open macro manually (F5), everything goes correctly. Even step by step goes right. Also, when I call the update process via a button in a worksheet, it works perfectly. Any idea, what might cause this problem?
Thanks
Sub auto_open()
If Range("H_User").Value = "" Then UserNameWindows 'Write a user that is using this workbook in the range H_User
If Range("H_Updated").Value < FileDateTime(Range("H_File_Data").Value) Then UpdateData
End Sub
Sub UpdateData()
Dim ActWB As String
ActWB = ActiveWorkbook.Name
Application.ScreenUpdating = False
ThisWorkbook.Activate
If Not FileExists(Range("H_File_Data").Value) Then
MsgBox "The data file is not available!", vbCritical
Workbooks(ActWB).Activate
Application.ScreenUpdating = True
Exit Sub
End If
Dim WB As String, oknoData As String, IsTeam As Boolean, User As String
Dim version As Integer, Subversion As Integer, DataPath As String
On Error GoTo konec
Application.EnableCancelKey = xlDisabled
IsTeam = False
User = Range("H_User").Value
WB = ActiveWindow.Caption
version = Range("H_version").Value
Subversion = Range("H_Subversion").Value
Range("C_Data_All").ClearContents
DataPath = Range("H_File_Data").Value
Workbooks.Open fileName:=DataPath, ReadOnly:=True
oknoData = ActiveWindow.Caption
If Range("H_version_Spec").Value <= version Or (Range("H_version_Spec").Value = version And Range("H_Subversion_Spec").Value <= Subversion) Then
FileUpdate
End If
'If there is no need to update the file then continue with in this procedure
End Sub
Sub FileUpdate()
Dim NewPath As String, NewWB As String, OldPath As String, OldWB As String, BackupWB As String, BackupPath As String
Dim MainWB As String, version As String, Subversion As String
Dim versionMax As Integer, SubversionMax As Integer, versionMin As Integer, SubversionMin As Integer
ThisWorkbook.Activate
version = Range("H_version").Value
Subversion = Range("H_Subversion").Value
OldPath = ThisWorkbook.FullName
OldWB = ThisWorkbook.Name
BackupWB = Left(ThisWorkbook.Name, Len(ThisWorkbook.Name) - 5) & "_old.xlsm"
BackupPath = ThisWorkbook.Path & "\" & BackupWB
If Not FileExists(Workbooks(OldWB).Names("H_File_Data").RefersToRange.Value) Then
MsgBox "The data file is not available!", vbCritical
Exit Sub
End If
Workbooks.Open fileName:=Workbooks(OldWB).Names("H_File_Data").RefersToRange.Value, ReadOnly:=True
MainWB = ActiveWorkbook.Name
If version = Range("O_Spec_version").Value And Subversion >= Range("O_Spec_Subversion").Value Then
'Just some little piece of code if the version is not lower
Else
If FileExists(BackupPath) Then Kill (BackupPath)
If Not FileExists(Range("H_Path_Spec_Actual").Value) Then
MsgBox "The spec template is not available!", vbCritical
Exit Sub
End If
ThisWorkbook.SaveAs BackupPath
Kill (OldPath)
'Continue with update
End If
End Sub
Function FileExists(FilePath As String) As Boolean
Dim fso As Object
Set fso = CreateObject("scripting.filesystemobject")
FileExists= fso.FileExists(FilePath)
End Function
Option Explicit
Private Sub Workbook_Open()
Dim BackupPath As String
Dim OldPath As String
BackupPath = "folder\Filename_old.xlsm"
With ThisWorkbook
OldPath = .FullName
.SaveCopyAs BackupPath
.Saved = True
.ChangeFileAccess xlReadOnly
Kill .FullName
.Close False
End With
End Sub

VBA: How can I password protect modules using code?

I have an file which saves a copy of itself to go to certain recipients, so you end up with many files that only contain recipient-specific information and the original maste file that contains all information. When the recipient-specific files are made, I have code that deletes everything but information that is related to that recipient and locks down the workbook and sheets with a randomly made password using the below function:
Function Pwd(iLength As Integer) As String
Dim i As Integer, iTemp As Integer, bOK As Boolean, strTemp As String
'48-57 = 0 To 9, 65-90 = A To Z, 97-122 = a To z
'amend For other characters If required
For i = 1 To iLength
Do
iTemp = Int((122 - 48 + 1) * Rnd + 48)
Select Case iTemp
Case 48 To 57, 65 To 90, 97 To 122: bOK = True
Case Else: bOK = False
End Select
Loop Until bOK = True
bOK = False
strTemp = strTemp & Chr(iTemp)
Next i
Pwd = strTemp
End Function
Is it possible to also lock down the modules so that they cannot be edited? What I want is the same functionality that Excel provide in Visual Basic by going to Tools -> VBAProject - Project Properties -> Protection, but to do this through the code so that it can be applied to each of the recipient specific files.
I can apply protection to the sheets using code like:
Sheets(1).Protect Password, True, True
And to the workbook with code like:
ActiveWorkbook.Protect Password, True, False
But is there something that I can use to lock down the modules?
Notwithstanding the good advice already given about the security of Excel passwords/protection, and the comprehensive (non-SendKeys) solution linked by Carl Colijn, I have used the dirty SendKeys method myself with some success - see example below, and here for more details on SendKeys. YMMV etc.
Note you will have to find the option "Trust access to the VBA project object model" in the Excel Trust Center > Macro Settings, or equivalent for your version of Excel
Sub UnprotectVBProj(ByRef WB As Workbook, ByVal Pwd As String)
Dim vbProj As Object
Set vbProj = WB.VBProject
If vbProj.Protection <> 1 Then Exit Sub ' already unprotected
Set Application.VBE.ActiveVBProject = vbProj
SendKeys "%TE" & Pwd & "~~"
End Sub
Sub ProtectVBProj(ByRef WB As Workbook, ByVal Pwd As String)
Dim vbProj As Object
Set vbProj = WB.VBProject
If vbProj.Protection = 1 Then Exit Sub ' already protected
Set Application.VBE.ActiveVBProject = vbProj
SendKeys "%TE+{TAB}{RIGHT}%V%P" & Pwd & "%C" & Pwd & "{TAB}{ENTER}"
End Sub

Is there method similar to 'Find' available when we Loop through folder (of files) using Dir Function in excel vba?

As we know, we use Find() method to find whether a string or any Microsoft Excel data type exists in an excel.
(Usually we do it on set of data)
I want to know if any such method available when we loop through folder(of files) using Dir function.
Situation:
I have an excel - 'FileNames.xlsx' in which 'Sheet1' has names of files having extensions .pdf/.jpg/.jpeg/.xls/.xlsx/.png./.txt/.docx/ .rtf in column A.
I have a folder named 'Folder' which has most(or all) of the files from 'FileNames.xlsx'.
I have to check whether all the file-names mentioned in the 'FileNames.xlsx' exist in 'Folder'.
For this I have written the below VBScript(.vbs):
strMessage =Inputbox("Enter No. of Files in Folder","Input Required")
set xlinput = createobject("excel.application")
set wb123 =xlinput.workbooks.Open("E:\FileNames.xlsx")
set sh1 =wb123.worksheets("Sheet1")
For i = 2 to strMessage +1
namei = sh1.cells(i,1).value
yesi = "E:\Folder"+ namei +
If namei <> yesi Then
sh1.cells(i,1).Interior.Color = vbRed
Else
End If
Next
msgbox "Success"
xlinput.quit
As I wasn't able to get the required Output I tried it recording a small Excel VBA Macro. (Changed FileNames.xlsx to FileNames.xlsm)
Sub LoopThroughFiles()
Dim lastRow As Long
lastRow = Sheets("Sheet1").UsedRange.Rows.Count
Dim MyFolder As String
Dim filename As Range
Dim MyFile As String
MyFolder = "E:\Folder"
For Each filename In Worksheets("Sheet1").Range("A2A:" & lastRow)
MyFile = Dir(MyFolder & "\*.xlsx")
'Here I actually need to pass all file extensions to Dir
Do While MyFile <> ""
If filename = MyFile Then
'Do Nothing
Else
filename.Interior.Color = vbRed
MyFile = Dir
Next
End Sub
The above is a failed attempt.
I thought of trying it with method similar to Find()
Sub LoopThroughFiles()
Dim lastRow As Long
'Dim LastFile As Long
'Is there need of it (LastFile variable)? I kept this variable
'to save (prior known) count of files in folder.
lastRow = Sheets("Sheet1").UsedRange.Rows.Count
'LastFile = 'Pass count of Files in folder to this variable.
Dim fileName As Range
For Each fileName In Worksheets("Sheet1").Range("A2:A" & lastRow)
Dim rngFnder As Range
On Error Resume Next
'Error at below line.
Set rngFnder = Dir("E:\Folder\").Find(filename)
'This line gives me error 'Invalid Qualifier'
'I am trying to use method similar to Find()
If rngFnder Is Nothing Then
filename.Interior.Color = vbRed
End If
Next
End Sub
But, I couldn't achieve the result. Can anyone tell me is there any such function available to 'Find' whether all filenames in an excel exist in a folder after looping through folder using Dir?
As per my knowledge, Dir function works with only one file extension at a time.
Is it possible to use Dir function for multiple file extensions at a time?
Expected Output:
Assume I have 8 filenames in 'FileNames(.xlsx/.xlsm)'. Out of which Arabella.pdf and Clover.png are not found in 'Folder', Then I want to color cells for these filenames in red background in excel as in below image.
Sub LoopThroughFiles()
Dim lastRow As Long
lastRow = Sheets("Sheet1").UsedRange.Rows.Count
Dim MyFolder As String
Dim filename As Range
Dim MyFile As String
MyFolder = "E:\Folder"
For Each filename In Worksheets("Sheet1").Range("A2:A" & lastRow)
MyFile = MyFolder & "\" & filename
If Not FileExists(MyFile) Then
filename.Interior.Color = vbRed
End If
Next
End Sub
Public Function FileExists(strFullpathName As String) As Boolean
If Dir(strFullpathName) <> "" Then
FileExists = True
Else
FileExists = False
End If
End Function
You can output a list of the files that are contained in the folder. I found a really helpful tutorial on that here: http://software-solutions-online.com/2014/03/05/list-files-and-folders-in-a-directory/#Jump1
If you then loop through both the original and the output lists and look for a match. Easiest is to first colour them all red, and un-colour the matches. Else you would need an additional if-statement that states: When you reach the last element in the original list, and no match has been found, then colour red.
Edit: For continuity's sake I copied the code bits of the link I mentioned above:
Getting all file names form within 1 folder:
Sub Example1()
Dim objFSO As Object
Dim objFolder As Object
Dim objFile As Object
Dim i As Integer
'Create an instance of the FileSystemObject
Set objFSO = CreateObject("Scripting.FileSystemObject")
'Get the folder object
Set objFolder = objFSO.GetFolder("D:StuffFreelancesWebsiteBlogArraysPics")
i = 1
'loops through each file in the directory and prints their names and path
For Each objFile In objFolder.Files
'print file name
Cells(i + 1, 1) = objFile.Name
'print file path
Cells(i + 1, 2) = objFile.Path
i = i + 1
Next objFile
End Sub

Éxcel-VBA Open Workbook in macro that have been called in a cell

I have some trouble with a function that I call from a cell in Excel.
The macro shall open a workbook get some data and then return a mathematical results to the cell.
But when I use the the following code it does not open the wordbook, just return #VALUE! to the cell. It break out of the code right after I tried to open the workbook.
' This Interpolation function is used to get data from other Excel sheets
Public Function DatasheetLookup(ExcelFile As String, ExcelSheet As String, xVal As Double, Optional isSorted As Boolean = True) As Variant
' abosolute or relative path?
If Not (Left(ExcelFile, 3) Like "[A-Z]:\") Or (Left(ExcelFile, 2) = "\\") Then
ExcelFile = ThisWorkbook.path & "\" & ExcelFile
End If
' does file exits?
If Dir(ExcelFile, vbDirectory) = vbNullString Then
DatasheetLookup = "No such file!"
Exit Function
End If
' open the source workbook, read only
Dim Wbk As Workbook
Dim WS As Worksheet
' Application.ScreenUpdating = False ' turn off the screen updating
Set Wbk = Workbooks.Open(ExcelFile)
' Run through all sheets in the source workBook to find "the one"
For Each WS In Wbk.Worksheets ' <-- Here it exit the code and return #VALUE!
If WS.Name <> ExcelSheet Then
DatasheetLookup = "Sheet not found!"
Else
Dim xRange As Range
Dim yRange As Range
xRange = WS.Range("A1", "A" & WS.UsedRange.Rows.Count)
yRange = WS.Range("B1", "B" & WS.UsedRange.Rows.Count)
Dim yVal As Double
Dim xBelow As Double, xAbove As Double
Dim yBelow As Double, yAbove As Double
Dim testVal As Double
Dim High As Long, Med As Long, Low As Long
Low = 1
High = WS.UsedRange.Rows.Count
If isSorted Then
' binary search sorted range
Do
Med = Int((Low + High) \ 2)
If (xRange.Cells(Med).Value) < (xVal) Then
Low = Med
Else
High = Med
End If
Loop Until Abs(High - Low) <= 1
Else
' search every entry
xBelow = -1E+205
xAbove = 1E+205
For Med = 1 To xRange.Cells.Count
testVal = xRange.Cells(Med)
If testVal < xVal Then
If Abs(xVal - testVal) < Abs(xVal - xBelow) Then
Low = Med
xBelow = testVal
End If
Else
If Abs(xVal - testVal) < Abs(xVal - xAbove) Then
High = Med
xAbove = testVal
End If
End If
Next Med
End If
xBelow = xRange.Cells(Low): xAbove = xRange.Cells(High)
yBelow = yRange.Cells(Low): yAbove = yRange.Cells(High)
DatasheetLookup = yBelow + (xVal - xBelow) * (yAbove - yBelow) / (xAbove - xBelow)
Exit For
End If
Next WS
Wbk.Close Savechanges:=False
Set Wbk = Nothing
Application.ScreenUpdating = True
End Function
I am not sure the reason for this specifically, but you cannot open a file in a user defined function. There are many additional actions that cannot be performed in a Function as well. This is also discussed in this Stack Overflow answer here.
However, in your case, you can easily cheat this limitation by opening the file you want to read before you call the function. I prepared a very basic demonstration of this, you will need to modify the code as needed to fit your particular example:
Code in "ThisWorkbook":
' when the workbook opens, also open the companion spreadsheet so it is available to use
Private Sub Workbook_Open()
Set Wbk = Workbooks.Open("C:\Users\lrr\Desktop\Myworkbook.xlsx")
End Sub
Code in "Module1":
Global Wbk As Workbook
Public Function testFunc()
' the workbook is already opened, so you may perform this iteration operation w/o any problems.
For Each WS In Wbk.Worksheets
testFunc = 1
Exit Function
Next WS
End Function
Code in Cell A1:
=testFunc()