Page 1 of 1

Extract Icon from Dll/exe at non standard size(WINDOWS ONLY)

Posted: Sat May 09, 2020 9:39 am
by tatanas
Hi,

Here is a way to extract an icon from a resource file (dll/exe) at the size you desire and use it with an image gadget (the function returns an icon handle so an imageID in PB) :

Code: Select all

; Macro LOWORD(dwValue) : dwValue & $FFFF : EndMacro
; Macro HIWORD(dwValue) : dwValue >> 16 : EndMacro

Prototype.i Proto_SHDefExtractIcon(pszIconFile.s, iIndex.i, uFlags.i, phiconLarge.i, phiconSmall.i, nIconSize.i)

Procedure ExtractArbitrarySizeIcon(pszPath.s, index, size)
	Protected hIcon
	OpenLibrary(0, "Shell32.dll")
	Protected SHDefExtractIcon_.Proto_SHDefExtractIcon = GetFunction(0, "SHDefExtractIconW")
	
	If SHDefExtractIcon_(pszPath, index, 0, @hIcon, #Null, size) = #S_OK
		ProcedureReturn hIcon
	EndIf
	
	ProcedureReturn 0
EndProcedure


If OpenWindow(0, 0, 0, 300, 50, "ImageGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
	
	
	hicon1 = ExtractArbitrarySizeIcon("c:\windows\system32\shell32.dll", 22, 16)
	ImageGadget(1, 0, 0, 50, 50, hicon1)
	
	hicon2 = ExtractArbitrarySizeIcon("c:\windows\system32\shell32.dll", 22, 24)
	ImageGadget(2, 50, 0, 50, 50, hicon2)
	
	hicon3 = ExtractArbitrarySizeIcon("c:\windows\system32\shell32.dll", 22, 32)
	ImageGadget(3, 100, 0, 50, 50, hicon3)	
	
	hicon4 = ExtractArbitrarySizeIcon("c:\windows\system32\shell32.dll", 22, 40)
	ImageGadget(4, 150, 0, 50, 50, hicon4)
	
	hicon5 = ExtractArbitrarySizeIcon("c:\windows\system32\shell32.dll", 22, 48)
	ImageGadget(5, 200, 0, 50, 50, hicon5)	
	
	Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf


DestroyIcon_(hicon1)
DestroyIcon_(hicon2)
DestroyIcon_(hicon3)
DestroyIcon_(hicon4)
DestroyIcon_(hicon5)
Msdn doc : https://docs.microsoft.com/en-us/window ... tracticonw
The doc doesn't match my experience. It seems you can use only the phiconLarge pointer to get every size you want...

Re: Extract Icon from Dll/exe at non standard size

Posted: Sat May 09, 2020 1:07 pm
by FlatEarth
Nice, thanks for sharing.

Re: Extract Icon from Dll/exe at non standard size

Posted: Sun May 10, 2020 12:50 pm
by blueb
I've been using some older 2013 code by 'Nalor' which still works well and you might fine useful. :)

see: https://www.purebasic.fr/english/viewto ... 43#p417143

Re: Extract Icon from Dll/exe at non standard size(WINDOWS O

Posted: Sun May 10, 2020 6:22 pm
by Kwai chang caine
Works fine here, thanks for sharing 8)

Re: Extract Icon from Dll/exe at non standard size(WINDOWS ONLY)

Posted: Sat Oct 09, 2021 5:12 am
by BarryG
Thanks tatanas - I was looking for this today!

Re: Extract Icon from Dll/exe at non standard size(WINDOWS ONLY)

Posted: Sat Oct 09, 2021 6:31 am
by Denis
Thanks tatanas, i discover this API.

I was working a lot about Icons, but even today, I don't clearly understand the difference between the small and large icons.
Any Idea?

i've tested your code with small Icon and it seems to works. I don't know if it will works with all Icon of all files.
I still don't understand what is the difference with small or large Icon with this API.
With your code modified a bit, I've extracted an icon 128x128 because it doesn't exist into the file.
The code modified to get icon from small Icon, hope i use this API correctly ...

Code: Select all

; Macro LOWORD(dwValue) : dwValue & $FFFF : EndMacro
; Macro HIWORD(dwValue) : dwValue >> 16 : EndMacro

Prototype.i Proto_SHDefExtractIcon(pszIconFile.s, iIndex.i, uFlags.i, phiconLarge.i, phiconSmall.i, nIconSize.i)

Procedure ExtractArbitrarySizeIcon(pszPath.s, index, size)
	Protected hIcon
	OpenLibrary(0, "Shell32.dll")
	Protected SHDefExtractIcon_.Proto_SHDefExtractIcon = GetFunction(0, "SHDefExtractIconW")
	
; 	If SHDefExtractIcon_(pszPath, index, 0, @hIcon, #Null, size) = #S_OK
; 		ProcedureReturn hIcon
; 	EndIf
	
	size << 16  ;  HIWORD of size.

	If SHDefExtractIcon_(pszPath, index, 0, #Null, @hIcon, size) = #S_OK
		ProcedureReturn hIcon
	EndIf
	
	ProcedureReturn 0
EndProcedure


If OpenWindow(0, 0, 0, 400, 200, "ImageGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
	
	
	hicon1 = ExtractArbitrarySizeIcon("c:\windows\system32\shell32.dll", 22, 16)
	ImageGadget(1, 0, 0, 50, 50, hicon1)
	
	hicon2 = ExtractArbitrarySizeIcon("c:\windows\system32\shell32.dll", 22, 24)
	ImageGadget(2, 50, 0, 50, 50, hicon2)
	
	hicon3 = ExtractArbitrarySizeIcon("c:\windows\system32\shell32.dll", 22, 32)
	ImageGadget(3, 100, 0, 50, 50, hicon3)	
	
	hicon4 = ExtractArbitrarySizeIcon("c:\windows\system32\shell32.dll", 22, 40)
	ImageGadget(4, 150, 0, 50, 50, hicon4)
	
	hicon5 = ExtractArbitrarySizeIcon("c:\windows\system32\shell32.dll", 22, 128)
	ImageGadget(5, 200, 0, 50, 50, hicon5)	
	
	Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf


DestroyIcon_(hicon1)
DestroyIcon_(hicon2)
DestroyIcon_(hicon3)
DestroyIcon_(hicon4)
DestroyIcon_(hicon5)

Re: Extract Icon from Dll/exe at non standard size(WINDOWS ONLY)

Posted: Sun Oct 10, 2021 3:51 am
by BarryG
Can someone with Windows 7 please run the below code and confirm if you get the same icon as in my screenshot?

Also, if your icon is different, could you please post a screenshot so I can see what it is. Thanks!

Image

Code: Select all

Prototype.i Proto_SHDefExtractIcon(pszIconFile.s,iIndex.i,uFlags.i,phiconLarge.i,phiconSmall.i,nIconSize.i)

Procedure IconExtractFromFile(file$,index,size=16)
  lib=OpenLibrary(#PB_Any,"shell32.dll")
  If lib
    SHDefExtractIcon_.Proto_SHDefExtractIcon=GetFunction(lib,"SHDefExtractIconW")
    If SHDefExtractIcon_(file$,index,0,@hIcon,0,size)=#S_OK
      ok=hIcon
    EndIf
    CloseLibrary(lib)
  EndIf
  ProcedureReturn ok
EndProcedure

If OpenWindow(0,0,0,100,100,"test",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
  hicon1 = IconExtractFromFile("c:\windows\system32\explorer.exe",8,16)
  ImageGadget(1,55,40,50,50,hicon1)
  Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
  DestroyIcon_(hicon1)
EndIf

Re: Extract Icon from Dll/exe at non standard size(WINDOWS ONLY)

Posted: Sun Oct 10, 2021 6:22 am
by Paul
BarryG wrote: Sun Oct 10, 2021 3:51 am Also, if your icon is different, could you please post a screenshot so I can see what it is. Thanks!
On my copy of Windows 7, Explorer.exe is not in System32 folder, it's in the main Windows folder and your code extracts this icon...

Image

Re: Extract Icon from Dll/exe at non standard size(WINDOWS ONLY)

Posted: Sun Oct 10, 2021 6:48 am
by BarryG
Thanks Paul, I'll ditch that idea then. Trying to get a generic Windows icon from a system file, but that's proving to be difficult. Can't include one with my app because it's copyrighted to Microsoft, so extracting it during runtime from somewhere was my plan.

Re: Extract Icon from Dll/exe at non standard size(WINDOWS ONLY)

Posted: Mon Oct 11, 2021 1:53 pm
by ChrisR
Extracting the icon at runtime was however a good idea
It was just the path that was not good, explorer.exe is in windows folder (%WINDIR%) in all windows versions.
A small addition to the procedure to pass only the filename or possibly correct the path, if the filename exists in %Path%

Code: Select all

Prototype.i Proto_SHDefExtractIcon(pszIconFile.s,iIndex.i,uFlags.i,phiconLarge.i,phiconSmall.i,nIconSize.i)

Procedure IconExtractFromFile(file$,index,size=16)
  If Not PathFileExists_(@file$)
    file$ = GetFilePart(file$)
    *buf=AllocateMemory(#MAX_PATH)
    PokeS(*buf,file$)
    PathFindOnPath_(*buf, #Null)
    file$=PeekS(*buf)
    FreeMemory(*buf)
  EndIf
  If PathFileExists_(@file$)
    lib=OpenLibrary(#PB_Any,"shell32.dll")
    If lib
      SHDefExtractIcon_.Proto_SHDefExtractIcon=GetFunction(lib,"SHDefExtractIconW")
      If SHDefExtractIcon_(file$,index,0,@hIcon,0,size)=#S_OK
        ok=hIcon
      EndIf
      CloseLibrary(lib)
    EndIf
    ProcedureReturn ok
  EndIf
EndProcedure

Define  Path$
;Path$ = "c:\windows\system32\explorer.exe"
;Path$ = GetEnvironmentVariable("WINDIR") + "\Explorer.exe"
Path$ = "Explorer.exe"

If OpenWindow(0,0,0,100,100,"test",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
  hicon1 = IconExtractFromFile(Path$,-205,16)
  ImageGadget(1,55,40,50,50,hicon1)
  Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
  DestroyIcon_(hicon1)
EndIf

Edit: With Paul's addition below
fryquez wrote: Mon Oct 11, 2021 3:58 pm PathFindOnPath_() expects a bigger buffer than it get's.

Re: Extract Icon from Dll/exe at non standard size(WINDOWS ONLY)

Posted: Mon Oct 11, 2021 3:28 pm
by Paul
@ ChrisR

On my Windows 10 machine your code works fine but on Windows 7 it just crashes.

Re: Extract Icon from Dll/exe at non standard size(WINDOWS ONLY)

Posted: Mon Oct 11, 2021 3:58 pm
by fryquez
PathFindOnPath_() expects a bigger buffer than it get's.

Re: Extract Icon from Dll/exe at non standard size(WINDOWS ONLY)

Posted: Mon Oct 11, 2021 4:27 pm
by Paul
fryquez wrote: Mon Oct 11, 2021 3:58 pm PathFindOnPath_() expects a bigger buffer than it get's.
Ah, you are right. If I change this part I no longer get a crash...

Code: Select all

  If Not PathFileExists_(@file$)
    file$ = GetFilePart(file$)
    
    *buf=AllocateMemory(#MAX_PATH)
    PokeS(*buf,file$)
    
    PathFindOnPath_(*buf, #Null)
    file$=PeekS(*buf)
    FreeMemory(*buf)
  EndIf
Unfortunately the results on Windows 7 are exactly the same as the code by BarryG
Image

Re: Extract Icon from Dll/exe at non standard size(WINDOWS ONLY)

Posted: Mon Oct 11, 2021 5:11 pm
by RASHAD
The problem with BarryG code is the icon number
Windows 10,11 icon 8
Windows 7,8.x icon 10
It's MS after all :)

Re: Extract Icon from Dll/exe at non standard size(WINDOWS ONLY)

Posted: Mon Oct 11, 2021 5:26 pm
by ChrisR
Indeed it works better with the right buffer size, thanks fryquez
I just tried it with Paul's addition with windows 7 in VMware and I get the same result.

I checked with IconsExtract, under windows 7, the 8th icon is the exclamation mark. The computer icon is the 10th.
So not the same position of the icon in explorer file as in windows 10.
It's probably better to use the icon's resource ID, using its negative value: hicon1 = IconExtractFromFile(Path$,-205,16)

Edit: RASHAD was faster :)