How to call an Excel VSTO function from an External Application? - automation

I am looking for a way I can automate an Excel VSTO Document level solution, to be called from an external application, similar to the VBA approach like "xlApp.Run("WorkBook", "MacroName") which we use to auto mate VBA solutions. As this will be a document level solution, Remoting or Webservices are not options. I am using VSTO 2010 and Framework 4 targeting Excel 2007 and 2010 versions.
Say, I have a Document Level Solution with a workbook named "TestBook" which contains a VSTO based class "MyClass" with a static function "GetData(string connection)" I am looking for a way to make a call to GetData function, from an external application. The solution itself will be loaded into excel by the external application too using Application.Workbooks.Open method.
To summarize: I am trying to find a way to get a reference to an object (ideally) and make a call to a function from a Document level solution after loading it. I am trying both ways like (External App)->(Addin)->(Document level object) but the route (External App)->(Document level Object) would be ideal.
Any pointers would be greatly appreciated.

I suspect you need to go about this a bit differently...
The call a method the way you want you might a to resort to something called a "COM Automation AddIn" and implement a UDF this way.
You can then just place the call to your method as a formula inside any sell of that workBook - Excel eveluates that formula by calling your implementation. You can even tell Excel to reevaluate it.
Another option might be to build the functionality as a so-called RTD-server which is referenced in your workbook...
For some information see these links:
http://msdn.microsoft.com/en-us/library/aa662931%28office.11%29.aspx
http://msdn.microsoft.com/en-us/library/aa140060%28office.10%29.aspx
http://weblogs.asp.net/kennykerr/archive/2008/11/13/Rtd3.aspx
http://msdn.microsoft.com/en-us/library/aa140059%28v=office.10%29.aspx
http://msdn.microsoft.com/de-de/library/microsoft.office.interop.excel.rtd%28v=office.11%29.aspx
http://exceldna.codeplex.com/workitem/5902 (free library to ease the development)
http://www.add-in-express.com/docs/net-excel-automation-addins.php and http://www.add-in-express.com/docs/net-excel-rtd-servers.php (commercial library to ease the development)

Related

Local (workbook-specific) functions without using VBA or VSTO

I am looking for a way to create workbook-local functions in C++ (unmanaged). VBA is not appropriate for my use case. Unfortunately, that's the only way I know of to create a workbook-local function. (I'm not terribly familiar with VSTO, so there may be a way there, but I cannot used managed code for this.) I know how to create functions as XLLs, and I know how to create an automation addin that exposes functions through IDispatch. However, both of these seem to be for global functions only.
Does anyone know a way to do this?
Worksheet != Workbook
I don't know of any way to create worksheet-local functions.
In VBA to expose functions you need a standard module (.bas) without the Option Private Module option set, exposing a Public Function - that function is usable in every worksheet of the workbook that's containing it.
A worksheet is essentially a class module, and lives as an object instance: its Public Function members are therefore not usable as worksheet functions.
Functions are workbook-local, not worksheet-local.
Now, that's probably what you meant anyway, given that the add-in approach is being referred to as creating "global" functions, which are available to all workbooks.
As #RubberDuck hinted, creating code via the VBIDE library doesn't require elevation and security settings tweaking if you're extending the VBE itself. This might help (it's the entry point for the add-in #RubberDuck and I are working on). This add-in edits, deletes and adds code in the VBE for a living, and doesn't require tweaking macro security settings.

VBA external modules

