Что нового

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

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 711
Код:
$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 711
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 711
gregaz сказал(а):
...вообще не определяет ЕХЕ- файлы (одна икона на все почти)?
Это я поспешил. Нужно использовать не расширение, а полное имя файла. Вот так все должно работать:

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

Yashied

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

gregaz

AutoIT Гуру
Сообщения
1 166
Репутация
299
Понятно,спасибо! :ok:
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5 379
Репутация
2 711
Кстати, последний способ практически идеален для твоей задачи (он позволяет вытащить иконки только либо маленькие, либо большие, что определены в 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 711
Что именно не работает? Попробуй _WinAPI_ShellExtractAssociatedIcon().
 
Автор
G

gregaz

AutoIT Гуру
Сообщения
1 166
Репутация
299
Yashied [?]
Что именно не работает? Попробуй _WinAPI_ShellExtractAssociatedIcon().
Насколько я понял в новых версиях перестала работать :
_WinAPI_ShellGetFileInfo (изменилась форма задания параметров)
_WinAPI_ShellExtractAssociatedIcon же не находит иконку для html-файла (например ...)

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

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5 379
Репутация
2 711
Как в реестре прописан параметр "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 711
Еще раз. Как в реестре прописан параметр "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
 
Верх