1

Тема: AHK: Извлечение иконок из dll и exe

Функция GetIconFromResource(dest, source, idx) извлекает иконку с порядковым номером idx из dll или exe файла, указанного в параметре source, и сохраняет её в файл, путь к которому указан в параметре dest. Если папка файла, указанного в dest, отсутствует, она будет создана скриптом, если это возможно. Чтобы извлечь из ресурса все иконки за раз, передайте idx равный 0. В этом случае к имени файла, указанного в dest, будет добавлен порядковый номер каждой из иконок.

GetIconFromResource(dest, source, idx)
{
   ; info: http://msdn.microsoft.com/en-us/library/ms997538.aspx
   SetBatchLines, -1
   if !hModule := Validation(dest, source, idx)
      Return
   
   for key, handle in FindGroup(source, idx, hModule)
   {
      pGroupInfo := GetResData(hModule, handle)
      if !IsObject(DataArray := CreateDataArray(hModule, pGroupInfo, CountIcons, size, idx ? idx : A_Index))
      {
         if (DataArray = 6)
            continue
         else
            break
      }
      pData := CreateDataBuff(DataArray, pGroupInfo, CountIcons, size)
      res := Data2icoFile(pData, dest, size, idx)
   } until !res
   
   DllCall("FreeLibrary", Ptr, hModule)
}

Validation(dest, source, idx)
{
   if !((idx + 1) > 0 && source := ValidateName(source))
      Return
   
   static dwFlags := (LOAD_LIBRARY_AS_DATAFILE := 0x2 | LOAD_LIBRARY_AS_IMAGE_RESOURCE := 0x20)
   if !hModule := DllCall("LoadLibraryEx", Str, source, Ptr, 0, UInt, dwFlags, Ptr)
      Return, 0, DllCall("MessageBox", Ptr, 0, Str, "Невозможно загрузить указанный модуль!`nОшибка " A_LastError, Str, "", UInt, 0)
         
   Return hModule
}

ValidateName(FilePath)
{
   SplitPath, FilePath,,, ext
   if !(ext ~= "i)exe|dll") && Error := "Допустимы только dll и exe файлы"
      Return, 0, DllCall("MessageBox", Ptr, 0, Str, Error, Str, "", UInt, 0)
   
   if !FileExist(FilePath)
   {
      EnvGet, PathVar, Path
      Loop, parse, PathVar, `;
         continue
      until FileExist(p := RTrim(A_LoopField, "\") . "\" . FilePath) && (FilePath := p) && found := 1
      
      if !found && Text := "Файл " FilePath " не найден"
         Return, 0, DllCall("MessageBox", Ptr, 0, Str, Text, Str, "", UInt, 0)
   }
   
   Return FilePath
}

FindGroup(source, idx, hModule)
{
   Names := [idx, 0]
   DllCall("EnumResourceNames", Ptr, hModule, UInt, RT_GROUP_ICON := 14
      , Ptr, RegisterCallback("EnumGroupIconProc", "Fast", 4), Ptr, pNames := Object(Names))
   ObjRelease(pNames)
   
   if (Names.3 = "") && Text := idx ? "Иконка " . idx . " в файле " . source . " не найдена"
                                    : "Иконки в файле " . source . " не найдены"
      Return, 0, DllCall("MessageBox", Ptr, 0, Str, Text, Str, "", UInt, 0)
   
   Names.Remove(1, 2)
   for i, Name in Names
      Names[i] := DllCall("FindResource", Ptr, hModule, Name + 1 ? "UInt" : "Str", Name, UInt, RT_GROUP_ICON, Ptr)
   Return Names
}

EnumGroupIconProc(hModule, Type, Name, lp)
{
   (Name>>16) ? Name := LTrim(StrGet(Name+0), "#")
   obj := Object(lp)
   if !obj.1
      obj.Insert(Name)
   else if ((obj.2 += 1) = obj.1)
      Return, 0, obj.3 := Name
   Return 1
}

GetResData(hModule, hRes)
{
   hData := DllCall("LoadResource", Ptr, hModule, Ptr, hRes, Ptr)
   Return DllCall("LockResource", Ptr, hData, Ptr)
}

CreateDataArray(hModule, pIconGroup, ByRef CountIcons, ByRef offset, idx)
{
   if NumGet(pIconGroup+0, 2, "UShort") != 1 && Text := "Ресурс " Idx " неправильного типа. Возможно, файл сжат или зашифрован`nПродолжить?"
      Return DllCall("MessageBox", Ptr, 0, Str, Text, Str, "Неправильный ресурс", UInt, 4)
   
   DataArray := [], RT_ICON := 3
   CountIcons := NumGet(pIconGroup+0, 4, "UShort")
   offset := 6 + CountIcons*16
   
   Loop % CountIcons
   {
      id := NumGet(pIconGroup + 6 + 14*A_Index - 2, "UShort")
      hRes := DllCall("FindResource", Ptr, hModule, UInt, id, UInt, RT_ICON, Ptr)
      pIcon := GetResData(hModule, hRes)
      DataArray[A_Index] := {ptr: pIcon
                           , size: s := NumGet(pIconGroup + 6 + 14*A_Index - 6, "UInt")
                           , offset: offset}
      offset += s
   }
   Return DataArray
}

CreateDataBuff(DataArray, pIconGroup, CountIcons, size)
{
   static IconData
   VarSetCapacity(IconData, size)
   DllCall("RtlMoveMemory", Ptr, &IconData, Ptr, pIconGroup, Ptr, 6)

   offset := 6
   Loop % CountIcons
   {
      DllCall("RtlMoveMemory", Ptr, &IconData + offset, Ptr, pIconGroup + offset - 2*(A_Index - 1), Ptr, 12)
      NumPut(DataArray[A_Index].offset, &IconData + offset + 12, "UInt"), offset += 16
      DllCall("RtlMoveMemory", Ptr, &IconData + DataArray[A_Index].offset, Ptr, DataArray[A_Index].ptr, Ptr, DataArray[A_Index].size)
   }
   Return &IconData
}

Data2icoFile(pData, Dest, size, idx)
{
   static i = 0
   SplitPath, Dest,, Dir, Ext, OutNameNoExt
   if !FileExist(Dir)
   {
      FileCreateDir, % Dir
      if ErrorLevel && Text := "Невозможно создать папку " Dir "`nОшибка " A_LastError
         Return, 0, DllCall("MessageBox", Ptr, 0, Str, Text, Str, "", UInt, 0)
   }
   
   (!idx) ? Dest := (Ext ? SubStr(Dest, 1, -(StrLen(Ext) + 1)) : Dest) . "(" . ++i . ")" . (Ext ? "." . Ext : "")
   
   if !(File := FileOpen(Dest, "w")) && Text := "Невозможно открыть файл " Dest "на запись`nОшибка " A_LastError
      Return, 0, DllCall("MessageBox", Ptr, 0, Str, Text, Str, "", UInt, 0)
         
   File.Seek(0), File.RawWrite(pData+0, size), File.Close()
   Return 1
}

Пример использования:

GetIconFromResource(A_Desktop "\Icons\test.ico", "Shell32.dll", 0)        ; извлекаем все иконки из Shell32.dll
GetIconFromResource(A_Desktop "\Icons2\test.ico", "Shell32.dll", 45)      ; извлекаем иконку с порядковым индексом 45 (счёт от 1)
Return

Связанная тема

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Skype dmitry_fiveg