VSTO Add-ins, COMAddIns and RequestComAddInAutomationService
The COMAddIns property is a collection of COMAddIn objects exposed by Office applications that support COM add-ins. The COMAddIn interface defines a small number of methods/properties, such as the ProgId of the add-in and the Connect state. It also defines an Object property. By default, the value of the Object property is null. An add-in can set this value itself, and the purpose is to expose this add-in’s functionality to external callers. VSTO supports this mechanism through a new virtual method on the AddIn base class, RequestComAddInAutomationService. Most developers will never use this service, and its an example of one of the little things that VSTO does to support the widest range of add-in developers. Here’s how it works.
In your VSTO add-in class, you decide which methods you want to expose to external callers, and wrap these methods in a ComVisible class. Then, you override the RequestComAddInAutomationService virtual method to return an instance of this class. For example, we’ll expose a DisplayMessage method. The calls to this method will be made via automation, so I’m defining an automation (dual) interface to define the method:
[ComVisible(true)]
[Guid("B523844E-1A41-4118-A0F0-FDFA7BCD77C9")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IAddinUtilities
{
void DisplayMessage();
}
I can also define a class that will implement the interface to perform some suitable operation in the method:
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
public class AddinUtilities : IAddinUtilities
{
public void DisplayMessage()
{
MessageBox.Show("Hello World");
}
}
Then, in my add-in class, I override RequestComAddInAutomationService to return an instance of this class. As you can see, the programming model for this method is very similar to the generic RequestService method that VSTO uses for the new extensibility interfaces (although of course the underlying mechanism is completely different):
public partial class ThisAddIn
{
private AddinUtilities addinUtilities;
protected override object RequestComAddInAutomationService()
{
if (addinUtilities == null)
{
addinUtilities = new AddinUtilities();
}
return addinUtilities;
}
}
Then, I can create a suitable Office document and insert some VBA to call this exposed method. So, if my add-in is an Excel add-in, I can create an XLSM (macro-enabled) workbook and Alt-F11 to get the VBA editor up. I’ll put a CommandButton on the worksheet, and code it to call the DisplayMessage method:
Private Sub CommandButton1_Click()
Dim addin As Office.COMAddIn
Dim automationObject As Object
Set addin = Application.COMAddIns("ExcelAddinService")
Set automationObject = addin.Object
automationObject.DisplayMessage
End Sub
From the VBA code, you can see that my add-in is registered as “ExcelAddinService”. All I’m doing is fetching the COMAddIn object that represents this particular add-in in the COMAddIns collection (specified by ProgId). Then, I’m fetching the Object property of this add-in, and invoking the exposed method. If you’re following along, joining the dots as it were, you can infer that the VSTO runtime takes the return value from the RequestComAddInAutomationService method in the add-in to set the COMAddIn::Object property to the instance of the AddinUtilities class defined in the add-in.
Note that my VBA is using late binding – I don’t have a reference to the add-in at design-time. If the add-in exposed many methods with varying signatures, it might be worth adding a reference to it in the VBA editor.
So, to show this, I’ll expand my add-in to expose a second method, this one takes a couple of parameters and uses them to interact with the active Worksheet.
[ComVisible(true)]
[Guid("B523844E-1A41-4118-A0F0-FDFA7BCD77C9")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IAddinUtilities
{
void DisplayMessage();
void SetCellValue(String cellAddress, object cellValue);
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
public class AddinUtilities : IAddinUtilities
{
public void DisplayMessage()
{
MessageBox.Show("Hello World");
}
public void SetCellValue(String cellAddress, object cellValue)
{
Excel.Worksheet sheet = (Excel.Worksheet)
Globals.ThisAddIn.Application.ActiveSheet;
Excel.Range cell = sheet.Cells.get_Range(
cellAddress, Type.Missing);
cell.Value2 = cellValue;
}
}
In my VBA, I can add a reference to the AddinService add-in, so that I get design-time intellisense. I’ll put a second CommandButton on the worksheet, and code it to call the SetCellValue method:
Private Sub CommandButton2_Click()
Dim addin As Office.COMAddIn
Dim utilities As ExcelAddinService.addinUtilities
Set addin = Application.COMAddIns("ExcelAddinService")
Set utilities = addin.Object
Call utilities.SetCellValue("a1", 456.78)
End Sub
I can even write a Windows Forms app to automate Excel externally, and invoke the exposed add-in methods. For example, this is a Windows Forms app with 2 buttons – I launch Excel when the form loads, and cache the Application and the add-in Object. Then, I call one of the exposed methods in each button Click handler:
public partial class WinTestAddinServiceForm : Form
{
public WinTestAddinServiceForm()
{
InitializeComponent();
}
private Excel.Application excel;
private ExcelAddinService.IAddinUtilities utils;
private void WinTestAddinServiceForm_Load(object sender, EventArgs e)
{
// Launch Excel, make it visible, and ensure there is
// at least one sheet.
excel = new Excel.Application();
excel.Visible = true;
excel.Workbooks.Add(Excel.XlSheetType.xlWorksheet);
// Fetch the add-in we want to exercise, and cache
// its exposed object.
object addinName = "ExcelAddinService";
Office.COMAddIn addin = excel.COMAddIns.Item(ref addinName);
utils = (ExcelAddinService.IAddinUtilities)addin.Object;
}
private void WinTestAddinServiceForm_FormClosed(
object sender, FormClosedEventArgs e)
{
// Clean up all Excel object references.
utils = null;
excel = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
}
private void displayMessage_Click(object sender, EventArgs e)
{
utils.DisplayMessage();
}
private void setCellValue_Click(object sender, EventArgs e)
{
utils.SetCellValue("a1", 123.45);
}
}
As you can see, I’ve obviously got a reference to the ExcelAddinService assembly in this project – that’s only so that I can use the IAddinUtilities interface. Realistically of course, the interface would be best defined in an assembly separate from the add-in.
This feature seems like a small thing, but actually its part of a wider strategy to support the idea of Office as a true development platform. That idea implies at least some minimal level of interconnectivity support between various custom pieces in a solution.
UPDATE NOTE: See here for an update to this post: https://blogs.msdn.com/andreww/archive/2008/08/11/why-your-comaddin-object-should-derive-from-standardolemarshalobject.aspx
Comments
Anonymous
January 17, 2007
The comment has been removedAnonymous
February 01, 2007
Hi M - a few questions:Did you return an instance of the AddinUtilities class in the RequestComAddInAutomationService method?How did you attribute the class?Did you make the assembly ComVisible?Did you mark it as Register for COM interop?ThanksAndrewAnonymous
March 12, 2007
I am also seeing the casting error when I try to write a C# (early-bound) program to replicate what I've seen here. The late-bound vbs version works fine. To answer your questions:Yes, I returned an instance of the AddinUtilities class in RequestComAddInAutomationService.attributes should match your example: [ComVisible(true)] [Guid("B523844E-1A41-4118-A0F0-FDFA7BCD77C9")] [InterfaceType(ComInterfaceType.InterfaceIsDual)] public interface IAddinUtilities { void DisplayMessage(); } [ComVisible(true)] [ClassInterface(ClassInterfaceType.None)] public class AddinUtilities : IAddinUtilities { public void DisplayMessage() { MessageBox.Show("Hello World"); } } Your example only has methods marked as ComVisible. I have tried marking the add in project to be comvisible such that reflector shows:// Assembly ExcelAddIn1, Version 1.0.0.0[assembly: AssemblyVersion("1.0.0.0")][assembly: AssemblyTitle("ExcelAddIn1")][assembly: Debuggable(DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.EnableEditAndContinue | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.Default)][assembly: CompilationRelaxations(8)][assembly: AssemblyDescription("")][assembly: Guid("8d818b4d-3f48-4e88-9e27-4559992a7325")][assembly: RuntimeCompatibility(WrapNonExceptionThrows=true)][assembly: ComVisible(true)] <==== HERE[assembly: AssemblyTrademark("")][assembly: AssemblyConfiguration("")][assembly: AssemblyFileVersion("1.0.0.0")][assembly: AssemblyCopyright("Copyright x00a9 2007")][assembly: AssemblyCompany("")][assembly: AssemblyProduct("ExcelAddIn1")]Beyond clicking "Make assembly COM-Visible" in the Build property Application..Assembly Information, is there another step to take?Thanks,ConorAnonymous
March 12, 2007
The example code provided works fine for C# early binding - you have to set your add-in project's Build settings to "Register for COM Interop" - as Andrew stated above.You do not need to set the 'make assembly com visible' as the decorations take care of the needed interfaces/classes.It will throw casting exceptions if you do not have this build option set.Anonymous
March 31, 2007
In my ThisAddIn Class , I cann't override RequestComAddInAutomationService method,what's the problem. Thanks & RegardsAnonymous
April 04, 2007
Scott,I had also your problem , the solution i found was to change my project to a vb.net one, that solved my problem.Now i have a problem each time i change the add i have to remove and add the updated addin is this correct or am i missing something ?Thanks in advanceTCAnonymous
September 25, 2007
Hi,How to implement similar functionality in shimmed add-in?Anonymous
October 07, 2007
Did you make the assembly ComVisible?Did you mark it as Register for COM interop?Anonymous
December 02, 2007
To Andrew Whitehead:your example is just what I need, except that I want to write the Addin in MS Studio 2005 VB, not in C++ (because (1) its 10 years since I last used C, and (2) I 'm trying to make an addin of a body of code, forms etc written in VBA)Is it possible? Any suggestions ?Anonymous
December 03, 2007
Apologies, I should have said Andrew Whitechapel, not Andrew Whitehead.Anonymous
January 25, 2008
Is there a way to avoid any VBA code and call managed routines directly? You see, I need to connect it to a KeyBinding command - a string kind of "ModuleName.MethodName", but instead of referencing to the corresponding VBA Module and Sub, I need to reference directly to my managed add-in (or customization) method. Is this possible? Having to write any VBA code would cause additional macro security issues and generally nullifies the idea. Thanks!Anonymous
February 12, 2008
Thanks for the great article, one question though. I've put together an addon using your example here for Outlook 2003.What I'd like to do is start outlook through automation ie:dim app as new Outlook.Application()Then go ahead and read in the addin's Object refrence.The problem is this: It works perfectly if outlook is already running, but if outlook is not running the Object always returns null.I'm guessing that my addin is not being loaded because Outlook.exe was started with automation, so the call to RequestComAddinAutomationService never happened.Do you know is this is the case? If so, do you know a work around, or an update to correct this behavior?Again, appriciate your work in writing this, it was a real help.Anonymous
February 19, 2008
I would be really grateful if you could describe how to overcom the early binding issue when calling an addin from .NetI can set the addin object to a .Net object and then call using late binding but i cannot for the life of my work out how to cast/set the addin object to my class created in the .Net addinI am using all the techniques described above. The addin is for Outlook 2003.Anonymous
April 09, 2008
Adam - apologies for taking so long to reply. For some reason the email notifications from this post stopped coming to me.Anyway, this works fine for me with Outlook being externally automated. The only caveat is that when you first launch Outlook, it will take some time to perform housekeeping before you can be sure that all add-ins are actually loaded.So, you need to wait a reasonable period of time before attempting to retrieve your add-in. I'm not sure there's a suitable event that Outlook fires that you could use - if not, you'll have to figure out some other workaround.Anonymous
April 09, 2008
Steve - apologies to you also for the delay.I'm not sure what you mean by the "early binding issue". Is this the same problem that Conor had? If so, the answer is the same one Mike Kelley gave - that is, make sure you have your project set to Register for COM interop.Anonymous
April 13, 2008
Like Oleg Krupnov I want to avoid any VBA macros and link a keyboard shortcut to a comvisible method. Any idea how this can be done?Example:Private Sub ThisAddIn_Startup(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Startup ' Start of VSTO generated code Me.Application = CType(Microsoft.Office.Tools.Excel.ExcelLocale1033Proxy.Wrap(GetType(Excel.Application), Me.Application), Excel.Application) ' End of VSTO generated code Me.Application.OnKey("^f", "ExcelAddIn2.tlb!HelloWorld") End Sub... and HelloWorld() is a comvisible function like this ...Imports System.Runtime.InteropServices<ClassInterface(ClassInterfaceType.AutoDual), ProgId("ExcelAddIn2.ComClass1"), ComVisible(True)> _Public Class ComClass1 Public Function HelloWorld() As Boolean MessageBox.Show("HelloWorld") Return True End FunctionEnd ClassAnonymous
April 14, 2008
Paul/OlegUnfortunately, both Excel's OnKey and Word's KeyBindings are restricted to VBA macros only: you cannot directly use these to invoke managed code.Anonymous
April 18, 2008
So I attempted to do what your last comment said about putting the interface a seperate assembly. When I do that, I only have following:namespace BTR.Extensibility.Excel.Interfaces{
}However when I try to COM register this, I get an error:RegAsm : warning RA0000 : No types were registeredAny ideas?[ComVisible( true )][InterfaceType( ComInterfaceType.InterfaceIsDual )]public interface IAddinUtilities{ void TestArchitecture( int count );}
Anonymous
April 18, 2008
Just to clarify...I'm having early binding cast problems as well.Currently I have 3 projects:1) An Add-in project with essentially ThisAddin.cs and AddinUtilities.cs.2) An inteface assembly/project with only IAddinUtilities.cs.3) A Windows form application for the test host. Both the test host and the add-in both reference the interfaces assembly.No assemblies have 'register for com interop' checked as I'm manually calling regasm after placing the assemblies in the appropriate directory (c:prog files...). I plan on having my setup project do this, so I don't want to have it 'automatically' done by VS.So I guess my question is, even given your setup above, does the IAddinUtilities interface show up in the registry anywhere? The warning about 'no types were registered' above that I wrote happened on manual regasm calls as well as VS calls due to 'register for com interop'.After regasm'ing both my add-in and interface assemblies, I have the following present in my registry (don't think I've missed anything):[HKEY_CLASSES_ROOTBTR.Extensibility.Excel.RBL.AddInUtilities]@="BTR.Extensibility.Excel.RBL.AddInUtilities"[HKEY_CLASSES_ROOTBTR.Extensibility.Excel.RBL.AddInUtilitiesCLSID]@="{9E5B4D63-4365-30A5-AA35-07B926BECDA5}"[HKEY_CLASSES_ROOTCLSID{9E5B4D63-4365-30A5-AA35-07B926BECDA5}]@="BTR.Extensibility.Excel.RBL.AddInUtilities"[HKEY_CLASSES_ROOTCLSID{9E5B4D63-4365-30A5-AA35-07B926BECDA5}Implemented Categories][HKEY_CLASSES_ROOTCLSID{9E5B4D63-4365-30A5-AA35-07B926BECDA5}Implemented Categories{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}][HKEY_CLASSES_ROOTCLSID{9E5B4D63-4365-30A5-AA35-07B926BECDA5}InprocServer32]@="mscoree.dll""ThreadingModel"="Both""Class"="BTR.Extensibility.Excel.RBL.AddInUtilities""Assembly"="BTR.Extensibility.Excel.RBL.2003, Version=1.0.0.0, Culture=neutral, PublicKeyToken=824d1f6daf83e0eb""RuntimeVersion"="v2.0.50727"[HKEY_CLASSES_ROOTCLSID{9E5B4D63-4365-30A5-AA35-07B926BECDA5}InprocServer321.0.0.0]"Class"="BTR.Extensibility.Excel.RBL.AddInUtilities""Assembly"="BTR.Extensibility.Excel.RBL.2003, Version=1.0.0.0, Culture=neutral, PublicKeyToken=824d1f6daf83e0eb""RuntimeVersion"="v2.0.50727"[HKEY_CLASSES_ROOTCLSID{9E5B4D63-4365-30A5-AA35-07B926BECDA5}ProgId]@="BTR.Extensibility.Excel.RBL.AddInUtilities"Any information would be appreciated.Anonymous
April 19, 2008
Terry - the regasm error message you're getting is because you can't regasm an assembly which doesn't contain any createable types. An interface is not a createable type. So, you'd need to declare a class in your interface assembly that implements the interface (as in the original example in my post). This class could either be the real class used by your code, or it could simply be a dummy placeholder with 'do nothing' methods that will never get instantiated but serves to allow regasm to register the interface.Anonymous
April 19, 2008
Thanks for the info. Either I'm not doing something right or there are other problems. Here's where I'm at:a) VSTOAddIn.dll - in RequestComAddInAutomationService, creates and returns AddinUtilities class.b) I've moved the IAddinUtilities and AddinUtilities interface/class back into the VSTOAddIn.dll to attempt to mimic your sample exactly and both are marked as COMVisible. Note: the code is pasted below.c) Automation Client.exe - VSTOAddIn.dll and tries to cast to early bound type:d) My registry still looks about the 'same'. In that I only have AddinUtilities in registry and not the IAddinUtilities (is that ever supposed to show up? I'm assuming yes).Let me know if it is worth sending you a sample project offline and if so, if/when we resolve issue, I could post a textual representation of the solution on your blog for others to see?Here's the relevant code:(from ThisAddin.cs)protected override object RequestComAddInAutomationService(){ if ( addinUtilities == null ) { addinUtilities = new AddInUtilities(); } return addinUtilities;}(This is entire my 'Automation.dll' code)namespace BTR.Extensibility.Excel.RBL.Automation{ [ComVisible( true )] [Guid( "5231D3C0-3465-493C-BFD8-9BEA4AAB25E1" )] [InterfaceType( ComInterfaceType.InterfaceIsDual )] public interface IAddinUtilities { void TestArchitecture( int count ); } [ComVisible( true )] [ClassInterface( ClassInterfaceType.None )] public class AddInUtilities : IAddinUtilities { public void TestArchitecture( int count ) { MessageBox.Show( count.ToString() ); } }}(Automation client snippet)var xlApp = Activator.CreateInstance( Type.GetTypeFromProgID( "Excel.Application" ) ) as Excel._Application;object progId = "BTR.Extensibility.Excel.RBL.2003";var comAddin = xlApp.COMAddIns.Item( ref progId );var test = comAddin.Object as IAddinUtilities; // I've tried the actual class AddInUtilities cast as wellAny information would be greatly appreciated.Anonymous
April 20, 2008
The comment has been removedAnonymous
April 20, 2008
Thanks for the info. The implicit types is more 'laziness' than anything else :$.Anyway, My COMAddIn.Object is set to _ComObject (or something like that) and I didn't see my GUID under HKCRInterface :OAs I mentioned above, I'm not marking the assembly as 'Register for COM interop' but rather manually running regasm after copying dll to proper location. There isn't something I need to run in addition to regasm to 'register interfaces' is there?You also asked about whether I have separate DLL or just an add-in DLL...just to answer (I stated above as well, but could be easily overlooked), I have only a single add-in dll.Sorry for the ignorance on COM interop, I've minimal experience with it. I appreciate the back and forth and still hoping you might have some idea and/or other users reading this might have had same problem and fixed (yn).Anyway, thanks again.Anonymous
April 20, 2008
I don't understand why you wouldn't use the VS 'Register for COM Interop' project setting. This not only registers your assembly for COM interop, it also exports and registers a COM typelib, which as I mentioned is cross-referenced by your interface registration. If you need to use regasm directly for some reason, then you should use the /tlb switch (or tlbexp) to get a typelib, and make sure that both are correctly registered. I'm not sure, but I suspect you'll also have to manually register your interface.Anonymous
April 21, 2008
Well, the only real reason for avoiding 'Register for COM Interop' is that I wanted to know the manual steps I needed to take so that I knew how to set it up on other people's computer. I probably have not given the VS setup project enough credit and maybe it'll figure all this out as well. In any case, I guess I'll try register for COM interop and see how far I get.Thanks for reply.Anonymous
June 09, 2008
I have Word COM AddIn created using VSTO SE 2005. It shows up and work fine when I open word out side my application.Through my application when I activate word throughOffice.Interop it doesnt show COMAddIn. a. The comAddin object is null when tried to access through object property, in the collections the addin is there. b. In the project properties of comAddIn "Register for com interop" is set to true. If I invoke word by writing contents through response object even at this point it doesnt show my comAdd in word.Anonymous
June 10, 2008
padvit - it's not completely clear what you're trying to do, but your add-in should be loaded even when you automate Word externally, which seems to be what you're doing (unless you're starting the Word process with the /a commandline switch). I don't know what you mean by a "response object" but if you're trying to automate Word on a server from an ASP page, you should know this is not a supported scenario - see here for details: http://support.microsoft.com/?id=257757. The only other likely issue is that there might be a delay (depending on how many add-ins you're loading, and what they're doing) before your add-in is correctly set in the COMAddIns collection.Anonymous
June 10, 2008
Andrew Thanks for the reply.To be more clear, I have asp.net page, Where Iam creating word application wordApp object using Office.Interop. if I invoke word through wordApp.visible =true;The word is invoked but my AddIn deployed is not visible to me.The same AddIn invoked outside my applicaion through "WINWORD.exe" pops up.Anonymous
June 10, 2008
padvit - as I said, there is no general reason why your add-in should not be available in the COMAddIns collection. Are you sure it is getting loaded? Have you debugged through to find out where it is failing?Anonymous
June 10, 2008
When I debug in the COMAddIns collection it is very much there, but the object is set to null.Anonymous
June 13, 2008
Hi Andrew, This is the code Iam usingThrough wordApp object iam creating the document.after creating the doc, Iam using the client side script to invoke the word as mentioned in the following articlehttp://support.microsoft.com/?id=257757.buffer = objLinkedDocFacade.GetFileContent(docGUID);Response.Clear();Response.AppendHeader("Content-Disposition", "inline; filename=" + DocumentName);Response.AppendHeader("Content-Length", buffer.Length.ToString());Response.ContentType = "application/msword";Response.CacheControl = "public";Response.OutputStream.Write(buffer, 0, buffer.Length);Response.End();but even then word is opening up but AddIn is not poppingup.Anonymous
June 16, 2008
padvit - I don't undersand what you're doing. You say you're invoking "client-side script" but then you list server-side ASP code. You say the script is mentioned in a support article, but the support article you list does not have any script in it. Previously, you said you created the Word Application object through "Office.Interop" which is part of the namespace for the Office PIAs, but it doesn't look like you're doing that at all.If you want to show us the code where you're creating the Word Application object, it might help. But in any event, please note that as I said before what you're doing (automating Word on the server) is not supported and not encouraged.Anonymous
June 30, 2008
Andrew,I have an implementation similar to the example that you have given ,My Addin can be launched thru a webpage , I need my Add-in to be created on an STA thread , but my exposed method is launched on an MTA thread . this is a problem for me as I launch a winform form the addin (similar to the "Word New" dialog) which in-turn launches a web widget and this activeX control cannot run on an MTA thread .Is there anyway we can mark the launch Method to ONLY launch as STA , I tried attributing the method ("[STAThread]") but to no luck .Anonymous
July 02, 2008
Matthew - please provide some clarification.If I understand you correctly, you have a web page, and the code behind the web page connects to an add-in running in Office?Anonymous
July 03, 2008
Andrew, I think the great enhancement for out-of-proc consumers of AddInUtilities could be if they derived from System.Runtime.InteropServices.StandardOleMarshalObject. The reason here is that by default all managed objects are neutrally threaded - hence out-of-proc calls will activate the AddInUtilities on the thread the RPC call came in. However, you can change by deriving from SOMO and this will cause the incoming calls to be marshalled onto the thread (or, to be more precise, COM apartment) the object has been initially created on (which is in most circumstances the main UI thread).Anonymous
July 10, 2008
Andrew,I have Javascript that launches the Add-in , its not code behind .Anonymous
July 10, 2008
Matthew/mathjcb - did you try Misha's suggestion? That is, to derive your AddInUtilities class from System.Runtime.InteropServices.StandardOleMarshalObject.Anonymous
July 11, 2008
Thanks Misha and Andrew . Deriving from System.Runtime.InteropServices.StandardOleMarshalObject" has solved my problem !Thanks again .!Anonymous
September 09, 2008
Thanks Andrew for the sample. It works well. I jsut have one questions, you said "In my VBA, I can add a reference to the AddinService add-in, so that I get design-time intellisense". Could you show me how to do that?I see the COM object under Tools --> COM Addins, but in VB, Tools --> References, I simply couldn't find it. I can click the browse button to find the dll but couldn't load it as it complains "Can't add reference to specfied file".Anonymous
September 09, 2008
PT - you need to add a reference to the typelib (.tlb) not the dll, from Tools | References in the VBE. If the tlb is not listed, check that it is being generated during the build. You should have "Register for COM Interop" checked in the build properties - this generates a tlb and registers it.Anonymous
September 10, 2008
Awesome, it worked!Many Thanks!Anonymous
November 20, 2008
I'm using your example to try to pass an object to the AddinUtilities class. AddinUtilities it's inheriting from StandardOleMarshalObject and implementing IAddinUtilities. If I pass an integer value it works fine, but as soon as I try to pass an object, either by value or by reference, I get an Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).Is it possible to pass complex objects or am I trying something impossible?Anonymous
December 01, 2008
I’ve posted a few times on the best way to expose methods from an add-in to automation clients – forAnonymous
December 09, 2008
Hi Andrew,Thanks a lot this wonderful article. I'm using this article as a starting point for my project.We have a requirement where we need to expose a class from the Word add-in, using which VB/VBA user can customize the Word Ribbon interface like hidding or disabling the different control. Can you please suggest me how should I do ribbon customization inside my exposed class?Here are some of the MSDN articles I'm referring but did not help:http://msdn.microsoft.com/hi-in/library/bb421511(en-us).aspxhttp://msdn.microsoft.com/en-us/library/bb194905.aspx#Office2007TaskPanesRibbonVBA_ActivatingtheRibbonWhenItLoadsAnonymous
December 26, 2008
Lilu - thanks for your comments. The articles you reference should provide you with the information you need for dynamic ribbon customization.You can expose an automation object from your add-in, as described in my blog posts. This object can expose suitable methods for showing/hiding ribbon controls, changing ribbon control images or labels, and so on. The basic mechanism behind this is as described in the quoted articles - that is, in your ribbon XML you specify callbacks for status methods such as getVisible, getEnabled, getImage, getLabel, and so on. Details of these callbacks are here: http://msdn.microsoft.com/en-us/library/aa722523.aspxThe main ribbon developer portal is here: http://msdn.microsoft.com/en-us/office/aa905530.aspxSo, your VBA could get hold of your add-in's automation object, and call methods. These methods internally would set some state (boolean flag for control visibility, text for label, etc), and then invoke the IRibbonUI.Invalidate or InvalidateControl methods to cause the ribbon to be refreshed.Anonymous
December 31, 2008
Hi Andrew ,I thought I'll let you know , I had to roll back Misha's suggestions on July 3rd for implementing System.Runtime.InteropServices.StandardOleMarshalObject interface for out of proc process to access add-in .After implementation we have observed weird behavior with some API's like UpdateToC stopped updating and content control enter and exit events had some issues .In short after implementing rhe interface we had issue with some native word behavior.Anonymous
December 31, 2008
Mathew - thanks very much for the update. When you get a chance, please could you provide a simple sample solution that reproduces the problems you're getting? That would help us to investigate this and figure out the root cause. Many thanks.Anonymous
January 07, 2009
Hi sir.I am developing application in VSTO Excel 2007 Workbook type.I have added some buttons in that now I want to give short cut key to that button like CTRL+L for one win forms.I am totally new to VSTO or windows application so please help me how to get rid of this problem.Thanks & RegardsManish manishagravat@gmail.comAnonymous
January 07, 2009
Manish - you can use the same technique as with standard Windows Forms. That is, put an ampersand "&" in the Text property, in front of the character you want to use for your shortcut.For example, set the Text property of a Button control to "H&ello". Then, the shortcut will be Alt-e.Anonymous
February 02, 2009
That's correct but I need same Shortcut for several buttons and different cell and with the combination with Ctrl+"Anykey" only.Anonymous
February 03, 2009
Manish - sorry, I don't understand your last comment. Please clarify.Anonymous
February 09, 2009
I have 10 buttons in different column like "A","B" ,"C", "D" and so on.Now what I want to do is based on my active cell I want to click that button using the short cut.the short cut will be same for all the buttons but those will be click based on active cell. I hope now you got the problem.and that shortcut should be ctrl based not alt base means ctrl + "L" something like that.Anonymous
February 13, 2009
manish - there's really no supported way to do this.Anonymous
February 15, 2009
The comment has been removedAnonymous
February 23, 2009
Andrew I have 2 VSTO project, one is an Excel Add-In, one is an Excel Workbook.I'd like the Add-In to be able to open the Excel Workbook, allow the user to enter some data, and then allow the Add-In to call an interface implemented by the workbook to send the data from the workbook to a database. So I need to open the workbook in excel, and also have an object reference in the Add-In to manage the workbook.Your example is allowing the workbook to call methods on the add-in, I'd like to go the other way, and can't seem to fit the pieces together.Anonymous
February 23, 2009
v-jbrown - the problem is that while Office exposes add-ins in a well-defined manner through the COMAddIns collection, it does not expose doc-level customizations in any way whatsoever. So, your design is going to have to initiate the conversation between add-in and doc-customization from the doc-customization. See here for more: http://blogs.msdn.com/andreww/archive/2008/03/20/integrating-doc-level-and-add-in-solutions.aspxAnonymous
February 23, 2009
Thanks for the info Andrew. Here's another question. When you open a VSTO workbook from a winforms app, similar to what you've done above, the code behind of the VSTO workbook is executed and all is well.However when you open the same workbook from an Excel add-in, the code behind is not executed.Is there a threading issue here? Perhaps the workbook is not opening on the UI thread?Anonymous
February 23, 2009
v-jbrown - there should be no problems opening a workbook and its associated customization from an add-in. Are you sure you're using the correct path for the workbook (typically, the copy in the targets folder for the workbook project)?Anonymous
May 05, 2009
The comment has been removedAnonymous
May 05, 2009
The comment has been removedAnonymous
May 05, 2009
AndrewThanks for the rapid response.I tried a delay and still no luck.regardsPaulAnonymous
May 06, 2009
Paul - a couple of questions for you.When are you trying to get the COMAddIn.Object? In the ThisAddIn_Startup or at some other time? Does it fail for all users on all machines all the time, or only under certain conditions? If the latter, can you identify any differences between the success states and the failure states? On a machine/user where it fails, does it always fail? When it fails, is the add-in itself fully loaded and otherwise fully functional?Anonymous
May 06, 2009
Andrew,I try to get the COMAddin,Object in the VBA code behind the spreadheet as followsSet addin = Application.COMAddIns("MQ2Addin")Set automationObject = addin.ObjectCall automationObject.MQ2MovementsIt has only failed for one user and I can not determine what changed on his machine or any pattern of the failure. It has also failed on a development machine and I am currently trying to ascertain why. It appears as though it does not even "see" the addin dll and thinks the COMAddin is loaded successfully.Once it has failed on a machine it always fails on that machine even after uninstall and re-install. I have installed to a fresh machine ie where it has not previously been installed and the Addin works fine. The Load behaviour is still set to 3 after the Excel spreadheet is opened but I have it deployed to HKLM so it does not appear in the COMAddin dialogue. It also does not appear as a disabled item and when I invoke it from cmd using VSTOSUPPRESSDISPLAYALERTS I get no error messages. regardsPaulAnonymous
May 06, 2009
The comment has been removedAnonymous
May 07, 2009
Aplogies if you get this more than onceDim automationObject As ObjectThe Exposed value is false on the machine where the Addin is not working and true on the one where it is. These are identical versions of the Addin and obviously this is the problem but what would cause that to happen ? regardsPaulAnonymous
May 07, 2009
Paul - and Q3?I think you're going to have to attach a debugger to the host process to try to figure out at what point the add-in is failing. Set breakpoints in ThisAddIn_Startup, RequestComAddInAutomationService, the ctor for the automation object class, etc, and step through.Anonymous
May 07, 2009
Andrew,Q3 I put a displaymessage in the startip and I see it on the devlopment mnachine but don't see when its installed on the machine that was not working.I afraid I do not know how to attach a debugger to the host process.....do you know of a step by step description of this anywhereregardsPaulAnonymous
May 07, 2009
Paul - open the add-in project in VS, and set lots of breakpoints. Then hit F5.See here for documentation: http://msdn.microsoft.com/en-us/library/sc65sadd.aspxAnonymous
May 07, 2009
So I need VS on the machine where the addin is not working ? It works in VS on the development machineAnonymous
May 07, 2009
Paul - sorry, no - you can set up remote debugging. That is, use VS on your dev machine to connect to the process on the bad machine. See here: http://msdn.microsoft.com/en-us/library/y7f5zaaa.aspx(this is a sub-topic of the main debugging docs I mentioned before).Anonymous
May 10, 2009
Hi Andrew,I am not getting to break points in the override class or the Thisaddin_Starup. I ran the VSTO trouble shooter and noticed a difference in the Office 2003 PIA assemblies for the Microsoft Office Forms PIA and whilst that is not used by my project I figure I would uninstall them and re-install them. I did the uninstall and rebooted but the troubleshooter still told me that they were installed. They are not showing in the Control Panel Add/Remove programs do you know how I can completely remove them ?regardsPaulAnonymous
May 11, 2009
Paul - that doesn't seem to make sense. If the add-inn is truly loaded then the ThisAddIn_Startup should get called. If the problem is CLR can't resolve assembly dependencies, you should be able to detect this with fuslogvw - the fusion log viewer http://msdn.microsoft.com/en-us/library/e74a18c4(VS.80).aspx.Also, can you confirm this is an Excel 2003 add-in? Which version of VSTO are you using?Re PIAs, these are normally installed with Office, although they can also be installed via the separate PIA Redist, and a dev set of the Office PIAs is instaleld with VS2008. So, uninstalling them depends on how you installed them in the first place.Anonymous
May 14, 2009
I have managed to fix the problem and I am a little red faced to say the least. I had checked for disabled Addins and none were listed but when I chcek the users machine again today (he has been away) low and behold its disabled, re-enabled it and all is working again.Thanks you for your help, at least I learned a lot about the various daignosis tools.kind regardsPaul