SaveHICONtoFile()

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

SaveHICONtoFile()

06 Sep 2017, 13:14

  • SaveHICONtoFile() is a GDI/User32 based function. It should work in all AutoHotkey versions that support File Object.
  • When you save different hicon with same filepath, windows explorer will show you only the first created icon.
    I guess ICON cache is filepath based, as I can see the correct icon if I move it to a different folder but cannot when I bring it back to the origianal folder.
    To refresh ICON cache you may use this one liner: DllCall( "Shell32\SHChangeNotify", UInt,0x08000000, UInt,0, Int,0, Int,0 ) ; SHCNE_ASSOCCHANGED
  • It is (almost) impossible for hicon to have a color table unless your screen bit depth is lesser than 16BPP. I had to step down my screen bit depth to 256 colors to write/test the functionality.
    You may leave it as is... or remove, if you find it redundant or makes the code lengthier.
  • All hicons i tested had only DDBs. The function unconditionally converts them to DIB, with User32 API CopyImage().
  • Info on ICON file structure: Identifying an ICON file and getting dimensions from it
Couple of snippets for quick testing
SaveHICONtoFile()

Code: Select all

SaveHICONtoFile( hicon, iconFile ) {                                ; By SKAN | 06-Sep-2017 | goo.gl/8NqmgJ
Static CI_FLAGS:=0x2008                                             ; LR_CREATEDIBSECTION | LR_COPYDELETEORG
Local  File, Var, mDC, sizeofRGBQUAD, ICONINFO:=[], BITMAP:=[], BITMAPINFOHEADER:=[]

  File := FileOpen( iconFile,"rw" )
  If ( ! IsObject(File) )
       Return 0
  Else File.Length := 0                                             ; Delete (any existing) file contents                                                   

  VarSetCapacity(Var,32,0)                                          ; ICONINFO Structure
  If ! DllCall( "GetIconInfo", "Ptr",hicon, "Ptr",&Var )
    Return ( File.Close() >> 64 )

  ICONINFO.fIcon      := NumGet(Var, 0,"UInt")
  ICONINFO.xHotspot   := NumGet(Var, 4,"UInt")
  ICONINFO.yHotspot   := NumGet(Var, 8,"UInt")
  ICONINFO.hbmMask    := NumGet(Var, A_PtrSize=8 ? 16:12, "UPtr")
  ICONINFO.hbmMask    := DllCall( "CopyImage"                       ; Create a DIBSECTION for hbmMask
                                , "Ptr",ICONINFO.hbmMask 
                                , "UInt",0                          ; IMAGE_BITMAP
                                , "Int",0, "Int",0, "UInt",CI_FLAGS, "Ptr" ) 
  ICONINFO.hbmColor   := NumGet(Var, A_PtrSize=8 ? 24:16, "UPtr") 
  ICONINFO.hbmColor   := DllCall( "CopyImage"                       ; Create a DIBSECTION for hbmColor
                                , "Ptr",ICONINFO.hbmColor
                                , "UInt",0                          ; IMAGE_BITMAP
                                , "Int",0, "Int",0, "UInt",CI_FLAGS, "Ptr" ) 

  VarSetCapacity(Var,A_PtrSize=8 ? 104:84,0)                        ; DIBSECTION of hbmColor
  DllCall( "GetObject", "Ptr",ICONINFO.hbmColor, "Int",A_PtrSize=8 ? 104:84, "Ptr",&Var )

  BITMAP.bmType       := NumGet(Var, 0,"UInt") 
  BITMAP.bmWidth      := NumGet(Var, 4,"UInt")
  BITMAP.bmHeight     := NumGet(Var, 8,"UInt")
  BITMAP.bmWidthBytes := NumGet(Var,12,"UInt")
  BITMAP.bmPlanes     := NumGet(Var,16,"UShort")
  BITMAP.bmBitsPixel  := NumGet(Var,18,"UShort")
  BITMAP.bmBits       := NumGet(Var,A_PtrSize=8 ? 24:20,"Ptr")
  
  BITMAPINFOHEADER.biClrUsed := NumGet(Var,32+(A_PtrSize=8 ? 32:24),"UInt")
                                                                      
  File.WriteUINT(0x00010000)                                        ; ICONDIR.idReserved and ICONDIR.idType 
  File.WriteUSHORT(1)                                               ; ICONDIR.idCount (No. of images)
  File.WriteUCHAR(BITMAP.bmWidth  < 256 ? BITMAP.bmWidth  : 0)      ; ICONDIRENTRY.bWidth
  File.WriteUCHAR(BITMAP.bmHeight < 256 ? BITMAP.bmHeight : 0)      ; ICONDIRENTRY.bHeight 
  File.WriteUCHAR(BITMAPINFOHEADER.biClrUsed < 256                  ; ICONDIRENTRY.bColorCount
                ? BITMAPINFOHEADER.biClrUsed : 0)
  File.WriteUCHAR(0)                                                ; ICONDIRENTRY.bReserved
  File.WriteUShort(BITMAP.bmPlanes)                                 ; ICONDIRENTRY.wPlanes
  File.WriteUSHORT(BITMAP.bmBitsPixel)                              ; ICONDIRENTRY.wBitCount
  File.WriteUINT(0)                                                 ; ICONDIRENTRY.dwBytesInRes (filled later) 
  File.WriteUINT(22)                                                ; ICONDIRENTRY.dwImageOffset  


  NumPut( BITMAP.bmHeight*2, Var, 8+(A_PtrSize=8 ? 32:24),"UInt")   ; BITMAPINFOHEADER.biHeight should be 
                                                                    ; modified to double the BITMAP.bmHeight  

  File.RawWrite( &Var + (A_PtrSize=8 ? 32:24), 40)                  ; Writing BITMAPINFOHEADER (40  bytes)               

  If ( BITMAP.bmBitsPixel <= 8 )                                    ; Bitmap uses a Color table!
  {
      mDC := DllCall( "CreateCompatibleDC", "Ptr",0, "Ptr" )       
      DllCall( "SaveDC", "Ptr",mDC )
      DllCall( "SelectObject", "Ptr",mDC, "Ptr",ICONINFO.hbmColor )
      sizeofRGBQUAD := ( BITMAPINFOHEADER.biClrUsed * 4 )           ; Colors used x UINT (0x00bbggrr)
      VarSetCapacity( Var,sizeofRGBQUAD,0 )                         ; Array of RGBQUAD structures 
      DllCall( "GetDIBColorTable", "Ptr",mDC, "UInt",0, "UInt",BITMAPINFOHEADER.biClrUsed, "Ptr",&Var )
      DllCall( "RestoreDC", "Ptr",mDC, "Int",-1 )
      DllCall( "DeleteDC", "Ptr",mDC )
      File.RawWrite(Var, sizeofRGBQUAD)                             ; Writing Color table 
  }
    
  File.RawWrite(BITMAP.bmBits, BITMAP.bmWidthBytes*BITMAP.bmHeight) ; Writing BITMAP bits (hbmColor)

  VarSetCapacity(Var,A_PtrSize=8 ? 104:84,0)                        ; DIBSECTION of hbmMask
  DllCall( "GetObject", "Ptr",ICONINFO.hbmMask, "Int",A_PtrSize=8 ? 104:84, "Ptr",&Var )

  BITMAP := []
  BITMAP.bmHeight     := NumGet(Var, 8,"UInt")
  BITMAP.bmWidthBytes := NumGet(Var,12,"UInt")
  BITMAP.bmBits       := NumGet(Var,A_PtrSize=8 ? 24:20,"Ptr")

  File.RawWrite(BITMAP.bmBits, BITMAP.bmWidthBytes*BITMAP.bmHeight) ; Writing BITMAP bits (hbmMask)
  File.Seek(14,0)                                                   ; Seeking ICONDIRENTRY.dwBytesInRes
  File.WriteUINT(File.Length()-22)                                  ; Updating ICONDIRENTRY.dwBytesInRes
  File.Close()
  DllCall( "DeleteObject", "Ptr",ICONINFO.hbmMask  )  
  DllCall( "DeleteObject", "Ptr",ICONINFO.hbmColor )
Return True  
}
Tags: How to save write HICON ICO ICON HICO to FILE GDI GDI+ GDIPlus
My Scripts and Functions: V1  V2
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: SaveHICONtoFile()

06 Sep 2017, 14:57

:clap: Great share! Thanks.
Spoiler
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: SaveHICONtoFile()

08 Sep 2017, 05:08

Helgef wrote::clap: Great share!
Thanks! :)

Some posts were moved to: https://autohotkey.com/boards/viewtopic.php?f=5&t=36795
My Scripts and Functions: V1  V2
Ashtefere
Posts: 14
Joined: 02 Dec 2017, 11:14

Re: SaveHICONtoFile()

05 Feb 2018, 06:35

This is amazing!

However, some apps don't export an icon file.

The two I know of are: DiscordPTB and Rainmeter.

Any change you could take a look and find the issue? This is beyond me to try!

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 182 guests