Extract Icon from exe,dll,ico,cur,bmp

Share your advanced PureBasic knowledge/code with the community.
nalor
Enthusiast
Enthusiast
Posts: 115
Joined: Thu Apr 02, 2009 9:48 pm

Extract Icon from exe,dll,ico,cur,bmp

Post by nalor »

Hi!

Lastly I tried to find some code that allows me to load even the bigger icons (128Pixel, 256Pixel) but couldn't find something really usefull (I know there's some code floating around to get SHIL_EXTRALARGE and SHIL_JUMBO, but in case there's no such Icon available you get the smaller Icon placed at the top left corner of an image with the requested size... not that usefull - at least if there's no easy way to check if a certain icon size is available or not).
So I googled and found some examples on the microsoft page and converted them to purebasic.

The procedures starting with "Icon_" are the ones ment for public use.

You've the choice of getting a special icon in it's original dimension/color depth (Icon_GetRealHdl) or let 'LoadImage_' choose the best available Icon and it also scales it to the desired dimensions (Icon_GetHdl).
With "Icon_GetCnt" you'll get the number of available icons inside a file (for example the purebasic.exe file comes with 2 different icons) and with "Icon_GetSizes" you'll get an array with all available sizes of a particular Icon in the file.
Don't forget to call 'Icon_DestroyHdl' when you don't need it any longer, this will free the used memory.

In my demonstration code it works the following way: click on the main tree elements and you get the scaled version, click on the different sub elements of an icon and you get the original size icon displayed.

Code: Select all

EnableExplicit

;- ############## ICON CODE ################

; http://msdn.microsoft.com/en-us/library/ms997538.aspx

Structure Icon_Dimension
	Width.i     ; Width
	Height.i    ; Height
	BitCnt.a    ; Bits per pixel: 1=2 Colors, 2=4 Colors, 4=16 Colors, 8=256 Colors, 24=TrueColor, 32=TrueColor+Alpha
EndStructure

Structure Icon_Size
	Array Icon_Size.Icon_Dimension(1)
EndStructure

Structure Icon_EnumData
	Mode.i ; what kind of data we'd like to get
	Nr.i   ; which Icon we'd like to get (1=first, 2=second, ..)
	RetVal.i; return value with the requested information
	Array Size.Icon_Dimension(1)  ; size of the icon we want or it holds the sizes of all available icons
EndStructure

Structure ICONDIRENTRY
	bWidth.a       ; Width, in pixels, of the image
	bHeight.a      ; Height, in pixels, of the image
	bColorCount.a  ; Number of colors in image (0 if >=8bpp)
	bReserved.a    ; Reserved ( must be 0)
	wPlanes.w      ; Color Planes
	wBitCount.w    ; Bits per pixel
	dwBytesInRes.l ; How many bytes in this resource?
	dwImageOffset.l; Where in the file is this image?
EndStructure

