Set CommandBarButton.Picture property (c++)

  • Thread starter Thread starter Eric
  • Start date Start date
E

Eric

I'm trying to convert a Delphi sample I saw into C++, and so far I'm
not having any luck. Can anyone tell me what is wrong with this code,
or show me a better example of how to set the
"CommandBarButton.Picture" property from an Outlook 2002 Add-in?

Note - I've removed error checking and tried to compress code snippet
as much as possible.


// Some variables
// NOTE - "disp" is an IDispatch interface of a CommandBarButton
DISPPARAMS dp={NULL,NULL,0,0};
IPictureDisp *pd;
PICTDESC pdesc;
VARIANT p1;

// Create a IPictureDisp from a TBitmap (bmp, initialized earlier)
memset(&pdesc,0,sizeof(pdesc));
pdesc.cbSizeofstruct = sizeof(pdesc);
pdesc.picType = PICTYPE_BITMAP;
pdesc.bmp.hbitmap = bmp->Handle;
pdesc.bmp.hpal = bmp->Palette;
OleCreatePictureIndirect(&pdesc,IID_IPictureDisp,FALSE,(void**)&pd);

// Get the Dispatch ID for the "Picture" property
// dispID = 1610940430, which should be correct (from MSDN)
LPOLESTR Name = L"Picture";
DISPID dispID;
disp->GetIDsOfNames(IID_NULL,&Name,1,LOCALE_USER_DEFAULT,&dispID);

// We just need to pass the IPictureDisp pointer
p1.vt = VT_DISPATCH;
p1.pdispVal = pd;
dp.cArgs=1;
dp.rgvarg=&p1;

// Make call to Invoke()
disp->Invoke(dispID,GUID_NULL,0,
DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF|DISPATCH_METHOD,
&dp,NULL,NULL,NULL);

I've tried several different calling params for the Invoke() call, but
it always fails. The code above produces the error 0x80020004,
"Paramater not found".
 
Still working on this one... I figured out this property is by value,
so the below code, plus setting the dp fields for the named arg
(DISP_ID_PROPPUT, or whatever it is) changed the error message from
"param not found", to "exception". I'm thinking I've got the params
correct now, and the problem is with the creation of the IPictureDisp
object. I can do other things with the IPictureDisp object, like
check its handle, height, and width, so I know I created it correctly,
but I'm not sure I created it the way Outlook wants it created.
 
The dp variable should be initialized as follows:

ULONG NParams = DISPID_PROPERTYPUT;
dp.rgvarg = =&p1;
dp.rgdispidNamedArgs = &NParams;
dp.cArgs = 1;
dp.cNamedArgs = 1;

Dmitry Streblechenko (MVP)
http://www.dimastr.com/
OutlookSpy - Outlook, CDO
and MAPI Developer Tool
 
Thank you. I am initializing it that way now. That is when I started
getting the other error returned from the Invoke() call:

0x80020009, DISP_E_EXCEPTION

A pretty generic error value, which my best guess is telling me that
something is wrong with the IPictureDisp pointer. Maybe incorrect
picture format? The same bitmaps work with the PasteFace method
though, I'm just trying to use this method to get the nice background
with OL2003.

Here is the section of code where I create the IPictureDisp. I tried
switching to LoadImage() instead of using the TBitmap, but I've also
used a TBitmap to load the image and used that to set "pdesc.bmp.hpal"
also. The call to OleCreatePictureIndirect returns 0, so not even any
warnings, and I can make Invoke() calls using "pd" which return
reasonable values for Handle, Width, and Height.

IPictureDisp *pd;
PICTDESC pdesc;
memset(&pdesc,0,sizeof(pdesc));
pdesc.cbSizeofstruct = sizeof(pdesc);
pdesc.picType = PICTYPE_BITMAP;

pdesc.bmp.hbitmap = LoadImage(hInst,MAKEINTRESOURCE(res),IMAGE_BITMAP,0,0,LR_LOADMAP3DCOLORS);

pdesc.bmp.hpal = NULL;

HRESULT hr = OleCreatePictureIndirect(&pdesc,IID_IPictureDisp,TRUE,(void**)&pd);
 
OK, I'll give you the full (long) version this time:

--------------------------
VARIANT p1;
IPictureDisp *pd;
PICTDESC pdesc;
memset(&pdesc,0,sizeof(pdesc));
pdesc.cbSizeofstruct = sizeof(pdesc);
pdesc.picType = PICTYPE_BITMAP;

pdesc.bmp.hbitmap =
LoadImage(hInst,MAKEINTRESOURCE(res),IMAGE_BITMAP,0,0,LR_LOADMAP3DCOLORS);

