What does Excel VBA keyword Any exactly mean? [duplicate] - excel

While trying to define an Any() function like python's, I found that I couldn't name anything "Any".
Attempting to name a Function, Sub, Const, or variable any will throw a syntax error and the VBA IDE will highlight it.
I know any not a particularly great name, but why is it throwing a syntax error? The only reason I could think of was that it might be a reserved keyword, but it's not.

VBA (Visual Basic for Applications) is not VB.NET, even though they share the same "Visual Basic" monikor and a similar syntax. (The linked documentation is for VB.NET, not VBA.)
VB6 and VBA in Microsoft Office (eg. Access, Excel) handles Any as a reserved word, and it cannot be used as an identifier:
You might also encounter errors if you use a reserved word to name a control, an object, or a variable. The error messages you receive don't necessarily tell you that a reserved word is the cause of the problem.
In VB.NET, however, there is no problem using Any as a variable name or other identifier:
Dim Any as String = "Hello world!" 'works just fine in VB.NET

To get an updated information to this question and comments, Microsoft states on their own page at https://learn.microsoft.com/en-us/dotnet/visual-basic/language-reference/error-messages/as-any-is-not-supported-in-declare-statements:
The Any data type was used with Declare statements in Visual Basic 6.0
and earlier versions to permit the use of arguments that could contain
any type of data. Visual Basic supports overloading, however, and so
makes the Any data type obsolete.

Related

How to iterate through the members of a standard module

