We help IT Professionals succeed at work.

How to save a icon?(create ico file)

nkraghu
nkraghu asked
on
6,134 Views
Last Modified: 2013-12-03
Hi everbody,
          I have a handle of an icon(HICON) which i got using SHGetFileInfo. I want to save this handle as an Icon file(.ico). I am using following procedure:

int  SaveMyIcon( HICON hIcon)
{
  int       fh, i, iResult;
  UINT      uiSize;
  DWORD     dwSize;
  OFSTRUCT  of;
  LPSTR lpGMem;

    if (!hIcon)
      return FALSE;

    dwSize  = GlobalSize(hIcon);
    lpGMem  = (LPSTR)GlobalLock(hIcon);
    fh = OpenFile ("myicon.ico", &of, OF_WRITE | OF_CREATE);

    if (fh == -1)  // If NOT opened successfully.
    {
     MessageBox(NULL, "Unable to create file", NULL, MB_OK );
     return FALSE;
    }

    uiSize = _lwrite(fh, (LPSTR)lpGMem, (UINT)dwSize);
    _lclose(fh);

    if (uiSize == -1 || uiSize < (UINT)dwSize)
    {
       MessageBox(NULL, "Unable to read file", NULL, MB_OK );
      GlobalUnlock(hIcon);
       return FALSE;
    }
    else // Everything worked, return hGMem.
    {
      GlobalUnlock(hIcon);
      return TRUE;
    }
}


This procedure is not working. I am unable to create a ico file of the handle using the above procedure. How to save an icon file using the handle of an icon?

Thanks in advance.
Comment
Watch Question

Use CreateFile not OpenFile
cast the handle to a byte* and write it to file?
WxW

Commented:
Is an HICON a global memory handle as HGLOBAL ? Who ensures that locking the HICON into memory returns the icon bytes ? Who ensures that a HICON isn't a window handle that is used by Windows in some other way ?

Safer approach is to draw the icon in a DC using DrawIconEx and save the dc as a bitmap.

Author

Commented:
The GlobalSize/GlobalLock function is returning INVALID_HANDLE error code. That's why my above said proc was not working.

But when I use 'SetIcon' function with the same handle  to display the icon in a static control, it is displaying properly.

I don't know why the HICON handle returned by SHGetFileInfo is differeing in SetIcon and GlobalSize cases...

I tried Daij-Djan idea, but failed because of the reason mentioned above.
CERTIFIED EXPERT
Author of the Year 2009

Commented:
What do you get if you call GetIconInfo?

How are you obtaining the HICON -- show the flags you use in the SHGetFileInfo  call.

I think that the technique you are using comes from old information that applies only to 32x32 icons:

     http://support.microsoft.com/default.aspx?scid=kb;EN-US;q104570

Note at the top that it applies to Win 3.1  !!!!

-- Dan

Author

Commented:
This is my SHGetFileInfo:

SHFILEINFO sfis;
SHGetFileInfo(m_Ext,FILE_ATTRIBUTE_NORMAL,
                       &sfis,
                       sizeof(sfis),
                       SHGFI_USEFILEATTRIBUTES|SHGFI_ICON|SHGFI_SHELLICONSIZE|SHGFI_SMALLICON);

where m_Ext refers to the extension(.txt/.doc etc)
When I use SHGFI_ICONLOCATION flag, it is neither returning the filename in displayname param in sfis.

If I use GetIconInfo, I am getting mask and color bitmaps. Is that sufficient to write ico file using these raw bytes by creating ico format file? If so, how to do this?

CERTIFIED EXPERT
Author of the Year 2009

Commented:
Creating an ICO file is a several step operation, including writing an ICONDIR record, and a series of ICONDIRENTRY records, and then the several ICONIMAGE structures.  It is covered here:

      Icons in Win32
      http://msdn.microsoft.com/library/en-us/dnwui/html/msdn_icons.asp

and it contains source code (see the file ICONS.C and the function WriteIconToICOFile() in that example).  I can't locate any simple Win32 API to write these files.

Maybe you don't need an icon???? You can get the HBITMAPs and there are several very simepl way to output these to a file.

-- Dan

Author

Commented:
Hi DanRollins,
                 I have tried that code. From GetIconInfo I just have hbmcolor bitmap and hbmmask bitmaps. I tried writing these two raw bitmap bits into the ico structure. But either visualstudio/ iconpro(msdn sample) is not displaying my icon properly.

                 I tried by writing 'one' icon image consisting of these two bitmaps(I used CreateBitmapInfoStruct). It's not displaying the icon properly.


Here's my snippet: If you could find any issues with this code kindly reply.

//The hIcon is the one which i got using SHGetFileInfo