pdesc.bmp.hpal = NULL;

HRESULT hr = OleCreatePictureIndirect(&pdesc,IID_IPictureDisp,TRUE,(void**)&pd);

if(FAILED(hr))
pd = NULL;

if(pd)
{
p1.vt = VT_DISPATCH;
p1.pdispVal = pd;
HRESULT hr = PutOleProp(disp,L"Picture",&v,1,&p1);
if(FAILED(hr))
{
MessageBeep(0);
}
pd->Release();
}
-------------------

PutOleProp above is what is returning the error value. PutOleProp is
my own helper function, which this is the code for it:

(I'm pretty sure my PutOleProp function is fine, even with variable
args and all, because I've been using pretty much this same function
for years, and it does work for every other OOM property I set)

---------------------

HRESULT PutOleProp(IDispatch *Disp,LPOLESTR Name,VARIANT *Result,int
cArgs...)
{
int Type = DISPATCH_PROPERTYPUT;
va_list marker;
DISPPARAMS dp={NULL,NULL,0,0};
DISPID dispidNamed=DISPID_PROPERTYPUT;
DISPID dispID;
HRESULT hr;

// Begin variable-argument list...
va_start(marker,cArgs);

CHECK(Disp);

// Get DISPID (Dispatch ID) for name passed...
hr = Disp->GetIDsOfNames(IID_NULL,&Name,1,LOCALE_USER_DEFAULT,&dispID);
if(FAILED(hr))
return(hr);

// Allocate memory for arguments...
MALLOC(VARIANT *Args=new VARIANT[cArgs+1]);

// Extract arguments (if any passed by caller)...
for(int i=cArgs-1; i>=0; i--)
memcpy(&Args,va_arg(marker,VARIANT*),sizeof(Args));

// End variable-argument section.
va_end(marker);

// Build DISPPARAMS (Dispatch params)
dp.cArgs=cArgs; //Count of params
dp.rgvarg=Args; //Reference to params (pointer)

dp.cNamedArgs = 1;
dp.rgdispidNamedArgs = &dispidNamed;

// This is an important step so the caller doesn't need to do this
everytime
// before they make this call. Some Invoke() calls will end up
calling
// VariantClear() which will end up freeing memory from a previous
call.
VariantInit(Result);

// Make the call to Dispatch::Invoke()
hr = Disp->Invoke(dispID,IID_NULL,LOCALE_SYSTEM_DEFAULT,(WORD)Type,&dp,Result,NULL,NULL);

// Free Args allocated with 'new' keyword
FREE(delete [] Args);

return(hr);
}
 
It looks like Type is DISPATCH_PROPERTYPUT, but it should be
DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF|DISPATCH_METHOD

Dmitry Streblechenko (MVP)
http://www.dimastr.com/
OutlookSpy - Outlook, CDO
and MAPI Developer Tool


Eric said:
OK, I'll give you the full (long) version this time:

--------------------------
VARIANT p1;
IPictureDisp *pd;
PICTDESC pdesc;
memset(&pdesc,0,sizeof(pdesc));
pdesc.cbSizeofstruct = sizeof(pdesc);
pdesc.picType = PICTYPE_BITMAP;

pdesc.bmp.hbitmap =
LoadImage(hInst,MAKEINTRESOURCE(res),IMAGE_BITMAP,0,0,LR_LOADMAP3DCOLORS);

pdesc.bmp.hpal = NULL;

HRESULT hr = OleCreatePictureIndirect(&pdesc,IID_IPictureDisp,TRUE,(void**)&pd);

if(FAILED(hr))
pd = NULL;

if(pd)
{
p1.vt = VT_DISPATCH;
p1.pdispVal = pd;
HRESULT hr = PutOleProp(disp,L"Picture",&v,1,&p1);
if(FAILED(hr))
{
MessageBeep(0);
}
pd->Release();
}
-------------------

PutOleProp above is what is returning the error value. PutOleProp is
my own helper function, which this is the code for it:

(I'm pretty sure my PutOleProp function is fine, even with variable
args and all, because I've been using pretty much this same function
for years, and it does work for every other OOM property I set)

---------------------

