So I'm another one of those wanting to use the ExecuteExcel4Macro Method call to retrieve data from specific cells and lookup ranges in closed workbooks. I have seen lots of examples and answers to problems here and elsewhere. I am (or will be) using a variation of a routine credited to John Walkenbach, and referenced here and on other forums. (See thread for 9311188.)
The call to ExecuteExcel4Macro fails with an error "1004 - Method 'ExecuteExcel4Macro' of object '_Global' failed". For me, that's not a lot to go on. I have double checked the directory paths, file and sheet names, all that. The DIR() function finds the file okay. I've even put the files in the root directory to eliminate path complexities or too-long of an argument to the Method. One complication is that I'm on a Mac with OS 10.8 and using Excel 2011. Mac OS uses ":" instead of "" for directory delimiters.
But I don't really need to get into all that because the problem seems to be something fundamental about the cell reference addressing. I can't get ExecuteExcel4Macro to execute successfully within the same worksheet with an Excel Function that addresses any cell or range, never mind about a remote, closed worksheet reference. So I have condensed my example code to the essentials – no remote reference, just functions on cells in one worksheet.
In the example below I have a simple routine that executes some sample Excel Functions and displays a MessageBox with either the successful result or the error message, along with the argument to the Method call. There's also a function that will convert the A1 style references to R1C1 when needed. The list of Functions are within the routine, just comment/uncomment as needed to execute whichever one to test.
Function MakeR1C1(A1Formula As String) As String
MakeR1C1 = Application.ConvertFormula( _
Formula:=A1Formula, _
fromReferenceStyle:=xlA1, _
toReferenceStyle:=xlR1C1, _
ToAbsolute:=xlAbsolute)
End Function
Sub TestExcel4Macro()
On Error GoTo ErrorTrap
Dim arg As String
' arg = "GET.CELL(42)"
' arg = "CHAR(65)"
' arg = "LEN(""ABCDE"")"
' arg = "SUM(2,5,8)"
' arg = "INFO(""directory"")"
' arg = "INFO(""numfile"")"
' arg = "SUM(A32:A34)"
' arg = "SUM(ValList)"
' arg = MakeR1C1("SUM(A32:A34)")
' arg = "SUM(R32C1:R34C1)"
Rtn = ExecuteExcel4Macro(arg)
MsgBox "COMPLETED" & Chr(13) & _
"arg: " & arg & Chr(13) & _
"Return Value: " & Rtn
Exit Sub
ErrorTrap:
Beep
MsgBox "FAILED" & Chr(13) & _
"arg: " & arg & Chr(13) & _
"Error number: " & Err & Chr(13) & _
Error(Err)
End Sub
The first six all work just fine, returning the values you would expect:
arg = "GET.CELL(42)" This returns the left margin, or whatever that is;
arg = "CHAR(65)" Good, you get an "A" for that;
arg = "LEN(""ABCDE"")" Nice, that's a 5;
arg = "SUM(2,5,8)" Okay, 15;
arg = "INFO(""directory"")" Yep, the directory path of the active workbook with the macro;
arg = "INFO(""numfile"")" And the number of sheets in the workbook (plus 1? whatever).
So from this I know I'm accessing the Method correctly; it does work; you don't use the "=" in the argument; and the two INFO() Functions tell me it's able to access info about this workbook; i.e. it doesn't require explicit full directory pathway to find itself.
Now some functions that make reference to cells in the worksheet. These all work fine as a Formula in a cell in the worksheet.
But they fail as a call to the Method, with the respective error codes:
arg = "SUM(A32:A34)" 13 - Type mismatch
As expected, the Method requires R1C1 style references.
arg = "SUM(ValList)" 13 - Type mismatch
Okay, not too surprising, so it won't work with a named range. Too bad, I was counting on that.
arg = MakeR1C1("SUM(A32:A34)") 1004 - Method 'ExecuteExcel4Macro' of object '_Global' failed
Now the puzzlement. The MakeR1C1() converts the A1 addressing okay to "SUM(R32C1:R34C1)".
arg = "SUM(R32C1:R34C1)" 1004 - Method 'ExecuteExcel4Macro' of object '_Global' failed
And setting the argument explicitly with the R1C1 style fails the same.
I'll be really embarrassed if this is due to something simple and obvious. But I'll risk it because I'm stumped.
If it's not so simple then, Gurus, have at it. If I get this simple reference addressing problem figured out, then the remote file reference should fall into place, too.
I'll be especially appreciative of anyone who can test these in a Windows version and let me know what you get. That's what I'm most worried about – a Mac incompatibility that I can't fix.
Thanks to all in advance.
PS: I hope I have marked up all the above correctly, I tried.
Edit: Maybe I should have mentioned that to run my TestExcel4Macro() subroutine, I just mash the F5 key while in the VBA editor.
Quote:
The Microsoft Excel 4.0 macro isn't evaluated in the context of the
current workbook or sheet. This means that any references should be
external and should specify an explicit workbook name. For example, to
run the Microsoft Excel 4.0 macro "My_Macro" in Book1 you must use
"Book1!My_Macro()". If you don't specify the workbook name, this
method fails.
Here is what worked for me (MS Excel 2010 under Windows smthg): you have to specify the workbook + Sheet before referring to the Cells; and also make the R1C1 conversion.
Sub TestExcel4Macro()
On Error GoTo ErrorTrap
Dim arg As String
Dim this_workbook As String
' workbook named "myBook" having a sheet called "mySheet" where my data is
this_workbook = "[myBook.xlsm]mySheet!"
arg = "SUM(" & this_workbook & "A32:A34)"
arg = MakeR1C1("SUM(A32:A34)")
Rtn = ExecuteExcel4Macro(arg)
MsgBox "COMPLETED" & Chr(13) & _
"arg: " & arg & Chr(13) & _
"Return Value: " & Rtn
Exit Sub
ErrorTrap:
Beep
MsgBox "FAILED" & Chr(13) & _
"arg: " & arg & Chr(13) & _
"Error number: " & Err & Chr(13) & _
Error(Err)
End Sub
Have you tried defining arg as Variant instead of String?
Related
IN VBA
Function SNAME(number As String) As String
Dim WORD$
WORD = "Sheet" + number
' SNAME = Worksheets(1).CodeName
For i = 1 To ThisWorkbook.Worksheets.Count
If Worksheets(i).CodeName = WORD Then SNAME = Worksheets(i).Name
Next i
End Function
IN EXCEL
=SUMIFS((SNAME(19)!$T$2:$T$9962),(SNAME(19)!$A$2:$A$9962),"=31",(SNAME(19)!$C$2:$C$9962),"<="&EDATE(TODAY(),-12))
Purpose:
The idea here is that I want to pass the sheets name to the range function based on its code name. the VBA function SNAME seems functional and returns the worksheet's name based on the VBA object code name. When I try to combine that with the range function in the equation, there seems to be an error I'm unsure how to resolve. When I replace SNAME(19) with the sheet name the Equation executes properly.
Goal:
I need to be able to reference the sheet using its code name to avoid future errors of individuals changing the name of the sheets. I'd really appreciate any advice someone might have on the issue.
As far as I know, Excel automatically corrects the formula if any name used there has been changed. So I'm not sure whether you need this construction. Anyway, to make it work we have to change SNAME(19)!$T$2:$T$9962 to something like INDIRECT("'" & SNAME(19) & "'!$T$2:$T$9962").
To make this cleaner, let's put quoting inside the function SNAME. Also, I'd change the type of number to Variant and put Exit For on success:
Function SNAME(number) As String
Dim sh As Worksheet
Dim WORD$
WORD = "Sheet" & number
For Each sh In ThisWorkbook.Worksheets
If sh.CodeName = WORD Then
SNAME = "'" & sh.Name & "'"
Exit For
End If
Next sh
End Function
In the formula, I would use LET to ease editing the function call and to call it once:
=LET(sh, SNAME(19),
SUMIFS(
INDIRECT(sh & "!$T$2:$T$9962"),
INDIRECT(sh & "!$A$2:$A$9962"),
"=31",
INDIRECT(sh & "!$C$2:$C$9962"),
"<=" & EDATE(TODAY(),-12)
)
)
I was wondering whether there is a smarter/more accurate method of debugging userform initializations in VBA that are called from a specific module.
Because when I write an erroneously piece of code in the Userform_initialisatize, I the error I receive states an error, but not where it occurs, the highlighted piece of code is simply call Userform_intitialize and I'm left guessing which piece of code in Sub Userform_intitialize contains the error.
So incrementally building a userform with test runs after every small addition of code work effectively to create a stable Userform initilisation code. Because I know what I changed since the last successful run, but it would save quite some time if I immediately know where the error occurs, especially in cases where trial runs consume a lot of time.
So are there ways to extract in which line the exact error occurs within a called Sub Userform_intitialize in vba Excel 2016?
As #A.S.H already mentioned the handler of Initialize event should remain private. Thats because the Initialize event itself is private so it is intended to be handled by the instances itself and not by anybody else.
So are there ways to extract in which line the exact error occurs within a called Sub Userform_intitialize in vba Excel 2016?
Regarding your question, yes it is still possible to extract the line number where the error occurred. But the lines needs to be part of the source code. Then the function VBA.Information.Erl can be used to get the line number. Example:
UserForm module
Option Explicit
Private Const MODULE_NAME As String = "MyUserForm."
Private Sub UserForm_Initialize()
On Error GoTo Error_In_UserForm_Initialize
1 Const procName As String = "UserForm_Initialize"
' Some code here
2 Dim d As Date
3 d = Now
4 MsgBox "Hi from my userform! 'Now' is '" & d & "'", vbInformation, "Info"
' Here error occures, max. value of Integer is 32.767
5 Dim i As Integer
6 i = 40000
7 Exit Sub
Error_In_UserForm_Initialize:
8 Dim errorDescription As String
9 With Err
10 errorDescription = "Error '" & .Number & "'" & _
" with description '" & .Description & "'" & _
" occured in procedure '" & MODULE_NAME & procName & "'" & _
IIf(Erl <> 0, " on line '" & CStr(Erl) & "'.", ".")
11 End With
12 MsgBox errorDescription, vbCritical, "Error"
End Sub
Some additional readings about Erl here or here.
I am pulling information from worksheets that are in a folder - all files are using the same template - into another worksheet to create a matrix of the information.
I am only pulling three cells from each template and the first two are pulling correctly, because they are on one sheet of the template model but the third is not populating and I am receiving an error.(Run time error 1004. Method of Range of Object_global failed") I am not sure if that has anything to do with my error or if it's something occurring before then.
I have the following:
Private Function GetValuePrice(path, file, sheet, ref)
Dim arg As String
If Right(path, 1) <> "\" Then path = path & "\"
arg = "'" & path & "[" & file & "]" & sheet & "'!" & _
Range(ref).Range("A1").Address(, , xR1C1)
Debug.Assert Var = 0
GetValuePrice = ExecuteExcel4Macro(arg)
End Function
I am receiving an error and when I hit debug it shows me that ref is empty.
(Run time error 1004. Method of Range of Object_global failed")
This is the code i have in the button to pull from the tabs Workload and Project Info from the templates in the folder.
Private Sub CommandButton21_Click()
ThisWorkbook.Sheets("Sheet1").Range("a166:h500").ClearContents
ThisWorkbook.Sheets("Sheet1").Range("j166:v500").ClearContents
Application.Calculation = xlCalculationManual
ProjectInfo
WorkLoad
Application.Calculation = xlCalculationAutomatic
End Sub
I'm not sure what to fix because I run another matrix with the same code but change the file destination and it works.
In my second sub, i was not pulling the correct variable to display in the matrix. I had declared "CC" but was displaying "AA". Therefore changed the AA to CC and it worked.
I have some code which looks for a value with a given sheet name in two separate workbooks.
What I want to do is when the first workbook does not have the sheet, instead of the following prompt coming up, it cancels/throws an error and using the error handling goes to the second spreadsheet. How do I do this?
Currently I am using this code to achieve this:
fFormString1 = "'" & wkBookRef1 & firstShtName & "'!$L$6/1000"
fFormString2 = "'" & wkBookRef2 & firstShtName & "'!$L$6/1000"
Application.DisplayAlerts = False 'Does nothing to the prompt
On Error GoTo tryTwo 'Following only throws error when prompt is canceled
ThisWorkbook.Sheets("Place").Range("E53").Formula = "=" & fFormString1
GoTo endTen
tryTwo:
ThisWorkbook.Sheets("Place").Range("E53").Formula = "=IFERROR(" & fFormString2 & ","""")"
On Error Resume Next
endTen:
Application.DisplayAlerts = True 'Does nothing to the prompt
Note: I wish to do this with the spreadsheet closed ideally. Or visually not present to improve speed and smoothness of operation for my client.
ExecuteExcel4Macro will return a value from a closed workbook. If the worksheet doesn't exist it will throw an error 1004 'A formula in this worksheet contains one or more invalid references.
ExternalWorksheetExists uses this to test it the worksheet exist.
Function ExternalWorksheetExists(FilePath As String, FileName As String, WorksheetName As String) As Boolean
If Right(FilePath, 1) <> "\" Then FilePath = FilePath & "\"
On Error Resume Next
Call ExecuteExcel4Macro("'" & FilePath & "[" & FileName & "]" & WorksheetName & "'!R3C3")
ExternalWorksheetExists = Err.Number = 0
On Error GoTo 0
End Function
When using ExecuteExcel4Macro, all references must be given as R1C1 strings. Here is an example of a valid string:
ExecuteExcel4Macro("'C:\Users\tinzina\Documents\[Book1.xlsm]Sheet1'!R6C12")
Borrowing heavily from Thomas' answer (full credit is due). However it seems that this didn't work for you.
Use ExecuteExcel4Macro but ascribe the value to the variable val. Then check if this is the error you are looking for Error(2023).
Please find the code below:
'Check if the sheet exists in the workbook, used to check which forecast file one should look in
Function ExtSheetExists(formString) As Boolean 'Form string is a formula string with both the worksheet and the workbook
Dim val As Variant
'Tries to execute formula and throws error if it doesn't exist
On Error Resume Next
val = ExecuteExcel4Macro(formString)
ExtSheetExists = (val <> Error(2023)) 'Returns False if the sheet does not exist based on Error 2023
On Error GoTo 0
End Function
I'm trying to call a function with a variable name that is generated at run time based upon a combo box value. This is straightforward in most languages but I can't seem to figure it out in Excel VBA, I suspect this is because I don't really understand how the compiler works. I've found several posts that are close but don't quite seem to do the trick. The code below is wrong but should give an idea of what I want.
Thanks
Sub main()
'run formatting macros for each institution on format button click
Dim fn As String
Dim x As Boolean
'create format function name from CB value
fn = "format_" & CBinst.Value
'run function that returns bool
x = Eval(fn)
...
End Sub
CallByName is what you'll need to accomplish the task.
example:
Code in Sheet1
Option Explicit
Public Function Sum(ByVal x As Integer, ByVal y As Integer) As Long
Sum = x + y
End Function
Code is Module1 (bas module)
Option Explicit
Sub testSum()
Dim methodToCall As String
methodToCall = "Sum"
MsgBox CallByName(Sheet1, methodToCall, VbMethod, 1, 2)
End Sub
Running the method testSum calls the method Sum using the name of the method given in a string variable, passing 2 parameters (1 and 2). The return value of the call to function is returned as output of CallByName.
You should write a function that accepts the CB value as a parameter and then uses a select case to call the appropriate formatting function.
Something similar to this
Function SelectFormatting(Name as String) As Boolean
Select Case CBinst.Value
Case "Text1":
SelectFormatting = Text1FormattingFunction()
Case "Text2":
.
.
.
End Select
End Function
The above will work but not with a large number of names
Use Application.Run(MacroName, Parameters)
You have to may sure that there is a macro but it is better than the above as there is no select statement.
With respect to my answer above you might also find this useful to check whether the macro exists
'=================================================================================
'- CHECK IF A MODULE & SUBROUTINE EXISTS
'- VBA constant : vbext_pk_Proc = All procedures other than property procedures.
'- An error is generated if the Module or Sub() does not exist - so we trap them.
'---------------------------------------------------------------------------------
'- VB Editor : Tools/References - add reference TO ......
'- .... "Microsoft Visual Basic For Applications Extensibility"
'----------------------------------------------------------------------------------
'- Brian Baulsom October 2007
'==================================================================================
Sub MacroExists()
Dim MyModule As Object
Dim MyModuleName As String
Dim MySub As String
Dim MyLine As Long
'---------------------------------------------------------------------------
'- test data
MyModuleName = "TestModule"
MySub = "Number2"
'----------------------------------------------------------------------------
On Error Resume Next
'- MODULE
Set MyModule = ActiveWorkbook.VBProject.vbComponents(MyModuleName).CodeModule
If Err.Number <> 0 Then
MsgBox ("Module : " & MyModuleName & vbCr & "does not exist.")
Exit Sub
End If
'-----------------------------------------------------------------------------
'- SUBROUTINE
'- find first line of subroutine (or error)
MyLine = MyModule.ProcStartLine(MySub, vbext_pk_Proc)
If Err.Number <> 0 Then
MsgBox ("Module exists : " & MyModuleName & vbCr _
& "Sub " & MySub & "( ) : does not exist.")
Else
MsgBox ("Module : " & MyModuleName & vbCr _
& "Subroutine : " & MySub & vbCr _
& "Line Number : " & MyLine)
End If
End Sub
'-----------------------------------------------------------------------------------