How to call function from another specific workbook in VBA? - vba

I would like to know if there is a way to call a VBA function or method from another specified workbook's module as it is possible for a specific worksheet without using the Application.Run
For the worksheet I can call for example :
ActiveSheet.MyTest()
if MyTest is defined in the sheet module
But I would like to call a function which is defined in a module
I tried :
ActiveWorkbook.MyTestModule()
ActiveWorkbook.VBProject.VBComponents("MyModule").MyTestModule(myArg)
which don't work generating an error Object does not support this method
I could call
Application.Run(ActiveWorkbook.name & "!MyTestModule", myArg)
But I am not sure of the error handling of the Application.Run and I would find cleaner to run directly the method

In the workbook you want to call from (I'll call this A), you could add a reference to the workbook that you want to call to (I'll call this B) as follows:
In workbook A, open the Microsoft Visual Basic for Applications window (for example, by pressing Alt+F11).
Select Tools, References.
In the References dialog that appears, choose Browse.
In the Add Reference dialog that appears, choose Microsoft Excel Files from the Files of type box, select the file that you want to call (B), and choose Open.
Choose OK to close the References dialog.
In file A, you should then be able to call public module-level functions in file B as if they were in file A. To resolve any naming conflicts, you can prefix calls by the "Project Name" for file B as specified in the General tab of the Project Properties dialog (accessible via the Properties command in the Microsoft Visual Basic for Applications Tools menu). For example, if the "Project Name" for file B was "VBAProjectB", you could call function F from file A using the syntax VBAProjectB.F.

By the way, this also works if you want to access a custom data type in another workbook:
' In workbook ABC, project name Library
Public Type Book_Data
Title As String
Pub_Date As Date
Pub_City As String
End Type
' In workbook DEF (after a ref to Library)
Dim Book_Info As Library.Book_Data
Book_Info.Title = "War and Peace"
Debug.Print Book_Info.Title

Note that if you want to call a method in a module of a different workbook without having the annoying behavior of a DLL-style link as mentioned in the other answers, you can use code like the below:
Dim otherWorkBook As Workbook
Set otherWorkBook = Workbooks.Open("myWorkbook.xlsm")
Call otherWorkBook.Sheets("SomeSheet").someMethod(arg1, arg2...)
Where someMethod() is a method which calls the actual method you're interested in within the module of the other sheet

Related

Can a Variable be stored in an Excel File that can not be accessed through Excel

I just discovered that in MS Word it is possible to store a Variable in a MS Word File that can not be accessed through the regular interface when running Microsoft Word.
Sub SetMyVariable()
Dim VARNAME As String
VARNAME = "HiddenVar"
ActiveDocument.Variables.Add VARNAME, "My special info"
End Sub
This gets saved in the XML Schema under word\settings.xml
I have tried using the ThisWorkbook Object in Excel, but it doesn't seem to have a Variable object that can be added like in word.
I want to know if there is something similar in Excel to store information/varialbes that get saved with the file.
PS: the closest thing I can think of (and use in codig) is a hidden named range.
You can try with the CustomXMLParts property of the Workbook which from the link seems a generic feature of Office products and available in Excel. Given you noted that a user would have to manually inspect the XML within the unzipped xlsx files then this seems to map to the Word Variables feature. The code sample just substitutes ThisWorkbook for ActiveDocument:
Option Explicit
Sub TextXMLPart()
Dim objXMLPart As CustomXMLPart
'add
Set objXMLPart = ThisWorkbook.CustomXMLParts.Add("<foo>bar</foo>")
'inspect
For Each objXMLPart In ThisWorkbook.CustomXMLParts
Debug.Print objXMLPart.XML
Next objXMLPart
End Sub
The accepted answer to this question (which focuses on Excel and vsto) states that:
Custom XML parts For an application-level add in, this is my preferred method of storing any application data that needs to be persisted in a saved xls file without ever being visible to the user.

Getting UCase, Trim, Left libary errors in VBA

