Last active
June 13, 2026 13:39
-
-
Save ynkdir/a543c492f70de26bf5580ce8c9a34bda to your computer and use it in GitHub Desktop.
enum resource
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # /// script | |
| # dependencies = ["win32more"] | |
| # /// | |
| import sys | |
| from ctypes import Structure, cast, memoryview_at, pointer, wstring_at | |
| from win32more import WINFUNCTYPE, Char, IntPtr, UInt16, WinError | |
| from win32more.Windows.Win32.Foundation import ( | |
| BOOL, | |
| HMODULE, | |
| PWSTR, | |
| TRUE, | |
| FreeLibrary, | |
| GetLastError, | |
| SetLastError, | |
| ) | |
| from win32more.Windows.Win32.Graphics.Gdi import DeleteObject | |
| from win32more.Windows.Win32.Media.KernelStreaming import RT_RCDATA, RT_STRING | |
| from win32more.Windows.Win32.Storage.FileSystem import VS_FIXEDFILEINFO | |
| from win32more.Windows.Win32.System.LibraryLoader import ( | |
| ENUMRESNAMEPROCW, | |
| ENUMRESTYPEPROCW, | |
| LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE, | |
| LOAD_LIBRARY_AS_IMAGE_RESOURCE, | |
| EnumResourceNamesExW, | |
| EnumResourceTypesExW, | |
| FindResourceExW, | |
| LoadLibraryExW, | |
| ) | |
| from win32more.Windows.Win32.System.LibraryLoader import LoadResource as _LoadResource | |
| from win32more.Windows.Win32.System.LibraryLoader import LockResource as _LockResource | |
| from win32more.Windows.Win32.System.LibraryLoader import ( | |
| SizeofResource as _SizeofResource, | |
| ) | |
| from win32more.Windows.Win32.UI.WindowsAndMessaging import ( | |
| IMAGE_BITMAP, | |
| IMAGE_CURSOR, | |
| IMAGE_ICON, | |
| LR_DEFAULTCOLOR, | |
| RT_ACCELERATOR, | |
| RT_ANICURSOR, | |
| RT_ANIICON, | |
| RT_BITMAP, | |
| RT_CURSOR, | |
| RT_DIALOG, | |
| RT_DLGINCLUDE, | |
| RT_FONT, | |
| RT_FONTDIR, | |
| RT_GROUP_CURSOR, | |
| RT_GROUP_ICON, | |
| RT_HTML, | |
| RT_ICON, | |
| RT_MANIFEST, | |
| RT_MENU, | |
| RT_MESSAGETABLE, | |
| RT_PLUGPLAY, | |
| RT_VERSION, | |
| RT_VXD, | |
| DestroyAcceleratorTable, | |
| DestroyCursor, | |
| DestroyIcon, | |
| DestroyMenu, | |
| LoadAcceleratorsW, | |
| LoadImageW, | |
| LoadMenuW, | |
| LoadStringW, | |
| ) | |
| # WORKAROUND: | |
| # ENUMRESTYPEPROCW is defined as | |
| # BOOL ENUMRESTYPEPROCW(HMODULE hModule, LPWSTR lpType, LONG_PTR lParam) | |
| # But lpType is actually LPWSTR or int. | |
| # Ctypes convert LPWSTR to str and it causes crash for int resource. | |
| ENUMRESTYPEPROCW_INTPTR = WINFUNCTYPE(BOOL, HMODULE, IntPtr, IntPtr) | |
| ENUMRESNAMEPROCW_INTPTR = WINFUNCTYPE(BOOL, HMODULE, IntPtr, IntPtr, IntPtr) | |
| # https://learn.microsoft.com/en-us/windows/win32/menurc/vs-versioninfo | |
| class VS_VERSIONINFO(Structure): | |
| _fields_ = [ | |
| ("wLength", UInt16), | |
| ("wValueLength", UInt16), | |
| ("wType", UInt16), | |
| ("szKey", Char * 16), | |
| ("Padding1", UInt16), | |
| ("Value", VS_FIXEDFILEINFO), | |
| ("Padding2", UInt16), | |
| ("Children", UInt16), | |
| ] | |
| def IS_INTRESOURCE(r): | |
| return r >> 16 == 0 | |
| def LoadLibraryResource(filename): | |
| hModule = LoadLibraryExW(filename, None, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LOAD_LIBRARY_AS_IMAGE_RESOURCE) | |
| if not hModule: | |
| raise WinError() | |
| return hModule | |
| def EnumResourceTypes(hModule, dwFlags=0, LangId=0): | |
| @ENUMRESTYPEPROCW_INTPTR | |
| def enumfunc(hModule, lpType, lParam): | |
| if not IS_INTRESOURCE(lpType): | |
| lpType = wstring_at(lpType) | |
| types.append(lpType) | |
| return TRUE | |
| types = [] | |
| lParam = 0 | |
| r = EnumResourceTypesExW(hModule, cast(enumfunc, ENUMRESTYPEPROCW), lParam, dwFlags, LangId) | |
| if not r: | |
| raise WinError() | |
| return types | |
| def EnumResourceNames(hModule, lpType, dwFlags=0, LangId=0): | |
| @ENUMRESNAMEPROCW_INTPTR | |
| def enumfunc(hModule, lpType, lpName, lParam): | |
| if not IS_INTRESOURCE(lpName): | |
| lpName = wstring_at(lpName) | |
| names.append(lpName) | |
| return TRUE | |
| names = [] | |
| lParam = 0 | |
| r = EnumResourceNamesExW(hModule, lpType, cast(enumfunc, ENUMRESNAMEPROCW), lParam, dwFlags, LangId) | |
| if not r: | |
| raise WinError() | |
| return names | |
| def FindResource(hModule, lpType, lpName, wLanguage=0): | |
| hResInfo = FindResourceExW(hModule, lpType, lpName, wLanguage) | |
| if not hResInfo: | |
| raise WinError() | |
| return hResInfo | |
| def SizeofResource(hModule, hResInfo): | |
| size = _SizeofResource(hModule, hResInfo) | |
| if size == 0: | |
| raise WinError() | |
| return size | |
| def LoadResource(hModule, hResInfo): | |
| hResData = _LoadResource(hModule, hResInfo) | |
| if not hResData: | |
| raise WinError() | |
| return hResData | |
| def LockResource(hResData): | |
| pResLock = _LockResource(hResData) | |
| if not pResLock: | |
| raise OSError("LockResource() failed") | |
| return pResLock | |
| def resource_data(hModule, lpType, lpName, wLanguage=0): | |
| hResInfo = FindResource(hModule, lpType, lpName, wLanguage) | |
| size = SizeofResource(hModule, hResInfo) | |
| hResData = LoadResource(hModule, hResInfo) | |
| pResLock = LockResource(hResData) | |
| return memoryview_at(pResLock, size, readonly=True).tobytes() | |
| def LoadAccelerators(hInstance, lpTableName): | |
| hAccel = LoadAcceleratorsW(hInstance, lpTableName) | |
| if not hAccel: | |
| raise WinError() | |
| return hAccel | |
| # RT_STRING resource is memory block which contains 16 strings. | |
| # Each strings are stored as <len:16bit> + <string>. | |
| def LoadStringBlock(hModule, lpName): | |
| block = [] | |
| for i in range(16): | |
| uID = ((lpName - 1) << 4) + i | |
| string = LoadString(hModule, uID) | |
| block.append((uID, string)) | |
| return block | |
| def LoadString(hInstance, uID): | |
| buf = IntPtr() | |
| SetLastError(0) | |
| r = LoadStringW(hInstance, uID, cast(pointer(buf), PWSTR), 0) | |
| if GetLastError() != 0: | |
| raise WinError() | |
| return wstring_at(buf.value, r) | |
| def LoadBitmap(hInstance, lpBitmapName): | |
| hBitmap = LoadImageW(hInstance, lpBitmapName, IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR) | |
| if not hBitmap: | |
| raise WinError() | |
| return hBitmap | |
| def LoadCursor(hInstance, lpCursorName): | |
| hCursor = LoadImageW(hInstance, lpCursorName, IMAGE_CURSOR, 0, 0, LR_DEFAULTCOLOR) | |
| if not hCursor: | |
| raise WinError() | |
| return hCursor | |
| def LoadIcon(hInstance, lpIconName): | |
| hIcon = LoadImageW(hInstance, lpIconName, IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR) | |
| if not hIcon: | |
| raise WinError() | |
| return hIcon | |
| def LoadMenu(hInstance, lpMenuName): | |
| hMenu = LoadMenuW(hInstance, lpMenuName) | |
| if not hMenu: | |
| raise WinError() | |
| return hMenu | |
| def main(): | |
| hModule = LoadLibraryResource(sys.argv[1]) | |
| for lpType in EnumResourceTypes(hModule): | |
| for lpName in EnumResourceNames(hModule, lpType): | |
| if lpType == RT_CURSOR: # 1 | |
| data = resource_data(hModule, lpType, lpName) | |
| print(f"RT_CURSOR: {lpName=}, {data[:20]}...") | |
| elif lpType == RT_BITMAP: # 2 | |
| hBitmap = LoadBitmap(hModule, lpName) | |
| print(f"RT_BITMAP: {lpName=}, {hBitmap=}") | |
| DeleteObject(hBitmap) | |
| elif lpType == RT_ICON: # 3 | |
| data = resource_data(hModule, lpType, lpName) | |
| print(f"RT_ICON: {lpName=}, {data[:20]}...") | |
| elif lpType == RT_MENU: # 4 | |
| hMenu = LoadMenu(hModule, lpName) | |
| print(f"RT_MENU: {lpName=}, {hMenu=}") | |
| DestroyMenu(hMenu) | |
| elif lpType == RT_DIALOG: # 5 | |
| data = resource_data(hModule, lpType, lpName) | |
| print(f"RT_DIALOG: {lpName=}, {data[:20]}...") | |
| elif lpType == RT_STRING: # 6 | |
| print(f"RT_STRING: {lpName=}") | |
| for uID, string in LoadStringBlock(hModule, lpName): | |
| print(f" {uID=}, {string=}") | |
| elif lpType == RT_FONTDIR: # 7 | |
| data = resource_data(hModule, lpType, lpName) | |
| print(f"RT_FONTDIR: {lpName=}, {data[:20]}...") | |
| elif lpType == RT_FONT: # 8 | |
| data = resource_data(hModule, lpType, lpName) | |
| print(f"RT_FONT: {lpName=}, {data[:20]}...") | |
| elif lpType == RT_ACCELERATOR: # 9 | |
| hAccel = LoadAccelerators(hModule, lpName) | |
| print(f"RT_ACCELERATOR: {lpName=}, {hAccel=}") | |
| DestroyAcceleratorTable(hAccel) | |
| elif lpType == RT_RCDATA: # 8 | |
| data = resource_data(hModule, lpType, lpName) | |
| print(f"RT_RCDATA: {lpName=}, {data[:20]}...") | |
| elif lpType == RT_MESSAGETABLE: # 8 | |
| data = resource_data(hModule, lpType, lpName) | |
| print(f"RT_MESSAGETABLE: {lpName=}, {data[:20]}...") | |
| elif lpType == RT_GROUP_CURSOR: # 12 | |
| hCursor = LoadCursor(hModule, lpName) | |
| print(f"RT_GROUP_CURSOR: {lpName=}, {hCursor=}") | |
| DestroyCursor(hCursor) | |
| elif lpType == RT_GROUP_ICON: # 14 | |
| hIcon = LoadIcon(hModule, lpName) | |
| print(f"RT_GROUP_ICON: {lpName=}, {hIcon=}") | |
| DestroyIcon(hIcon) | |
| elif lpType == RT_VERSION: # 16 | |
| data = resource_data(hModule, lpType, lpName) | |
| version_info = VS_VERSIONINFO.from_buffer_copy(data) | |
| print(f"RT_VERSION: {lpName=}, {data[:20]=}...") | |
| print(" VS_VERSIONINFO(") | |
| print(f" wLength = {version_info.wLength}") | |
| print(f" wValueLength = {version_info.wValueLength}") | |
| print(f" wType = {version_info.wType}") | |
| print(f" szKey = {version_info.szKey}") | |
| print(f" Padding1 = {version_info.Padding1}") | |
| print(" Value = VS_FIXEDFILEINFO(") | |
| print(f" dwSignature = {version_info.Value.dwSignature}") | |
| print(f" dwStrucVersion = {version_info.Value.dwStrucVersion}") | |
| print(f" dwFileVersionMS = {version_info.Value.dwFileVersionMS}") | |
| print(f" dwFileVersionLS = {version_info.Value.dwFileVersionLS}") | |
| print(f" dwProductVersionMS = {version_info.Value.dwProductVersionMS}") | |
| print(f" dwProductVersionLS = {version_info.Value.dwProductVersionLS}") | |
| print(f" dwFileFlagsMask = {version_info.Value.dwFileFlagsMask}") | |
| print(f" dwFileFlags = {version_info.Value.dwFileFlags}") | |
| print(f" dwFileOS = {version_info.Value.dwFileOS}") | |
| print(f" dwFileType = {version_info.Value.dwFileType}") | |
| print(f" dwFileSubtype = {version_info.Value.dwFileSubtype}") | |
| print(f" dwFileDateMS = {version_info.Value.dwFileDateMS}") | |
| print(f" dwFileDateLS = {version_info.Value.dwFileDateLS}") | |
| print(" )") | |
| print(f" Padding2 = {version_info.Padding2}") | |
| print(f" Children = {version_info.Children}") | |
| print(" )") | |
| elif lpType == RT_DLGINCLUDE: # 17 | |
| data = resource_data(hModule, lpType, lpName) | |
| print(f"RT_DLGINCLUDE: {lpName=}, {data[:20]}...") | |
| elif lpType == RT_PLUGPLAY: # 19 | |
| data = resource_data(hModule, lpType, lpName) | |
| print(f"RT_PLUGPLAY: {lpName=}, {data[:20]}...") | |
| elif lpType == RT_VXD: # 20 | |
| data = resource_data(hModule, lpType, lpName) | |
| print(f"RT_VXD: {lpName=}, {data[:20]}...") | |
| elif lpType == RT_ANICURSOR: # 21 | |
| data = resource_data(hModule, lpType, lpName) | |
| print(f"RT_ANICURSOR: {lpName=}, {data[:20]}...") | |
| elif lpType == RT_ANIICON: # 22 | |
| data = resource_data(hModule, lpType, lpName) | |
| print(f"RT_ANIICON: {lpName=}, {data[:20]}...") | |
| elif lpType == RT_HTML: # 23 | |
| data = resource_data(hModule, lpType, lpName) | |
| print(f"RT_HTML: {lpName=}, {data[:20]}...") | |
| elif lpType == RT_MANIFEST: # 24 | |
| data = resource_data(hModule, lpType, lpName) | |
| print(f"RT_MANIFEST: {lpName=}, {data=}") | |
| else: | |
| assert isinstance(lpType, str) | |
| data = resource_data(hModule, lpType, lpName) | |
| print(f"{lpType}: {lpName=}, {data[:20]}...") | |
| FreeLibrary(hModule) | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment