First: I'm using Excel 2007, but the code has to work for Excel 2003 as well.
My problem is the following: I need to access cells in a different workbook, which may be closed. The following code can be found all around the web:
Function Foo()
Dim cell As Range
Dim wbk As Workbook
Set wbk = Workbooks.Open("correct absolute path")
' wbk is Nothing here so the next statement fails.
Set cell = wbk.Worksheets("Sheet1").Range("A1")
Foo = cell.Value
wbk.Close
End Function
sadly, wbk is Nothing after the open statement (I'd love to give a better error message, but no idea how I'd do that; what I'd give for a real IDE and an useful language :/). The absolute path is correct and points to a valid excel xlsx file.
Also I assume the best way to do this, is to "cache" the workbook and not open/close it every time the function is called? Any possible problems with that (apart from having to handle the situation when the workbook is already open obviously)?
Image while stepping through:
I can reproduce this problem. It only happens to me when I attempt to paste this code into a user-defined function.
I believe this is by design (the quote is for XL 2003, but the same thing happens to me on XL 2010)
Using VBA keywords in custom functions
The number of VBA keywords you can use in custom functions is smaller than the number you can use in macros. Custom functions are not allowed to do anything other than return a value to a formula in a worksheet or to an expression used in another VBA macro or function. For example, custom functions cannot resize windows, edit a formula in a cell, or change the font, color, or pattern options for the text in a cell. If you include "action" code of this kind in a function procedure, the function returns the #VALUE! error.
http://office.microsoft.com/en-us/excel-help/creating-custom-functions-HA001111701.aspx
The only workaround I've found is to call this kind of code via a normal macro. Something like selecting the cells to apply it to, then looping over Selection or the like.
You can use this (similar to what Bruno Leite proposed, but much simpler to write):
Dim excelApp As New Excel.Application
excelApp.Visible = False
Set WB = excelApp.Workbooks.Open(FileName, xlUpdateLinksNever, True)
As UDFs are called repeatedly, you should make sure to do an excelApp.Quit before exiting the function (and a WB.close(False) before) to avoid having countless Excel instances running on your box.
I spent some thoughts on it and came to the conclusion that you cannot mess around with the workbooks of the current instance of excel while executing a UDF. On the other hand, opening a second instance of excel will do the job without interference.
To get data from Workbook without is open, you can use this, with ADO connection.
To use in Excel 2007 change this
Microsoft.Jet.OLEDB.4.0
to
Provider=Microsoft.ACE.OLEDB.12.0
and
Extended Properties=\"Excel 8.0;HDR=Yes;\
to
Extended Properties=\"Excel 12.0;HDR=Yes;\
[]'s
The workaround of putting my routine into a separate macro in the workbook module, and calling that macro from the Workbook_BeforeSave code, seems to have done the trick.
I've had a similar issue, but in my case it's a "Workbooks.Open(filename)" command at the start of a small routine embedded in Workbook_BeforeSave. VBA just skips right over the line of code as if it weren't there, it doesn't even report an Err.Code or Err.Description.
The only clue for me was that it's part of the Workbook_BeforeSave routine, and the limits with Functions above seem to indicate that could be a possible cause. So I dug around further to find more details.
It seems that Workbook_BeforeSave disables Excel from opening more files, and I guess there's a good reason for doing that, since the File > Open option is still visible in the File menu, but it can't be clicked. Strangely, the Open toolbar icon/button still works, and so whilst I can manually open the file from there, I wonder if it's because it's impossible to call this action from VBA code and that's why they allowed it?
You don't have to "Set" a cell, It's part of the workbook class (as far as I know). Just use the following...
foo = wbk.Worksheets("Sheet1").Range("A1").Value
I would suggest that you open you the new workbook upon opening the calling workbook, in the worbook_open event.
You then store the new workbook reference in a global variable.
Then the function called by your cell uses the said global variable instead of trying to open a new workbook. This way you go around the limitations.
PS : Of course global variable are to be avoided, some sort of container would be better than a direct global variable.
You can check the error in a proper way by using the following code:
filelocation = c:\whatever\file.xlsx
On Error GoTo Handler 'this is key as if the next row returns an error while opening the file it will jump to the Handler down there.
Set wkb2 = Workbooks.Open(filelocation, ReadOnly)
Handler:
MsgBox "File " & filelocation & " does not exist or cannot be reached, please review and try again"
I know that this does not answer the question (that's why I also landed in this thread, as I cannot open the file and can't understand why is that so)
Cheers,
RV
Related
I need to change Print Settings to Print Active Sheets instead of Print Selection.
I tried using macro recorder, but it did not capture any code.
Also, this simple vba code just print the active sheet, but it does not change print settings as required at run time.
ActiveSheet.PrintOut
In advance, your help is appreciated.
The setting in discussion is workbook specific. You cannot change it globally even using another language, because of the previous mentioned truth. I mean, using a specific language method.
But you can do it, using VBA, simple running a piece of code like the following one printing two sheets. Next time, for that specific workbook the setting will be changed. You obtain what you need when the code runs and also change the setting:
Sub printActiveSheets()
Dim wb As Workbook
Set wb = ActiveWorkbook
Sheets(Array(wb.Sheets(1).name, wb.Sheets(2).name)).PrintOut
End Sub
It will print first and second sheet and will also make the change you need.
In order to check that, please manually print a selection, then look at that specific setting. It will be changed in 'Print Selection`. Then, run the above code and make the same check. You will see 'Print Active Sheets'. It will also be memorized for that specific workbook if you save it.
It is not easy to change that setting, let us say, As Default, being valid for all open workbooks. I cannot swear that it is impossible. If I will take it like a challenge I will maybe find some Registry keys able to be handled (as Excel does) in order to do it for all involved workbooks. But the effort cannot be covered by the result, I think. This is the way Excel works, should be the essence of the answer, I also think.
I have a simple code that runs upon initialization of a userform that sets a few workbook and worksheet variables so they can be used easily throughout the rest of my modules, and the references can be easily changed in one place if the file moves. I recently migrated my workbooks from my desktop to a separate server/drive, and accordingly updated the file pathways; however, when I try to run the code now I immediately get the message:
"Run-time error '-2147352565 (8002000b)': Can't move focus to the control because it is invisible, not enabled, or of a type that does not accept the focus."
This error occurs on the line
Set ReportWkbk = Workbooks("N:\ rest of file pathway here\QuaRT_Template.xlsm")
Is there anything that can be happening on the server/drive that is causing this issue? If so, is there anything I can do to fix it? I do work with other excel workbooks saved in the same location that seem to have no issue being referenced, though their references are in the workbook itself, not through Visual Basic.
The Subscript Out of Range error occurs because the Excel workbook being referenced is not open (or opening) in the same instance of Excel. You can easily reproduce the error by creating two workbooks (name one WorkBook2.xlsx) and ensuring they open in separate instances of Excel. Then run this code:
Sub OpenWkbkNames()
Dim wbk As Workbook
For Each wbk In Workbooks
Debug.Print wbk.Name
Next
'Hmm. Workbook 2 is not listed, but lets activate it and see what happens.
Workbooks("workbook2.xlsx").Activate
End Sub
You could avoid the issue by looping through the names of the workbooks open in THIS instance of Excel to ensure its available.
I've been researching Run-time error '-2147352565 (8002000b)' and found this: https://msdn.microsoft.com/en-us/library/microsoft.visualstudio.vsconstants.disp_e_badindex.aspx
Apparently, that is VS's way of saying Subscript Out of Range too. I suspect (but have no solid proof) that since the code worked locally but the issue appeared after the file was migrated to a shared drive that Windows is taking a long time to open the file so opens it in a new instance of Excel. Essentially, Excel gets impatient and decides to move on while Windows completes its task. I base this assumption on the detection logic that was added to Excel 2013 (https://blogs.office.com/2013/06/03/opening-workbooks-by-running-separate-instances-of-excel/).
Good day.
This may be simple and all, but I would just like to know.
Within the first Sub of my Form, I placed at the very beginning of the code the following line:
MsgBox (ActiveWorkbook)
Such line would usually prompt the workbook active (as the call suggests). When placed in other sections of the project, it surely works, but not here. Instead, this error appears:
Run-time error '438':
Object doesn't support this property or method
So, I'd just like to gain more understanding on the subject. Thanks.
PS. The sub I'm placing that line of code in is a simple button_Click().
Thanks again.
I have to question the "Such line would usually prompt the workbook active (as the call suggests)" assertion.
MsgBox takes a Variant as an argument, but one that can be successfully cast to a String (MsgBox$ is almost always better practice).
ActiveWorkbook returns a Workbook object, and a Workbook cannot be cast to a String. I don't recall off the top of my head what the default property of Workbook is, but I know it isn't convertible into a String. If you want to get the name of the Workbook, you need this:
MsgBox$ ActiveWorkbook.Name
You would need that anywhere, Form, Class, Module, ...anywhere.
Better practice would be to get a reference to the active Workbook and use that throughout your code. See How to avoid using Select in Excel VBA macros.
I am making my first VBA program and trying to run the following function. The function checks a specific named range for the first row which does not have a value greater than it's leading value, but less than 1.
Public Function findPurchase()
Dim CRT As Range
Set CRT = Range("CostRateTable")
Dim existsBetter As Boolean
existsBetter = True
Dim r As Integer
r = 2
Dim c As Integer
c = 4
While existsBetter
Dim Found As Boolean
FoundBetter = False
While Not FoundBetter And c <= CRT.Columns.Count
If CRT(r, c) > CRT(r, 2) And CRT(r, c) < 1 Then
FoundBetter = True
Else
c = c + 1
End If
Wend
existsBetter = FoundBetter
If existsBetter Then
r = r + 1
End If
Wend
findPurchase = CRT(r, 3)
'MsgBox(findPurchase)
End Function
I know the function does what it is supposed to because I have both manually checked the table of values, removed the comment ' from the MsgBox, and used the debug tools to step in and out of each of the functions steps as it went through the table. However, when I reference the function in Excel with =findPurchase() I'm given a #NAME? error. The function even shows up in the function auto-complete box when I begin to type its name. When I write other functions, both with and without parameters, I can reference them just fine, for example:
Function addtwo()
addtwo = 1 + 2
End Function
What am I doing wrong with my function which causes it not to work?
You are getting that error because you have a module with the same name as the function.
Change that name to say find_Purchase and everything will be fine :) See the image below...
I had the same issue myself. It turned out that I "Saved As..." another file and macros were not enabled for that file. No banner on the top appeared, but a #NAME? error was generated.
I reopened the file, enabled macros, and the problem was resolved.
Make sure you have placed the function in a Standard Module. The error message means Excel can't find the function.
When Excel opens an unkown workbook containing VBA-Code, it usually asks for macros to be enabled by the user (depending on the application settings).
If the user then enables the macros, all event-driven procedures will be started, such as auto_open or others.
Custom VBA Functions however require for a full recalculation of the workbook. Otherwise the functions return-value still is #NAME, as the calculation is only done directly after opening the workbook.
In order to work directly at the first time opening, one has to add the following line to the workbook_open event
'
' Workbook open event
Private Sub Workbook_Open()
Application.CalculateFullRebuild
End Sub
Check "Trust access to the VBA project object model" in Macro settings from Macros security
One reason for this problem is security restrictions.. I had this problem and I activate "Enable all macros" from security center, and the problem solved
I had a similar persistent problem with one of my functions when everything else seemed fine.
Open the worksheet & go to the Developer Tab. Open VBA, and back on the Developer ribbon select "View Code". See if it opens any similar Code (apart from your Module) specific to that worksheet (eg. Sheet2 (Code). I found that I had duplicated the code on the worksheet in addition to the Module. Delete the "worksheet" code. (You may need to save the workbook & re-open at this stage). When I deleted the worksheet code, the module function then worked.
In addition to checking some of the above mentioned items, you might need to specify the filename where the custom function is actually defined, e.g. cell content
=XLstart.xlsm!myCustomFunc(Arg1,Arg2)
where myCustomFunc is defined in the startup file XLstart.xlsm.
Following the Excel help for "Correct a #NAME? error":
In the formula bar, select the [suspect] function name.
In the Name Box (to the left of the formula bar), click the arrow and then select a [user-defined] function from the list that Excel suggests.
This will add the filename per the above format.
MS 2010, Windows 10.
Here's why I got that error. This answer is not provided so far.
If you have two or more workbooks (spreadsheets) open, then you may have your module under the other workbook - not the only you want to do the calculation on. This may seem impossible but ... as soon as you open the Developer/VBA code editor Excel wants to show you the structure (objects, modules, etc) of every open workbook. It's not what I expect as a developer, but there it is. So like me, you may have pressed 'Add module' and dropped the code in another workbook and worksheet.
If this is your issue, nothing mention above will work. Move your VBA module and code to the correct spreadsheet visible through this VBA code editor.
True,
I had the same (in Excel 2010) and when I migrated to Excel 2016 , the function prototype was shown, but when I completed the function, the #NAME error was shown with a pop-up... so the code was never triggered.
It turned out I had a Macro of the same name as a Sub or UDF function !
I renamed the Macro, and then it worked
Cheers
Another cause I found for the #NAME? error is that the macro workbook with the custom function has a range name the same as the function name. I changed the function name and solved the problem.
This solution applies to users with an Excel installed in another language than "United States English":
I had a similar problem when making a copy of the active workbook to duplicate it and immediately opened the copy afterwards:
Non-working code:
ThisWorkbook.SaveCopyAs NewFileName
Set wb = Workbooks.Open(FileName:=NewFileName)
This always showed me several cells with Error 2029 / "#NAME?". If I opened the Workbook "the official way" via the File-Menu it worked as expected.
I solved the issue by adding the parameter "local:=true" to the open statement:
Working code:
ThisWorkbook.SaveCopyAs NewFileName
Set wb = Workbooks.Open(FileName:=NewFileName, Local:=True)
as VBA expected english function names in my German workbook. With this parameter VBA is told directly to use the local names.
I hope that helps someone not to loose several hours, as I did...
Short answer - if the function was working before, RESTART YOUR COMPUTER.
Long answer - I had this same thing happen to me. The problem is that the function I had created had been working for months. Then one day it just started showing a #NAME error instead of working like it was before. I had tried closing all other excel workbooks and even closing excel all-together and re-opening the sheet. Nothing seemed to work. Then for kicks, I edited the code to where I knew VBA would complain that there is a compile error. Surprisingly, it didn't complain. OK... I saved and closed excel anyways and then restarted my computer.
Once rebooted, I re-opened the excel workbook. Then VBA finally gave me a compile error. So I changed my function back to the original code I had before and now the sheet is running the function like it is supposed to. No more #NAME error.
Not sure all of those steps are necessary, but simply restarting the computer seems to have fixed my issue.
I want to write a little logging function in an excel add-in that I will be calling from many different workbooks. I'd like to be able to just call it by passing only the log text, and the log function itself could handle the timestamp, workbookname, etc.
However, I cannot use either ThisWorkbook or ActiveWorkbook to determine which workbook was responsible for making the call, as Thisworkbook will return a reference to the add-in itself, whereas VBA code running in a workbook other than the workbook with active focus in Excel could make the call, but the ActiveWorkbook will return the one that has focus in the window.
Application.Caller looked like a possible solution, but this seems to work only when the function is called from a cell, not from VBA.
Is what I'm trying to do impossible?
Update
According to > 1 person, this is in fact impossible. If anyone happens to know some clever workaround please speak up.
Ok, so having read the question properly I'll try again...
So, to state the problem:
you want a routine written in an addin, that when called from vba in another workbook can work out (among other things) which workbook contains the vba that made the call, without having to pass this information explicitly.
As stated this is not possible (this is a similar question to accessing the call stack from code: something that is to my knowledge not possible)
However you can almost get what you want like this
Declare your log function like this:
Sub MyLogger(wb as Workbook, LogText as String)
Dim CallerName as String
CallerName = wb.name
' your code...
End Sub
Then wherever you call the sub use
MyLogger ThisWorkbook, "Log Text"
Not quite as good as passing nothing, but at least its always the same
To get the name of the calling workbook, use
Application.Caller.Worksheet.Parent.Name
Application.Caller returns information about how Visual Basic was called. If called from a custom function entered in a single cell, a Range object specifying that cell is returned
Having got a reference to the cell, .Worksheet.Parent.Name gives you the name of the workbook
Note that Application.Caller will return other things depending on how your function is called (see VBA help for details)
In an Add-In Function called by an Excel Worksheet Array Entered Function Call, I find that "Application.Caller.Parent.name" gives the Sheet Name (Tab Name, not sheet number).
I had the same issue when coding a custom function. Function works well, but anytime another workbook is calculated or activated, all cells using that function revert to #value. It can be very frustrating when working with multiple files using this formula.
To get the Workbook I used:
Dim CallingWb As Workbook
Set CallingWb = Application.Caller.Parent.Parent
This should work if your function is in a cell.
Too late for the original post, but might help others!