zen3511 replied...
26-Nov-06 04:40 AM
Hi

I have an unmanaged C++ COM addin for Word that and I'm attempting to
add a new tab to the Office 2007 ribbon.

Although my new ribbon tab displays (i.e. GetCustomUI is called and the
xml is OK) , Word reports that the callback for OnAction is 'not found'
when I click any buttons.

My class implements IRibbonExtensibility:

public IDispatchImpl<IRibbonExtensibility, &IID_IRibbonExtensibility,
&LIBID_MyIRibbonExtensibility, /*wMajor =*/ 1, /*wMinor =*/ 0>

and includes

COM_INTERFACE_ENTRY(IRibbonExtensibility)

in the COM map, so I don't see why the callback isn't found.

In the xml, the OnAction entry is onAction="ButtonHandler" and the
corresponding class function is

STDMETHOD(ButtonHandler)(IRibbonControl *RibbonControl)
{
...
}

Having read every article on IRibbonExtensibility on Google and tried
everything within them (and much more besides) I'm now at a dead-end.

Incidentally, I created a c# addin with the same xml and that works
fine, but I would have the same issues as above when creating a shim,
so this is'nt a solution.

So, if there is anyone on the planet that knows anything at all about
this, I would be very grateful for any help or advice.

Regards

Steve H
IRibbonExtensibility
(1)
Callback
(1)
Problem
(1)
Unmanaged
(1)
Addin
(1)
Attempting
(1)
Office
(1)
Ribbon
(1)
  Patrick Schmid [MVP] replied...
26-Nov-06 12:16 PM
Your callback is wrong:
C++: HRESULT OnAction([in] IRibbonControl *pControl)

From this document:
http://msdn2.microsoft.com/en-us/library/aa722523.aspx

Patrick Schmid [OneNote MVP]
--------------
http://pschmid.net
***
Office 2007 RTM Issues: http://pschmid.net/blog/2006/11/13/80
Office 2007 Beta 2 Technical Refresh (B2TR):
http://pschmid.net/blog/2006/09/18/43
***
Customize Office 2007: http://pschmid.net/office2007/customize
OneNote 2007: http://pschmid.net/office2007/onenote
***
Subscribe to my Office 2007 blog: http://pschmid.net/blog/feed
  zen3511 replied...
27-Nov-06 09:30 AM
Hi Patrick

Thanks very much for your reply. After some further thought it turned
out that I made the mistake of adding the callback as a member of of
IRibbonExtensibility rather than my class - the confusion arose, in
part, from misinterpretation of
http://blogs.msdn.com/mshneer/archive/2006/05/31/doclevelribbons.aspx.

people with existing C++ -based COM addins for Office will probably
want to use the Ribbon interface, it would be helpful for Microsoft to
publish a definitive article on extending the Ribbon using C++ / ATL.

Best Regards

Steve H
  Soc replied...
29-Nov-06 04:23 PM
Hi Steve,

I would appreciate if you can post a code snippet how you implemented
IRibbonExtensibility. I have been looking for exactly this but hasn't found a
way yet.

In your code you have "LIBID_MyIRibbonExtensibility", did you declare your
own LIBID?

&LIBID_MyIRibbonExtensibility, /*wMajor =*/ 1, /*wMinor =*/ 0>

Thanks in advance,

TJain
  zen3511 replied...
04-Dec-06 11:09 AM
Hi

Sorry about the delay in replying - I don't view this NG regularly.

Yes, I did declare my own LIBID, mainly because I didn't want to import
mso.dll for Office 12, to make compatibility with earlier versions of
Office easier.


The idl to generate the tlb looks like this:

//import "oaidl.idl";
//import "ocidl.idl";

[
uuid(716C8EF9-79BC-4113-9117-47BF2E02F089),
version(1.0),
helpstring("s2sIRibbonExtensibility Object Library"),
]

