如何在Python中从EXE提取128x128图标位图数据
我正在尝试使用win32gui从Windows的.exe文件中提取图标。我发现了两个功能:ExtractIconEx()和ExtractIcon()。
通过这两个功能,我只能获取到32x32或16x16大小的图标。下面这个链接只提供了提取32x32图标的方法。如何从EXE中提取32x32的图标位图数据并将其转换为PIL图像对象?
我需要提取128x128或更大尺寸的图标。有没有什么方法可以从.exe文件中提取更大尺寸的图标呢?
2 个回答
我想提取默认的图标和不同的尺寸。根据Alexei和Audionautics在32x32讨论中的回答,这里是代码。
# Use wchar_t function version (FindResourceW rather than FindResourceA)
from __future__ import unicode_literals
# pywin32 imports
import win32con
import win32api
import win32file
import win32gui
import win32ui
import pywintypes
# ctypes configuring. pywin32 has no a lot of required functions
import ctypes
import ctypes.util
# memcpy used to copy data from resource storage to our buffer
libc = ctypes.CDLL(ctypes.util.find_library('c'))
libc.memcpy.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t]
libc.memcpy.restype = ctypes.c_char_p
# patch FindResourceW, ctypes.windll.kernel32.SizeofResource
FindResourceW = ctypes.windll.kernel32.FindResourceW
FindResourceW.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
FindResourceW.restype = ctypes.c_void_p
SizeofResource = ctypes.windll.kernel32.SizeofResource
SizeofResource.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
SizeofResource.restype = ctypes.c_size_t
# Using LoadLibrary (rather than CreateFile) is required otherwise
# LoadResource, FindResource and others will fail
PATH = "C:\\Program Files\\Internet Explorer\\iexplore.exe"
hlib = win32api.LoadLibraryEx(PATH, 0, 2)
# get icon groups, default is the first group
icon_groups = win32api.EnumResourceNames(hlib, win32con.RT_GROUP_ICON)
group_name = icon_groups[0]
print group_name
hRes = win32api.LoadResource(hlib, win32con.RT_GROUP_ICON, group_name)
mem_icon_dir = ctypes.windll.kernel32.LockResource(hRes)
# 32 bits color; 16 and 256 colors are too old
# iterate through the common sizes
icon_sizes = (16, 24, 32, 48, 96, 256)
for icon_size in icon_sizes:
icon_name = ctypes.windll.user32.LookupIconIdFromDirectoryEx(mem_icon_dir, True, icon_size, icon_size, 0x00000000);
hResInfo = FindResourceW(hlib, icon_name, win32con.RT_ICON)
size = ctypes.windll.kernel32.SizeofResource(hlib, hResInfo)
rec = win32api.LoadResource(hlib, win32con.RT_ICON, icon_name)
mem_icon = ctypes.windll.kernel32.LockResource(rec)
# And this is some differ (copy data to Python buffer)
binary_data = (ctypes.c_ubyte * size)()
libc.memcpy(binary_data, mem_icon, size)
hIconRet = ctypes.windll.user32.CreateIconFromResourceEx(binary_data, size, True, 0x00030000, 0, 0, 0x00000000);
info = win32gui.GetIconInfo(hIconRet)
bminfo = win32gui.GetObject(info[4])
# generate bitmap by drawing the icon
hdc = win32ui.CreateDCFromHandle(win32gui.GetDC(0))
hbmp = win32ui.CreateBitmap()
hbmp.CreateCompatibleBitmap(hdc, bminfo.bmWidth, bminfo.bmHeight)
hcdc = hdc.CreateCompatibleDC()
hcdc.SelectObject(hbmp)
win32gui.DrawIconEx(hcdc.GetHandleOutput(), 0, 0, hIconRet, bminfo.bmWidth, bminfo.bmHeight, 0, 0, 0x0003)
hbmp.SaveBitmapFile(hcdc, "icon-%03dx%03d-%05d-%03d.bmp" % (bminfo.bmWidth, bminfo.bmHeight, group_name, icon_name))
win32gui.DestroyIcon(hIconRet)
我做了一些研究并且发布了相关内容。如果你只想看看结果代码(我希望这正是你想要的),可以在下面的“水平线”之后找到它。
首先,我尝试使用下面的代码来确定文件中存储了哪些图标大小:
# Using LoadLibrary (rather than CreateFile) is required otherwise
# LoadResource, FindResource and others will fail
PATH = ... # Valid file path
hlib = win32api.LoadLibrary(PATH)
# This loop should print sizes of resources icons
icon_names = win32api.EnumResourceNames(hlib, win32con.RT_ICON)
for icon_name in icon_names:
rec = win32api.LoadResource(hlib, win32con.RT_ICON, icon_name)
hicon = win32gui.CreateIconFromResource(rec, True)
info = win32gui.GetIconInfo(hicon)
bminfo = win32gui.GetObject(info[3])
print("%2d: 0x%08X -> %d %d " % (icon_name, hicon, bminfo.bmWidth, bminfo.bmHeight))
当文件中只包含16x16和32x32像素的图标时,一切都很好,这里是Windows XP计算器的输出:
1: 0x0093051B -> 32 32
2: 0x005B0513 -> 32 32
3: 0x007004CB -> 32 32
4: 0x002E04C9 -> 32 32
5: 0x033A04C5 -> 32 32
6: 0x00780487 -> 32 32
7: 0x0052045D -> 32 32
8: 0x055D053D -> 32 32
但当我尝试在一个包含大图标的文件时,我遇到了异常:
Traceback (most recent call last):
File "extract_icon.py", line 50, in <module>
hicon = win32gui.CreateIconFromResource(rec, True)
pywintypes.error: (0, 'CreateIconFromResource', 'No error message is available')
经过一些研究,我发现大图标并不是以ico
格式存储的,而是以png
格式存储的(在我的情况下)。
当然,我不知道你的.exe
文件具体是什么样的(它的内部结构),但在分析了我电脑上的几个.exe
文件后,我发现大于32x32或16x16像素的图标很可能是以.png
文件的形式存在的(你可以使用例如PE Explorer来检查,试用版是有的)。
所以为了从资源中读取图像,我使用了这篇关于C++的指南。这里的主要目标是获取指向图像资源真实数据的指针,并将其复制到Python的缓冲区。最后一步是将其保存到文件中(我想你可以自己把它转换成PIL格式)。
读取大资源的完整代码:
# Use wchar_t function version (FindResourceW rather than FindResourceA)
from __future__ import unicode_literals
# pywin32 imports
import pywintypes
import win32ui
import win32gui
import win32con
import win32api
import win32file
# ctypes configuring. pywin32 has no a lot of required functions
import ctypes
import ctypes.util
# memcpy used to copy data from resource storage to our buffer
libc = ctypes.CDLL(ctypes.util.find_library('c'))
libc.memcpy.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t]
libc.memcpy.restype = ctypes.c_char_p
# All Windows backslashes must be escaped to LoadLibrary worked correctly '\' -> '\\'
PATH = ...
# WARNING: Assumed that icon_name - VALID resource ID
# It can be determined in loop when enumerating resources:
# if exception at CreateIconFromResource raised than this code appropriate
# otherwise resource is standard icon and first code snippet can be used.
# If resources Id exactly known then it can be hardcoded as in this code
icon_name = 1
try:
hlib = win32api.LoadLibrary(PATH)
# This part almost identical to C++
hResInfo = ctypes.windll.kernel32.FindResourceW(hlib, icon_name, win32con.RT_ICON)
size = ctypes.windll.kernel32.SizeofResource(hlib, hResInfo)
rec = win32api.LoadResource(hlib, win32con.RT_ICON, icon_name)
mem_pointer = ctypes.windll.kernel32.LockResource(rec)
# And this is some differ (copy data to Python buffer)
binary_data = (ctypes.c_ubyte * size)()
libc.memcpy(binary_data, mem_pointer, size)
# Save it
with open("icon.png", "wb") as test_file:
test_file.write(bytearray(binary_data))
except pywintypes.error as error:
print "ERROR: %s" % error.strerror
raise
更新:
自动查找非图标资源并提取到名为“Resource_XX”的文件中的代码:
# Same IMPORT's as previously should be used
# All Windows backslashes must be escaped to LoadLibrary worked correctly '\' -> '\\'
PATH = ...
def extract(rec):
try:
hicon = win32gui.CreateIconFromResource(rec, True)
except pywintypes.error as error:
# Check on appropriate error
if error.winerror != 6:
raise
print("Resource %2d isn't .ico, extract" % icon_name)
# This part almost identical to C++
hResInfo = ctypes.windll.kernel32.FindResourceW(hlib, icon_name, win32con.RT_ICON)
size = ctypes.windll.kernel32.SizeofResource(hlib, hResInfo)
mem_pointer = ctypes.windll.kernel32.LockResource(rec)
# And this is some differ (copy data to Python buffer)
binary_data = (ctypes.c_ubyte * size)()
libc.memcpy(binary_data, mem_pointer, size)
# Save it
with open("Resource_%s.png" % icon_name, "wb") as extract_file:
extract_file.write(bytearray(binary_data))
else:
info = win32gui.GetIconInfo(hicon)
bminfo = win32gui.GetObject(info[3])
print("Resource %2d is .ico: 0x%08X -> %d %d " %
(icon_name, hicon, bminfo.bmWidth, bminfo.bmHeight))
try:
hlib = win32api.LoadLibrary(PATH)
icon_names = win32api.EnumResourceNames(hlib, win32con.RT_ICON)
for icon_name in icon_names:
rec = win32api.LoadResource(hlib, win32con.RT_ICON, icon_name)
extract(rec)
except pywintypes.error as error:
print "ERROR: %s" % error.strerror
raise