python, ctypes and GetIconInfo issue

eryk sun eryksun at gmail.com
Fri May 6 19:38:49 EDT 2016


On Fri, May 6, 2016 at 8:36 AM,  <mymyxin at gmail.com> wrote:
>
>> 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.
> I assume you are referring to this block of code
>
>     GetIconInfo = windll.user32.GetIconInfo
>     GetIconInfo.argtypes   = [HICON, POINTER(ICONINFO)]
>     GetIconInfo.restype    = BOOL
>     GetIconInfo.errcheck   = ErrorIfZero
>
> where as you use
>
>     user32 = ctypes.WinDLL('user32', use_last_error=True)
>     user32.GetIconInfoExW.errcheck = check_bool
>     user32.GetIconInfoExW.restype = wintypes.BOOL
>     user32.GetIconInfoExW.argtypes = (
>         wintypes.HICON, # _In_  hIcon
>         PICONINFOEX,)   # _Out_ piconinfoex
>
> I've checked ctype docu included in python but don't find any hint about your concerns.
> May I ask you, do you know additional documents/sites which I can use to get a better
> understanding about caching issue? Or did I miss something from used documentation?

You haven't missed anything in the documentation. The ctypes docs need
work, and some of the examples are bad, if not wrong. For example, the
GetModuleHandleA examples incorrectly handle the pointer result
because they were never updated for 64-bit Windows. One can't use a
Python function as the restype with a C function that returns a
pointer because it will be truncated to a C int. Whoever wrote the
GetModuleHandleA examples either doesn't know how this feature is
implemented in ctypes (probably not, since I think Thomas Heller wrote
the example), or doesn't know that a Windows HMODULE is a pointer to
the module's base address, or was just writing sloppy code in the era
of 32-bit Windows.

In this case, look at the CDLL [1] and LibraryLoader [2] classes. Note
how CDLL.__getattr__ caches function pointers using setattr(self,
name, func). Note how LibraryLoader.__getattr__  caches libraries
using setattr(self, name, dll), and how it instantiates the library
using self._dlltype(name), with no way to specify use_last_error=True.
This has caused real problems for projects such as colorama (fixed)
and pyreadline (still broken), and I've seen potential problems in
several other projects that naively copy the cdll and windll examples
from the docs. It's not their fault. The docs are just bad on this
subject.

[1]: https://hg.python.org/cpython/file/v3.5.1/Lib/ctypes/__init__.py#l314
[2]: https://hg.python.org/cpython/file/v3.5.1/Lib/ctypes/__init__.py#l410

>> Using __del__ is convenient, but note that you can't
>> reuse an instance without manually calling DeleteObject on the
>> bitmaps.
>
> Don't understand this. Isn't this covered by your example in base class?

I'm talking about reusing an instance, to avoid the cost of repeated
allocation and deallocation. For example:

    info = ICONINFOEX()
    for hIcon in hIcons:
        user32.GetIconInfoExW(hIcon, ctypes.byref(info))
        print('fIcon    : %d' % info.fIcon)
        print('wResID   : %d' % info.wResID)
        print('szModName: %s' % info.szModName)
        gdi32.DeleteObject(info.hbmMask)
        gdi32.DeleteObject(info.hbmColor)



More information about the Python-list mailing list