Similar to this question (but in my case not VSTO SE), however, I just want to confirm that it is not possible to create a UDF using pure VSTO in Visual Studio 2005 and Excel 2003 - so, to absolutely clear, my question is:
Is it possible to create a Excel 2003 UDF using Visual Studio 2005 and a VSTO solution without using any VBA or other tricks?
I'm aware of ManagedXLL, ExcelDNA, Excel4Net etc but don't want to consider those for the moment.
Thanks
Concerning whether there is a way around COM or VBA I don't think that it is possible (at least not without any very dirty tricks). The reason is that the only way Office can execute external code (i.e. you add-in) is via COM. Even VSTO is still using the old IDTExtensibility2 COM interface underneath. IDTExtensibility2 is a COM interface that all add-ins for Microsoft Office applications must implement.
Before VSTO, Office add-ins had to implement this IDTExtensibility2 interface themselves. In such a COM based add-in (or COM-visible managed add-in) you can simply add your UDF as described here.
However, now with VSTO, there is an additional layer of abstraction: VSTO uses a so-called Solution Loader implementing IDTExtensibility2, which is a dll provided by the VSTO runtime. This means that your add-in is no longer COM-visible. Hence, if you added a UDF to your VSTO add-in it won't be visible to Office.
Paul Stubbs explains on his blog how to do with VSTO and VBA: How to create Excel UDFs in VSTO managed code
Create a class with your functions in VSTO
<System.Runtime.InteropServices.ComVisible(True)>
Public Class MyManagedFunctions
Public Function GetNumber() As Integer
Return 42
End Function
End Class
Wire up your class to VBA in VSTO
Private Sub ThisWorkbook_Open() Handles Me.Open
Me.Application.Run("RegisterCallback", New MyManagedFunctions)
End Sub
Create Hook for managed code and a wrapper for the functions in VBA
In a VBA module in your spreadsheet or document
Dim managedObject As Object
Public Sub RegisterCallback(callback As Object)
Set managedObject = callback
End Sub
Public Function GetNumberFromVSTO() As Integer
GetNumberFromVSTO = managedObject.GetNumber()
End Function
Now you can enter =GetNumberFromVSTO()
in a cell, when excel starts the cell
value should be 42.
I don't understand why you want to do this?
VSTO and exposing UDFs via COM interop (from .NET) are two different tasks.
Why do you want to host a UDF method inside of a VSTO project?
The way you register the .net UDF assembly means it will have to be in a seperate project to the VSTO project. However if you wanted to share data between the two apps then you have a variety of native .net methods for this, or simply "call" the UDF function from the appropriate range object within your VSTO project.
Is there a reason that you feel it is necessary to have UDF in VSTO?
In this article Eric Carter goes on to explain how to do what you're asking. At the top he even links to an update of the aforementioned blog post.
Create the UDF as Eric Carter explained and pass as parameter to your UDF an Excel range. You're able to access Excel's object model through VSTO by using the given range:
Excel.Range rg = param1 as Excel.Range;
Excel.Workbook wb = rg1.Worksheet.Application.ActiveWorkbook;
I am not familiar with a method of creating a UDF in Excel 2003 using VS2005 and VSTO without having at least a bit of VBA. Here are 2 links that discuss this a bit further:
<Link>
<Link>
Related
I am interested in creating a custom VSTO Excel Addin to provide customized classes for VBA in Excel. This link provides a walkthrough for this task, however there is one question left for me: How to provide intellisense support for VBA for this customized routines.
The IntelliSense technology available in VBA is based on the COM technology. So, if you try using the late-binding technology with any Office application automated you will not find the IntelliSense for such objects as well. Only early-binding technology allows using this technology and get intellisense available to developers. You need to create a COM interface which could be implemented on both sides. After adding a COM reference to that library you may get hints like intellisense.
Out of the box VSTO doesn't provide anything for getting the IntelliSense available in VBA.
I'm currently writing a VSTO add-in for Excel in C#, and am having trouble getting the undo functionality to work correctly.
As far as I can tell from the documentation, you are meant to use Application.OnUndo to register an undo callback. However, it's not clear to me whether it's possible for the Procedure argument to refer to a C# method.
Ideally I would like to set the undo callback to an instance method, eg:
this.Application = Globals.ThisAddIn.Application;
// ...
this.Application.OnUndo("Undo color change", "this.UndoTextColorChange");
Unfortunately, while this registers an undo, actually clicking 'undo' in Excel gives the error:
Cannot run the macro 'this.UndoTextColorChange'. The macro may not be available in this workbook or all macros may be disabled.
To me, this almost suggests that the Procedure argument has to be a VB macro (rather than a C# method). However, it's also possible that I haven't been able to work out the fully-qualified procedure name to use in the .OnUndo call.
Is it possible to have Application.OnUndo call a C# method? If so, what should I use as the argument for Procedure? If not, how is undo functionality typically implemented in C# VSTO add-ins?
You are right that the Procedure argument of the Application.OnUndo has to be a VBA macro.
If you want the Application.OnUndo to call a C# method you will have to add a VBA macro to your workbook which will be triggered by the Application.OnUndo. Then the macro may call C# code from your VSTO add-in. Here is a good article describing how you can call VSTO code from VBA macro.
In order to inject a VBA macro into a workbook from your VSTO add-in you may create an .xlam Excel Add-in and distribute it together with the VSTO Add-in (basically put it into the same folder or even embed it into VSTO Add-in itself as a resource file).
The .xlam Excel Add-in will be very simple and will contain a single function like below:
Sub UndoLastAction()
Application.COMAddIns("YourVSTOLibraryName").UndoInVSTO
End Sub
When VSTO Add-in is started it can load .xlam add-in, so all the workbooks will have access to that VBA macro even if the workbook itself is not macro-enabled.
Below is a sample how you can load .xlam Excel Add-in from VSTO:
var undoManager = Globals.ThisAddIn.Application.AddIns.Add("UndoManager.xlam", true);
undoManager.Installed = true;
Now, when you execute a certain modification via your VSTO Add-in and then would like to be able to Undo it you will have to call Application.OnUndo:
Globals.ThisAddIn.Application.OnUndo("Undo Last Operation", "UndoManager.xlam!UndoLastAction");
When Application.OnUndo is executed the Excel will allow user to click "Undo" button which will trigger UndoLastAction macro declared in the .xlam Add-in which will trigger UndoInVSTO function declared in YourVSTOLibraryName dll where you can do whatever you need to undo your last operation.
I'm currently developing a VSTO Word Document-Level addin, and a WPF app. The WPF app uses the classes and methods in the Microsoft.Office.Interop.Word.dll assembly to open the Word DOCX file associated with the Document-Level addin.
My WPF app needs to communicate with the VSTO Word Document-Level addin to update the document while the DOCX file is open.
I found an article on the web called VSTO Add-ins, COMAddIns and RequestComAddInAutomationService that shows how to communicate to an Excel Addin from VBA. I thought I could use the same ideal to make my WPF app communicate with my VSTO Word Document-Level addin.
The article describes how to override the RequestComAddInAutomationService method in the add-in class.
public partial class ThisAddIn
{
private AddinUtilities addinUtilities;
protected override object RequestComAddInAutomationService()
{
if (addinUtilities == null)
{
addinUtilities = new AddinUtilities();
}
return addinUtilities;
}
}
My VSTO Word Document-Level addin doesn't have a ThisAddin class that inherits from the Microsoft.Office.Tools.AddinBase class. It has a ThisDocument class that inherits from the Microsoft.Office.Tools.Word.DocumentBase class. And the DocumentBase class doesn't contain a method called RequestComAddInAutomationService that can be overridden.
So the question is, is it possible for another app to communicate with a VSTO Word Document-Level addin? And if so can you please show me a C# example of how to do it?
The VSTO technology doesn't have any tools that expose code in a document-level customization to outside code, such as an Add-in. It apparently is possible, although I've never tried it. There's a discussion on MSDN, in the VSTO forum, that outlines the steps involved.
https://social.msdn.microsoft.com/Forums/vstudio/en-US/10decfd2-b1b6-4bf2-aa99-16c1f08b9159/call-code-in-doclevel-assembly-from-addin?forum=vsto
I've copied McLean Schofields "Answer" to the question below as there's no knowing when the link may break.
That discussion mentions a sample from Andrew Whitechapel. That sample is no longer available, but his blog article that relates to it is:
http://blogs.msdn.com/b/andreww/archive/2008/03/20/integrating-doc-level-and-add-in-solutions.aspx.
Quote from McLean Schofield in MSDN post Dec. 16 2008:
"In the sample, .NET remoting is used to send the IDocumentCommands object from the document-level customization to the application-level add-in, and to transmit calls that the add-in makes on this object to the document customization. .NET remoting comes into play in the definition of the DocumentCommands class in the document customization. This class
implements the IDocumentCommands interface, which is how the add-in views this object, and it also derives from MarshalByRefObject, which enables it to be transmitted to the add-in (across application domains) via .NET remoting.
In a nutshell, this is how the sample works:
1.When the add-in loads, its override of the RequestComAddInAutomationService method exposes an IAddInCommands object to the document customization (and to any other Office solution that can access the Application.COMAddIns collection). This feature of add-ins is documented in more detail in Calling Code in Application-Level Add-ins from Other Office Solutions. When you expose an object in an add-in in the way described in this topic, you enable the object to be accessed by other Office solutions via COM interop.
2.When the document customization starts, code in ThisDocument_Startup gets the COMAddIn object that represents the add-in, and calls the RegisterDocument method exposed by the add-in to send it an instance of the IDocumentCommands object defined by the document customization. Because the instance it sends derives from MarshalByRefObject, this object is automatically sent to the add-in via .NET Remoting. For more information about .NET remoting, see Remotable and Non-remotable Objects.
3.Now the communication infrastructure is set up. When the user clicks on the actions pane button in the document customization, the btnCallAddIn_Click event handler uses the exposed IAddInCommands object to call the InsertText method defined in the add-in - this call uses the "standard CLR COM interop" that Andrew refers to. When the user clicks on the Ribbon button created by the add-in, the btnCallDocument_Click event handler uses the exposed IDocumentCommands object to call the InsertText method defined in the document customization - this call relies on .NET remoting.
Defining interfaces that are referenced by both the document-level customization and the application-level add-in is necessary in this scenario. For the add-in to call a method defined by the document customization (or vice versa), the solutions need a common "contract" object that defines the method. In order for both solutions to use the same object, the object must implement an interface that is defined in an assembly that both solutions reference. If you were to instead define this interface separately, once in the customization and again in the add-in, these interfaces would be viewed at run time as separate types, and the communication wouldn't work."
A Refresh button/method in an Excel Add-In needs to be invoked via an external winform application. Here is where I am up to:
private Microsoft.Office.Interop.Excel.Application excel = new Microsoft.Office.Interop.Excel.Application();
public FormMain()
{
InitializeComponent();
RefreshExcelSheet(#C:\a.xls");
}
private bool RefreshExcelSheet(string path)
{
using (var wb = excel.Workbooks.Open(path).WithComCleanup())
How do I click the Refresh button or simply invoke its event?
I was looking at these articles but they are using VBA, I want a Winform app to open the spreadsheets and click the button:
Accessing a VSTO application-addin types from VBA (Excel)
Expose VSTO functionality to VBA w/o local admin
The simplest way to solve this problem is to just expose the existing custom .net method as a COM method by making is a com callable wrapper object (CCW).
VSTO provides you with a simple method to expose the COM Automation server.
http://blogs.msdn.com/b/andreww/archive/2007/01/15/vsto-add-ins-comaddins-and-requestcomaddinautomationservice.aspx
EDIT BY OP:
Andrew Whitechapel has updated the article, but I cannot get it to work. Even with StandardOleMarshalObject and Register for COM interop I still get the same error message as detailed in this article: http://blogs.msdn.com/b/andreww/archive/2008/08/11/why-your-comaddin-object-should-derive-from-standardolemarshalobject.asp
Anonymous Type's suggestion is surely the best approach if you can extend the Excel add-in.
If it's a 3rd party tool it's still possible to use the ribbon's IAccessible interface to invoke the ribbon button. However, you can expect this to be much more complicated. In case you have to follow that path here are some links to get you started:
How to get Ribbon custom Tabs IDs?
http://www.codeproject.com/Articles/38906/UI-Automation-Using-Microsoft-Active-Accessibility
I have a C++ .xll addIn for Excel. The following test worksheet function works fine with the xll loaded:
LPXLOPER __stdcall Test()
{
return &xlstring("Yayage",true)
}
i.e. =Test() returns "Yayage" in the worksheet. xlstring is my own custom method which turns a string into an excel string and specifies whether excel or the xll are responsible for freeing its' memory.
I intend to use VBA to produce a 'GUI companion' AddIn for versions 2003 and previous, and a C# Excel 2007 AddIn project for 2007 onwards. These will both work with a version-independant .xll that communicates with a web service.
Both C# and VBA have the following Excel functions:
Application.ExecuteExcel4Macro(string)
Application.Evaluate(string)
Application.Run(...)
These work fine (Except .Run which, in C#, wants to take 32 parameters) with normal worksheet functions that return strings, e.g. in VBA:
MsgBox Application.<Any above method>("=Trim("" Billy Bob Fred "")")
Produces a message box containing "Billy Bob Fred"
However, I cannot get this to work with any of my custom worksheet functions. Registering them as commands instead of worksheet functions doesn't work either.
I've tried different styles of function name, with and without = / (), and just about everything else I can think of.
Can anyone help me with this?
Is your xlstring returning the address of a temporary object?