I have a application that opens excel files. When I run a macro function in my excel that was opened with the application. I'm getting Compile error "Can't find project or library" on the "UCase" "Trim" "Left" just to name a few. In my macro functions, I have multiply cases of using the above functions. I also have references to "Visual Basic For Application", "Microsoft Excel 12.0 Object Library", "OLE Automation", "Microsoft Office 12.0 Library", Microsoft Forms 2.0 Object Library."
If I run excel by itself without the application, there is no errors. Is there any explanation to why this is happening? Libraries mix match? Works fine for the developer and a few but as for the rest of users, they will get these errors.
It's because those methods belong to Excel Application so that you must call them by preceding their name with the Excel object name (and possibly with its relevant member, too) you must have instantiated before
for instance
example 1: late binding
Option Explicit
Sub LateBindingExcel()
Dim xlApp As Object 'declaring your application object as of "Object" type doesn't require any reference to Excel library
' open an Excel session
Set xlApp = CreateObject("Excel.Application")
' call Excel application WorksheetFunction.Trim()
MsgBox xlApp.WorksheetFunction.Trim(" see how spaces get trimmed by this function ")
End Sub
example 2: early binding
Option Explicit
Sub EarlyBindingExcel()
Dim xlApp As Excel.Application 'declaring your application object as of "Excel.Application" type requires adding Excel library reference to your project
' open an Excel session
Set xlApp = CreateObject("Excel.Application")
' call Excel application WorksheetFunction.Trim()
MsgBox xlApp.WorksheetFunction.Trim(" see how spaces get trimmed by this function ")
End Sub
one sensible difference between the two binding "styles" is that the latter allows you exploiting IntelliSense features while the former doesn't
It is likely due to the 'Visual Basic for Applications' in the Tool reference refers to non-presence library. This may happen if the developer use the library in their customized directory instead of default library.
There are 2 means to solve the issue.
Make global replacement of all string related functions (Format, Left, Right, Mid, Trim etc) with prefix VBA. e.g. VBA.Left. This will force the use of functions within standard library.
Move all your excel sheets to another new workbook and then, select the 'Visual Basic for Applications' from Tool reference.

VBA function error when other users try to use it

I've made this short function to find whether a name is "given name surname" or "surname, given name", however when this is run by another user (on another PC), the result function In error #NAME? :
Function FindName_Function(NameCell As String) As String
Dim FindComma As Long
Dim FindName As String
FindComma = InStr(1, NameCell, ",")
If FindComma <> 0 Then
FindName = VBA.Right(NameCell, Len(NameCell) - FindComma)
Else
FindName = VBA.Left(NameCell, InStr(1, NameCell, " ") - 1)
End If
FindName_Function = FindName
End Function
This is how the function is called:
This is the formula:
="Hello "&FindName_Function(INDEX(Table_HP_Effective_contact_list;MATCH(SiteID;Table_HP_Effective_contact_list[Site];0);4))&","
I believe you use the function as a UDF (User Defined Function) and the #NAME error indicates that the function can't be found or executed. Make sure you store the UDF on a discoverable location and has permission to run. It is not clear from your question -where- you stored the UDF and what the security settings are on the client machines.
What I did is create a new Workbook, added a new Module to the Workbook, copied the UDF in the Module, used it in a cell on the new Workbook and worked without problems. So my guess from the limited information provided is that you stored the UDF in a different location outside the Workbook, inaccessible for the other users to find.
On a side note:
- the VBA. prefix is not necessarily needed
- test if the name is empty, InStr will fail if the name is empty
If you want a better answer, please elaborate on the location of the UDF (where did you create/store the UDF) and what are the macro security settings currently in place on the machines you see the error on.
if u save the function in the same workbook and saved the workbook in *.xlsm format, then the possible cause is user did not enable macro when opening the file.
if u save the function in the same workbook and saved the workbook in *.xlsx format, then u saved it in the wrong format.
if u save the function in another workbook, then that workbook should be saved in Excel Add-In format (*.xlam) and the Add-in must be loaded in Excel.
hope this helps
+
Try use the insert function window to find the function. Select category = "User Defined".
If the function is listed, then try call it from there.
If the function is not listed, then for sure macro for that workbook is not enabled.

How to access a Word public variable in Excel VBA

