Compartir a través de


Using RibbonX with C++ and ATL

Today's Guest Writer: Eric Faller

Eric is a Software Design Engineer on the Office User Experience team focused on user interface extensibility for Office developers.

Another source of frequently-asked RibbonX questions is around the complexity of writing an add-in in C++. Compared to the ease of use of C# or VB.NET, C++ requires a much deeper understanding of what's really going on under the covers and often involves hand-implementing much of the "magic" that the higher-level languages take care of automatically.

This post covers the details of RibbonX's communication with COM add-ins via the IRibbonExtensibility and IDispatch interfaces and shows an example of creating an add-in with ATL. It's primarily intended for C++ developers, but if you're writing an add-in with .NET you may find it useful to understand what the CLR is automatically doing for you under the hood.

IRibbonExtensibility

As soon as Office boots up a COM Add-In, it checks if it implements the IRibbonExtensibility interface on its main Connect class via a QueryInterface() call for IID_IRibbonExtensibility (defined in the MSO.DLL typelibrary). If it does, it takes the IRibbonExtensibility pointer and QI's it for the IDispatch interface and saves both pointers off in a safe place.

Note that Office queries the IRibbonExtensibility interface for IDispatch, instead of the main interface. Normally this is unimportant, but it allows complicated add-ins to split their IDispatch interfaces off onto multiple objects if they provide multiple IDispatch implementations. For example, Excel add-ins can provide User-Defined Functions (UDFs) via IDispatch, and they usually won't want to have all of their RibbonX callbacks and UDFs on the same object.

Next, RibbonX will call the IRibbonExtensibility::GetCustomUI() method and get the XML for each type of Ribbon that's currently open. Most applications have only one Ribbon that's open all the time (Word, Excel, PowerPoint and Access), but Outlook has many different Ribbon types, any number of which can be open at a given time. GetCustomUI() can be called at arbitrary points after the add-in boots if the user opens up a new type of Ribbon, so add-ins should not do any extraneous processing inside that function or assume that it will always be called immediately after the add-in boots. GetCustomUI() should simply fetch and return the appropriate XML, without any side effects.

Once the appropriate XML is parsed and applied, RibbonX will invoke the add-in's "onLoad" callback (if it exists), as well as any "get" callbacks (such as getEnabled, getVisible or getLabel). These callbacks are all invoked via the IDispatch pointer that was queried for above.

IDispatch

If you're unfamiliar with IDispatch-based interfaces, you may be curious how it is that Office can call arbitrary C++ functions in an add-in, given only their names. For example, consider a button specified with this XML:

<button id="MyButton" onAction="ButtonClicked"/>

In my add-in I can write a ButtonClicked() function, but once it's complied and linked, the "ButtonClicked" name is optimized away and we're left with just a memory address where the function's code begins. How does Office find and call the function? Obviously there's something magic going on, and it's known as IDispatch.

IDispatch is a COM interface used for "dispatching" function calls to objects when their types are unknown or need to be late-bound. It's the reason that this VBA code works even though the "word" variable is not strongly typed:

Dim word
Set word = Application
word.CheckSpelling ("misspellled")

The IDispatch interface contains a whole bunch of methods which you can read all about in the documentation, but the main two to be concerned with are GetIDsOfNames() and Invoke().

The GetIDsOfNames() method provides a mapping between names (strings) and "DISPIDs", which are basically integers that represent functions or properties. With the example button above, Office will call into the add-in's GetIDsOfNames() method and ask "hey, do you implement the ButtonClicked function?", and the add-in with either say "yes I do, and it's DISPID number 2" (for example), or "no, I don't implement that function."

Once the function is found, the IDispatch::Invoke() method is used to actually call the function. Invoke() takes the DISPID of the function, an array of parameters, and gets the return value back. In our example Office will call the add-in's Invoke() method and say "call your ButtonClicked function with this IRibbonControl parameter and let me know how it goes."