Structure ICONDIR
	idReserved.w ; Reserved (must be 0)
	idType.w     ; Resource Type (1 for icons)
	idCount.w    ; How many images?
	Array idEntries.ICONDIRENTRY(1) ;An entry for each image (idCount of 'em)
EndStructure

Enumeration
	#Icon_GetSclHdl   ; Icon might be scaled (in case a 256Pixel is requested and only 16Pixel is available, it's scaled)
	#Icon_GetCnt      ; How many different Icons are available
	#Icon_GetRealHdl  ; Get real icon - nothing is scaled! In case there's no fitting Icon you'll get nothing
	#Icon_GetSizes    ; Get List of available Icon Sizes
EndEnumeration

Procedure __Icon_EnumCallback(hLibrary, lpszType, lpszName, *Data.Icon_EnumData)
	; 20130707..nalor..implemented support for icons with low BPP (1,2,4)
	Protected hIcon.i
	Protected hRsrc.i
	Protected hGlobal.i
	Protected Cnt.i
	Protected *GrpIconDir.GRPICONDIR
	Protected *IconImage.ICONIMAGE
	Protected Colors.a	
	
	Select *Data\Mode
		Case #Icon_GetCnt
			*Data\RetVal+1
			
		Case #Icon_GetSclHdl
			*Data\RetVal+1	
			If *Data\Nr=*Data\RetVal ; we've reached the requested Icon
				*Data\RetVal=LoadImage_(hLibrary, lpszName, #IMAGE_ICON, *Data\Size(0)\Width, *Data\Size(0)\Height, 0)
				ProcedureReturn #False ; no need to continue enumerating
			EndIf
			
		Case #Icon_GetSizes
			*Data\RetVal+1	
			If *Data\Nr=*Data\RetVal ; we've reached the requested Icon
				;Find the group resource which lists its images
				hRsrc=FindResource_(hLibrary, lpszName, lpszType)
				If hRsrc
					;Load And Lock To get a pointer To a GRPICONDIR
					hGlobal=LoadResource_(hLibrary, hRsrc)
					If hGlobal
						*GrpIconDir=LockResource_(hGlobal)
						If *GrpIconDir
							ReDim *Data\Size(*GrpIconDir\idCount-1)
							For Cnt=0 To *GrpIconDir\idCount-1
								*Data\Size(Cnt)\Width=PeekA(@*GrpIconDir\idEntries[Cnt]\bWidth) ; peeka is used because 'Byte' is signed and we want an unsigned result
								*Data\Size(Cnt)\Height=PeekA(@*GrpIconDir\idEntries[Cnt]\bHeight)
							
								If *Data\Size(Cnt)\Height=0
									*Data\Size(Cnt)\Height=256
								EndIf
								If *Data\Size(Cnt)\Width=0
									*Data\Size(Cnt)\Width=256
								EndIf
								
								Select *GrpIconDir\idEntries[Cnt]\bColorCount
									Case 0  ; it's an icon with at least 256 colors
										*Data\Size(Cnt)\BitCnt=*GrpIconDir\idEntries[Cnt]\wBitCount
									Case 2
										*Data\Size(Cnt)\BitCnt=1
									Case 4
										*Data\Size(Cnt)\BitCnt=2
									Case 16
										*Data\Size(Cnt)\BitCnt=4
								EndSelect							
								
;								Debug "Callback Cnt >"+Str(Cnt+1)+"< Size >"+Str(*Data\Size(Cnt)\Width)+" x "+Str(*Data\Size(Cnt)\Height)+"< Col >"+Str(*Data\Size(Cnt)\BitCnt)+"<"
							Next
						Else
							*Data\RetVal=-3
						EndIf
					Else
						*Data\RetVal=-2
					EndIf
				Else
					*Data\RetVal=-1
				EndIf			
				ProcedureReturn #False ; no need to continue enumerating
			EndIf				
						
		Case #Icon_GetRealHdl

			Select *Data\Size(0)\BitCnt
				Case 1
					Colors=2
				Case 2
					Colors=4
				Case 4
					Colors=16
				Default
					Colors=1 ; an impossible value
			EndSelect			
			
			*Data\RetVal+1
; 			Debug "Current IconNr >"+Str(*Data\RetVal)+"< Dest >"+Str(*Data\Nr)+"<"
			If *Data\Nr=*Data\RetVal ; we've reached the requested Icon			
				; http://msdn.microsoft.com/en-us/library/ms997538.aspx
				;Find the group resource which lists its images
				
				hRsrc=FindResource_(hLibrary, lpszName, lpszType)
				If hRsrc
					;Load And Lock To get a pointer To a GRPICONDIR
					hGlobal=LoadResource_(hLibrary, hRsrc)
					If hGlobal
						*GrpIconDir=LockResource_(hGlobal)
						If *GrpIconDir
							; Using an ID from the group, Find, Load And Lock the RT_ICON
							*Data\RetVal=0 ; in case the requested icon is not available, "0" will be the return value
							For Cnt=0 To *GrpIconDir\idCount-1
								If PeekA(@*GrpIconDir\idEntries[Cnt]\bWidth)=*Data\Size(0)\Width And
								   PeekA(@*GrpIconDir\idEntries[Cnt]\bHeight)=*Data\Size(0)\Height And
								   (*GrpIconDir\idEntries[Cnt]\wBitCount=*Data\Size(0)\BitCnt Or *Data\Size(0)\BitCnt=0 Or *GrpIconDir\idEntries[Cnt]\bColorCount=Colors)
									
									hRsrc=FindResource_(hLibrary, *GrpIconDir\idEntries[Cnt]\nID, #RT_ICON)
									If hRsrc
										hGlobal=LoadResource_(hLibrary, hRsrc)
										If hGlobal
											*IconImage=LockResource_(hGlobal) ;Here, *IconImage points To an ICONIMAGE Structure
											If *IconImage
												*Data\RetVal=CreateIconFromResourceEx_(*IconImage, SizeofResource_(hLibrary, hRsrc), #True, $30000, 0, 0, 0)
											Else
												*Data\RetVal=-6
											EndIf
										Else
											*Data\RetVal=-5
										EndIf
									Else
										*Data\RetVal=-4
									EndIf
									ProcedureReturn #False ; we found the specific icon
								EndIf
							Next
						Else
							*Data\RetVal=-3
						EndIf
					Else
						*Data\RetVal=-2
					EndIf
				Else
					*Data\RetVal=-1
				EndIf
				ProcedureReturn #False ; no need to continue enumerating
			EndIf					
				
	EndSelect			
	
	ProcedureReturn #True

EndProcedure

Procedure.i __Icon_GetRealHdlICO(File.s, Width.i, Height.i, BPP.a=0)
	Protected pIconDir.ICONDIR ;We need an ICONDIR To hold the Data
	Protected *IconImage.ICONIMAGE
	Protected FileHdl.i
	Protected Cnt.i
	Protected Colors.a
	Protected RetVal.i=0
	
	Select BPP
		Case 1
			Colors=2
		Case 2
			Colors=4
		Case 4
			Colors=16
		Default
			Colors=1 ; an impossible value
	EndSelect
			
	
	FileHdl=ReadFile(#PB_Any, File)
 	If FileHdl
 		pIconDir\idReserved=ReadWord(FileHdl) ; Read the Reserved word
 		pIconDir\idType=ReadWord(FileHdl) ; Read the Type word - make sure it is 1 For icons
 		If (pIconDir\idType=#IMAGE_ICON Or pIconDir\idType=#IMAGE_CURSOR) ; it's an Icon or a Cursor
 			pIconDir\idCount=ReadWord(FileHdl) ; Read the count - how many images in this file?
 			ReDim pIconDir\idEntries(pIconDir\idCount -1) ; Reallocate IconDir so that idEntries has enough room For idCount elements
 			If ReadData(FileHdl, @pIconDir\idEntries(0), SizeOf(ICONDIRENTRY) * pIconDir\idCount) ; Read the ICONDIRENTRY elements
 				RetVal=0
 				For Cnt=0 To pIconDir\idCount-1
;  					Debug "CurIcon >"+Str(pIconDir\idEntries(Cnt)\bWidth)+"<>"+Str(pIconDir\idEntries(Cnt)\bHeight)+"<>"+Str(pIconDir\idEntries(Cnt)\wBitCount)+"<"
					If PeekA(@pIconDir\idEntries(Cnt)\bWidth)=Width And
					   PeekA(@pIconDir\idEntries(Cnt)\bHeight)=Height And
					   (pIconDir\idEntries(Cnt)\wBitCount=BPP Or BPP=0 Or pIconDir\idEntries(Cnt)\bColorCount=Colors) 					
 					
	 					*IconImage=AllocateMemory(pIconDir\idEntries(Cnt)\dwBytesInRes) ; Allocate memory To hold the image
	 					If *IconImage
	  						FileSeek(FileHdl, pIconDir\idEntries(Cnt)\dwImageOffset) ; Seek To the location in the file that has the image
	  						If ReadData(FileHdl, *IconImage, pIconDir\idEntries(Cnt)\dwBytesInRes) ;Read the image Data
	  							RetVal=CreateIconFromResourceEx_(*IconImage, pIconDir\idEntries(Cnt)\dwBytesInRes, #True, $30000, 0, 0, 0)
	  						Else
	  							Debug "ERROR!! Reading ICONIMAGE data (__Icon_GetRealHdlICO)"
	  							RetVal=-5
	  						EndIf
	
	  						FreeMemory(*IconImage)
	 						Break;
	 					Else
	 						Debug "ERROR!! Allocating Memory (__Icon_GetRealHdlICO)"
	 						RetVal=-4
	 					EndIf
	 				EndIf
 				Next
 			Else
 				Debug "ERROR!! Reading ICONDIRENTRY data (__Icon_GetRealHdlICO)"
 				RetVal=-3
 			EndIf
		Else
			Debug "ERROR!! it's not an icon or a cursor (__Icon_GetRealHdlICO)"
			RetVal=-2
		EndIf
		CloseFile(FileHdl)
	Else
		Debug "ERROR!! reading file (__Icon_GetRealHdlICO)"
		RetVal=-1
	EndIf
	
	ProcedureReturn RetVal
EndProcedure

Procedure.i __Icon_GetSizesICO(File.s, *Sizes.Icon_Size)
	; 20130707..nalor..implemented support for icons with low BPP (1,2,4)	
	Protected pIconDir.ICONDIR ;We need an ICONDIR To hold the Data
	Protected FileHdl.i
	Protected Cnt.i
	Protected RetVal.i=0
	
	FileHdl=ReadFile(#PB_Any, File)
 	If FileHdl
 		pIconDir\idReserved=ReadWord(FileHdl) ; Read the Reserved word
 		pIconDir\idType=ReadWord(FileHdl) ; Read the Type word - make sure it is 1 For icons
 		If (pIconDir\idType=#IMAGE_ICON Or pIconDir\idType=#IMAGE_CURSOR) ; it's an Icon or a Cursor
 			pIconDir\idCount=ReadWord(FileHdl) ; Read the count - how many images in this file?
 			ReDim pIconDir\idEntries(pIconDir\idCount -1) ; Reallocate IconDir so that idEntries has enough room For idCount elements
 			If ReadData(FileHdl, @pIconDir\idEntries(0), SizeOf(ICONDIRENTRY) * pIconDir\idCount) ; Read the ICONDIRENTRY elements
 				
 				ReDim *Sizes\Icon_Size(pIconDir\idCount -1)
 				For Cnt=0 To pIconDir\idCount-1
					*Sizes\Icon_Size(Cnt)\Width=PeekA(@pIconDir\idEntries(Cnt)\bWidth) ; peeka is used because 'Byte' is signed and we want an unsigned result
					*Sizes\Icon_Size(Cnt)\Height=PeekA(@pIconDir\idEntries(Cnt)\bHeight)
					
					If *Sizes\Icon_Size(Cnt)\Width=0
						*Sizes\Icon_Size(Cnt)\Width=256
					EndIf
					If *Sizes\Icon_Size(Cnt)\Height=0
						*Sizes\Icon_Size(Cnt)\Height=256
					EndIf						
					
					Select pIconDir\idEntries(Cnt)\bColorCount
						Case 0  ; it's an icon with at least 256 colors
							*Sizes\Icon_Size(Cnt)\BitCnt=pIconDir\idEntries(Cnt)\wBitCount
						Case 2
							*Sizes\Icon_Size(Cnt)\BitCnt=1
						Case 4
							*Sizes\Icon_Size(Cnt)\BitCnt=2
						Case 16
							*Sizes\Icon_Size(Cnt)\BitCnt=4
					EndSelect					
				
					Debug "__Icon_GetSizesICO Cnt >"+Str(Cnt+1)+"/"+Str(pIconDir\idCount)+"< Size >"+Str(*Sizes\Icon_Size(Cnt)\Width)+" x "+Str(*Sizes\Icon_Size(Cnt)\Height)+"< BitCnt >"+Str(*Sizes\Icon_Size(Cnt)\BitCnt)+"<"
 
				Next
				RetVal=1
 			Else
 				Debug "ERROR!! Reading ICONDIRENTRY data (__Icon_GetSizesICO)"
 				RetVal=-3
 			EndIf
		Else
			Debug "ERROR!! it's not an icon or a cursor (__Icon_GetSizesICO)"
			RetVal=-2
		EndIf
		CloseFile(FileHdl)
	Else
		Debug "ERROR!! reading file (__Icon_GetSizesICO)"
		RetVal=-1
	EndIf
	
	ProcedureReturn RetVal
EndProcedure

Procedure.i Icon_GetSizes(File.s, *Sizes.Icon_Size, IconNr.i=1)
	Protected IconData.Icon_EnumData
	Protected hLibrary.i
	Protected ImgHdl.i
	
	IconData\RetVal=0

	Select LCase(GetExtensionPart(File))
		Case "ico", "cur"
			IconData\RetVal=__Icon_GetSizesICO(File, *Sizes)
			Debug "Sizes >"+ArraySize(*Sizes\Icon_Size())+"<"
			Debug "Width >"+Str(*Sizes\Icon_Size(0)\Height)+"<"
			
		Case "bmp"
			ImgHdl=LoadImage(#PB_Any, File)
			If ImgHdl
				ReDim *Sizes\Icon_Size(0)
				*Sizes\Icon_Size(0)\Height=ImageHeight(ImgHdl)
				*Sizes\Icon_Size(0)\Width=ImageWidth(ImgHdl)
				*Sizes\Icon_Size(0)\BitCnt=ImageDepth(ImgHdl, #PB_Image_OriginalDepth)
				FreeImage(ImgHdl)
			Else
				Debug "ERROR!! Loading File (Icon_GetSizes)"
				IconData\RetVal=-1
			EndIf
			
		Case "exe", "dll"
			hLibrary = LoadLibraryEx_(File, #Null, #LOAD_LIBRARY_AS_DATAFILE)
			If hLibrary
				IconData\Mode=#Icon_GetSizes
				IconData\Nr=IconNr
				EnumResourceNames_(hLibrary, #RT_GROUP_ICON, @__Icon_EnumCallback(), @IconData)
				FreeLibrary_(hLibrary)
				
				If IconData\RetVal ; detection of sizes succesfull
					If CopyArray(IconData\Size(), *Sizes\Icon_Size())
						IconData\RetVal=#True
					Else
						Debug "Error CopyArray"
						IconData\RetVal=#False
					EndIf
				Else
					IconData\RetVal=#False
					Debug "Error Callback (Icon_GetSizes)"
				EndIf								
			EndIf				

	EndSelect
	
	ProcedureReturn IconData\RetVal
EndProcedure

Procedure.i Icon_GetHdl(File.s, Width.i=16, IconNr.i=1, Height.i=0)
	Protected IconData.Icon_EnumData
	Protected hLibrary.i
	
	If Height=0
		Height=Width
	EndIf

	Select LCase(GetExtensionPart(File))
		Case "ico"
			IconData\RetVal=LoadImage_(#Null, @File, #IMAGE_ICON, Width, Height, #LR_LOADFROMFILE)
			
		Case "cur"	
			IconData\RetVal=LoadImage_(#Null, @File, #IMAGE_CURSOR, Width, Height, #LR_LOADFROMFILE)
		
		Case "bmp"
			IconData\RetVal=LoadImage_(#Null, @File, #IMAGE_BITMAP, Width, Height, #LR_LOADFROMFILE)
			
		Case "exe", "dll"
			hLibrary = LoadLibraryEx_(File, #Null, #LOAD_LIBRARY_AS_DATAFILE)
			If hLibrary
				IconData\RetVal=0
				IconData\Size(0)\Width=Width
				IconData\Size(0)\Height=Height
				IconData\Nr=IconNr
				IconData\Mode=#Icon_GetSclHdl
		      EnumResourceNames_(hLibrary, #RT_GROUP_ICON, @__Icon_EnumCallback(), @IconData)
				FreeLibrary_(hLibrary)
			EndIf
		Default
			IconData\RetVal=0
	EndSelect
	
	ProcedureReturn IconData\RetVal
EndProcedure

Procedure.i Icon_GetRealHdl(File.s, Width.i=16, IconNr.i=1, Height.i=0, BPP.a=0)
	Protected IconData.Icon_EnumData
	Protected hLibrary.i
	Protected ImgHdl.i
	
	If Width=256 And LCase(GetExtensionPart(File))<>"bmp" ; this rule is not for BMP files!
		Width=0 ; Width256 is stored as 0 (because it's only 1 Byte, so 255 is max.)
	EndIf
	
	If Height=0 Or Height=256
		Height=Width
	EndIf
	
	IconData\RetVal=0

	Select LCase(GetExtensionPart(File))
		Case "ico", "cur"
			IconData\RetVal=__Icon_GetRealHdlICO(File, Width, Height, BPP)
			
		Case "bmp"
			ImgHdl=LoadImage(#PB_Any, File)
			If ImgHdl
				If ImageHeight(ImgHdl)=Height And
				   ImageWidth(ImgHdl)=Width And
				   (ImageDepth(ImgHdl, #PB_Image_OriginalDepth)=BPP Or BPP=0)
					IconData\RetVal=Icon_GetHdl(File, Width, 1, Height)
				Else
					Debug "BMP size is not available"
					IconData\RetVal=0
				EndIf
				FreeImage(ImgHdl)
			Else
				Debug "ERROR!! Loading File (Icon_GetSizes)"
				IconData\RetVal=-1
			EndIf			
			
			
		Case "exe", "dll"
			hLibrary = LoadLibraryEx_(File, #Null, #LOAD_LIBRARY_AS_DATAFILE)
			If hLibrary
				IconData\Mode=#Icon_GetRealHdl
				IconData\Nr=IconNr
				IconData\Size(0)\Width=Width
				IconData\Size(0)\Height=Height
				IconData\Size(0)\BitCnt=BPP
				EnumResourceNames_(hLibrary, #RT_GROUP_ICON, @__Icon_EnumCallback(), @IconData)
				FreeLibrary_(hLibrary)
			EndIf				
			
	EndSelect	
	
	ProcedureReturn IconData\RetVal
EndProcedure

Procedure.i Icon_GetCnt(File.s)
	Protected IconData.Icon_EnumData
	Protected hLibrary.i
	
	Select LCase(GetExtensionPart(File))
		Case "exe", "dll"	
			IconData\Mode=#Icon_GetCnt
			IconData\RetVal=0
			
			hLibrary = LoadLibraryEx_(File, #Null, #LOAD_LIBRARY_AS_DATAFILE)
			If hLibrary
				EnumResourceNames_(hLibrary, #RT_GROUP_ICON, @__Icon_EnumCallback(), @IconData)
				FreeLibrary_(hLibrary)
			EndIf
		Case "ico", "cur", "bmp"
			IconData\RetVal=1
			
		Default
			IconData\RetVal=-1 ; File not supported
	EndSelect
	
	ProcedureReturn IconData\RetVal
EndProcedure

Procedure Icon_DestroyHdl(hIcon.i)
	DestroyIcon_(hIcon)
EndProcedure

Procedure Icon_GetInfo(hIcon.i, *Size.Icon_Dimension)
	; http://stackoverflow.com/questions/1913468/how-to-determine-the-size-of-an-icon-from-a-hicon
	Protected IconInf.ICONINFO
	Protected BMInf.BITMAP
	Protected RetVal.i=1
	
	If (GetIconInfo_(hIcon, IconInf))
		
		If (IconInf\hbmColor) ; ' Icon has colour plane
			If (GetObject_(IconInf\hbmColor, SizeOf(BITMAP), @BMInf))
				*Size\Width = BMInf\bmWidth
				*Size\Height = BMInf\bmHeight
				*Size\BitCnt = BMInf\bmBitsPixel
				DeleteObject_(IconInf\hbmColor)
			Else
				RetVal=-3
				Debug "ERROR!! GetObject failed (Icon_GetInfo)"
			EndIf
		Else ;' Icon has no colour plane, image data stored in mask
			If (GetObject_(IconInf\hbmMask, SizeOf(BITMAP), @BMInf))
				*Size\Width = BMInf\bmWidth
				*Size\Height = BMInf\bmHeight / 2
				*Size\BitCnt = 1
				DeleteObject_(IconInf\hbmMask)
			Else
				RetVal=-2
				Debug "ERROR!! GetObject failed (Icon_GetInfo)"
			EndIf
		EndIf
	Else
		RetVal=-1
		Debug "ERROR! GetIconInfo failed (Icon_GetInfo)"
	EndIf
	
	;Debug "Width >"+Str(*Size\Width)+"< Height >"+Str(*Size\Height)+"< BPP >"+Str(*Size\BitCnt)+"<"
	
	ProcedureReturn RetVal
EndProcedure

Procedure Icon_SaveToPNG(hIcon.i, DestFile.s)
	; http://blogs.msdn.com/b/oldnewthing/archive/2010/10/21/10078690.aspx
	; another approach to combine image and mask:
	; http://forums.purebasic.com/german/viewtopic.php?p=85298&sid=5a3cfda0f787dc2caec0e8834da98b0a#p85298
	
	Protected TmpImage.i
	Protected TmpMask.i
	Protected IcoInfo.Icon_Dimension
	Protected BmpHdl.i
	Protected ImageDC.i
	Protected MaskDC.i
	Protected PosX.i, PosY.i
	Protected ModeARGB.b=#False
	Protected RetVal=1
	Protected FreeTmpImage.b=#False
	Protected FreeTmpMask.b=#False
	
	If LCase(GetExtensionPart(DestFile))<>"png"
		DestFile+".png"
	EndIf
	
	If Not Icon_GetInfo(hIcon, @IcoInfo)
		Debug "ERROR!! Icon_GetInfo failed!"
		RetVal=-1
	EndIf
	
	If RetVal
		TmpImage=CreateImage(#PB_Any, IcoInfo\Width, IcoInfo\Height, 32)
		If TmpImage
			FreeTmpImage=#True
		Else
			Debug "ERROR!! CreateImage (TmpImage) failed!"	
			RetVal=-2
		EndIf
	EndIf
	
	If RetVal
		ImageDC=StartDrawing(ImageOutput(TmpImage))
		If Not ImageDC
			Debug "ERROR!! StartDrawing failed!"
			RetVal=-3
		EndIf
	EndIf
	
	If RetVal
		If DrawIconEx_(ImageDC, 0, 0, hIcon, 0, 0, 0, 0, #DI_IMAGE);#DI_IMAGE) ;#DI_MASK)
			; Check if Icon uses 0RGB or ARGB
			DrawingMode(#PB_2DDrawing_AlphaChannel)
			For PosY=0 To IcoInfo\Height-1
				For PosX=0 To IcoInfo\Width-1
					If (Alpha(Point(PosX, PosY))>0) ; ARGB detected
						ModeARGB=#True
						Break 2
					EndIf
				Next
			Next
			StopDrawing()
		Else
			Debug "ERROR!! DrawIconEx failed!"
			RetVal=-4
		EndIf
	EndIf

	If RetVal		
		If ModeARGB
			Debug "ARGB used"
		Else
			Debug "0RGB used"
			
			If StartDrawing(ImageOutput(TmpImage)) ;set alpha channel to "opaque"
				DrawingMode(#PB_2DDrawing_AlphaChannel)
				For PosY=0 To IcoInfo\Height-1
					For PosX=0 To IcoInfo\Width-1
						Plot(PosX, PosY, RGBA(0,0,0,255))
					Next
				Next
				StopDrawing()	
			Else
				Debug "ERROR!! StartDrawing (TmpImage) failed!"
				RetVal=-5
			EndIf
			
			If RetVal
				TmpMask=CreateImage(#PB_Any, IcoInfo\Width, IcoInfo\Height, 24)
				If Not TmpMask
					Debug "ERROR!! CreateImage (TmpMask) failed!"
					RetVal=-6
				EndIf
			EndIf
			
			If RetVal
				MaskDC=StartDrawing(ImageOutput(TmpMask))
				If MaskDC
					FreeTmpMask=#True
				Else
					Debug "ERROR!! StartDrawing (TmpMask) failed!"
					RetVal=-7
				EndIf
			EndIf
			
			If RetVal
				If Not DrawIconEx_(MaskDC, 0, 0, hIcon, 0, 0, 0, 0, #DI_MASK)
					Debug "ERROR!! DrawIconEx failed (MaskDC)"
					RetVal=-8
				EndIf
			EndIf
			
			If RetVal	
				For PosY=0 To IcoInfo\Height-1
					For PosX=0 To IcoInfo\Width-1
						If (Point(PosX, PosY)=$FFFFFF) ; white Pixel - should be fully transparent in Image
							StopDrawing()
							If StartDrawing(ImageOutput(TmpImage))
								DrawingMode(#PB_2DDrawing_AlphaChannel)
								Plot(PosX, PosY, RGBA(0,0,0,0))
								StopDrawing()
							Else
								Debug "ERROR!! StartDrawing (TmpImage) failed!"
								RetVal=-9
								Break 2
							EndIf
								
							If Not StartDrawing(ImageOutput(TmpMask))
								Debug "ERROR!! StartDrawing (TmpMask) failed!"
								RetVal=-10
								Break 2
							EndIf
						EndIf
					Next
				Next
				If RetVal
					StopDrawing()
				EndIf
			EndIf
		EndIf
	EndIf
	
	If FreeTmpMask
		FreeImage(TmpMask)
	EndIf
			
	If RetVal
		If Not SaveImage(TmpImage, DestFile, #PB_ImagePlugin_PNG, #Null, 32)
			Debug "ERROR!! SaveImage ("+DestFile+") failed!"
			RetVal=-11
		EndIf
	EndIf
	
	If FreeTmpImage
		FreeImage(TmpImage)
	EndIf

	ProcedureReturn RetVal
EndProcedure



;- ############## EXAMPLE CODE ################

Enumeration #PB_Compiler_EnumerationValue
  #MainWin
EndEnumeration

Enumeration #PB_Compiler_EnumerationValue
  #File_Select
  #Filename
  #Image
  #Icon_Tree
  #Scale_Text
  #Scale_Width
  #Scale_Height
  #Image_Save
EndEnumeration

Declare ResizeGadgetsMainWin()
Declare LoadIcon(EventType)

Procedure ParseIconFile(File.s)
	Protected IconSizes.Icon_Size
	Protected IconCnt.a
	Protected Cnt.i
	Protected IcCnt.i
	
	ClearGadgetItems(#Icon_Tree)
	SetGadgetText(#Filename, File)
	
	IconCnt=Icon_GetCnt(File)
	For Cnt=1 To IconCnt
		AddGadgetItem(#Icon_Tree, -1, "IconNr. "+Str(Cnt), Icon_GetHdl(File, 16, Cnt), 0)
		SetGadgetItemData(#Icon_Tree, CountGadgetItems(#Icon_Tree)-1, Cnt)
		Icon_GetSizes(File, @IconSizes, Cnt)
		
		For IcCnt=0 To ArraySize(IconSizes\Icon_Size())
			AddGadgetItem(#Icon_Tree, -1, Str(IconSizes\Icon_Size(IcCnt)\Width)+"x"+Str(IconSizes\Icon_Size(IcCnt)\Height)+"@"+Str(IconSizes\Icon_Size(IcCnt)\BitCnt)+"BPP", 0, 1)
			SetGadgetItemData(#Icon_Tree, CountGadgetItems(#Icon_Tree)-1, Cnt)
		Next
		
		If IsGadget(#Image)
			FreeGadget(#Image)
		EndIf		
	Next	
EndProcedure

Procedure SelectFile()
	Protected File.s

	File=OpenFileRequester("Please select file", "", "FileWithIcon|*.ico;*.cur;*.exe;*.dll;*.bmp", 1)
	If File
		ParseIconFile(File)		
	EndIf

EndProcedure

Procedure LoadIcon(EventType.i)
	Protected IconNr.i
	Protected Width.i
	Protected Height.i
	Protected BitCnt.a
	Protected Text.s
	Protected IconHdl.i
	Protected OldIconHdl.i
	Protected File.s
	Protected Nr.i
	
	Select EventType
		Case #PB_EventType_LeftClick 
			Text=GetGadgetText(#Icon_Tree)
			Nr=GetGadgetState(#Icon_Tree)
			File=GetGadgetText(#Filename)
			IconNr=GetGadgetItemData(#Icon_Tree, Nr)
			If IconNr
			
				Select (GetGadgetItemAttribute(#Icon_Tree, Nr, #PB_Tree_SubLevel))
					Case 0
						Width=Val(GetGadgetText(#Scale_Width))
						Height=Val(GetGadgetText(#Scale_Height))
						IconHdl=Icon_GetHdl(File, Width, IconNr, Height)
						
					Case 1
						Width=Val(StringField(Text, 1, "x"))
						Text=StringField(Text, 2, "x")
						Height=Val(StringField(Text, 1, "@"))
						Text=StringField(Text, 2, "@")
						BitCnt=Val(StringField(Text, 1, "BPP"))				
					
						IconHdl=Icon_GetRealHdl(File, Width, IconNr, Height, BitCnt)
				EndSelect
				
				If IconHdl
					If IsGadget(#Image)
						OldIconHdl=GetGadgetState(#Image)
						If (OldIconHdl)
							Icon_DestroyHdl(OldIconHdl)
						EndIf
						FreeGadget(#Image)
					EndIf
					ImageGadget(#Image, 180, 40, Width, Height, IconHdl, #PB_Image_Border)
				EndIf				
			EndIf	
	EndSelect

EndProcedure

Procedure SaveIcon()
	Protected File.s

	If IsGadget(#Image)
		File=SaveFileRequester("Define destination file", "", "PNG-Image (*.png)|*.png", 0)
		If File
			If Not Icon_SaveToPNG(GetGadgetState(#Image), File)
				MessageRequester("ERROR", "Icon_SaveToPNG failed!")
			EndIf
		EndIf
	Else
		MessageRequester("Information", "Nothing to save!")
	EndIf	
	
EndProcedure

Procedure OpenMainWin()
  OpenWindow(#MainWin, 0, 0, 460, 390, "IconDisplay", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_SizeGadget | #PB_Window_ScreenCentered)
  ButtonGadget(#File_Select, 430, 10, 20, 20, "...")
  StringGadget(#Filename, 10, 10, 410, 20, "", #PB_String_ReadOnly)
  TreeGadget(#Icon_Tree, 10, 40, 160, 260)
  TextGadget(#Scale_Text, 30, 340, 110, 20, "Size of scaled Icon:")
  StringGadget(#Scale_Width, 20, 360, 60, 20, "256", #PB_String_Numeric)
  StringGadget(#Scale_Height, 100, 360, 60, 20, "256")
  ButtonGadget(#Image_Save, 30, 310, 110, 20, "Save as Image")
EndProcedure

Procedure ResizeGadgetsMainWin()
  Protected WindowWidth, WindowHeight
  WindowWidth = WindowWidth(#MainWin)
  WindowHeight = WindowHeight(#MainWin)
  ResizeGadget(#File_Select, WindowWidth - 30, 10, 20, 20)
  ResizeGadget(#Filename, 10, 10, WindowWidth - 50, 20)
  ResizeGadget(#Icon_Tree, 10, 40, 160, WindowHeight - 130)
  ResizeGadget(#Scale_Text, 30, WindowHeight - 50, 110, 20)
  ResizeGadget(#Scale_Width, 20, WindowHeight - 30, 60, 20)
  ResizeGadget(#Scale_Height, 100, WindowHeight - 30, 60, 20)
  ResizeGadget(#Image_Save, 30, WindowHeight - 80, 110, 20)
EndProcedure


Procedure MainWin_Events(event)

	Select event
		Case #PB_Event_SizeWindow
			ResizeGadgetsMainWin()
			
		Case #PB_Event_CloseWindow
			ProcedureReturn #False
		
		Case #PB_Event_Gadget
			Select EventGadget()
				Case #File_Select
					SelectFile()
				Case #Icon_Tree
					LoadIcon(EventType())  	
				Case #Image_Save
					SaveIcon()
			EndSelect
	EndSelect
	ProcedureReturn #True
EndProcedure

Define event.i

UsePNGImageEncoder()
UseJPEGImageEncoder()

OpenMainWin()

CompilerIf #PB_Compiler_Debugger
	ParseIconFile(#PB_Compiler_Home+"PureBasic.exe")
CompilerEndIf

Repeat
  event = WaitWindowEvent()
Until MainWin_Events(event) = #False

Have fun with it! :D

Update 20130707 - Added 2 more functions:
Icon_GetInfo - used to get the dimensions of an icon (needs the hIcon handle as parameter)
Icon_SaveToPNG - used to save an icon to a png image with transparency (needs the hIcon handle as parameter)
Fixed a small bug, BitPerPixel Counts<8 didn't work properly in the previous version
updated demonstration code with a "Save" Button :)

Update 20130708 - Fixed small error where I forgot to change "d:\test.png" to 'File' ;) Sorry about that!
Last edited by nalor on Mon Jul 08, 2013 7:09 pm, edited 2 times in total.
User avatar
idle
Always Here
Always Here
Posts: 5097
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Extract Icon from exe,dll,ico,cur,bmp

Post by idle »

nice, thanks for sharing!
Windows 11, Manjaro, Raspberry Pi OS
Image
User avatar
luis
Addict
Addict
Posts: 3876
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Re: Extract Icon from exe,dll,ico,cur,bmp

Post by luis »

Nice demo, tried with 256x256 icons and it works.

Can be useful, thanks :)
"Have you tried turning it off and on again ?"
A little PureBasic review
davido
Addict
Addict
Posts: 1890
Joined: Fri Nov 09, 2012 11:04 pm
Location: Uttoxeter, UK

Re: Extract Icon from exe,dll,ico,cur,bmp

Post by davido »

Really nice demo.

Thanks for sharing. :D
DE AA EB
rsts
Addict
Addict
Posts: 2736
Joined: Wed Aug 24, 2005 8:39 am
Location: Southwest OH - USA

Re: Extract Icon from exe,dll,ico,cur,bmp

Post by rsts »

Nice one.

Thanks for sharing.
said
Enthusiast
Enthusiast
Posts: 342
Joined: Thu Apr 14, 2011 6:07 pm

Re: Extract Icon from exe,dll,ico,cur,bmp

Post by said »

Very nice :lol: thanks for sharing
Just wondering why you have not added save to file :!:
nalor
Enthusiast
Enthusiast
Posts: 115
Joined: Thu Apr 02, 2009 9:48 pm

Re: Extract Icon from exe,dll,ico,cur,bmp

Post by nalor »

Added 2 more functions, fixed a small bug and a "Save Icon" button is available now :)

Although I created it more or less to read icons, it's still not bad to have the option to save them to a png-image.

Updated the first post.
said
Enthusiast
Enthusiast
Posts: 342
Joined: Thu Apr 14, 2011 6:07 pm

Re: Extract Icon from exe,dll,ico,cur,bmp

Post by said »

Thanks nalor for adding Save to file (i just thought it would be more complete with it)

just a little note, you need to change:

Code: Select all

If Not Icon_SaveToPNG(GetGadgetState(#Image), "d:\test.png")
by

Code: Select all

If Not Icon_SaveToPNG(GetGadgetState(#Image), File)
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5357
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Extract Icon from exe,dll,ico,cur,bmp

Post by Kwai chang caine »

Useful !!
Thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
nalor
Enthusiast
Enthusiast
Posts: 115
Joined: Thu Apr 02, 2009 9:48 pm

Re: Extract Icon from exe,dll,ico,cur,bmp

Post by nalor »

@said: You're absolutely right! Corrected the code in the first post!
User avatar
NoahPhense
Addict
Addict
Posts: 1999
Joined: Thu Oct 16, 2003 8:30 pm
Location: North Florida

Re: Extract Icon from exe,dll,ico,cur,bmp

Post by NoahPhense »

Very nice. Thanks.
Post Reply