如何在Python中从EXE文件中提取128x128图标位图数据

9
我正在尝试使用win32gui从Windows的.exe文件中提取图标。我找到了ExtractIconEx()和ExtractIcon()功能。 我只能从上述功能中获取32x32或16x16大小的图标。以下链接仅回答了提取32x32图像的方法。 如何从EXE中提取32x32图标位图数据并将其转换为PIL图像对象? 我需要提取大小为128x128或更大的图标。有没有关于如何从exe文件中提取更大尺寸图标的想法?
2个回答

13
我已经做了一些研究并发布了它。如果您只想查看结果代码(我希望这正是您要求的),您可以在下面的“水平线”后找到它。
首先,我尝试使用下一个代码来确定文件资源中存储的图标大小:
# 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++的guide。这里的主要目标是获取指向图像资源实际数据的指针,并将其复制到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

我尝试了逐个设置代码:PATH = "C:\Program Files\VideoLAN\VLC\vlc.exe"它返回空图像,我尝试了多个不同的文件。我有什么遗漏吗? - sj7
我已经在http://www.splashtop.com/上尝试过,对我有效。是否有任何异常或仅为空文件(可能需要转义反斜杠)?您确定定义了有效的资源ID(icon_name)吗?我现在在VLC上尝试。我会尽快更新答案。 - mblw
@sj7 我已经测试过了,它在我的 VLC 上可以工作:http://get.videolan.org/vlc/2.1.3/win32/vlc-2.1.3-win32.zip。首先,我需要转义反斜杠(否则 LoadLibrary 将无法工作),第二个正确的资源(png 图标)的 ID 为 3。PATH = "c:\\vlc-2.1.3\\vlc.exe"icon_name = 3。这有帮助吗? - mblw
我已经添加了try-except块,但它仅用于非英语环境的调试(否则字符串错误无法读取)。 - mblw

1

我想提取默认图标和不同大小的图标。基于Alexei在32x32线程中的回答和Audionautics的回答,这里是代码。

# 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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接