Page 2 of 2

DllPackIcons()

Posted: 20 Sep 2017, 15:13
by SKAN
DllPackIcons( Folder, DLL, nICON, nGROUPICON )

Code: Select all

DllPackIcons( Folder, DLL, nICON:=1, nGROUPICON:=1 ) {  ; By SKAN | 21-Sep-2017 | goo.gl/DjDxzW
  Local RT_ICON := 3, RT_GROUPICON := 14, hUPD := 0
  Local ICONDATA, nBytes, nOffset, ICONDIR, ICONDIRENTRY, nImages, pICONDIRENTRY 
  Local File, fICONDIRENTRY, fList := "", IsFolder := 0 

  nICON := ( nICON<1 ? 1 : nICON ),     nGROUPICON := ( nGROUPICON<1 ? 1 : nGROUPICON )

  If ( IsFolder := DllCall( "shlwapi\PathIsDirectory", "Str",Folder ) )
     Loop, Files, %Folder%\*.*
       fList .= A_LoopFileLongPath "`n"
  fList := Trim( IsFolder ? fList : Folder, "`n`r" )        
           
  Loop, Parse, fList, `n, `r
  {
      If ( DllCall( "Shell32\ExtractIcon", "Ptr",0, "Str",A_LoopField, "Int",-1 ) <> 1 )
        Continue

      File := FileOpen(A_LoopField,"r-wd")
      If ( IsObject(File)=0 Or File.ReadUINT()<>0x00010000 )
      { 
          File.Close()
          Continue
      }     

      hUPD := ( hUPD ? hUPD : DllCall( "BeginUpdateResource", "Str",DLL, "Int",0, "Ptr" ) )
      nImages := File.ReadUSHORT()
      VarSetCapacity(ICONDIRENTRY,14*nImages+6,0)
      File.Seek(0,0)
      File.RawRead(ICONDIRENTRY,6)
      pICONDIRENTRY := &ICONDIRENTRY+6
      fICONDIRENTRY := 6
      Loop % nImages
      {
          File.Seek(fICONDIRENTRY,0)
          File.RawRead(pICONDIRENTRY+0,16)
          nBytes  := NumGet(pICONDIRENTRY+ 8,"UInt")
          nOffset := NumGet(pICONDIRENTRY+12,"UInt")
          NumPut( nICON,pICONDIRENTRY+12,"UShort")
          File.Seek(nOffset,0) 
          File.RawRead( ICONDATA, nBytes )

          DllCall( "UpdateResource", "Ptr",hUPD, "Ptr",RT_ICON, "Ptr",nICON
                                   , "Int",0, "Ptr",&ICONDATA, "UInt",nBytes )
          nICON += 1
          pICONDIRENTRY += 14 
          fICONDIRENTRY += 16
      } 
      DllCall( "UpdateResource", "Ptr",hUPD, "Ptr",RT_GROUPICON, "Ptr",nGROUPICON
                               , "Int",0, "Ptr",&ICONDIRENTRY, "UInt",14*nImages+6 )
      nGROUPICON += 1
      File.Close()
  }
  hUPD := ( hUPD ? DllCall( "EndUpdateResource", "Ptr",hUPD, "Int",0 ) >> 64 : 0 )
Return 1
}


Example 1:
Icons will be added in the order populated by File system.
It NTFS, The .ICO files will be in alphabetical order and in FAT32 the list will be in no particular order.

Code: Select all

DLL := DllCreateEmpty( A_ScriptDir "\Icons.dll" )
DllPackIcons( "D:\MyIcons\", DLL )
Example 2:
Icons will be added exactly as it appears in the "files" list.

Code: Select all

DLL := DllCreateEmpty( A_ScriptDir "\Icons.dll" )
Files := "
(
D:\MyIcons\Icon3.ico
D:\MyIcons\Icon1.ico
D:\MyIcons\Icon2.ico
)"
DllPackIcons( Files, DLL )

Re: Functions for Resource-Only DLL

Posted: 21 Sep 2017, 04:39
by hoppfrosch
Thanks SKAN - my tests work without a flaw yet...! :dance:

What would be nice (but I don't know whether it's possible at all): name the icons within the DLL according their filenames (without extension of course) - instead of using an anonymous number ....

Re: Functions for Resource-Only DLL

Posted: 21 Sep 2017, 05:55
by just me
It's possible for icon groups, but afaik you cannot extract an icon by name using the built-in AHK commands/functions.

Re: Functions for Resource-Only DLL

Posted: 21 Sep 2017, 12:10
by SKAN
hoppfrosch wrote:Thanks SKAN - my tests work without a flaw yet...! :dance:
Glad to know.
I forgot to mention:
DllPackIcons() doesn't trust file extensions. Each and every file is opened and confirmed to have an ICONDIR in header.
This is because .BMP files can be renamed to .ICO and Windows will accept/load it as icon.
So, If you have a .ICO that is not being added, make sure it isn't a BMP file.
What would be nice (but I don't know whether it's possible at all): name the icons within the DLL according their filenames (without extension of course)
I had thought of it. As just me said, the names aren't very useful as AutoHotkey loads icon by its position (1 based index) rather than its ID.
Also implementing it would be hard. For example, if icon filename is either "HOPP.ico" or "123.ico", I know the first one should be saved as string and the latter as a number. What if you order/name your icons like "0001.ico", "0002.ico... "1234.ico".
Then I will have to accept a filelist as a CSV with two parameters.
hoppfrosch wrote:instead of using an anonymous number ....
The numbers matches actual index AutoHotkey uses. If you look at the code, you may see that the functions doesn't allow using 0 as the starting number, specifically for this reason (and also: 0 should be stored as a string)

:)

Re: Functions for Resource-Only DLL

Posted: 25 Sep 2017, 00:01
by hoppfrosch
Naming is not really a problem - I just had my icons clearly named and was "disappointed" at the first look, after seeing my names got lost within the dll. I'm fine with this.

Thanks SKAN for your great work. :clap:

Re: Functions for Resource-Only DLL

Posted: 25 Sep 2017, 17:47
by gwarble
the names aren't very useful as AutoHotkey loads icon by its position (1 based index) rather than its ID.
if you wanted to use named icon group resources you can access them by the native winapi calls, LoadIcon or LoadImage

Probably more trouble than its worth in most cases, but if you had lots of icons to deal with it might be beneficial in some cases