M
Mark Smith
I have an addin in C++ which adds buttons to every inspector and explorer.
This addin is base on the Outlook 2000 type libraries, and needs to remain
that way. I just recently discovered the way to add a custom picture to
these buttons using IPictureDisp and the "Picture" property of the button.
This seems to work just fine on all inspectors and explorers, except when I
try to do it in a compose e-mail inspector (new e-mail, reply, forward,
etc.). In this case I get a "Catastrophic Failure" (0x8000ffff) from the
IDispatch::Invoke() call, and the image does not show up. So far I have
only seen this happen in Outlook 2003 (both with and without SP2).
When I looked around online for some info on this I found two possible
explanations:
1) The code is running in a different thread from the main addin thread.
I've double checked that this is not the case for me.
2) A bug in Outlook 2003 (now I can't remember if it said SP2 or not).
Are there any other explanations? Is there anything I can do about this
bug? It would be nice to fix it, but if it is an Outlook bug, I was hoping
to get a confirmation from a more reliable source.
Much of this is adapted from code I found online. One post I saw about that
code said that IPictureDisp can't be used here, and IPicture should be
instead, but this method requires an IDispatch (which IPictureDisp is and
IPictureDisp is not). Plus the fact that it already works fine in most
cases.
I'd be surprised if this was relevant, but one step I do before all of this
is to set the image using the PutStyle() and PutFaceId() method, so as to
have a fall back in case the IPictureDisp method doesn't work
Here are my relevant snippets of code (some error handling removed for
clarity):
_button is: CComQIPtr<Office::_CommandBarButton> &_button
// Put image on button
PICTDESC pdesc;
memset(&pdesc, 0, sizeof(pdesc));
pdesc.cbSizeofstruct = sizeof(pdesc);
pdesc.picType = PICTYPE_BITMAP;
pdesc.bmp.hbitmap = (HBITMAP)LoadImage(GetModuleHandle("pmlinkol.dll"),
MAKEINTRESOURCE(IDB_JOURNAL), IMAGE_BITMAP, 16, 16, LR_DEFAULTCOLOR |
LR_SHARED);
CComPtr<IPictureDisp> pd;
if (SUCCEEDED(OleCreatePictureIndirect(&pdesc, IID_IPictureDisp, TRUE,
(void**)&pd)) && (pd != NULL)) {
VARIANT p1;
p1.vt = VT_DISPATCH;
p1.pdispVal = pd;
hr = putOleProp(_button, L"Picture", 1, &p1);
if (FAILED(hr)) {
// Show the error
...
}
}
static HRESULT putOleProp(IDispatch *const _pDisp, LPOLESTR _pName, int
cArgs...)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// Begin variable-argument list...
va_list marker;
va_start(marker, cArgs);
// Extract arguments (if any passed by caller)...
VARIANT *const _pArgs = new VARIANT[cArgs + 1];
for (int i = cArgs - 1; i >= 0; i--) {
memcpy(&_pArgs, va_arg(marker, VARIANT*), sizeof(_pArgs));
}
// End variable-argument section.
va_end(marker);
// Get DISPID (Dispatch ID) for name passed...
DISPID dispID;
HRESULT hr = _pDisp->GetIDsOfNames(IID_NULL, &_pName, 1,
LOCALE_USER_DEFAULT, &dispID);
if (FAILED(hr)) {
return hr;
}
// Build DISPPARAMS (Dispatch params)
DISPID dispidNamed = DISPID_PROPERTYPUT;
DISPPARAMS dp = {_pArgs, &dispidNamed, cArgs, 1};
// Make the call to Dispatch::Invoke()
CComVariant result;
hr = _pDisp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_PROPERTYPUT, &dp, &result, NULL, NULL);
// Free Args allocated with 'new' keyword
delete [] _pArgs;
return hr;
}
Thank you,
Mark Smith
This addin is base on the Outlook 2000 type libraries, and needs to remain
that way. I just recently discovered the way to add a custom picture to
these buttons using IPictureDisp and the "Picture" property of the button.
This seems to work just fine on all inspectors and explorers, except when I
try to do it in a compose e-mail inspector (new e-mail, reply, forward,
etc.). In this case I get a "Catastrophic Failure" (0x8000ffff) from the
IDispatch::Invoke() call, and the image does not show up. So far I have
only seen this happen in Outlook 2003 (both with and without SP2).
When I looked around online for some info on this I found two possible
explanations:
1) The code is running in a different thread from the main addin thread.
I've double checked that this is not the case for me.
2) A bug in Outlook 2003 (now I can't remember if it said SP2 or not).
Are there any other explanations? Is there anything I can do about this
bug? It would be nice to fix it, but if it is an Outlook bug, I was hoping
to get a confirmation from a more reliable source.
Much of this is adapted from code I found online. One post I saw about that
code said that IPictureDisp can't be used here, and IPicture should be
instead, but this method requires an IDispatch (which IPictureDisp is and
IPictureDisp is not). Plus the fact that it already works fine in most
cases.
I'd be surprised if this was relevant, but one step I do before all of this
is to set the image using the PutStyle() and PutFaceId() method, so as to
have a fall back in case the IPictureDisp method doesn't work
Here are my relevant snippets of code (some error handling removed for
clarity):
_button is: CComQIPtr<Office::_CommandBarButton> &_button
// Put image on button
PICTDESC pdesc;
memset(&pdesc, 0, sizeof(pdesc));
pdesc.cbSizeofstruct = sizeof(pdesc);
pdesc.picType = PICTYPE_BITMAP;
pdesc.bmp.hbitmap = (HBITMAP)LoadImage(GetModuleHandle("pmlinkol.dll"),
MAKEINTRESOURCE(IDB_JOURNAL), IMAGE_BITMAP, 16, 16, LR_DEFAULTCOLOR |
LR_SHARED);
CComPtr<IPictureDisp> pd;
if (SUCCEEDED(OleCreatePictureIndirect(&pdesc, IID_IPictureDisp, TRUE,
(void**)&pd)) && (pd != NULL)) {
VARIANT p1;
p1.vt = VT_DISPATCH;
p1.pdispVal = pd;
hr = putOleProp(_button, L"Picture", 1, &p1);
if (FAILED(hr)) {
// Show the error
...
}
}
static HRESULT putOleProp(IDispatch *const _pDisp, LPOLESTR _pName, int
cArgs...)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// Begin variable-argument list...
va_list marker;
va_start(marker, cArgs);
// Extract arguments (if any passed by caller)...
VARIANT *const _pArgs = new VARIANT[cArgs + 1];
for (int i = cArgs - 1; i >= 0; i--) {
memcpy(&_pArgs, va_arg(marker, VARIANT*), sizeof(_pArgs));
}
// End variable-argument section.
va_end(marker);
// Get DISPID (Dispatch ID) for name passed...
DISPID dispID;
HRESULT hr = _pDisp->GetIDsOfNames(IID_NULL, &_pName, 1,
LOCALE_USER_DEFAULT, &dispID);
if (FAILED(hr)) {
return hr;
}
// Build DISPPARAMS (Dispatch params)
DISPID dispidNamed = DISPID_PROPERTYPUT;
DISPPARAMS dp = {_pArgs, &dispidNamed, cArgs, 1};
// Make the call to Dispatch::Invoke()
CComVariant result;
hr = _pDisp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_PROPERTYPUT, &dp, &result, NULL, NULL);
// Free Args allocated with 'new' keyword
delete [] _pArgs;
return hr;
}
Thank you,
Mark Smith