Parameters and return values are passed around in VARIANT structs, which are basically big unions that can contain values of many different types. We could go into lots of detail about how to set up and use VARIANTs, but fortunately there are ATL classes that take care of all of this for us so there's normally no reason to worry about them.

That pretty much sums up the high-level overview of how IDispatch works, so let's see it in action and build a simple RibbonX add-in in C++ with ATL.

Building a simple C++/ATL RibbonX add-in

The steps for creating a C++ RibbonX add-in start off pretty much the same as for a C# add-in:

  1. Open up Visual Studio

  2. Click "New Project"

  3. Select "Extensibility" under "Project types" and choose "Shared Add-in"

  4. Give it a name and click OK:


    Click to view full picture

  5. Click through the wizard that shows up, making sure to check "Create an Add-in using Visual C++/ATL" and "I would like my Add-in to load when the host application loads."

Now you have an empty C++ add-in. Click "Build Solution" just to make sure that it all compiles OK with no problem.

Next, open up Class View, right-click on your CConnect class and select "Add -> Implement Interface…" In the dialog that pops up, select the "Microsoft Office 12.0 Object Library <2.4>" type library and add the "IRibbonExtensibility" interface from it:

Note: you may have an older type library registered instead (such as "Office 11.0 Object Library") if you previously had older versions of Office installed on the same computer. In those cases you can just browse to the "OFFICE12" version of MSO.DLL and select it manually.

Once you're done with that, Visual Studio should have auto-generated your GetCustomUI() function for you. Delete its "return E_NOTIMPL;" and paste in some valid code, like this:

 STDMETHOD(GetCustomUI)(BSTR RibbonID, BSTR * RibbonXml)
{
  if (!RibbonXml)
    return E_POINTER; 
  *RibbonXml = SysAllocString(    L"<customUI xmlns=\"https://schemas.microsoft.com/office/2006/01/customui\">"
    L" <ribbon>"
    L"   <tabs>"
    L"    <tab id=\"CustomTab\"" 
    L"         label=\"Custom Tab\">" 
    L"     <group id=\"CustomGroup\"" 
    L"            label=\"Custom Group\">" 
    L"       <button id=\"CustomButton\"" 
    L"               imageMso=\"HappyFace\""
    L"               size=\"large\"" 
    L"               label=\"Click me!\"" 
    L"               onAction=\"ButtonClicked\"/>" 
    L"     </group>" 
    L"    </tab>" 
    L"   </tabs>" 
    L" </ribbon>" 
    L"</customUI>"   ); 
  return (*RibbonXml ? S_OK : E_OUTOFMEMORY); 
} 

Now, a real add-in would obviously not hard-code its XML like this (embedding it as a resource in the DLL would be much better), but this suffices for our simple demo. Don't do this at home!

At this point we should try to compile the add-in and see our dummy button sitting on the Ribbon. Unfortunately when I tried compiling at this stage, there were several compilation errors in the auto-generated code due to namespace conflicts between the MSO type library and other Windows headers. I did these things to fix it:

  1. Open up "stdafx.h" and move the #import statement for MSO.dll from the bottom of the file up next to the #import statement for the Extensibility library inside the #pragma blocks (remove any 'no_namespace' annotations from that line as well)
  2. Add "using namespace Office;" to the top of the Connect.h file.

Now we can build successfully and see our button:

If we click it we get an error saying "The callback function 'ButtonClicked' was not found," which makes sense since we haven't written that function or implemented it via IDispatch yet. Let's use ATL to do that now.

Unfortunately Visual Studio 2005 doesn't seem to have a "New ATL Interface" wizard, but we can get the same thing accomplished by creating a generic ATL class and then deleting the implementation. Click "Add Class…" on the Standard Toolbar and select "ATL Simple Object" in the ATL category. Name the object something like "CallbackInterface" and hit Finish.