Is it possible to have modules be external to the actual Excel file and call the functions/subs within them externally?
My thinking is if there are multiple Excel files that use the same module, instead of updating each one of those files separately when I make a change, can I just update the one module stored on a server or something?
I have doing something like you describe for years. You can move your VBA code to a VB6 ActiveX dll, organize it into classes, and load that dll as a reference from Excel VBA.
This is a good way to reuse non-workbook specific code. For instance, I have code that queries a mainframe. I like to call it from Excel, but the details of the connection and how data is passed are contained in a dll that I can load from Excel, VB6, Word, .NET, wherever. I have a similar dll for reading data from AutoCAD drawings, one for interfacing with a product DB on a MySQL server, etc.
The code that remains in Excel tends to be simple formatting stuff. Say I return a variant array of strings (technically a COM SAFEARRAY) from some library that I wrote. I would then output it into Excel, maybe do a text-to-columns, and have a list of results returned to the user.
You can also pass and return more complex data structures. The beauty of VB6/COM Automation (and I didn't appreciate this until I learned to do it the harder way in VB.NET or C#) is that the data will flow in and out of your components seamlessly and all the necessary interfaces will be created for you.
The main change to your code will be replacing things like ThisWorkbook or ActiveSheet with explicit parameters like (Byval sht as Excel.Worksheet). These will be caught at compile time in VB6 (since it doesn't know what ThisWorkbook is), so you cannot overlook them; you are forced to pass an explicit reference.
I also notice that my code inside the dll becomes more paranoid if it receives a Worksheet or other Excel object as a parameter. In VBA you might have had more assurance that you were passing a good object since it was private to a given workbook. The dll does not know who is calling it, so I view the passed-in object with more suspicion (check if Nothing, sheet name, formatting clues to ensure I am using what I think I am using).
The only downside I see is that you will have to get a copy of Visual Basic 6.0. I bought mine in 1998. It is no longer available from Microsoft, but surely there is someone out there who will sell it to you. The latest service pack is SP6.
You will also have to become familiar with "regsvr32" and "regsvr32 /u" to deal with the "ActiveX can't create component" errors as you open your workbooks on various computers. I just publish my dlls to a mapped network drive and re-register them on the user's computers whenever there is a significant change. Obviously this is a intranet/single company solution. Publishing the updated versions is much more of a pain the farther you are distributed.
Not sure if this would satisfy your needs, but you could create your common module as an "add-in" and so install it so that all files that you open in the same instance of excel would have access to the add-in code.
It would not be my recommended way of doing it because I would be worried about suitable testing of all the excel files that use it, when you make a change, plus the added complexity of getting users to install your add-in (this may not be an issue for you). I have a "developersToolkit" module I use across 8 different Workbooks, but I import the module into each workbook so its stand alone and I can also test changes for compatibility with each of the 8 workbooks.

VBA and late binding: Where do I find the numeric equivalents to constants?

I'm a complete VBA newbie, having decided to teach myself over a weekend, so forgive the stupid question(s). I'm trying to automate some routine tasks involving generating Word documents or emails from an Excel Spreadsheet. Because there will be multiple software versions involved, I am using late binding to open Word and Outlook. My question is: Where can I find a simple reference telling me what the index numbers are that correspond to the application constants? I have killed a lot of time googling to learn that, for example, the Outlook foldertype for "Contacts" is "10". Maybe someone knows of a web link that could save me countless hours of searching?
Update: http://msdn.microsoft.com/en-us/library/office/gg278936%28v=office.14%29.aspx seems to have some of the information I need, although it's not always intuitive where the information is. For example, if it contains the outlook folder type constants, I haven't found them yet.
See here
Enumeration http://msdn.microsoft.com/en-us/library/office/ff860961(v=office.15).aspx
OlDefaultFolders Enumeration http://msdn.microsoft.com/en-us/library/office/ff861868(v=office.15).aspx
I would recommend to add the relevant object libraries to your project as References during development time. You do this by using the Tools - References Menu in the VBA Editor. This makes developing a lot easier as you can use intellisense while writing the code.
If you need only a few Enums or single Constants in your code the easiest way to get their values is to hit [F2] in in VBA Editor while the object libraries are still referenced. Then search for the constants name and copy its value to your code.
Just using the numeric values of the constants in your code makes the code pretty hard to read. So I would recommend to re-declare all the Enums/Constants you actually use in a module in your own project. That massively improves the readability of your code.
So, instead of just copying the value from the VBA Object Browser, I suggest you copy the name and the value and put it your own code as a constant declaration. For your example of the Outlook contacts folder this will look like this:
Public Const olFolderContacts = 10
You can then use the constant in your procedures as you would do with Early Binding.
Should you work on a larger automation project using many of the constants from any one of the Office Object Libraries, you can download ready-made VBA modules containing all the Office constants from my website. You can then just import the relevant modules into your project and are ready to go.
After you finished the main development work, you remove the linked libraries from your project and declare the relevant object variables As Object instead of the actual type.
Always remember to compile your project not to miss any declaration that does not work late binding.

How to Reuse Code with VBA

What is the best way to avoid duplicating code when working in VBA?
I'm used to languages where I can just add an import statement and get access to all a class's public properties and functions, so I can just create a utility class with some common functions and have access to that in any project I choose to import it to. Any time I want to update one of those functions, one edit is all it takes to get it working across all projects.
Is there any good way to replicate this functionality in VBA?
What follows focuses on Excel but I am pretty sure the same would apply to any Office products.
The easy way is to save your reusable code as an addin (*.xla for Excel 2003, *.xlam for Excel 2007+). You then add the addin to Excel and all the spreadsheets you open will have access to the custom functions you have in your addin. If you add specific VBA code to a spreadsheet, you can add a reference to your addin and your VBA code will have access to all the public sub, function and classes of your addin.
In my organisation, we use 3 home made addins - they are stored in C:\Program Files\OrganisationName. And everybody has access to them. When an update is made, we only need to copy the new version to everybody's hard drive and restart Excel and they have the new version.
The addins contain utilities functions such as functions to:
read data from / write data to spreadsheets / files / databases.
usual data manipulation such as removing duplicates from a list
advanced statistical functions
etc.
A few drawbacks:
If you have several instances of Excel open, only one can update the addin, the other instances are in read-only mode
If Excel crashes, the auto recovery mode generally does not save the changes you made on your addin (TBC on newer versions) - there are a few tools to auto save regularly
An alternative is to develop xlls or COM libraries in VB or C# for example, but this is something I have not tried.
There are plenty of tutorials online if you need a more detailed procedure.

How do I add a "browse for file" dialog box to the transferText command?

I'm creating a macro in MS Access that imports a CSV file into a table. I'm using the TransferText action to import the CSV string, and I want to allow the user to browse for the file that contains the CSV string.
How do I show the "browse" dialog box to enable the user to choose the file?
See API: Call the standard Windows File Open/Save dialog box. However this requires VBA code.
An alternative (which I would say is second choice to the API call recommended by #Tony Toews and #draice) would be to use the Application.FileDialog object. This has been part of the Office automation library for as long as VBA has been in Access, but in recent versions of Access (starting with either A2002 or A2003, I don't know which), the top-level Access Application has provided a wrapper for this object. Beware, though, that without a reference to the Office automation library, the ENUM values that show up in Intellisense can't be used without a reference (a helpful error message informs you of this and offers to create the reference). In short, if you use it's best to use it as you would any automation object with late binding, with the exception that you don't have to initialize the top-level object with Application.CreateObject, as it's already there for you to use.
EDIT:
#draice asks:
I don't understand the following
statements that you wrote: "the
top-level Access Application has
provided a wrapper for this object"
"it's best to use it as you would any
automation object with late binding"
Tony's API code will work in every version of Windows, and because MS believes in backward compatibility, they will never break this API call in future versions of Windows.
the FileDialog object is not easy to use in VBA unless you add the reference to the Office Automation library. It is better to minimize the number of references in your Access database, because all sorts of things can mess them up and cause your app to break (any missing reference will prevent all VBA code from running). In order to avoid problems from missing references, we use late binding so that the code you write is not dependent on the outside libary.
Microsoft might remove this object from future versions of Access. The FileSearch object is an analogous situation, in that it was introduced in A95/97 as part of the Office Automation library, and then a wrapper around it was created in A2000, but Microsoft removed it in A2007 (and provided no alternative at all). MS could choose to remove the FileDialog object in future versions of Access and then your code would break. But the API call is never going to break.
Answer can be found at http://www.access-programmers.co.uk/forums/showthread.php?p=917371#post917371.