HRESULT PutOleProp(IDispatch *Disp,LPOLESTR Name,VARIANT *Result,int
cArgs...)
{
int Type = DISPATCH_PROPERTYPUT;
va_list marker;
DISPPARAMS dp={NULL,NULL,0,0};
DISPID dispidNamed=DISPID_PROPERTYPUT;
DISPID dispID;
HRESULT hr;

// Begin variable-argument list...
va_start(marker,cArgs);

CHECK(Disp);

// Get DISPID (Dispatch ID) for name passed...
hr = Disp->GetIDsOfNames(IID_NULL,&Name,1,LOCALE_USER_DEFAULT,&dispID);
if(FAILED(hr))
return(hr);

// Allocate memory for arguments...
MALLOC(VARIANT *Args=new VARIANT[cArgs+1]);

// Extract arguments (if any passed by caller)...
for(int i=cArgs-1; i>=0; i--)
memcpy(&Args,va_arg(marker,VARIANT*),sizeof(Args));

// End variable-argument section.
va_end(marker);

// Build DISPPARAMS (Dispatch params)
dp.cArgs=cArgs; //Count of params
dp.rgvarg=Args; //Reference to params (pointer)

dp.cNamedArgs = 1;
dp.rgdispidNamedArgs = &dispidNamed;

// This is an important step so the caller doesn't need to do this
everytime
// before they make this call. Some Invoke() calls will end up
calling
// VariantClear() which will end up freeing memory from a previous
call.
VariantInit(Result);

// Make the call to Dispatch::Invoke()
hr = Disp->Invoke(dispID,IID_NULL,LOCALE_SYSTEM_DEFAULT,(WORD)Type,&dp,Result,NUL
L,NULL);

// Free Args allocated with 'new' keyword
FREE(delete [] Args);

return(hr);
}

---------------------

The Invoke() call above is what fails with the DISP_E_EXCEPTION value.

---------

"Dmitry Streblechenko \(MVP\)" <[email protected]> wrote in message
Hmmm... I don't know. What is your latest code?

Dmitry Streblechenko (MVP)
http://www.dimastr.com/
OutlookSpy - Outlook, CDO
and MAPI Developer Tool
 
I tried that, but got the same error. I appreciate any other ideas
you may have. Thank you.


Just a note-
I think the only time you want/need to specify all three of those
flags is if you don't know or don't care what type of Invoke it is.
In this case the invoke command for "Picture" should be
"DISPATCH_PROPERTYPUT", at least that is what the type library
contains.

Although, I did notice one odd thing in the type library, which I
assume is just a typo... When I used the OleView utility which comes
with MSVC++ I see this for the "Picture" property of a
CommandBarButton:

[id(0x6005000e), propput, helpcontext(0x00001779)]
HRESULT Picture([in] IPictureDisp* ppdispPicture);

Usually "pp" means a pointer to a pointer, but "IPictureDisp*" means
just a pointer. In my code I'm passing just a pointer, however I did
try passing a pointer to a pointer, and no luck. It does seem like it
should be just a pointer, since Outlook doesn't need to modify it, but
I thought it was an interesting fact, which could mean that inside the
actual code somewhere they have an actual bug because a developer was
also thinking it was a pointer to a pointer, but it isn't.
 
No, you *must* use all 3, otherwise it simply does not work.
I wouldn't have written the original Delphi code if not for
DISPATCH_PROPERTYPUTREF, in Delphi it would be as simple as
OleVariant(disp).Picture:=pd;

Dmitry Streblechenko (MVP)
http://www.dimastr.com/
OutlookSpy - Outlook, CDO
and MAPI Developer Tool


Eric said:
I tried that, but got the same error. I appreciate any other ideas
you may have. Thank you.


Just a note-
I think the only time you want/need to specify all three of those
flags is if you don't know or don't care what type of Invoke it is.
In this case the invoke command for "Picture" should be
"DISPATCH_PROPERTYPUT", at least that is what the type library
contains.

Although, I did notice one odd thing in the type library, which I
assume is just a typo... When I used the OleView utility which comes
with MSVC++ I see this for the "Picture" property of a
CommandBarButton:

[id(0x6005000e), propput, helpcontext(0x00001779)]
HRESULT Picture([in] IPictureDisp* ppdispPicture);

Usually "pp" means a pointer to a pointer, but "IPictureDisp*" means
just a pointer. In my code I'm passing just a pointer, however I did
try passing a pointer to a pointer, and no luck. It does seem like it
should be just a pointer, since Outlook doesn't need to modify it, but
I thought it was an interesting fact, which could mean that inside the
actual code somewhere they have an actual bug because a developer was
also thinking it was a pointer to a pointer, but it isn't.


"Dmitry Streblechenko \(MVP\)" <[email protected]> wrote in message
It looks like Type is DISPATCH_PROPERTYPUT, but it should be
DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF|DISPATCH_METHOD