Now in Class View we have several new objects: an ATL interface called "ICallbackInterface" and an implementation class called "CCallbackInterface." We don't need the implementation, so go ahead and delete all the CallbackInterface.* files from the Solution Explorer. ICallbackInterface is what we care about and it's defined in our add-in's IDL file.

Back in Class View, right-click on ICallbackInterface and select "Add -> Add Method…" In the Add Method Wizard, add a method named "ButtonClicked" with one [in] parameter of type IDispatch* called RibbonControl:

This parameter is the IRibbonControl object that's passed to all RibbonX callbacks. Since "IRibbonControl" isn't in the parameter type dropdown, we have to go with its base type, which is IDispatch (IRibbonControl is not a type supported by the VARIANT structure). If we need it later, we can always call QueryInterface() on it with IID_IRibbonControl and get it.

Now that our interface is defined, right click on the CConnect class and select "Implement Interface…" again to add ICallbackInterface along with IRibbonExtensibility. Double-click the ButtonClicked function in Class View to be taken to the auto-generated implementation. Swap out its placeholder content with something meaningful, like this:

STDMETHOD(ButtonClicked)( IDispatch * RibbonControl)
{
  // Add your function implementation here.

  MessageBoxW(NULL,
     L"The button was clicked!",
     L"Message from ExampleATLAddIn",
     MB_OK | MB_ICONINFORMATION);

  return S_OK;
}

Now when we compile we should see this MessageBox when we click the button. However, there are a couple of problems left before we can do that, the first of which is "error LNK2001: unresolved external symbol _LIBID_ExampleATLAddInLib." Since our DLL is both the source and consumer of our new typelibrary for ICallbackInterface, we need to link in the MIDL-generated C files for it. In Solution Explorer, add the "AddIn_i.c" file, which is the output from running MIDL on our AddIn.idl file. This new file will inherit the solution defaults for PCH files ("Use Precompiled Headers (/Yu)"), which isn't what we want, so right-click on it and switch the file to "Not Using Precompiled Headers".

The last work item is to set up the COM_MAP to properly route the IDispatch calls to our ICallbackInterface. In Connect.h, switch the IDispatch line in the COM_MAP to ICallbackInterface instead of IRibbonExtensibility:

BEGIN_COM_MAP(CConnect)
  COM_INTERFACE_ENTRY2(IDispatch, ICallbackInterface)
  COM_INTERFACE_ENTRY(AddInDesignerObjects::IDTExtensibility2)
  COM_INTERFACE_ENTRY(IRibbonExtensibility)
  COM_INTERFACE_ENTRY(ICallbackInterface)
END_COM_MAP()

Once that's all built, try out the add-in and see that it works!

That's basically all there is to making a C++ RibbonX add-in with ATL. Obviously a more complicated add-in would have many more callbacks, but the only additional work would be to right-click on ICallbackInterface and select "Add Method.." for each one. Different types of callbacks have different parameters, so you just need to make sure that your callbacks match the C++-style signatures in the RibbonX documentation. A "getLabel" callback, for example, would have the same parameters, except it would have an additional "[out, retval] BSTR *Label" parameter for returning the label.

For more info about RibbonX, check out the documentation mentioned above, the Developer category on this blog, or the Office Discussion Groups if you have other questions not specifically related to the topics of this article.

Update: Eric has made the resulting Visual Studio 2005 project available for download.