library s2sIRibbonExtensibility
{
importlib("stdole2.tlb");

// Forward declare all types defined in this typelib
interface IRibbonControl;
interface IRibbonExtensibility;

// ============== IRibbonControl
[
odl,
uuid(000C0395-0000-0000-C000-000000000046),
helpcontext(0x00046500),
dual,
oleautomation
]

interface IRibbonControl : IDispatch {
[id(0x00000001), propget, helpcontext(0x00046501)]
HRESULT Id([out, retval] BSTR* Id);
[id(0x00000002), propget, helpcontext(0x00046502)]
HRESULT Context([out, retval] IDispatch** Context);
[id(0x00000003), propget, helpcontext(0x00046503)]
HRESULT Tag([out, retval] BSTR* Tag);
};

// ============== IRibbonExtensibility
[
odl,
uuid(000C0396-0000-0000-C000-000000000046),
helpcontext(0x000468e8),
dual,
oleautomation
]

interface IRibbonExtensibility : IDispatch {
[id(0x00000001), helpcontext(0x000468e9)]
HRESULT GetCustomUI(
[in] BSTR RibbonID,
[out, retval] BSTR* RibbonXml);
};

};


In my addin's class header I have the following relevant entries:


using namespace Office;
using namespace Word;
using namespace Excel;
using namespace s2sIRibbonExtensibility;

...
...
class ATL_NO_VTABLE Cs2signoa :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<Cs2signoa, &CLSID_s2signoa>,
public IDispatchImpl<Is2signoa, &IID_Is2signoa, &LIBID_s2soaLib,
/*wMajor =*/ 1, /*wMinor =*/ 0>,
public IDispatchImpl<_IDTExtensibility2, &IID__IDTExtensibility2,
&LIBID_AddInDesignerObjects>,
public IDispatchImpl<IRibbonExtensibility, &IID_IRibbonExtensibility,
&LIBID_s2sIRibbonExtensibility, /*wMajor =*/ 1, /*wMinor =*/ 0>,

...
...

BEGIN_COM_MAP(Cs2signoa)
COM_INTERFACE_ENTRY(Is2signoa)
COM_INTERFACE_ENTRY2(IDispatch, Is2signoa)
COM_INTERFACE_ENTRY(_IDTExtensibility2)
COM_INTERFACE_ENTRY(IRibbonExtensibility)
END_COM_MAP()

...
...

public:
...
// IRibbonExtensibility
STDMETHOD(raw_GetCustomUI)(BSTR RibbonID, BSTR *RibbonXml)
{
HRSRC hr = FindResource (_AtlBaseModule.GetModuleInstance (),
MAKEINTRESOURCE(IDR_XML_DATA1), TEXT("XML_DATA"));
if (hr)
{
HGLOBAL hgres = LoadResource (_AtlBaseModule.GetModuleInstance (),
hr);
if (hgres)
{
LPSTR ptr = (LPSTR)LockResource (hgres);
CComBSTR cbs = ptr;
*RibbonXml = cbs.m_str;
}
}
return S_OK;
}

// for ribbon
STDMETHOD(BtnClick)(IUnknown* pObj);
STDMETHOD(GetImage)(IUnknown* pObj, IPictureDisp ** ppdispImage);


I modified my class's idl to include the following:

interface Is2signoa : IDispatch{
[id(1), helpstring("method BtnClick")] HRESULT BtnClick([in] IUnknown*
pControl);
[id(2), helpstring("method GetImage")] HRESULT GetImage([in] IUnknown*
pControl, [out, retval] IPictureDisp ** ppdispImage);
};

Note that BtnClick and GetImage are in the xml that defines what to
call for the OnAction and GetImage functionality.

The BtnClick function looks like this:

STDMETHODIMP Cs2signoa::BtnClick(IUnknown* pObj)
{
CComQIPtr<IRibbonControl>  pRibbonControl;
pRibbonControl = pObj;
_bstr_t bstrID = pRibbonControl->Id;

if (lstrcmp (bstrID, TEXT("Button1")) == 0)
{
// button 1 stuff here
}
... rest omitted for clarity
return S_OK;
}

The GetImage function is complex owing to the *very tedious*
requirement that the button images be in .png format to ensure correct
transparency. The simplest method of handling this is to invoke GDI+,
once the png is laboriously loaded from the application resources

