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:
#import ".\2007\IRibbonExtensibility.tlb" named_guids
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
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