We help IT Professionals succeed at work.

How to save an Icon to file from an HICON

PinTail
PinTail asked
on
Medium Priority
1,010 Views
Last Modified: 2013-12-03
I thought this would be pretty simple, but it turns out to be quite complicated.

The problem in more detail is this.

Assume that you have only an HICON (which was passed to your function) and wish to write an .ico file (file containing an ICON resource formatted according to the spec)

I am having terrible trouble getting enough information about the Icon to be able to populate the appropriate structures embedded in the resource.


Particularly I have been unable to get GetDIBits to return sensible data

here is a relevant code snippet.


Func( HICON hIconLarge, hIconSmall)
{
ICONINFO iiLarge, iiSmall;

CBitmap oBMLargeColor, oBMLargeMask, oBMSmallColor, oBMSmallMask;
BITMAP bmLargeColor, bmLargeMask, bmSmallColor, bmSmallMask;
BITMAPINFO bmiLargeColor, bmiLargeMask, bmiSmallColor, bmiSmallMask;


// If lpvBits is NULL, GetDIBits examines the first member of the first structure pointed to by lpbi.
// This member must specify the size, in bytes, of a BITMAPCOREHEADER or a BITMAPINFOHEADER structure.
// The function uses the specified size to determine how the remaining members should be initialized.

// If lpvBits is NULL and the bit count member of BITMAPINFO is initialized to zero,
// GetDIBits fills in a BITMAPINFOHEADER structure or BITMAPCOREHEADER without the color table.
// This technique can be used to query bitmap attributes.


// we want a BITMAPINFOHEADER type
bmiLargeColor.bmiHeader.biSize = bmiLargeMask.bmiHeader.biSize = \
bmiSmallColor.bmiHeader.biSize = bmiSmallMask.bmiHeader.biSize = sizeof(BITMAPV4HEADER);

// we want the color table
bmiLargeColor.bmiHeader.biBitCount = bmiLargeMask.bmiHeader.biBitCount = \
bmiSmallColor.bmiHeader.biBitCount = bmiSmallMask.bmiHeader.biBitCount = 1;



     
     GetIconInfo(hIconLarge, &iiLarge);
     GetIconInfo(hIconSmall, &iiSmall);


     oBMLargeColor.Attach(iiLarge.hbmColor);
     oBMLargeMask.Attach(iiLarge.hbmMask);
     oBMSmallColor.Attach(iiSmall.hbmColor);
     oBMSmallMask.Attach(iiSmall.hbmMask);

     oBMLargeColor.GetBitmap(&bmLargeColor);
     oBMLargeMask.GetBitmap(&bmLargeMask);
     oBMSmallColor.GetBitmap(&bmSmallColor);
     oBMSmallMask.GetBitmap(&bmSmallMask);


     CDC* pDC = GetWindowDC();
     HDC hDC = pDC->m_hDC;
     if( 0 == GetDIBits(hDC, iiLarge.hbmColor, bmLargeColor.bmHeight-1, 0, NULL, &bmiLargeColor, DIB_RGB_COLORS) )
          ShowLastError();
     GetDIBits(hDC, iiLarge.hbmMask, bmLargeMask.bmHeight-1, 0, NULL, &bmiLargeMask, DIB_RGB_COLORS);
     GetDIBits(hDC, iiSmall.hbmColor, bmSmallColor.bmHeight-1, 0, NULL, &bmiSmallColor, DIB_RGB_COLORS);
     GetDIBits(hDC, iiSmall.hbmMask, bmSmallMask.bmHeight-1, 0, NULL, &bmiSmallMask, DIB_RGB_COLORS);



     ReleaseDC(pDC);
     oBMLargeColor.Detach();
     oBMLargeMask.Detach();
     oBMSmallColor.Detach();
     oBMSmallMask.Detach();

     DeleteObject(iiLarge.hbmColor);
     DeleteObject(iiLarge.hbmMask);
     DeleteObject(iiSmall.hbmColor);
     DeleteObject(iiSmall.hbmMask);

}
Comment
Watch Question

Author

Commented:
OK, after lots of digging around I have discovered htta unless the BITMAPINFO structure passed to GetDIBits is zero's out before the call,it returns a load of garbage.

It is unclear why this would be the case.  I was setting the appropriate members, and assuming the rest would be filled in.  actually I have to comment this line to get it to work
     // we want the color table
//     bmiLargeColor.bmiHeader.biBitCount = bmiLargeMask.bmiHeader.biBitCount = \
//          bmiSmallColor.bmiHeader.biBitCount = bmiSmallMask.bmiHeader.biBitCount = 1;


Now, I still need to understand how to get at the color table, if any, and then propulate the three Icon resource structures as defined in this article
\MSDN\2001JUL\1033\techart.chm::/html/msdn_icons.htm

The GetDIBits function seems very flakey, but looks like it should provide me with the color information required, I can see that I will be playing around with it a bit more.

ANy help in advance would be appreciated
jkr
CERTIFIED EXPERT
Top Expert 2012

Commented:
There seems to be an easier method - see http://support.microsoft.com/support/kb/articles/Q104/5/70.asp

Author

Commented:
Yes, I saw that, doesn't seem to work.
GlobalSize always returns 0 (zero)
and GlobalLock fails to return a valid pointer.

I can only imagine that the person that wrote that article had never actually tried to implement the code; either that or it only works when the Icon is loaded via the method described.

Since I have no control over how the Icon is being loaded - I only get passed an HICON - I cannot use this method.

Any other help ??

Commented:
I think you have to fill the BITMAPINFO structure you give in to GetDIBits, because GetDIBits tries to return whatever you want to have. And what you want to have, you define by filling the BITMAPINFO structure. Just settings it zero is not right, I think. You should call GetObject for the 2 bitmaps you get from GetIconInfo. GetObject will tell you want you have to fill into the BITMAPINFO structure. Only if you do that, GetDIBits will return the color values to you. At least I understand the documentation this way...

Regards, Madshi.
PinTail:

Please provide feedback in your open questions.

thanks!
amp
community support moderator
Commented:
PAQed - no points refunded (of 300)

modulo
Community Support Moderator