STDMETHODIMP Cs2signoa::GetImage(IUnknown* pObj, IPictureDisp **
ppdispImage)
{
HRESULT hr = S_OK;

CComQIPtr<IRibbonControl>  pRibbonControl;
pRibbonControl = pObj;
_bstr_t bstrID = pRibbonControl->Id;

PICTDESC pd;

pd.cbSizeofstruct = sizeof (PICTDESC);
pd.picType = PICTYPE_BITMAP;


GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;

gdiplusStartupInput.DebugEventCallback = NULL;
gdiplusStartupInput.SuppressBackgroundThread = FALSE;
gdiplusStartupInput.SuppressExternalCodecs = FALSE;
gdiplusStartupInput.GdiplusVersion = 1;
GdiplusStartup (&gdiplusToken, &gdiplusStartupInput, NULL);

int idPNG;

if (lstrcmp (bstrID, TEXT("Button1")) == 0)
idPNG = IDR_PNG_DATA1;
... others omitted for clarity

HRSRC hResource = FindResource (_AtlBaseModule.GetResourceInstance (),
MAKEINTRESOURCE(idPNG), TEXT("PNG_DATA"));
if (!hResource)
return hr;

DWORD dwImageSize = SizeofResource (_AtlBaseModule.GetResourceInstance
(), hResource);
const void* pResourceData = LockResource (LoadResource
(_AtlBaseModule.GetResourceInstance (), hResource));
if (!pResourceData)
return hr;

HGLOBAL hBuffer = GlobalAlloc (GMEM_MOVEABLE, dwImageSize);
if (hBuffer)
{
void* pBuffer = GlobalLock (hBuffer);
if (pBuffer)
{
CopyMemory (pBuffer, pResourceData, dwImageSize);

IStream* pStream = NULL;
if (::CreateStreamOnHGlobal (hBuffer, FALSE, &pStream) ==
S_OK)
{
Bitmap *pBitmap = Bitmap::FromStream (pStream);
pStream->Release();

if (pBitmap)
{
pBitmap->GetHBITMAP (0, &pd.bmp.hbitmap);
hr = OleCreatePictureIndirect (&pd, IID_IDispatch, TRUE, (LPVOID
*)ppdispImage);

delete pBitmap;
}
}
GlobalUnlock (pBuffer);
}
GlobalFree (hBuffer);
}

GdiplusShutdown (gdiplusToken);

return hr;
}

Please note that the above code does not include the usual error
handlers and is very much a first attempt, requiring cleaning up,
testing, etc.

Regards

Steve H
Office - Hi Steve,Thanks a lot for posting the code.
Asked By soch
05-Dec-06 01:32 PM
Hi Steve,

Thanks a lot for posting the code.

I was stuck from last two days and this code helped me move forward.

Now, I am able to get the button clicks.

Thanks again for your help.

-Teena
help
Callback Interface functions NOT being Called Office Hello All, I have a office outlook ATL-COM addin. In the main class I am implementing IRibbonExtensibility and a callback interface interface ICallbackInterface which implements the callback functions of the buttons I create in Ribbon interface. My interface MAP looks like this BEGIN_COM_MAP(CConnect) COM_INTERFACE_ENTRY2(IDispatch, IRibbonExtensibility) COM_INTERFACE_ENTRY(AddInDesignerObjects::IDTExtensibility2) COM_INTERFACE_ENTRY(IRibbonExtensibility) COM_INTERFACE_ENTRY(ICallbackInterface) COM_INTERFACE_ENTRY_IID(IID_IExchExt, IExchExt) COM_INTERFACE_ENTRY_IID(IID_IExchExtSessionEvents, IExchExtSessionEvents) COM_INTERFACE_ENTRY_IID(IID_IExchExtMessageEvents, IExchExtMessageEvents) END_COM_MAP() The problem which I am facing is, When I click on the button in Ribbon Interface callback function is not being called at all!! The error which outlook is giving me is
for outlook 2007. On Inspector window i have added one edit box(I have implemented IRibbonExtensibility interface ), in that edit box i have to show timer indicating how much time is spent while reading or composing mail. My Problem is how should i set text in edit box on the inspector window so each inspector window has different time depending on when they are created. I have callback function which will take text of the edit control, also i am invalidating that control so when time changes my callback function will be called to set the text. Any help or suggestion will be appreciated What exactly is the question? Is it how to determine which Inspector is firing the callback? Just use the control object passed to you in the callback and use its Context property. That is the Inspector and from there you can get do that? As I said: Just use the control object passed to you in the callback and use its Context property. That is the Inspector. Use that Inspector to see which for outlook 2007. On Inspector window i have added one edit box(I have implemented IRibbonExtensibility interface ), in