On Thu, May 5, 2016 at 3:47 PM, <[email protected]> wrote: > > I try to make the GetIconInfo function work, but I can't figure out > what I'm doing wrong. > > From the MSDN documentation the function is > > https://msdn.microsoft.com/en-us/library/windows/desktop/ms648070%28v=vs.85%29.aspx > > # BOOL WINAPI GetIconInfo( > # _In_ HICON hIcon, > # _Out_ PICONINFO piconinfo > # ); > > which I defined as > > GetIconInfo = windll.user32.GetIconInfo > GetIconInfo.argtypes = [HICON, POINTER(ICONINFO)] > GetIconInfo.restype = BOOL > GetIconInfo.errcheck = ErrorIfZero
Please avoid windll. It caches the loaded library, which in turn caches function pointers. So all packages that use windll.user32 are potentially stepping on each others' toes with mutually incompatible function prototypes. It also doesn't allow configuring use_last_error=True to enable ctypes.get_last_error() for WinAPI function calls. > The structure piconinfo is described as > https://msdn.microsoft.com/en-us/library/windows/desktop/ms648052%28v=vs.85%29.aspx > > # typedef struct _ICONINFO { > # BOOL fIcon; > # DWORD xHotspot; > # DWORD yHotspot; > # HBITMAP hbmMask; > # HBITMAP hbmColor; > # } ICONINFO, *PICONINFO; > > my implementation is > > class ICONINFO(Structure): > __fields__ = [ > ('fIcon', BOOL), > ('xHotspot', DWORD), > ('yHotspot', DWORD), > ('hbmMask', HBITMAP), > ('hbmColor', HBITMAP), > ] The attribute name is "_fields_", not "__fields__", so you haven't actually defined any fields and sizeof(ICONINFO) is 0. When you pass this empty struct to GetIconInfo, it potentially overwrites and corrupts existing data on the heap that can lead to a crash later on. Here's the setup I created to test GetIconInfo and GetIconInfoEx. Maybe you can reuse some of this code, but if you're using XP this won't work as written because GetIconInfoEx was added in Vista. Note the use of a __del__ finalizer to call DeleteObject on the bitmaps. Otherwise, in a real application, calling GetIconInfo would leak memory. Using __del__ is convenient, but note that you can't reuse an instance without manually calling DeleteObject on the bitmaps. import ctypes from ctypes import wintypes kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) user32 = ctypes.WinDLL('user32', use_last_error=True) gdi32 = ctypes.WinDLL('gdi32') MAX_PATH = 260 IMAGE_ICON = 1 class ICONINFO_BASE(ctypes.Structure): def __del__(self, gdi32=gdi32): if self.hbmMask: gdi32.DeleteObject(self.hbmMask) self.hbmMask = None if self.hbmColor: gdi32.DeleteObject(self.hbmColor) self.hbmColor = None class ICONINFO(ICONINFO_BASE): _fields_ = (('fIcon', wintypes.BOOL), ('xHotspot', wintypes.DWORD), ('yHotspot', wintypes.DWORD), ('hbmMask', wintypes.HBITMAP), ('hbmColor', wintypes.HBITMAP)) class ICONINFOEX(ICONINFO_BASE): _fields_ = (('cbSize', wintypes.DWORD), ('fIcon', wintypes.BOOL), ('xHotspot', wintypes.DWORD), ('yHotspot', wintypes.DWORD), ('hbmMask', wintypes.HBITMAP), ('hbmColor', wintypes.HBITMAP), ('wResID', wintypes.WORD), ('szModName', wintypes.WCHAR * MAX_PATH), ('szResName', wintypes.WCHAR * MAX_PATH)) def __init__(self, *args, **kwds): super(ICONINFOEX, self).__init__(*args, **kwds) self.cbSize = ctypes.sizeof(self) PICONINFO = ctypes.POINTER(ICONINFO) PICONINFOEX = ctypes.POINTER(ICONINFOEX) def check_bool(result, func, args): if not result: raise ctypes.WinError(ctypes.get_last_error()) return args kernel32.GetModuleHandleW.errcheck = check_bool kernel32.GetModuleHandleW.restype = wintypes.HMODULE kernel32.GetModuleHandleW.argtypes = ( wintypes.LPCWSTR,) # _In_opt_ lpModuleName # DeleteObject doesn't call SetLastError gdi32.DeleteObject.restype = wintypes.BOOL gdi32.DeleteObject.argtypes = ( wintypes.HGDIOBJ,) # _In_ hObject user32.LoadImageW.errcheck = check_bool user32.LoadImageW.restype = wintypes.HANDLE user32.LoadImageW.argtypes = ( wintypes.HINSTANCE, # _In_opt_ hinst wintypes.LPCWSTR, # _In_ lpszName wintypes.UINT, # _In_ uType ctypes.c_int, # _In_ cxDesired ctypes.c_int, # _In_ cyDesired wintypes.UINT,) # _In_ fuLoad user32.DestroyIcon.errcheck = check_bool user32.DestroyIcon.restype = wintypes.BOOL user32.DestroyIcon.argtypes = ( wintypes.HICON,) # _In_ hIcon user32.GetIconInfo.errcheck = check_bool user32.GetIconInfo.restype = wintypes.BOOL user32.GetIconInfo.argtypes = ( wintypes.HICON, # _In_ hIcon PICONINFO,) # _Out_ piconinfo # requires Vista+ user32.GetIconInfoExW.errcheck = check_bool user32.GetIconInfoExW.restype = wintypes.BOOL user32.GetIconInfoExW.argtypes = ( wintypes.HICON, # _In_ hIcon PICONINFOEX,) # _Out_ piconinfoex if __name__ == '__main__': hMain = kernel32.GetModuleHandleW(None) hIcon = user32.LoadImageW(hMain, wintypes.LPCWSTR(1), IMAGE_ICON, 0, 0, 0) try: info = ICONINFOEX() user32.GetIconInfoExW(hIcon, ctypes.byref(info)) print('fIcon : %d' % info.fIcon) print('wResID : %d' % info.wResID) print('szModName: %s' % info.szModName) finally: user32.DestroyIcon(hIcon) The __main__ test outputs the following for me in 3.5 and 2.7: fIcon : 1 wResID : 1 szModName: C:\Program Files\Python35\python.exe fIcon : 1 wResID : 1 szModName: C:\Program Files\Python27\python.exe -- https://mail.python.org/mailman/listinfo/python-list