BOOL WriteIconToICOFile( HICON hIcon, LPCTSTR szFileName )
{
    HANDLE          hFile;
    UINT        i;
    DWORD          dwBytesWritten;
      LPBYTE lpBits,lpBits2;              // memory pointer
      
      ICONINFO iconinfo;
      PBITMAPINFO pbi;
      ICONIMAGE icoim;
      int ret;

      ::GetIconInfo(hIcon,&iconinfo);


      
      PBITMAPINFO pBitmapInfo1=CreateBitmapInfoStruct(iconinfo.hbmColor);
      PBITMAPINFO pBitmapInfo2=CreateBitmapInfoStruct(iconinfo.hbmMask);


      HDC hdc=::GetDC(NULL);
      HDC memdc=CreateCompatibleDC(hdc);


      lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pBitmapInfo1->bmiHeader.biSizeImage);
      lpBits2 = (LPBYTE) GlobalAlloc(GMEM_FIXED, pBitmapInfo2->bmiHeader.biSizeImage);

      //HBITMAP hOld=(HBITMAP)::SelectObject(hdc,iconinfo.hbmColor);

      ret=GetDIBits(memdc,iconinfo.hbmColor,0,(WORD)pBitmapInfo1->bmiHeader.biHeight, lpBits, pBitmapInfo1,
                  DIB_RGB_COLORS);

      if(lpBits==NULL)
            return FALSE;


      ret=GetDIBits(memdc,iconinfo.hbmMask,0,(WORD)pBitmapInfo2->bmiHeader.biHeight, lpBits2, pBitmapInfo2,
                  DIB_RGB_COLORS);
      

      if(lpBits2==NULL)
            return FALSE;

      ///Fill IconImage
      icoim.icHeader.biBitCount=pBitmapInfo1->bmiHeader.biBitCount;
      icoim.icHeader.biSize=pBitmapInfo1->bmiHeader.biSize;
      icoim.icHeader.biWidth=pBitmapInfo1->bmiHeader.biWidth;
      icoim.icHeader.biHeight=pBitmapInfo1->bmiHeader.biHeight + pBitmapInfo2->bmiHeader.biHeight;
      icoim.icHeader.biPlanes=pBitmapInfo1->bmiHeader.biPlanes;      
      icoim.icHeader.biSizeImage=pBitmapInfo1->bmiHeader.biSizeImage;      
      
      icoim.icColors[1]=pBitmapInfo1->bmiColors[1];

      icoim.lpXOR = lpBits;
      icoim.lpAND = lpBits2;
      ///Fill IconImage

    // open the file
    if( (hFile = CreateFile( szFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL )) == INVALID_HANDLE_VALUE )
    {
        AfxMessageBox(  "Error Opening File for Writing" );
        return FALSE;
    }
    // Write the header
    if( ! WriteICOHeader( hFile, 1 ) )
    {
        AfxMessageBox(  "Error Writing ICO File" );
        CloseHandle( hFile );
        return FALSE;
    }
    // Write the ICONDIRENTRY's
    for( i=0; i<1; i++ )
    {
        ICONDIRENTRY    ide;

        // Convert internal format to ICONDIRENTRY
        ide.bWidth = 16;
        ide.bHeight = 16;
        ide.bReserved = 0;
        ide.wPlanes = pBitmapInfo1->bmiHeader.biPlanes;
        ide.wBitCount = pBitmapInfo1->bmiHeader.biBitCount;
        if( (ide.wPlanes * ide.wBitCount) >= 8 )
            ide.bColorCount = 0;
        else
            ide.bColorCount = 1 << (ide.wPlanes * ide.wBitCount);
            
        ide.dwBytesInRes = pBitmapInfo1->bmiHeader.biSize;
        ide.dwImageOffset = CalculateImageOffset( pBitmapInfo1, 0 );
        // Write the ICONDIRENTRY out to disk
        if( ! WriteFile( hFile, &ide, sizeof( ICONDIRENTRY ), &dwBytesWritten, NULL ) )
            return FALSE;
        // Did we write a full ICONDIRENTRY ?
        if( dwBytesWritten != sizeof( ICONDIRENTRY ) )
            return FALSE;
    }
    // Write the image bits for each image
      
    for( i=0; i<1; i++ )
    {
        /*DWORD dwTemp = pBitmapInfo1->bmiHeader.biSizeImage;

        // Set the sizeimage member to zero
        //pBitmapInfo1->bmiHeader.biSizeImage = 0;
        // Write the image bits to file
        if( ! WriteFile( hFile, lpBits, pBitmapInfo1->bmiHeader.biSize, &dwBytesWritten, NULL ) )
            return FALSE;
        if( dwBytesWritten != pBitmapInfo1->bmiHeader.biSize )
            return FALSE;
            //pBitmapInfo1->bmiHeader.biSizeImage = dwTemp;

            if( ! WriteFile( hFile, lpBits2, pBitmapInfo2->bmiHeader.biSize, &dwBytesWritten, NULL ) )
            return FALSE;
        if( dwBytesWritten != pBitmapInfo2->bmiHeader.biSize )
            return FALSE;
       
            // set it back
        pBitmapInfo2->bmiHeader.biSizeImage = dwTemp;*/

            if( ! WriteFile( hFile, &icoim, sizeof(ICONIMAGE), &dwBytesWritten, NULL ) )
            return FALSE;

            if( dwBytesWritten != sizeof(ICONIMAGE) )
            return FALSE;

    }

      // Free memory.
      
    CloseHandle( hFile );

      GlobalFree((HGLOBAL)lpBits);
      GlobalFree((HGLOBAL)lpBits2);

    return FALSE;
}

