"Catastrophic Failure" setting Picture property of button in compose e-mail inspector in OL2003

  • Thread starter Thread starter Mark Smith
  • Start date Start date
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
 
Is this with WordMail?

Picture and Mask take an IPictureDisp object, which cannot be passed across
process boundaries. Although COM addins run in-process with Outlook WordMail
doesn't really. It's a subclassing of msword.exe and although used by
Outlook runs in the Word process space. Passing an IPictureDisp object to a
WordMail item will always fail and result in errors.

If a WordMail object is detected in an Inspector you have to use the
PasteFace method of passing the button image from the clipboard.




Mark Smith said:
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
 
Yup, that appears to be the problem. Thanks!.

Mark Smith

Ken Slovak - said:
Is this with WordMail?

Picture and Mask take an IPictureDisp object, which cannot be passed
across process boundaries. Although COM addins run in-process with Outlook
WordMail doesn't really. It's a subclassing of msword.exe and although
used by Outlook runs in the Word process space. Passing an IPictureDisp
object to a WordMail item will always fail and result in errors.

If a WordMail object is detected in an Inspector you have to use the
PasteFace method of passing the button image from the clipboard.




Mark Smith said:
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

 
Back
Top