My goal is to list the procedures exposed by the compiled VBProject, as visible in the Excel Macros menu or when typing a cell formula which makes appear availble UDF. A Solution that list any member (Enum, Sub, Function, etc.) of a CodeModule or of the full VBProject would be ideal.
I am not looking for a code parsing solution.
I could find old mentions of a way to do so in VB with the VBIDE library, which ( should / used to ) expose a VBIDE.Member class (accessible via the CodeModule.Members property) with those interesting properties:
Member.Name As String
Member.Type As VBIDE.vbext_MemberType
Member.Scope As VBIDE.vbext_Scope
Here is a bigger list, probably exhaustive:
Bindable
DisplayBind
StandarMethod
Browsable
HelpContextID
StaticCategory
Hidden
Type
Code
Location
Name
UIDefault
Collection
PropertyPage
VBE
DefaultBind
RequestEdit
Description
Scope
For a reason I don't understand, I cannot access them via VBA and the VBIDE library, they are not even listed as hidden members in the Object Browser.
Is there a way to get them?
Rubberduck (previously known as Mat's Mug here on Stack Overflow) aka Mathieu Guindon suggests it is possible though: https://rubberduckvba.wordpress.com/2016/03/24/a-reflection-on-vba-reflection/
Another idea: how to use Reflection with VBA?
Again, he suggests it is possible: https://rubberduckvba.wordpress.com/2019/04/10/whats-wrong-with-vba/
Note: the Rubberduck COM Add-In that integrates with the VBE, is able to do it (using .NET as I understand it)

Microsoft documentation for a typical Excel 365 object refers to "an object in the Applies To list", but where is the "Applies to list"?

I think my Question's title clearly expresses the essence of the information I need. I Googled where is the "applies to list" in vba excel and a few variations of that, all with quotes as shown, which should make Google look for that exact phrase. I also used Google Advanced Search, specifying "this exact word or phrase" to be "applies to list" (with and without quotes). No hits.
For example, here is the last documentation that I viewed yesterday:
<<
MakeCompiledFile method
...
Causes the current project to be written as a DLL. The DLL name is specified by the BuildFileName property.
Syntax
object.MakeCompiledFile
The object placeholder represents an object expression that evaluates to an object in the Applies To list... >>
... which is never shown. I've lived without seeing such a list for many years, but who knows what a person might learn from viewing one.
This doesn't help with the web-based docs, but if you locate the corresponding CHM file in your Office install you can see there's an "Applies To" link at the top - that will give you a list of object types or link you directly to the related object.
So I'd guess the web version was converted from the CHM (but with missing functionality).

Using Text Functions in VBA

I am using LEFT, UCASE, MID and a few other text functions.
eg If UCase(Mid(VBAFormField, 4, 3)) = "ABC" Then
For most users the code works fine. On one PC it gives an error and seems to need modification.
eg If UCase(Application.Worksheetfunction.Mid(VBAFormField, 4, 3)) = "ABC" Then
Things I tried:
Excel versions are identical
VBA Reference libraries are the same on both computers
Any ideas?
I have frequently experienced that VBA (2010) will highlight a missing library for an item for which the library is not missing because another library in fact is missing. It's usually just the first reference to any library in the code. So you should look for a missing library further down in the code, more likely, a not so standard object.
I have also frequently experienced (with any version of VBA up to 365) that errors aren't shown right away but are "remembered". I have explained this to myself by acknowledging that given a large project VBA will assemble the parts it needs as it needs them but once an error was discovered it won't be overlooked a second time and is flagged right away. This behavior could explain why only one of your PCs objects to the code. The others just didn't get around to it yet.
I would guess there must be an add-on or a language setting that is causing a conflict. However as Tim Williams pointed out, you're using the worksheet function in VBA when a VBA function exists that does this exact task you need to complete it.
While probably not necessary, you could go even further and scope the formulas to be vba specific just to ensure the proper library is being utilized.
MsgBox VBA.UCase(VBA.Mid("SomeText!", 4, 3))

Compile throws a "User-defined type not defined" error but does not go to the offending line of code

Symptoms
This is a symptom specifically when compiling an Excel VBA project. The following error occurs:
User-defined type not defined
However, the code that produces this error is not highlighted by the compiler and so I cannot identify the issue.
What I already know and have tried
This is a "User-defined type not defined" error that I have seen before with simple issues such as naming something As Strig instead of As String. However, this particular error is only popping up during the Debug > Compile VBAProject menu option and when the error message box pops up it does not highlight the line of code that the error is occurring in.
After a lot of research I have found that this bug can be related to missing references and I have ruled this out as I have included all needed references and Toolbox objects.
To ensure I wasn't missing any obvious missing Dim statements I have added Option Explicit to all code pages (forms included) to make sure nothing was missing. The error still shows when running a compile.
There is also this known bug that states the issue has been known to happen because of the VB6 projects using binary compatibility:
Turn off Binary Compatibility and compile the project. Visual Basic
will highlight the line of code that contains the User Defined Type
that is not defined. After resolving the problem, Binary Compatibility
can be turned back on.
I found this article via this Question and Answer, however, I cannot find this option in the standard Excel VBA editor.
Help save mine and others' sanity!
I know from Google searches and other questions that I am not the only one who has had this issue.
I have tried going through the code manually but there are simply too many lines to feasibly do so.
Is there a way of turning off Binary Compatibility in Excel VBA projects? How do people find this offending line of code if they can't debug to what they need to change? Any help would be lovely!
Thank you in advance.
Edit: I have found the offending line of code and so my particular issue is solved The problem is still here after removing that particular line - it was a misspelt control name on a form being referenced in its code. This still does not solve the particular issue of how you would go about finding this offending code was the issue. Are we able to find a good way of finding the offending code when this bug happens so others in the future can avoid this agony?
My solution is not good news but at least it should work.
My case is: I have a .xlsm from a coworker. 1) I open it and click the button: it works fine. 2) I save the file, close excel, open the file again: now it doesn't work anymore. The conclusion is: the code is OK but excel can't manage references correctly. (I've tried removing the re-adding the references without any success)
So the solution is to declare every referenced object as Variant and use CreateObject("Foo.Bar") instead of New Foo.Bar.
For example:
Dim objXML As MSXML2.DOMDocument
Set objXML = New MSXML2.DOMDocument
Replaced by:
Dim objXML As Variant
Set objXML = CreateObject("MSXML2.DOMDocument")
I had exactly the same problem (always seems to occur when I try to implement a Interface onto a userform. Download and install Code Cleaner from here. This is a freeware utility that has saved me on numerous occasions. With your VBA project open, run the "Clean Code..." option. Make sure you check the "backup project" and/or "export all code modules" to safe locations before running the clean. As far as I understand it, this utility exports and then re-imports all modules and classes, which eliminates compiler errors that have crept into the code. Worked like a charm for me! Good luck.
Since it sounds like you've tried many different potentional solutions, you'll probably have to do this the long methodical way now.
Create a new blank workbook. Then piece by piece copy your old workbook into it. Add a reference, write a little bit of code to test it. Ensure it compiles, ensure it runs. Add a sub or function, again, write a little test sub to run it, also ensure it compiles. Repeat this process slowly adding and testing everything.
You can speed this up a bit by first trying larger chunks, then when you find one that triggers the problem, remove it and break it into smaller peices for testing.
Either you will find the offender, or you'll have a new workbook that magically does not have the problem. The latter would be due to some sort of hidden corruption in the workbook file, probably in the binary vbproject part.
Welcome to the world of debugging without debuggers or other helpful tools to do the heavy lifting for you!
Just letting you all know I had this problem too. Rather than the code, the issue lay with what macro a button was calling. (It had been calling the 'createroutes.createroutes' macro, but I had renamed the 'createroutes' module to 'routes'.)
Therefore the problem was fixed by pointing the button to the correct location.
Had a similiar experience, but it was because I had renamed an enum in one of my classes. I exported and re-imported the Classes that had referred to the old enum and the error message disappeared. This suggests it is a caching issue in the VBA environment.
I was able to fix the error by
Completely closing Access
Renaming the database file
Opening the renamed database file in Access.
Accepted various security warnings and prompts.
Not only did I choose to Enable Macros, but also accepted to make the renamed database a Trusted Document.
The previous file had also been marked as a Trusted Document.
Successfully compile the VBA project without error, no changes to code.
After the successful compile, I was able to close Access again, rename it back to the original filename. I had to reply to the same security prompts, but once I opened the VBA project it still compiled without error.
A little history of this case and observations:
I'm posting this answer because my observed symptoms were a little different than others and/or my solution seems unique.
At least during part of the time I experienced the error, my VBA window was showing two extra, "mysterious" projects. Regrettably I did not record the names before I resolved the error. One was something like ACXTOOLS. The modules inside could not be opened.
I think the original problem was indeed due to bad code since I had made major changes to a form before attempting to update its module code. But even after fixing the code the error persisted. I knew the code worked, because the form would load and no errors. As the original post states, the “User-defined type not defined” error would appear but it would not go to any offending line of code.
Prior to finding this error, I ensured all necessary references were added. I compacted and repaired the database more than once. I closed down Access and reopened the file numerous times between various fix attempts. I removed the suspected offending form, but still got the error. I tried other various steps suggested here and on other forums, but nothing fix the problem.
I stumbled upon this fix when I made a backup copy for attempting drastic measures, like deleting one form/module at a time. But upon opening the backup copy, the problem did not reoccur.
I had this problem with an ordinary VB6 program. It turned out that I had omitted a class definition, not a user-defined type. Apparently VB saw something like "Thing.name" and assumed Thing was a UDT. Yes, it's a serious VB6 bug, but you could hardly expect Microsoft to support something they sold sixteen years ago. So what versions of the various products involved are you using? This is only interesting if it occurs with a product that MS supports.
I know this is old, but I had a similar problem and found a fix:
I had the same issue with a module I ported from Excel into Access, in an unrelated UDF I was dimming 'As Range' but ranges don't exist in Access. You may be using a variable type without having the proper reference library turned on.
If you have any non-standard dims google them and see if you're missing the reference to that library under tools.
-E
For future reference -
I had this issue with this piece of code in Microsoft Access with the debugger highlighting the line with the comment:
Option Compare Database
Option Explicit
Dim strSQL As String
Dim rstrSQL As String
Dim strTempPass As String
Private Sub btnForgotPassword_Click()
On Error GoTo ErrorHandler
Dim oApp As Outlook.Application '<---------------------------------Offending line
Dim oMail As MailItem
Set oApp = CreateObject("Outlook.application") 'this is the "instance" of Outlook
Set oMail = oApp.CreateItem(olMailItem) 'this is the actual "email"
I had to select references that were previously unselected. They were
Microsoft Outlook 15.0 Object Library
Microsoft Outlook View Control
For the Scripting.Dictionary type, you can either use late binding (as already pointed out ) with:
Dim Dict as Object
Set Dict = CreateObject("Scripting.Dictionary")
Which works, but you don't get the code auto completion. Or you use early binding, but you need to make sure that VBA can find the Scripting.Dictionary type by adding the reference to the Microsoft Scripting Library via VBA-->Tools-->References--> "Microsoft Scripting Runtime". Then you can use:
Dim Dict as Scripting.Dictionary
Set Dict = New Scripting.Dictionary
... and auto completion will work.
Possible solution, you are trying to work with Powerpoint via Excel VBA and you didn't activate Powerpoint Object Library first.
To do this, in the VBA editor top menu select Tools, References, then scroll down to click the library called Microsoft Powerpoint xx.x Object Library. Office 2007 is library 12, each Office version has a different library. FYI, I've experienced some odd errors and file corruption when I activate the 2007 library but someone tries to open and run this macro using Excel 2003. The old version of Excel doesn't recognize the newer library, which seems to cause problems.
When I had this error in a MS Access database I used the /decompile command line switch along with the Compact/Repair option. Worked for me.
I had removed a reference that I was sure my code no longer used and started getting this error.
Late Binding
This error can occur due to a missing reference. For example when changing from early binding to late binding, by eliminating the reference, some code may remain that references data types specific the the dropped reference.
Try including the reference to see if the problem disappears.
Maybe the error is not a compiler error but a linker error, so the specific line is unknown. Shame on Microsoft!
After years I have discovered one, if not the, answer to the Microsoft bug of the 'User-defined type not defined' error in Excel. I'm running Excel 2010 for Windows.
If you have a UDF named for example 'xyz()', then if you invoke a non-existent entity beginning with that name followed by a period followed by other chars -- e.g., if you try to invoke non-existent range name 'xyz.abc', the stupid app throws that wrong msg., after which it returns you to your sheet.
In my case it was especially unnerving, because I have UDFs named with just one letter, e.g. x(), y(), etc., and I also have range names that include periods--'x.a', 'c.d', etc. Every time I, say, misspelled a range name--for example, 'x.h', the 'User-defined …' error was thrown simply because a UDF named 'x()' existed somewhere in my project.
It took several hrs. to diagnose. Suggestions above to incrementally remove code from your project, or conversely strip all code and incrementally add it back in, were on the right track but they didn't follow thru. It has nothing to do with code per se; it has only to do with the name of the first line of code in each proc, namely the Sub MyProc or Function MyProc line naming the proc. It was when I commented out one of my 1-letter-named UDFs in a completely unrelated part of the project that the bugged error msg. went away, and from there, after another hr. or so, I was able to generalize the rule as stated.
Perhaps the bug also occurs with punctuation characters other than period ('.') But there aren't very many non-alpha chars permitted in a range name; underline ('_') is allowed, but using it in the manner described doesn't seem to throw the bug.
Jim Luedke
I had the same error yesterday: I had two classes, cProgress and cProgressEx, in my project, one of which was no longer used, and when I removed cProgress class I was given this same compilation error.
I managed to fix the error as follows:
Exported all modules, forms and classes to the hard drive
Removed everything from the project
Saved the project
Reimported all modules, forms and classes, removed cProgress class, and compiled.
The error disappeared.
An different problem with the same symptom: I had a class implemented which was not defined. It was wrapped with a #if that I thought should have not allowed the compiler to see it, but not so. Remove the comment out the Implements statement and all is well. I assume importing the definition would also work...
I had the same issue, if you enable Microsoft Scripting Runtime you should be good. You can do so under tools > references > and then check the box for Microsoft Scripting Runtime. This should solve your issue.
A bit late, and not a complete solution either, but for everyone who gets hit by this error without any obvious reason (having all the references defined, etc.) This thread put me on the correct track though. The issue seems to originate from some caching related bug in MS Office VBA Editor.
After making some changes to a project including about 40 forms with code modules plus 40 classes and some global modules in MS Access 2016 the compilation failed.
Commenting out code was obviously not an option, nor did exporting and re-importing all the 80+ files seem reasonable.
Concentrating on what had recently been changed, my suspicions focused on removal of one class module.
Having no better ideas I re-cereated an empty class module with the same name that had previously been removed. And voliá the error was gone!
It was even possible to remove the unused class module again without the error reappearing, until any changes were saved to a form module which previously had contained a WithEvents declaration involving the now removed class.
Not completely sure if WithEvents declaration really is what triggers the error even after the declaration has been removed. And no clues how to actually find out (without having information about the development history) which Form might be the culprit...
But what finally solved the issue was:
In VBE - Copy all code from Form code module
In Access open Form in Design view and set Has Module property to No
Save the project (this removes any code associated with Form)
(Not sure if need to close and re-open Access, but I did it)
Open Form in Design view and set Has Module property back to Yes
In VBE paste back the copied out module code
Save
I had this same problem when I inherited another analyst's workbook. He assigned macros to each button on his worksheet. When I changed the name of the module (not the procedure name), it kicked up this error.
The fix was easy. Have the button run an _OnClick() event procedure in the worksheet and have that procedure explicitly Call MyProcedureName().
I've seen this error too when the code stopped at the line:
Dim myNode As MSXML2.IXMLDOMNode
I found out that I had to add "Microsoft XML, v6.0" via Tools > Preferences.
Then it worked for me.
I don't have an answer as to what is actually wrong or to directly fix it.
I can report that exporting all the code and forms, then importing them into a fresh workbook fixed it for me. All the work was recovered, and nothing more needed to be done to get back to work. Since that works, it seems clear that it must be a bug in Excel, or whatever it is would still be missing in the new version.
I do have a possible clue to how this Excel (2007) bug works. It seems to be associated with the removal of a class module (or two). (But not every class module, every time for some reason.) No code was using any part of the removed modules (or the new imported version would not work either). One module had been cut down to nothing but some comments (in hope of removing anything that could be "missing" later) so that there was no code visibly connecting it to any other part of the project. But the error came up shortly after I removed the code module entirely. The error also stopped when the removed (but exported just in case) modules were returned. So for convenience, this project had been packing two unused code modules to avoid this error.
It appears then, that somehow some element of some departed modules remains in the VBA environment, even if nothing is being used from these (there two in this case) class modules anywhere in the project. Consequently, the debug/compile finds that element, and it is no surprise that something is missing because it is. Since the thing that is missing is nowhere in the project there is nothing to highlight so there is a message only and nothing else happens. That would explain the observed behavior.

Excel 2007 UDF: how to add function description, argument help?

The description
I am writing a couple of Excel UDFs in COM Servers. I'd like to get the standard help (Insert Function dialog) that you get when you press fx. Yes, I can see my COM Server listed in among the Category drop down, but
I also see Equals, GetHashCode, GetType, and ToString (which are fairly undesirable to expose to the Excel user),
selecting my COM Server brings up the *Function Arguments*[1] dialog with no argument information and no description of the function.
Here is the lameness that I get:
Insert Function dialog http://www.iwebthereforeiam.com/files/Insert%20function%20dialog.gif
Excel Function Arguments dialog http://www.iwebthereforeiam.com/files/Function%20Arguments%20dialog.gif
The question
Are there .NET attributes I could put on the methods to pass this through to Excel?
Can I provide a description of the function?
Can I provide a description of the parameters?
Can I provide a category name for my functions, so that I get something better than just the ProgID?
(I see that it looks sadly easy to do in ExcelDNA, but I am not going that route. Emulating govert's code [custom attributes, a loader of some sort, etc.] looks like it would be pretty hard.)
Additional background
If you have not done work with Excel + COM Servers before, here are some useful resources to get up to speed:
Previous StackOverflow questions:
How to get COM Server for Excel written in VB.NET installed and registered in Automation Servers list?
How Add a COM-Exposed .NET Project to the VB6 (or VBA) References Dialog?
Other resources:
Writing user defined functions for Excel in .NET
Build and Deploy a .NET COM Assembly
Writing Custom Excel Worksheet Functions in C#
Edit 2009-10-20 14:10
I tried out calling Application.MacroOptions in a Sub New().
No Sub New()
Semi-acceptable: Function is listed under category ProgID.
Shared Sub New()
Not acceptable: build-time error.Cannot register assembly "...\Foo.dll".
Exception has been thrown by the target of an invocation.
Sub New()
Not acceptable: category is not listed in Insert Function dialog.
I suspect this is a problem both for MacroOptions and for the more involved route recommended by Charles.
Edit 2009-10-20 14:55
On the plus side, Mike's recommendation to create an interface to implement did kill off the annoying extra methods that were exposed.
Edit 2009-10-20 15:00
This Microsoft article from early 2007 (via Mike's link) seems a rather complete answer on the topic:
Automation Add-ins and the Function
Wizard
Each Automation Add-in has its own
category in the Excel Function Wizard.
The category name is the ProgID for
the Add-in; you cannot specify a
different category name for Automation
Add-in functions. Additionally, there
is no way to specify function
descriptions, argument descriptions,
or help for Automation Add-in
functions in the Function Wizard.
1 Huh, a StackOverFlow bug. It looks like you cannot italicize a string inside an explicit HTML ul-list?
Some of this is easy to correct, other parts of it is rather hard. All of it is do-able, though, if you are willing to put the time in.
You wrote:
I also see Equals, GetHashCode,
GetType, and ToString (which are
fairly undesirable to expose to the
Excel user)
Yes, agreed, this definitely undesirable, but it can be prevented. This is occurring because your class is inheriting from 'System.Object', as all .NET classes do, and your default interface that is exposed to COM is including these members. This occurs, for example, if you use the 'ClassInterfaceAttribute', using the setting 'ClassInterfaceType.AutoDual'.
E.g. in C#:
[ClassInterface(ClassInterfaceType.AutoDual)]
In VB.NET:
<ClassInterface(ClassInterfaceType.AutoDual)>
The use of 'ClassInterfaceType.AutoDual' should be avoided, however, in order to prevent the members inherited from 'System.Object' from being exposed (as well as to prevent potential versioning issues in the future). Instead, define your own interface, implement the interface in your class, and then mark your class with the 'ClassInterface' attribute with a value of 'ClassInterfaceType.None'.
E.g., using C#:
[ComVisible(true)]
[Guid("5B88B8D0-8AF1-4741-A645-3D362A31BD37")]
public interface IClassName
{
double AddTwo(double x, double y);
}
[ComVisible(true)]
[Guid("010B0245-55BB-4485-ABAF-46DF4356DB7B")]
[ProgId("ProjectName.ClassName")]
[ComDefaultInterface(typeof(IClassName))]
[ClassInterface(ClassInterfaceType.None)]
public class ClassName : IClassName
{
public double AddTwo(double x, double y)
{
return x + y;
}
}
Using VB.NET:
<ComVisible(True)> _
<Guid("5B88B8D0-8AF1-4741-A645-3D362A31BD37")> _
Public Interface IClassName
Function AddTwo(ByVal x As Double, ByVal y As Double) As Double
End Interface
<ComVisible(True)> _
<Guid("010B0245-55BB-4485-ABAF-46DF4356DB7B")> _
<ProgId("ProjectName.ClassName")> _
<ComDefaultInterface(GetType(IClassName))> _
<ClassInterface(ClassInterfaceType.None)> _
Public Class ClassName
Implements IClassName
Public Function AddTwo(ByVal x As Double, ByVal y As Double) As Double _
Implements IClassName.AddTwo
Return x + y
End Function
End Class
By making use of the 'ClassInterfaceAtribute' with a value of 'ClassInterfaceType.None', the inherited 'System.Object' memebers are excluded, because the class's interface is not made COM-visible. Instead, only the implemented interface ('IClassName' in this example) is exported to COM.
The above is also making use of the 'ComDefaultInterfaceAttribute'. This is not very important, and does nothing if you implement only one interface -- as in this example -- but it is a good idea in case you add an interface later, such as IDTExtensibility2.
For more detail on this, see:
(1) Managed Automation Add-ins by Andrew Whitechapel.
(2) Writing Custom Excel Worksheet Functions in C# by Gabhan Berry.
Ok, now to the hard part. You wrote:
Selecting my COM Server brings up the
Function Arguments[1] dialog with no argument information and no
description of the function.
Can I provide a description of the
function?
Can I provide a description of the
parameters?
Can I provide a category name for my
functions, so that I get something
better than just the ProgID?
The easiest approach here is to make use of the Application.MacroOptions method. This method enables you to provide a description of the function and specify which category under which you want it to be displayed. This approach does not allow you to specify any information for the functions parameters, unfortunately, but techniques that allow you to do so are very complicated, which I'll get to later. [Correction: The 'Application.MacroOptions' method only works for UDFs created via VBA and cannot be used for automation add-ins. Read on for more complex approaches to handle registration of UDFs containe in an automation add-ins -- Mike Rosenblum 2009.10.20]
Note that the help files for Excel 2003 and help files for Excel 2007 state that a string can be provided to the category argument in order to provide a custom category name of your choice. Beware, however, that the help files for Excel 2002 do not. I do not know if this is an omission in the Excel 2002 help files, or if this is a new capability as of Excel 2003. I'm guessing the latter, but you would have to test to be sure.
The only way to get your parameter information into the Function Wizard is to use a rather complex technique involving the 'Excel.Application.ExecuteExcel4Macro' method. Be warned though: many Excel MVPs have struggled with this approach and failed to produce a result that is reliable. More recently, though, it appears that Jan Karel Pieterse (JKP) has gotten it worked out and has published the details here: Registering a User Defined Function with Excel.
Skimming that article you'll see that it is not for the faint of heart. Part of the problem is that he wrote it for VBA / VB 6.0 and so all that code would have to be translated to VB.NET or C#. The key command, however, is the 'Excel.Application.ExecuteExcel4Macro' method, which is exposed to .NET, so everything should work fine.
As a practical matter, however, I vastly prefer using the 'Excel.Application.MacroOptions' approach because it is simple and reliable. It does not provide parameter information, but I have not yet had a strong need to motivate me to take on the 'ExecuteExcel4Macro' approach.
So, good luck with this, but my advice would be to utilize the 'MacroOptions', unless you are being paid by the hour. ;-)
-- Mike
Follow-up to Hugh's Replies
I tried out calling
Application.MacroOptions in a Sub
New().
No Sub New() Semi-acceptable: Function
is listed under category ProgID.
Shared Sub New() Not acceptable:
build-time error. Cannot register
assembly "...\Foo.dll". Exception has
been thrown by the target of an
invocation.
Sub New() Not acceptable: category is
not listed in Insert Function dialog.
I suspect this is a problem both for
MacroOptions and for the more involved
route recommended by Charles.
You can't use shared (aka "static") classes or constructors when exposing your classes to COM because COM has no knowledge of this concept and so it cannot compile -- as you found out! You might be able to apply a 'COMVisibleAttribute' with a value of 'False' to the shared constructor, to at least allow it to compile. But this wouldn't help you in this case anyway...
Trying to register your automation add-in via the automation add-in itself might prove tricky. I realize that this is desirable in order to keep it as a single, stand-alone component, but it might not be possible. Or at least this won't be easy.
The issue is that automation add-ins are demand loaded. That is, they are not really there until Excel attempts to access the first worksheet function from your automation add-in. There are two issues related to this:
(1) If you put your registration code within the constructor for your class, then, by definition, your function wizard information cannot exist until the function has been called for the first time.
(2) Your constructor might be executing when Excel is not ready to accept automation commands. For example, an automation add-in is typically demand-loaded when the user begins to type in the name of one of the user-defined functions (UDFs) defined in the automation add-in. The result is that the cell is in edit-mode when your automation add-in first loads. If you have automation code within your constructor during edit mode, many commands will fail. I do not know if the 'Excel.Application.MacroOptions' or 'Excel.Application.Excel4Macro' methods have a problem with this, but many commands will choke when trying to execute while the cell is in edit mode. And if the automation add-in is being loaded for the first time because it is being called while the Function Wizard is open, I have no idea if these methods can work right.
There is no easy solution to this if you wish to have your automation add-in to be completely stand-alone with no other support. You can, however, create a managed COM add-in that will register your automation add-in for you via 'Excel.Application.MacroOptions' or the 'Excel.Application.Excel4Macro' approach when Excel starts up. The managed COM add-in class can be in the same assembly as that of your automation add-in, so you still only need one assembly.
By the way, you could even use a VBA workbook or .XLA add-in to do the same -- use the Workbook.Open event in VBA to call the registration code. You just need something to call your registration code when Excel starts up. The advantage to using VBA in this case is that you could utilize the code from the Jan Karel Pieterse's Registering a User Defined Function with Excel article as-is, without having to translate it to .NET.
On the plus side, Mike's
recommendation to create an interface
to implement did kill off the annoying
extra methods that were exposed.
lol, I'm glad something worked!
This Microsoft article from early 2007
(via Mike's link) seems a rather
complete answer on the topic:
Automation Add-ins and the Function
Wizard
Each Automation Add-in has its own
category in the Excel Function Wizard.
The category name is the ProgID for
the Add-in; you cannot specify a
different category name for Automation
Add-in functions. Additionally, there
is no way to specify function
descriptions, argument descriptions,
or help for Automation Add-in
functions in the Function Wizard.
This is a limitation for the 'Excel.Application.MacroOptions' approach only. (My apologies, I had forgotten about this limitation of the 'Excel.Application.MacroOptions' method with respect to automation add-ins when I wrote my original answer, above.) The more-complex 'Excel.Application. ExecuteExcel4Macro ' approach, however, absolutely does work for automation add-ins. It should also work for .NET ("managed") automation add-ins as well, because Excel has no idea whether it is loading a COM automation add-in created via VB 6.0/C++ versus a managed COM automation add-in created using VB.NET/C#. The mechanics are exactly the same from the COM side of the fence because Excel has no idea what .NET is, or that .NET even exists.
That said, the 'Excel.Application.Excel4Macro' approach would definitely be a lot of work...
You could either use one of the .Net Excel systems such as ExcelDNA or ADDIN Express, or try to adapt one of the VBA/VB6 solutions: look at Laurent Longre's FunCustomise
http://xcell05.free.fr/english/index.html
or Jan Karel Pieterse's article at http://www.jkp-ads.com/Articles/RegisterUDF00.asp
which uses a function overloading hack.

Resources