Dmitry Streblechenko (MVP)
http://www.dimastr.com/
OutlookSpy - Outlook, CDO
and MAPI Developer Tool
 
Wait a sec, you are requesting IID_IPictureDisp, not IID_IPicture!
IID_IPictureDisp is a dispinterface, it does not have a v-table other than
that derived from IDispatch, not wonder you were getting DISP_E_EXCEPTION.

Dmitry Streblechenko (MVP)
http://www.dimastr.com/
OutlookSpy - Outlook, CDO
and MAPI Developer Tool


Eric said:
I tried that, but got the same error. I appreciate any other ideas
you may have. Thank you.


Just a note-
I think the only time you want/need to specify all three of those
flags is if you don't know or don't care what type of Invoke it is.
In this case the invoke command for "Picture" should be
"DISPATCH_PROPERTYPUT", at least that is what the type library
contains.

Although, I did notice one odd thing in the type library, which I
assume is just a typo... When I used the OleView utility which comes
with MSVC++ I see this for the "Picture" property of a
CommandBarButton:

[id(0x6005000e), propput, helpcontext(0x00001779)]
HRESULT Picture([in] IPictureDisp* ppdispPicture);

Usually "pp" means a pointer to a pointer, but "IPictureDisp*" means
just a pointer. In my code I'm passing just a pointer, however I did
try passing a pointer to a pointer, and no luck. It does seem like it
should be just a pointer, since Outlook doesn't need to modify it, but
I thought it was an interesting fact, which could mean that inside the
actual code somewhere they have an actual bug because a developer was
also thinking it was a pointer to a pointer, but it isn't.


"Dmitry Streblechenko \(MVP\)" <[email protected]> wrote in message
It looks like Type is DISPATCH_PROPERTYPUT, but it should be
DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF|DISPATCH_METHOD

Dmitry Streblechenko (MVP)
http://www.dimastr.com/
OutlookSpy - Outlook, CDO
and MAPI Developer Tool
 
Thanks, I'll try switching that on Monday and let you know. I was
kind of wondering if that was the problem, but the type library, and
every other clue I had found all referenced IPictureDisp, and never
IPicture, so even though I was thinking that might be the issue, I
just never tried that because all the clues were pointing away from
it.
 
I just couldn't wait to try this idea (nothing like working late on a
Friday). However, still getting the same error.

I tried switching IID_IPictureDisp to IID_IPicture, same error.

I made sure I had all 3 of those type flags set, PUT|PUTREF|METHOD,
and still same error.

I tried also changing IPictureDisp *pd, to IPicture *pd, same error.
At first I was getting errors about trying to assign it to an
IDispatch, but I tried getting around that by sending it as a
VT_UKNOWN, and also just type casting it to the IDispatch pointer.
Still no luck.

This is what my most recent code looks like, incase anyone got lost.
I'm not going to repost the PutOleProp() function, that is still the
same and I've tried with an without the PUTBYREF and METHOD flags:

VARIANT p1;
IPictureDisp *pd;
PICTDESC pdesc;
memset(&pdesc,0,sizeof(pdesc));
pdesc.cbSizeofstruct = sizeof(pdesc);
pdesc.picType = PICTYPE_BITMAP;

pdesc.bmp.hbitmap =
LoadImage(hInst,MAKEINTRESOURCE(res),IMAGE_BITMAP,0,0,LR_LOADMAP3DCOLORS);

pdesc.bmp.hpal = NULL;

HRESULT hr = OleCreatePictureIndirect(&pdesc,IID_IPicture/*Disp*/,TRUE,(void**)&pd);

if(FAILED(hr))
pd = NULL;

if(pd)
{
p1.vt = VT_DISPATCH;
p1.pdispVal = pd;
HRESULT hr = PutOleProp(disp,L"Picture",&v,1,&p1);
if(FAILED(hr))
{
MessageBeep(0);
}
pd->Release();
}
 
Wow! I figured it out! My code was actually working all along,
pretty much any version of the code I had tried before would have
worked.

The ONLY problem was that I set the "Protection" property for my
custom toolbar which I added, which I'm trying to add these buttons
to. Once I turned off the protection, everything worked great.

Guess that is what I get for playing around with that undocumented (or
not very well documented) function. Who would have ever though that
locking down a bar with the intent that the user couldn't customize
it, would also make it so my own code couldn't swap out a picture.
This has to be a bug of some sort, but of course MS will call it a
feature, even though they didn't bother to lock down the "PasteFace"
method.

Sorry for the MS bashing, just force of habit, actually I really like
Microsoft, especially the MVPs. Thanks for all your time Dmitry!
 
Back
Top