Что нового

[Файловая система] Как корректно найти иконку файла ?

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
Как корректно найти иконку файла ?
При использовании ф-ии :
Код:
_WinAPI_AssocQueryString
она выдает разнообразные форматы

Попытка их коррекции показана здесь :
Код:
#include <WindowsConstants.au3>
   #include <GUITreeView.au3>
   #include <File.au3>
   #include <WinAPIEx.au3>  
 
   $hGUI = GUICreate("Test", 400, 500)

   $hTreeView = GUICtrlCreateTreeView(6, 6, 300, 450, -1, $WS_EX_STATICEDGE+$WS_EX_CLIENTEDGE)
   _GUICtrlTreeView_SetBkColor($hTreeView , 0xDBDBDB )
   
   $idDiscItem=GUICtrlCreateTreeViewItem("C:",$hTreeView)
   _GUICtrlTreeView_SetIcon($hTreeView,$idDiscItem,"shell32.dll",7)
   $Exit_Button = GUICtrlCreateButton("Exit", 330, 470, 60, 20)
 
   GUISetState()
	
   While 1
	  $nMsg = GUIGetMsg()
	   
	  Switch $nMsg
		 Case -3, $Exit_Button
			Exit
		 Case Else
			If $nMsg <= 0 Then ContinueLoop
			$iItem_CtrlID = GUICtrlRead($hTreeView)
			$sFullName=StringReplace(_GUICtrlTreeView_GetTree($hTreeView, $iItem_CtrlID) ,'|','\')
			UpDateTV($hTreeView,$iItem_CtrlID)
			;If StringInStr ( FileGetAttrib ($sFullName ) , "D")  Then ContinueLoop
			;ShellExecute($sFullName)
	  EndSwitch
   WEnd

   Func UpDateTV($hTV,$idItem)
	  Dim $aIcon[3]=[2,0,0]
	  If _GUICtrlTreeView_GetChildren($hTV,$idItem) =False  Then 
		 $aFolderList=_FileListToArray( $sFullName,'*',2)
		 For $i =1 To UBound($aFolderList)-1
			$idChild=GUICtrlCreateTreeViewItem ( $aFolderList[$i], $idItem )
			_GUICtrlTreeView_SetIcon($hTV,$idChild,"shell32.dll",3)
		 Next
		 $aFileList=_FileListToArray( $sFullName,'*',1)
		 For $i =1 To UBound($aFileList)-1
			$idChild=GUICtrlCreateTreeViewItem ( $aFileList[$i], $idItem )
			$sExt=StringRegExpReplace($aFileList[$i], '^.*\.', '.')
			$sFullName=StringReplace(_GUICtrlTreeView_GetTree($hTV, $idItem) ,'|','\')
			$sFull=$sFullName & '\' & $aFileList[$i]
			;$sIcon=_WinAPI_AssocQueryString($sFull, $ASSOCSTR_DEFAULTICON); найти иконку,соотв расширению файла
			$sIcon=_WinAPI_AssocQueryString($sExt, $ASSOCSTR_DEFAULTICON); найти иконку,соотв расширению файла
			ConsoleWrite( @error & @TAB & $sIcon & @LF)
			If StringInStr ( $sIcon, ",") Then 
			   $aIcon=StringSplit($sIcon,',','|')
			ElseIf  StringInStr ( $sIcon, ".") Then
			   $aIcon[1]=$sIcon
			ElseIF $sIcon='%1' Then 
			   $aIcon[1]=$sFull
			Else
			   $aIcon[1]="shell32.dll"
			EndIf
			_GUICtrlTreeView_SetIcon($hTV,$idChild,$aIcon[1],$aIcon[2])
		 Next
		 _GUICtrlTreeView_Expand($hTV, $idItem)
	  EndIf 
   EndFunc

Скорректировать удается не все.
Есть более корректный способ получения иконки либо коррекции ?


Добавлено:
Сообщение автоматически объединено:

В частности не удалась моя коррекция ЕХЕ-файлов (не для всех правильная)
Не получил иконку для Htm, Html-файлов файлов-рисунков и др.
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Код:
$sIcon = _WinAPI_AssocQueryString($sExt, $ASSOCSTR_DEFAULTICON)
$aIcon[1] = StringRegExpReplace($sIcon, '^\s*(?>"([^"]*+)"|([^,]*?)\s*(?:,|$)).*+', '\1\2')
$aIcon[2] = StringRegExpReplace($sIcon, '.*?(?>[\s,]([+-]?\d++)\s*|)$', '\1') + 0
Switch $aIcon[1]
	Case ''
		$aIcon[1] = @SystemDir & '\shell32.dll'
		$aIcon[2] = 0
	Case '%1'
		If Not _WinAPI_ExtractIconEx($sFull, -1, 0, 0, 0) Then
			$aIcon[1] = @SystemDir & '\shell32.dll'
			If $sExt = 'exe' Then
				$aIcon[2] = 2
			Else
				$aIcon[2] = 0
			EndIf
		Else
			$aIcon[1] = $sFull
			$aIcon[2] = 0
		EndIf
EndSwitch
 
Автор
G

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
Yashied
Спасибо , Вроде находит
Кроме Htm, Html . Не вытаскивает при '%1'
Что задать исключение по расширению Html, Htm ?
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
gregaz сказал(а):
Что задать исключение по расширению Html, Htm ?

Не получится, %1 к .htm/.html??? Это делает сам Explorer. Можно еще попробывать другой вариант (но к .htm/.html тоже не работает !?):

Код:
Func UpDateTV($hTV, $idItem)
	Dim $aIcon[3] = [2, 0, 0]
	If _GUICtrlTreeView_GetChildren($hTV, $idItem) = False Then
		$aFolderList = _FileListToArray($sFullName, '*', 2)
		For $i = 1 To UBound($aFolderList) - 1
			$idChild = GUICtrlCreateTreeViewItem($aFolderList[$i], $idItem)
			_GUICtrlTreeView_SetIcon($hTV, $idChild, "shell32.dll", 3)
		Next
		$aFileList = _FileListToArray($sFullName, '*', 1)
		For $i = 1 To UBound($aFileList) - 1
			$idChild = GUICtrlCreateTreeViewItem($aFileList[$i], $idItem)
			 $tSHFILEINFO  = _WinAPI_ShellGetFileInfo($sFullName  & '\' & $aFileList[$i], BitOR($SHGFI_ICON, $SHGFI_SMALLICON, $SHGFI_USEFILEATTRIBUTES))
 			$hIcon = DllStructGetData($tSHFILEINFO, 'hIcon')
			_GUICtrlTreeView_SetHIcon($hTV, $idChild, $hIcon)
			_WinAPI_FreeIcon($hIcon)
		Next
		_GUICtrlTreeView_Expand($hTV, $idItem)
	EndIf
EndFunc   ;==>UpDateTV



Измененная функция _GUICtrlTreeView_SetIcon() для работы с хендлами.

Код:
Func _GUICtrlTreeView_SetHIcon($hWnd, $hItem = 0, $hIcon = 0, $iImageMode = 6)
	If $Debug_TV Then __UDF_ValidateClassName($hWnd, $__TREEVIEWCONSTANT_ClassName)

	If $hItem = 0 Then $hItem = 0x00000000

	If $hItem <> 0x00000000 And Not IsHWnd($hItem) Then $hItem = _GUICtrlTreeView_GetItemHandle($hWnd, $hItem)
	If $hItem = 0x00000000 Or $hIcon = 0x00000000 Then Return SetError(1, 1, False)

	If Not IsHWnd($hWnd) Then $hWnd = GUICtrlGetHandle($hWnd)

	Local $tTVITEM = DllStructCreate($tagTVITEMEX)

;	Local $tIcon = DllStructCreate("handle")
;	Local $i_count = DllCall("shell32.dll", "uint", "ExtractIconExW", "wstr", $sIconFile, "int", $iIconID, _
;			"handle", 0, "handle", DllStructGetPtr($tIcon), "uint", 1)
;	If @error Then Return SetError(@error, @extended, 0)
;	If $i_count[0] = 0 Then Return 0

	Local $hImageList = _SendMessage($hWnd, $TVM_GETIMAGELIST, 0, 0, 0, "wparam", "lparam", "handle")
	If $hImageList = 0x00000000 Then
		$hImageList = DllCall("comctl32.dll", "handle", "ImageList_Create", "int", 16, "int", 16, "uint", 0x0021, "int", 0, "int", 1)
		If @error Then Return SetError(@error, @extended, 0)
		$hImageList = $hImageList[0]
		If $hImageList = 0 Then Return SetError(1, 1, False)

		_SendMessage($hWnd, $TVM_SETIMAGELIST, 0, $hImageList, 0, "wparam", "handle")
	EndIf

;	Local $hIcon = DllStructGetData($tIcon, 1)
	Local $i_icon = DllCall("comctl32.dll", "int", "ImageList_AddIcon", "handle", $hImageList, "handle", $hIcon)
	$i_icon = $i_icon[0]
	If @error Then
		Local $iError = @error, $iExtended = @extended
;		DllCall("user32.dll", "int", "DestroyIcon", "handle", $hIcon)
		; No @error test because results are unimportant.
		Return SetError($iError, $iExtended, 0)
	EndIf

;	DllCall("user32.dll", "int", "DestroyIcon", "handle", $hIcon)
	; No @error test because results are unimportant.

	Local $iMask = BitOR($TVIF_IMAGE, $TVIF_SELECTEDIMAGE)

	If BitAND($iImageMode, 2) Then
		DllStructSetData($tTVITEM, "Image", $i_icon)
		If Not BitAND($iImageMode, 4) Then $iMask = $TVIF_IMAGE
	EndIf

	If BitAND($iImageMode, 4) Then
		DllStructSetData($tTVITEM, "SelectedImage", $i_icon)
		If Not BitAND($iImageMode, 2) Then
			$iMask = $TVIF_SELECTEDIMAGE
		Else
			$iMask = BitOR($TVIF_IMAGE, $TVIF_SELECTEDIMAGE)
		EndIf
	EndIf

	DllStructSetData($tTVITEM, "Mask", $iMask)
	DllStructSetData($tTVITEM, "hItem", $hItem)

	Return __GUICtrlTreeView_SetItem($hWnd, $tTVITEM)
EndFunc   ;==>_GUICtrlTreeView_SetHIcon
 
Автор
G

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
Yashied [?]
Можно еще попробывать другой вариант (но к .htm/.html тоже не работает !?):

с htm/.html у меня работает , но неверно вообще не определяет ЕХЕ- файлы (одна икона на все почти)?
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
gregaz сказал(а):
...вообще не определяет ЕХЕ- файлы (одна икона на все почти)?

Это я поспешил. Нужно использовать не расширение, а полное имя файла. Вот так все должно работать:

Код:
$tSHFILEINFO = _WinAPI_ShellGetFileInfo($sFullName & '\' & $aFileList[$i], BitOR($SHGFI_ICON, $SHGFI_SMALLICON, $SHGFI_USEFILEATTRIBUTES))
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Нет, удаляет иконку из памяти, т.к. _GUICtrlTreeView_SetHIcon() копирует эту иконку во внутренний список, она нам больше не нужна и ее необходимо удалить, что бы не расходовать по напрасну память.
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Кстати, последний способ практически идеален для твоей задачи (он позволяет вытащить иконки только либо маленькие, либо большие, что определены в Windows), но если нужно достать иконку произвольного размера (например 128x128), то тут получается полная ж***, и другого способа, кроме как копаться в реестре я до сих пор не нашел...
 
Автор
G

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
Yashied [?]
Кстати, последний способ практически идеален для твоей задачи

Да , этого достаточно . Классно получилось. :beer:
Давно искал корректное решение этой задачи
 
Автор
G

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
Yashied [?]
Вот так все должно работать:
Код: AutoIt [Выделить]
$tSHFILEINFO = _WinAPI_ShellGetFileInfo($sFullName & '\' & $aFileList[$i], BitOR($SHGFI_ICON, $SHGFI_SMALLICON, $SHGFI_USEFILEATTRIBUTES))
Теперь уже не работает

Видоизменил:
Код:
$tSHFILEINFO = DllStructCreate($tagSHFILEINFO)
_WinAPI_ShellGetFileInfo($sFullName  & '\' & $aFileList[$i],$SHGFI_ICON, $SHGFI_SMALLICON,$tSHFILEINFO )
 $hIcon = DllStructGetData($tSHFILEINFO, 'hIcon')
  _GUICtrlTreeView_SetHIcon($hTV, $idChild, $hIcon)
  _WinAPI_DestroyIcon($hIcon)

Но html неверно определяет

Пытался изменить на :
Код:
Local $tSHFILEINFO
_WinAPI_ShellGetFileInfo($sFullName  & '\' & $aFileList[$i], $SHGFI_ICON, $SHGFI_SMALLICON, $tSHFILEINFO)

Все равно не корректно находит

Как все же найти иконку, соответствующую типу файла ?
Где-то я видел новое решение , но не нащел где ?
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Что именно не работает? Попробуй _WinAPI_ShellExtractAssociatedIcon().
 
Автор
G

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
Yashied [?]
Что именно не работает? Попробуй _WinAPI_ShellExtractAssociatedIcon().

Насколько я понял в новых версиях перестала работать :
_WinAPI_ShellGetFileInfo (изменилась форма задания параметров)
_WinAPI_ShellExtractAssociatedIcon же не находит иконку для html-файла (например ...)

Опять встала задача отображения иконок в TreeView для различных типов файла.
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Как в реестре прописан параметр "DefaultIcon" для .html файлов? Вот пример, у меня все прекрасно работает.

Код:
#Include <WinAPIEx.au3>

GUICreate('MyGUI', 128, 128)
GUICtrlCreateIcon('', 0, 48, 48, 32, 32)
GUICtrlSendMsg(-1, 0x0172, 1, _WinAPI_ShellExtractAssociatedIcon('.html'))
GUISetState()

Do
Until GUIGetMsg() = -3
 
Автор
G

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
Может я неверно выразился.
Находит, но у меня некорректно .Это не иконка html.
Что это неверны записи реестра.?
Но ведь Total Commander , например, отображает их верно.



Добавлено:
Сообщение автоматически объединено:

Т.е. находится как иконка "shell32.dll" , 0
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Еще раз. Как в реестре прописан параметр "DefaultIcon" для .html файлов? В качестве параметра в функцию _WinAPI_ShellExtractAssociatedIcon() можно передать либо полный путь к файлу, либо только одно расширение ".html". Если в реестре иконка прописана как "%1", то в первом случае, иконка будет взята из самого файла (если это возможно), во втором - пустая иконка ("shell32.dll", 0), т.к. взять иконку неоткуда в принципе.
 
Автор
G

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
Yashied [?]
Как в реестре прописан параметр "DefaultIcon" для .html файлов?

Если смотреть в системном редакторе реестра , а где еще ?
То там записано для .htm и .html :

Код:
( По умолчанию )       htmlfile
Content type              text/html
PerceivedType            text
 
Верх