BOOL WriteICOHeader( HANDLE hFile, UINT nNumEntries )
{
    WORD    Output;
    DWORD      dwBytesWritten;

    // Write 'reserved' WORD
    Output = 0;
    if( ! WriteFile( hFile, &Output, sizeof( WORD ), &dwBytesWritten, NULL ) )
        return FALSE;
    // Did we write a WORD?
    if( dwBytesWritten != sizeof( WORD ) )
        return FALSE;
    // Write 'type' WORD (1)
    Output = 1;
    if( ! WriteFile( hFile, &Output, sizeof( WORD ), &dwBytesWritten, NULL ) )
        return FALSE;
    // Did we write a WORD?
    if( dwBytesWritten != sizeof( WORD ) )
        return FALSE;
    // Write Number of Entries
    Output = (WORD)nNumEntries;
    if( ! WriteFile( hFile, &Output, sizeof( WORD ), &dwBytesWritten, NULL ) )
        return FALSE;
    // Did we write a WORD?
    if( dwBytesWritten != sizeof( WORD ) )
        return FALSE;
    return TRUE;
}

DWORD CalculateImageOffset( PBITMAPINFO lpIR, UINT nIndex )
{
    DWORD      dwSize;
    UINT    i;

    // Calculate the ICO header size
    dwSize = 3 * sizeof(WORD);
    // Add the ICONDIRENTRY's
    dwSize += 1 * sizeof(ICONDIRENTRY);
    // Add the sizes of the previous images
    for(i=0;i<nIndex;i++)
        dwSize += lpIR->bmiHeader.biSize;
    // we're there - return the number
    return dwSize;
}


CERTIFIED EXPERT
Author of the Year 2009

Commented:
>> It's not displaying the icon properly.

Please explain.  The above code looks like it saves an icon file.  It does not display anything.  

Can you open the resulting .ICO file with Visual Studio or can you read it with the LoadImage API function?

-- Dan
CERTIFIED EXPERT
Author of the Year 2009

Commented:
I worked out a simpler technique to save an icon represented by an HICON to a .ICO file.

The basic technique is to
  1)  OleCreatePictureIndirect -- create an IPicture
  2)  CreateStreamOnHGlobal -- create a stream
  3)  IPicture::SaveAsFile        -- write the the tream in 'native' icon format
  4)  Then get the handle, and get a pointer to its data, and write it to a file.
  5)  Be sure to DestroyIcon after SHGetFileInfo to prevent a memory leak.

I verified that I get a small icon or regular one, depending upon settings in SHGetFileInfo.

I verified the .ICO file by Importing it into Visual Studio.  I assume that LoadImage would also work.

This was a good challenge.  It is surprising that I could find no complete, simple examples that used this.  Someone who knows more about IStreams could probably simplify this even further.

-- Dan
=-=--=-=-=-=-=-=-=-=-=-==-=-=-=--==-=-=-=--==-=-=--=-==-=--=
#include <afxctl.h>

