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:


  5. Click to view full picture


  6. 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=\”http://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 (52)

  1. Andy says:

    This is great information.

    Can I do this without Visual Studio 2005?

  2. MSDNArchive says:

    Yep – the steps should be the same if you are using Visual Studio .NET (2001/2003).  I am not sure about previous versions of VS (6, etc.)

    If you aren’t using Visual Studio at all, check your C++ compiler documentation to see if it supports building COM add-ins for Office.  If it doesn’t have explicit support, you may have to write a lot of code by hand in order to duplicate all the stuff that Visual Studio generates automatically.

  3. Andre says:

    Could you please upload the sample project?

  4. MSDNArchive says:

    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

  5. jensenh says:

    I’ll post the solution on officeblogs.net and link it to the article tonight.

  6. jensenh says:

    OK, it’s posted.  Check the last line of the updated post.

  7. Connor says:

    This is great. It’s exactly what I’ve been looking for.

  8. David says:

    Can I do this with the Express Editions from MS?  This is great to be able to customize.

  9. MSDNArchive says:

    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

  10. Kevin says:

    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!

  11. MSDNArchive says:

    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);

  12. Agron says:

    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?

  13. MSDNArchive says:

    MSO.DLL is part of Office, and RibbonX only applies to ‘eXtending’ the built-in UI of Office, so it’s not actually a full-featured set of components that can be reused in other apps.

    But, the UI can be licensed for free, see this post for details:

    http://blogs.msdn.com/jensenh/archive/2006/11/21/licensing-the-2007-microsoft-office-user-interface.aspx

    There are several 3rd-party component vendors that provide controls you can use in your own apps.  For example:

    – DotNetBar (http://www.devcomponents.com/dotnetbar/)

    – SandRibbon (http://www.divelements.com/net/controls/sandribbon/)

    – etc..

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

  15. Gilles says:

    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 ?

  16. MSDNArchive says:

    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?

  17. Gilles says:

    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.

  18. Ken Hagan says:

    "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)

  19. MSDNArchive says:

    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.

  20. john says:

    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!

  21. MSDNArchive says:

    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).

  22. John says:

    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.

  23. eddiedoey says:

    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.

  24. MSDNArchive says:

    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)

  25. michelle yang says:

    Were looking for a UI Engineer,  I thought you might be interested in this job.

    If you are, please send your word doc resume asap.  Also they need someone who knows .Net

    Please see the following job description below. If your not interested if you can referr someone that would be great.

    Thanks Sean!

    Michelle Yang

    Technical Recruiter

    Senior UI Engineer – Build the future system for a fast growing technology company – San Francisco

    Location: San Francisco  Permanent Full Time

    The Senior Software Engineer – Presentation Tier will play a significant role in the design and development of the presentation tier components of real-time custom trading applications for capital markets using rich client technology such as Java Swing and .NET Windows Forms.  The candidate will be responsible for doing GUI development for a set of large complex applications rendering large data sets.

    Candidates must have experience in delivering real-time mission critical service based applications and a good understanding of issues involved in enterprise client-server software systems.  Working as part of a team the Senior Software Engineer will work on design and development of reusable, library level components for data manipulation and display in both the server presentation tier and desktop client.    The core technology team provides application architecture and components for our application development and professional service teams.  Members of the senior engineering staff are expected to contribute to the design and architecture of library-level components used across our product suite, and must be able to educate our internal teams and external clients on using our components effectively.   We are looking for an individual with great communications and mentoring skills.  

    We are looking for intelligent, professional candidates with a strong technical background, with experience in building sophisticated rich desktop clients as part of distributed multi-tiered applications.   The ideal candidate has experience in the presentation tier for real-time, high volume distributed systems, such as in finance or telecoms. A passion for high-quality user interfaces is a must, and an interest in or background in usability engineering is helpful.  

    A preference will be given to candidates with relevant financial industry experience, in particular front-office pricing, trading, and real-time systems.  We will also consider candidates from a non-banking background provided that they have an equivalent strong technical background, with some real-time distributed systems experience.  

    REQUIRED EXPERIENCE

    At least 5 years of professional experience in software development.

    Deep understanding of core programming topics such as multithreading, garbage collection, and distributed computing.

    Significant experience in designing, building, and maintaining library level software components used by multiple teams or products.

    Experience in building reusable GUI components in a modern window system framework.

    Substantial experience building client-server applications.

    Strong Object Oriented Analysis and Design skills.

    Strong communications and presentation skills.

    DESIRED EXPERIENCE

    Good understanding of and hand-on experience with both Java Swing and .NET Windows Forms.

    Exposure to usability design and engineering.

    Capital markets domain knowledge & experience, or an appetite to learn.

    Some exposure to Web client technologies

    EDUCATION

    B.S. in Computer Science or equivalent.

    CALL TO ACTION

    Keywords: Java, Swing, .Net, UI, multithreading, garbage collection, Core Java, Capital Markets, Fixed Income, real time applications, design and development, San Francisco

  26. michelle yang says:

    Were looking for a UI Engineer,  I thought you might be interested in this job.

    If you are, please send your word doc resume asap.  Also they need someone who knows .Net

    Please see the following job description below. If your not interested if you can referr someone that would be great.

    Michelle Yang

    yangmichell@gmail.com

    Technical Recruiter

    Senior UI Engineer – Build the future system for a fast growing technology company – San Francisco

    Location: San Francisco  Permanent Full Time

    The Senior Software Engineer – Presentation Tier will play a significant role in the design and development of the presentation tier components of real-time custom trading applications for capital markets using rich client technology such as Java Swing and .NET Windows Forms.  The candidate will be responsible for doing GUI development for a set of large complex applications rendering large data sets.

    Candidates must have experience in delivering real-time mission critical service based applications and a good understanding of issues involved in enterprise client-server software systems.  Working as part of a team the Senior Software Engineer will work on design and development of reusable, library level components for data manipulation and display in both the server presentation tier and desktop client.    The core technology team provides application architecture and components for our application development and professional service teams.  Members of the senior engineering staff are expected to contribute to the design and architecture of library-level components used across our product suite, and must be able to educate our internal teams and external clients on using our components effectively.   We are looking for an individual with great communications and mentoring skills.  

    We are looking for intelligent, professional candidates with a strong technical background, with experience in building sophisticated rich desktop clients as part of distributed multi-tiered applications.   The ideal candidate has experience in the presentation tier for real-time, high volume distributed systems, such as in finance or telecoms. A passion for high-quality user interfaces is a must, and an interest in or background in usability engineering is helpful.  

    A preference will be given to candidates with relevant financial industry experience, in particular front-office pricing, trading, and real-time systems.  We will also consider candidates from a non-banking background provided that they have an equivalent strong technical background, with some real-time distributed systems experience.  

    REQUIRED EXPERIENCE

    At least 5 years of professional experience in software development.

    Deep understanding of core programming topics such as multithreading, garbage collection, and distributed computing.

    Significant experience in designing, building, and maintaining library level software components used by multiple teams or products.

    Experience in building reusable GUI components in a modern window system framework.

    Substantial experience building client-server applications.

    Strong Object Oriented Analysis and Design skills.

    Strong communications and presentation skills.

    DESIRED EXPERIENCE

    Good understanding of and hand-on experience with both Java Swing and .NET Windows Forms.

    Exposure to usability design and engineering.

    Capital markets domain knowledge & experience, or an appetite to learn.

    Some exposure to Web client technologies

    EDUCATION

    B.S. in Computer Science or equivalent.

    CALL TO ACTION

    Keywords: Java, Swing, .Net, UI, multithreading, garbage collection, Core Java, Capital Markets, Fixed Income, real time applications, design and development, San Francisco

  27. bob says:

    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

  28. Paul Irish says:

    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.

  29. Jensen Harris’ blog hosts an interesting article on Using RibbonX with C++ and ATL . RibbonX is the user

  30. Michael says:

    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 ?

  31. MSDNArchive says:

    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?

  32. Michael says:

    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);

  33. MSDNArchive says:

    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?

  34. Michael says:

    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.

  35. MSDNArchive says:

    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?)

  36. Michael says:

    Its a button control on a custom tab on the Outlook inspector.

  37. MSDNArchive says:

    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.

  38. Michael says:

    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.

  39. lencastro says:

    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.

  40. MSDNArchive says:

    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.

  41. lencastro says:

    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

  42. MSDNArchive says:

    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.

  43. "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

  44. MSDNArchive says:

    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.

  45. Sean Rohead says:

    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

  46. MSDNArchive says:

    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.)

  47. Sean Rohead says:

    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?

  48. MSDNArchive says:

    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!

  49. Sean Rohead says:

    Changing the method signature fixed my problem.

    Thanks!!

  50. Paul O'Rama says:

    Hi,

    This is an informative blog, but it’s updates are too infrequent. Can you update on a more regualr basis?

    Thanks!

    Paul

  51. boe says:

    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!

  52. anothr user says:

    One new subscriber from Anothr Alerts