Path: csiph.com!fu-berlin.de!uni-berlin.de!not-for-mail From: eryk sun Newsgroups: comp.lang.python Subject: Re: python, ctypes and GetIconInfo issue Date: Thu, 5 May 2016 19:09:21 -0500 Lines: 175 Message-ID: References: Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 X-Trace: news.uni-berlin.de tefLHK+qdpdnFrGZt92zLw+Usf10hJVnoIFSKYjPnUeA== Return-Path: X-Original-To: python-list@python.org Delivered-To: python-list@mail.python.org X-Spam-Status: OK 0.000 X-Spam-Evidence: '*H*': 1.00; '*S*': 0.00; 'received:209.85.223': 0.03; 'args': 0.04; 'finally:': 0.05; 'memory.': 0.05; '*args,': 0.07; '__name__': 0.07; 'calls.': 0.07; 'incompatible': 0.07; 'url:msdn': 0.07; "%s'": 0.09; 'bool': 0.09; 'subject:ctypes': 0.09; 'typedef': 0.09; 'def': 0.13; 'subject:python': 0.14; 'thu,': 0.15; "'__main__':": 0.16; '2.7:': 0.16; '2016': 0.16; '__del__': 0.16; 'caches': 0.16; 'configuring': 0.16; 'dword': 0.16; 'heap': 0.16; "others'": 0.16; 'outputs': 0.16; 'pointers.': 0.16; 'prototypes.': 0.16; 'received:io': 0.16; 'received:psf.io': 0.16; 'result:': 0.16; 'stepping': 0.16; 'subject:issue': 0.16; 'url:85)': 0.16; 'vista.': 0.16; 'wrote:': 0.16; 'later': 0.16; 'attribute': 0.18; 'library,': 0.18; 'skip:u 30': 0.18; 'try:': 0.18; 'cc:2**0': 0.20; 'otherwise,': 0.20; 'work,': 0.21; 'ctypes': 0.22; 'struct': 0.22; 'pass': 0.22; 'cc:no real name:2**0': 0.22; 'code,': 0.23; 'defined': 0.23; "haven't": 0.24; 'import': 0.24; 'cc:addr:gmail.com': 0.24; 'written': 0.24; 'header:In-Reply-To:1': 0.24; "doesn't": 0.26; 'figure': 0.27; 'message-id:@mail.gmail.com': 0.27; 'function': 0.28; 'skip:u 20': 0.28; 'crash': 0.29; 'skip:k 30': 0.29; 'raise': 0.29; "i'm": 0.30; 'skip:g 30': 0.30; "can't": 0.32; 'skip:_ 10': 0.32; 'maybe': 0.33; 'class': 0.33; 'structure': 0.34; 'info': 0.34; 'received:google.com': 0.35; 'instance': 0.35; 'but': 0.36; 'skip:i 20': 0.36; 'created': 0.36; 'received:209.85': 0.36; 'loaded': 0.36; 'url:library': 0.36; 'to:addr:python-list': 0.36; 'pm,': 0.36; 'subject:: ': 0.37; 'turn': 0.37; 'doing': 0.38; "won't": 0.38; 'received:209': 0.38; 'test': 0.39; 'data': 0.39; 'to:addr:python.org': 0.40; 'some': 0.40; 'skip:u 10': 0.61; 'avoid': 0.61; 'real': 0.62; 'reuse': 0.66; 'url:%28': 0.66; 'url:%29': 0.66; 'potentially': 0.67; 'url:%1': 0.67; 'url:en-us': 0.72; '260': 0.84; 'leak': 0.91 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:from:date:message-id:subject:to :cc; bh=CYUxXDi6AWBsJbYGRL0eFLIHWESOWhmJk4woNsizAJQ=; b=A03i+ZuPhDbU1MSUS/t7fo4rPm4K1QRuDRZCoi5pu0jykdAhOx62zJJ94oiwts0LK0 f1K8+IrRA86VuBLR6iNo9KZnxb5xaL6P+BbWe82atCOIdIH3O9QmNXudLveyD8wgzWyM MZ8uq7XmXKhoqUspGC71IrX7JVdWYzG3NA90kU5xPIG9THuoORUCRiyMt8eQzKDUJqL5 ux6yvPFoZBZszpr+eaw38CFDxYaH+IpSUveIeR9qQl629HN74N0y4b36gsLO9q4QvIR2 0L3huP+QtcLlxjLF9NoBVbzG9dUHjmTD3JihcQEqCSrE9MJjFKofY8CCy2LfWhASDrSJ +bgg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to:cc; bh=CYUxXDi6AWBsJbYGRL0eFLIHWESOWhmJk4woNsizAJQ=; b=k3baeCavJx/isajgsSNYdeZqlo+fc5z0bdrE3VM7Ak2mEYDPaxkXq0GNqjxwwgPWib E8WHMBQY8ZlFj8prmy2jdFnJCdrrYD//nxUBXhyaSGjIXdLrXU61DgyboNvlhmqGwmpU w5v2qnnk3yEyd/IunVI5KANJ6cC3QxA1cwXNV2pP2yhqlaqDcIX6o7wb00ZOPWb7RDIJ EU9hgGHmgG5E4LAGVl1zLje6lmdloRKSZM5aYLN3v/M+Olxh5rVHdy2cfAOXs5PBJo/b S4DjXNHVlhXvtCUpj0i6s5G6eeOX45YqG+pnC/1iNhfB9CpDNoJVmA61YjElMG3cqG2g kzhg== X-Gm-Message-State: AOPr4FXXz7/0lzIGmqwVSxvLWf3yGxf8h4JYy+l+OBR6fXrzE+8MyFR6cLOD4vIjy339tpreCgPlI3f6TIaK2g== X-Received: by 10.107.14.209 with SMTP id 200mr20165370ioo.73.1462493401495; Thu, 05 May 2016 17:10:01 -0700 (PDT) In-Reply-To: X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: General discussion list for the Python programming language List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Mailman-Original-Message-ID: X-Mailman-Original-References: Xref: csiph.com comp.lang.python:108203 On Thu, May 5, 2016 at 3:47 PM, 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