void CD08Dlg::OnButton1()
{
      SHFILEINFO rSFI;
      memset(&rSFI,0, sizeof(rSFI) );
      SHGetFileInfo(".ZIP",FILE_ATTRIBUTE_NORMAL,
                  &rSFI,
                  sizeof(rSFI),
                  SHGFI_USEFILEATTRIBUTES|SHGFI_ICON|SHGFI_SHELLICONSIZE|SHGFI_SMALLICON
      );

      HICON hIcon= rSFI.hIcon;

      LPPICTURE pPicture;
      PICTDESC  rPD;

      rPD.cbSizeofstruct= sizeof(PICTDESC);
      rPD.picType=        PICTYPE_ICON;
      rPD.icon.hicon=     hIcon;
      
      HRESULT  hr;
      IStream* pStream= 0;
      HGLOBAL  hMem=    0;

      hr= OleCreatePictureIndirect( &rPD, IID_IPicture, FALSE, (void**)&pPicture);

      hr= CreateStreamOnHGlobal(0, TRUE, &pStream );

      long lActual;
      hr= pPicture->SaveAsFile( pStream, TRUE, &lActual );
      hr= pPicture->Release();

      hr= GetHGlobalFromStream( pStream, &hMem );
      int nSize= GlobalSize( hMem );           // eyeball for debug (same as lActual)

      char szFileName[]="c:\\temp\\test2.ico";

      HANDLE hFile= CreateFile( szFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
      DWORD nActual= 0;

      LPSTR pMem= (LPSTR)GlobalLock( hMem );

      WriteFile( hFile, pMem, nSize, &nActual, 0 );
      CloseHandle( hFile );

      //-------------------------------- plug those leaks!
      GlobalUnlock( hMem );
      GlobalFree( hMem );
      DestroyIcon( hIcon );
}

Author

Commented:
HI DanRollins
           I meant to say that after saving icon with my proc, I was not able to open that icon. Anyway i tried your method and it worked :-)

           But now I am getting a icon with black background. It's not transparent. I came to saving icons because I was not able to save transparent gifs/jpgs. But with icons too,  it's the same case. Is there is any workaround to this solution (to get transparent icon). I tried to open the icon which was saved following your method. In visualstudio(VC6) it said unknown filetype. Loadimage worked and I am able open the icon in iconpro. (Anyway I will be accepting Danrollins answer).
           
          If I am able to get this transparent solution also, it will be helpful.

       
CERTIFIED EXPERT
Author of the Year 2009
Commented:
This one is on us!
(Get your first solution completely free - no credit card required)
UNLOCK SOLUTION
CERTIFIED EXPERT
Author of the Year 2009

Commented:
If you have Visual Studio .NET, there is a new ALT class named CImage (it does not use .NET, but does use msimg32.dll and gdi32.lib).  It has the ability to save files in all of the most common formats, including GIF and ICO.  It has a "SetTransparentColor"  method that might let you save transparent GIFs.  

I'll bet that this newer technology is much better at handling 256-color ICONS and icons of various sizes.

-- Dan
The problem (as I see it) is that icons can be translucent.
Not only transparent or not (100% or 0%)

Newer icons have an alpha channel. That must be accounted for.

Author

Commented:
Can we save icons with CImage? I guess we need encoderclass id for reading and writing. We can only READ icon but cannot write. I have tried the same with Bitmap.save(Which overloads save function of Image class) method in .NET

                    In .NET case I had tried for saving gifs. But that too had this black background issue. That's why I came to saving icons in alternate ways mentioned by Dan.
CERTIFIED EXPERT
Author of the Year 2009

Commented:
CImage Class
       http://msdn.microsoft.com/library/en-us/vclib/html/vcrefCImage.asp
CImage::IsTransparencySupported()
      http://msdn.microsoft.com/library/en-us/vclib/html/vcrefcimageistransparencysupported.asp

CImage::Save()
      http://msdn.microsoft.com/library/en-us/vclib/html/vcrefcimagesave.asp

The guidFileType can be ImageFormatGIF or any of the values in
      http://msdn.microsoft.com/library/en-us/gdicpp/GDIPlus/GDIPlusReference/Constants/ImageFileFormatConstants.asp

including (apparemtly) ImageFormatIcon and ImageFormatGIF

-- Dan
CERTIFIED EXPERT
Author of the Year 2009

Commented:
Daij-Djan,
We have already estatbished that the method in your first link (and the first piece of code in your second link) does not work in this situation.

The UNDOCUMENTED  OleSavePictureFile API function used in your second link does save some steps, but the result it the same... An icon with a black background for all icon formats except 32x32  and the 32x32 image ends up with reduced color resolution.

The OleCreatePictureIndirect API seems to create 16-color icons and seems to not preserve the mask.   I am continuing to check some other options.  Perhaps the HDC used in the IPicture object can be manipulated.

-- Dan
Sorry in that case...
Do you have to preserve the alpha-channel?

If not converting it to a bmp and  saving THAT is an option

Gain unlimited access to on-demand training courses with an Experts Exchange subscription.

Get Access
Why Experts Exchange?

Experts Exchange always has the answer, or at the least points me in the correct direction! It is like having another employee that is extremely experienced.

Jim Murphy
Programmer at Smart IT Solutions

When asked, what has been your best career decision?

Deciding to stick with EE.

Mohamed Asif
Technical Department Head

Being involved with EE helped me to grow personally and professionally.

Carl Webster
CTP, Sr Infrastructure Consultant
Empower Your Career
Did You Know?

We've partnered with two important charities to provide clean water and computer science education to those who need it most. READ MORE

Ask ANY Question

Connect with Certified Experts to gain insight and support on specific technology challenges including:

  • Troubleshooting
  • Research
  • Professional Opinions
Unlock the solution to this question.
Join our community and discover your potential

Experts Exchange is the only place where you can interact directly with leading experts in the technology field. Become a member today and access the collective knowledge of thousands of technology experts.

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.