I'm trying to automate some report generation where Excel VBA is doing all the work. My employer has a standardized set of templates of which all documents are supposed to be generated from. I need to populate one of these templates from Excel VBA. The Word templates utilize VBA extensively.
This is (some of) my Excel VBA code:
Sub GenerateReport() ' (Tables, InputDataObj)
' code generating the WordApp object (works!)
WordApp.Documents.Add Template:="Brev.dot"
' Getting user information from Utilities.Userinfo macro in Document
Call WordApp.Run("Autoexec") ' generating a public variable
Call WordApp.Run("Utilities.UserInfo")
' more code
End sub
In the Word VBA Autoexec module, a public variable named user is defined and declared. The Userinfo sub from the Utilities module populates user. Both these routines are run without any complaints from VBA. I would then like to be able to access the user variable in my Excel VBA, but I get the following error
Compile Error: Variable not yet created in this context.
How can I access the Word VBA variable in Excel VBA? I thought it more or less was the same?
EDIT: the user variable is a user defined Type with only String attributes. Copying the Word VBA functions that populate the user variable is absolutely doable, just more work than I though was necessary...
In a Word module:
Public Function GetUserVariable() As String '// or whatever data type
GetUserVariable = user
End Function
In an Excel module:
myUser = WordApp.Run("GetUserVariable")
Alternatively, you could be able to replicate the variables value - as it's called user I suspect it is returning some information about a user, or author, of a document. In which case one of the following might be what you're after:
'// Username assigned to the application
MsgBox WordApp.UserName
'// Username defined by the system
MsgBox Environ$("USERNAME")
'// Name of the author of the file specified
MsgBox CreateObject("Shell.Application").Namespace("C:\Users\Documents").GetDetailsOf("MyDocument.doc", 9)
Another option - if you could only add a line of code to the Utilities.UserInfo sub (after setting your public variable):
ActiveDocument.Variables("var_user") = user
Then you could access it easily afterwards in Excel:
Sub GenerateReport() ' (Tables, InputDataObj)
' code generating the WordApp object (works!)
'I am assuming your WordApp object is public, as you don't declare it.
'Capture the new document object
Dim newdoc as Object
set newdoc = WordApp.Documents.Add(Template:="Brev.dot")
' Getting user information from Utilities.Userinfo macro in Document
Call WordApp.Run("Autoexec") ' generating a public variable
Call WordApp.Run("Utilities.UserInfo")
'Get and show the value of "user"
Dim user as String
user = newdoc.Variables("var_user")
msgbox, user
End Sub
This is assuming that useris a string.
EDIT: As it is a requirement to work only on the Excel VBA, I would definely try the approach suggested by Scott and MacroMan - replicating the same functionality of the Word macros in Excel - if possible.
I assume that you've already ruled out the possibility of using an edited copy of the original template, set in a public folder...
For the sake of completness, there is another possibility: actually it is possible to inject VBA code in a Word document without the VBProject Object Model, by "brute force". If you rename a Word document as a .zip file and open it, you will notice a \word\vbaProject.bin file in it. This file contains the VBA project for the document and, in principle, one could add or change VBA code by modifying or replacing it.
I did some tests transplanting code from one document to another by simply copying the vbaProject.bin file, and the concept works. If you are interested in learning more about this file, this topic could be of use.
Notice, however, that to do what you want with such a technique would be somewhat complex (it would involve, for starters, updating zip files from your Excel VBA), and would require a lot of experimentation to mitigate the risk of accidentally corrupting your files. Definetly not recommended if you are looking for an easy and simple solution - but it is possible.

VBA - force excel to save in custom format

For a project at work i would like to allow the user to create an export of data to be saved for a single project, since it will not be possible to save my main workbook for each project.
I have created a function that exports the data to another workbook, but now i want to make sure that the user won't change the exported data directly (By setting another file format, i think the users would think more about opening the workbook, and therefore not do it by "accident")
So my question is:
Can i force the user to save the file as a .clg file?
Edit:
I solved the problem by saving the file with .SaveAs and specifying the format as "51" (The same as .xlsx"). I used GetSaveAsFilename to allow the user to decide the location (i.e. the project folder), and only browse for .clg files.
Dim sFullName As String
sFullName = exportFile.Application.GetSaveAsFilename(thisFile.Sheets("Project information").Cells(1, 2).Value, "Export files (*.clg),*.clg", , "Choose export location", "Export")
If Not Len(sFullName) = 0 Or sFullName = "False" Then
exportFile.SaveAs Filename:=sFullName, FileFormat:=51
End If
exportFile.Close
If someone wants to use Mr. Monshaw's approach, one way to go is to create a class module, which has a variable withevents - which means that any event that fires in the variable, fires in your module.
Public WithEvents wbk As Workbook
Just remember to keep the instance of the class in memory, for example by adding it to a collection.
Another way to use the BeforeSave is to dynamically write the code, see http://www.ozgrid.com/forum/showthread.php?t=163903
Is 'clg' a format you use? or just a random not xls/x extension?
If the file does need to be viewed and viewed in excel (guessing your not exporting windows catalogue files?) then changing the extension might not be the best move.
If you create your export using VBA, then you can automate some things to make it more apparent to the users that they should not be editing the data.
The Workbook.SaveAs method has the option for "ReadOnlyRecommended" which will inform the user that the workbook should be opened read-only when opening it. (its optional though, so they could ignore it)
Assuming the data is always read-only you could also lock the workbook so that no changes can be made.
Update
If you want to save an Excel Workbook with a custom extension (and not as a custom format) then using the Workbook.SaveAs method will work. ex: ThisWorkbook.SaveAs "Report.clg"
You'll either need to have your export code do this for you, or if its a more manual process you can just create a macro with a button to invoke the save-as for you instead of using the standard save-as dialogue.
If you want to save as a custom format, and it is not one of the ones supported by Workbook.SaveAs (supported formats) you will have to do as #Mr.Monshaw suggested and watch for the onbeforesave and generate the file manually. (unlikely someone has done an excel to windows catalogue converter)
im not sure what the constant for clg would be but the fileformat property of SaveAs in vba would allow you to decide what format to save in
i saw a clever solution to something similar to this if you can find the fileformat, just add a macro to your book with a workbook event like this:
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Call SaveAsCLG(SaveAsUI) 'set save type
Cancel = True 'cancel user save
End Sub
that will override a user save-as request