Comments

  • Anonymous
    December 08, 2006
    This is great information. Can I do this without Visual Studio 2005?

  • Anonymous
    December 08, 2006
    The comment has been removed

  • Anonymous
    December 08, 2006
    Could you please upload the sample project?

  • Anonymous
    December 08, 2006
    I sent the example project to Jensen, but it looks like he might have just left for a nice long vacation.  He probably won't be around to update the article for a while, so in the meantime you can download the sample project here: http://efaller.com/work/ExampleATLAddIn.zip

  • Anonymous
    December 08, 2006
    I'll post the solution on officeblogs.net and link it to the article tonight.

  • Anonymous
    December 10, 2006
    The comment has been removed

  • Anonymous
    December 11, 2006
    This is great. It's exactly what I've been looking for.

  • Anonymous
    December 11, 2006
    Can I do this with the Express Editions from MS?  This is great to be able to customize.

  • Anonymous
    December 11, 2006
    I don't have the Express Editions installed to test this so I'm not absolutely sure, but according to this VS feature matrix, it appears that the Express Editions do not support "writing add-ins" under Extensibility :( http://msdn2.microsoft.com/en-us/vstudio/aa700921.aspx

  • Anonymous
    December 13, 2006
    This is excellent information, this is the only place that I have seen with comprehensive steps for adding ribbon support in unmanaged code. I do have one question though, I have everything running well but cannot seem to get the Id or Tag from the IRibbonControl in any of my callbacks. I am able to find the dispid without a problem using getidsofnames with “Id” and “Tag” but invoke always fails. Is there something that I am doing blatantly wrong. Could you post a code snippet for doing this? Thank you very much!

  • Anonymous
    December 13, 2006
    I'm not sure why it isn't working - are you using DISPATCH_PROPERTYGET instead of DISPATCH_METHOD? Normally I wouldn't use the IDispatch interface - in this case I'd just use the IRibbonControl interface, like this: Office::IRibbonControl pRibbonControl; pDispatch->QueryInterface(Office::IID_IRibbonControl, (void*)(&pRibbonControl)); pRibbonControl->get_Tag(&bstrTag);

  • Anonymous
    December 14, 2006
    I don't need to own office 12 to use MSO.DLL do I? Can I install my application that uses these cool controls on computers that don't have office 12? Is there a redist package?

  • Anonymous
    December 14, 2006
    The comment has been removed

  • Anonymous
    December 14, 2006
    In order to create Office Business Applications (OBAs), you need to understand the basics. There are

  • Anonymous
    December 17, 2006
    Great code ! I tried to do exactly the same thing in a shim DLL generated with the Shim Wizard v2 (my addin is in C#, but I need a C++/ATL DLL to deploy it in a better way). For an unknown reason, my callbacks aren't working. Actually, the GetCustomUI method is found correctly but my "OnAction" and "GetImage" callbacks are still not found. Any idea ?

  • Anonymous
    December 18, 2006
    Note that GetCustomUI is called directly on the interface, but the callbacks use IDispatch, as discussed above.  Are you forwarding all of the IDispatch methods (GetIDsOfNames, Invoke(), etc) from your shim to your managed DLL?

  • Anonymous
    December 20, 2006
    It works ! I used the IDispatchImpl class of ATL which implements the GetIDsOfNames(), Invoke() for me so it wasn't the problem. I finally managed to have the callbacks to work recreating a blank shared add-in with the wizard (like you did), implementing IRibbonExtensibility and ICallbackInterface and then including the CLR Loading methods of the Shim in this new addin. There was probably something wrong in the ATL options of the project generated by the Shim wizard.

  • Anonymous
    December 22, 2006
    "Note that Office queries the IRibbonExtensibility interface for IDispatch, instead of the main interface. Normally this is unimportant, but it allows complicated add-ins to split their IDispatch interfaces off onto multiple objects if they provide multiple IDispatch implementations. For example, Excel add-ins can provide User-Defined Functions (UDFs) via IDispatch, and they usually won't want to have all of their RibbonX callbacks and UDFs on the same object." Er, you do realise that this violates the transitivity requirement for QueryInterface? (http://msdn2.microsoft.com/en-us/library/ms810016.aspx)

  • Anonymous
    December 22, 2006
    Yes, if an add-in were to actually do this it would violate QI transitivity.  It's not recommended and most tools (ATL, CLR-COM interop, etc.) won't let you do it, but the option is there for complex C++ add-ins if they need it.

  • Anonymous
    December 26, 2006
    I am new to C++ and ATL; but, how the heck would I use late binding to do this stuff so my addin works in older versions of outlook? I appreciate any feedback or tips. Thanks!

  • Anonymous
    December 26, 2006
    Since you're using C++ it should be pretty straightforward to make your add-in work on both Outlook 2007 and older versions (at least as far as RibbonX is concerned). Previous versions of Outlook will simply not query for IID_IRibbonExtensibility, so you can have all that code there but it just won't run. Your best best will probably be to link your add-in against the OFFICE11 version of the MSO.DLL typelibrary and just manually copy over the GUID and definition of IRibbonExtensibility from the OFFICE12 version. It might work to just link against the 12 version and deploy that on 11, but I haven't tried it (I know that will not work for managed .NET add-ins because of PIA signing, but I am not sure about unmanaged).

  • Anonymous
    December 26, 2006
    Thanks! I did not realize using IDispatchImpl was already taking care of all this. I did try using OFFICE12 dll and everything worked fine all the way back to Outlook 2000. I don't undertand COM all that weel although I have created many programs (funny I know). Maybe I will try the GUID idea just to make sure??? Thanks again for the quick response.

  • Anonymous
    December 30, 2006
    Is there any samples to get at the office button through code? It says its possible in the customization guide for developers, doesn't tell you how.

  • Anonymous
    December 30, 2006
    It's just a tag in the XML under <ribbon> so you can get at it just like it's another tab: <customUI ...> <ribbon>  <officeMenu>   <!-- put your controls here -->  </officeMenu> </ribbon> </customUI> Note that the Office Button itself cannot be altered, but the contents of the Office Menu can be (clicking the Button drops the Menu - the nomenclature is a bit confusing)

  • Anonymous
    January 05, 2007
    The comment has been removed

  • Anonymous
    January 05, 2007
    The comment has been removed

  • Anonymous
    January 08, 2007
    I don't know how to implement like "home style in word 2007" ,I have known gallery,but I have tried many times and failed. I have read your html,but i have not found xml descption.Could you tell me how to implment it ? thanks

  • Anonymous
    January 13, 2007
    I know this isn't your doing, but hopefully someone in Microsoft is escalating this issue: http://www.campaignmonitor.com/blog/archives/2007/01/microsoft_takes_email_design_b.html We just can't believe how absurd this is.

  • Anonymous
    January 16, 2007
    Jensen Harris’ blog hosts an interesting article on Using RibbonX with C++ and ATL . RibbonX is the user

  • Anonymous
    January 17, 2007
    I'm using C++ 6.0 I have implememnted GetIDsFromNames and Invoke. The problem I have is I can't get my Invoke to work correctly. For example OnAction callback I can do a GetIDsFromNames for ID (returns dispid 1) but when I try to Invoke it I get back an errorcode of 800a01a8 In fact any callback gives me the same problem. Any ideas please ?

  • Anonymous
    January 17, 2007
    I don't know what that error code is. Where does it come from?  I'm confused about how you are calling Invoke, Office should call it automatically after GetIDsOfNames.  Do you have the "Show add-in user interface errors" option turned on, and does it give you any more information?

  • Anonymous
    January 17, 2007
    You are correct, Outlook is calling my Invoke method. Sorry I wasn't clear. It after this point that things are not working correctly. In my Invoke routine called by Outlook after makeing sure its the correct dispid I then do the following to get the Id of the RibbonControl - I know I canb use SmartPointers but I still get an error and I'm trying to track things down. Ribbon::IRibbonControl * pCtrl = NULL; LPDISPATCH pDisp = pDispParams->rgvarg[0].pdispVal; pDisp->QueryInterface(Ribbon::IID_IRibbonControl, (LPVOID *)&pCtrl); OLECHAR * szId = L"Id"; DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0}; DISPID dspid; VARIANT vtResult; // this returns S_OK and dispid = 1 hr = pCtrl->GetIDsOfNames(IID_NULL, &szId, 1, LOCALE_SYSTEM_DEFAULT, &dspid); // this returns 800a01a8 hr = pCtrl->Invoke(dspid, IID_NULL, lcid, DISPATCH_PROPERTYGET, &dispparamsNoArgs, &vtResult, NULL, NULL);

  • Anonymous
    January 17, 2007
    Ah, I see now.  I'm not sure why it's returning that error, but maybe it is related to the LCID you are passing in (I don't see where that's defined). But, since you already have the IRibbonControl pointer, why don't you just call pCtrl->get_Id(&bstrId) instead of doing all of the IDispatch stuff?

  • Anonymous
    January 17, 2007
    The lcid is passed to me by Outlook - I have tried the default and it makes no difference. pCtrl->get_Id(&bstrID) also fails with the same error. I'm doing the IDispatch stuff to try to figure out where there error is happening. The error code of 800a01a8 means something about Object Required - which I don't understand because I was passed the pointer to IRibbonControl.

  • Anonymous
    January 17, 2007
    I am looking at the code for get_ID() and it can only return these values: 0x80007000 E_OUTOFMEMORY 0x80004005 E_FAIL 0x80004003 E_POINTER 0x00000000 S_OK So I have no idea where 0x800A01A8 is coming from (?). What kind of control is this and where is it? (Button, gallery, etc, in a custom tab on an Outlook inspector?)

  • Anonymous
    January 17, 2007
    Its a button control on a custom tab on the Outlook inspector.

  • Anonymous
    January 17, 2007
    I can't reproduce the problem here, so I don't know what else to try :(.  I am using an ATL-generated IDispatch like discussed above, so it's possible it has something to do with your homemade IDispatch implementation, though I don't know what it would be. Hopefully you can work around it without needing the ID property, or you could try packing up a simple repro case and posting it in the Office support forums.  Someone there should be able to take a look at it.

  • Anonymous
    January 17, 2007
    Thanks for trying. I need the ptr to IRibbonControl - all my buttons use the same OnAction callback - I then use the Id to distinguish which button I'm working with. An interesting thing that I have noted is that all my button callbacks, getVisible, getLabel etc are all passing me just one parameter in Invoke (which answers to a QueryInterface for IRibbonControl). Looking at the docs, getVisible should return 2 parameters ... So I'm not sure what is going on.

  • Anonymous
    January 21, 2007
    if (!RibbonXml) return E_POINTER;  *RibbonXml = SysAllocString( L"<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui">" L" <ribbon startFromScratch="flase">" L"   <tabs>" L"    <tab idMso="TabNewMailMessage">" L"     <group id="GroupTest"" L"            label="Test"" L"            insertAfterMso="GroupClipboard">" L"       <button id="CustomButton"" L"               imageMso="HappyFace"" L"               size="large"" L"               label="Click me!"" L"               onAction="ButtonClicked"/>" L"     </group>" L"    </tab>" L"   </tabs>" L" </ribbon>" L"</customUI>"  );  return (*RibbonXml ? S_OK : E_OUTOFMEMORY); In outlook 2007,i am adding a group test next to the clipboard in the Message tab.My above code is not working..Plz help Finally one more question? Also i need use xml file separately...plz tell me how to do it with vc++ 2005.

  • Anonymous
    January 22, 2007
    Make sure to enable "Show add-in user interface errors" in the options dialog.  Then it will show you where your XML fails to validate.  I can see a couple of errors to fix, such as startFromScratch="flase". As for loading from a file, it's just a string, so it shouldn't be too difficult. Searching for "load string from file" ought to come up with some code examples.

  • Anonymous
    January 23, 2007
    how to get a PNG file,how to converted into IpicureDisp and how to return back to office. give some sample code... one more question? why do we need to implement ICallbackInterface? Plz                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               Explain in detail.. i am using VC++2005

  • Anonymous
    January 23, 2007
    For info on PNGs and IPictureDisps, see this other blog post: http://blogs.msdn.com/jensenh/archive/2006/11/27/ribbonx-image-faq.aspx In the example above, the ATL classes use the typeinfo defined by ICallbackInterface in order to know how to invoke the callbacks, so it's necessary if you want to use ATL to auto-generate your IDispatch implementation.  If you're not using ATL, it's not necessary. You might have better luck posting further questions to the official support groups linked at the bottom of the article above.  This is not a support forum.

  • Anonymous
    January 23, 2007
    "you may have an older type library registered instead (such as "Office 11.0 Object Library") if you previously had older versions of Office installed on the same computer. In those cases you can just browse to the "OFFICE12" version of MSO.DLL and select it manually." Can I download this file from microsoft web site? best regards from http://www.aerofun.gsi.pl

  • Anonymous
    January 24, 2007
    No, MSO.dll is part of Office.  It doesn't make sense to download it separately because you could not test any code written against it unless you have Office 2007 installed.

  • Anonymous
    January 25, 2007
    Your code works great for creating new ribbon items, but I'm having problems repurposing an existing ribbon item. I have added the following to my xml:  <commands>    <command idMso="FileSaveAs" onAction="BuiltInControlClicked"/>    <command idMso="FileSaveAsPowerPoint97_2003" onAction="BuiltInControlClicked"/>  </commands> I have added the following to my idl: [id(6), helpstring("method BuiltInControlClicked")] HRESULT BuiltInControlClicked([in] IDispatch* ribbonControl, [in,out] VARIANT_BOOL* cancel); I have added the following to my CConnect class: STDMETHOD(BuiltInControlClicked)(IDispatch* ribbonControl, VARIANT_BOOL* cancel); However, I get an error when Office tries to call the callback ("An error occurred while calling the callback"). Any suggestions about what I might be doing wrong? Thanks

  • Anonymous
    January 25, 2007
    This error message means that the IDispatch::Invoke() call returned a failure HRESULT code, but the EXCEPINFO was not filled in and the code was not E_INVALIDARG or DISP_E_BADPARAMCOUNT (each of those conditions would give you a different message). So my first thought would be that your callback is successfully getting invoked, but maybe it's returning a failure code? If not, then the error code must be coming from the ATL classes that implement your IDispatch for you (maybe ATL is determining that your IDL info is malformed or something like that). When you added the new method to your IDL, did you use the ATL wizard to do it? You can do it manually, but the wizard does several steps which are easy to miss when doing it manually (updating the IDL, the typeinfo, the interface, the implementation, etc.)

  • Anonymous
    January 26, 2007
    I have a breakpoint in my callback which is never reached so the ATL classes must be returning the error.  I created the callback once manually firat and then created a second callback using the ATL wizard and got the same result both times.  Is it possible that the documentation about the expected method signature is out of date?

  • Anonymous
    January 26, 2007
    Sorry about that - you're right that the signature in the documentation is out of date.  For the second parameter it should be "VARIANT*" instead of "VARIANT_BOOL*" in order to work with ATL's IDispatch parameter marshaling. I figured that was not the problem since I would expect E_INVALIDARG to be returned in this case, but ATL seems to return a generic error code instead. I'll get that documentation page updated with a note to use VARIANT instead of VARIANT_BOOL if you're using ATL. Thanks for pointing out this problem!

  • Anonymous
    January 26, 2007
    Changing the method signature fixed my problem. Thanks!!

  • Anonymous
    January 28, 2007
    Hi, This is an informative blog, but it's updates are too infrequent. Can you update on a more regualr basis? Thanks! Paul

  • Anonymous
    February 05, 2007
    I appreciate the article but one would have to ask why the chuck didn't MS just include the ability to drag and drop "ribbons/commands/buttons into the tabs.   Hmm - before it was right click on the toolbar, customize - remove buttons I didn't use drag in buttons I did.   Now if I just learn programming I could probably create a button in 5 minutes - times the 30 I would change - OH yes, much simpler - thanks MS!

  • Anonymous
    April 07, 2007
    One new subscriber from Anothr Alerts