On Mon, May 7, 2018 at 9:57 AM, Michael C <mysecretrobotfact...@gmail.com> wrote: > Sorry I don't understand your suggestion. > > Use "ctypes.CDLL" and "ctypes.WinDLL" > > this works, > mydll = cdll.LoadLibrary('test.dll') > print(mydll.sum(3,2)) > > and this doens't > mydll = cdll('test.dll') > print(mydll.sum(3,2)) > > What's the syntax of what you suggested?
Loading the library is simply `mydll = ctypes.CDLL('test')`. Note that CDLL is uppercase. Python is a case-sensitive language. The base shared library class is CDLL. The constructor takes the following parameters: name, mode=DEFAULT_MODE, handle=None, use_errno=False, and use_last_error=False. For the first 3 parameters, if the shared library `handle` isn't provided, it calls ctypes._dlopen(mode, name) to load the library. The CDLL constructor dynamically defines a function-pointer subclass, with custom _flags_ and _restype_. The _restype_ is c_int (i.e. a 32-bit C integer). The base _flags_ value is _FUNCFLAG_CDECL because CDLL uses the cdecl calling convention. For the last two constructor parameters, use_errno includes _FUNCFLAG_USE_ERRNO, and use_last_error includes _FUNCFLAG_USE_LASTERROR. The above function pointer class is used by the __getitem__ method of a CDLL instance, which calls self._FuncPtr((name_or_ordinal, self)) to return a function pointer instance, e.g. mydll['sum']. (Using a subscript also allows ordinal look-up, such as mydll[42].) The function pointer is not cached, so it has to be instantiated ever time it's accessed this way. On the other hand, the __getattr__ method of CDLL calls __getitem__ and caches the function pointer via setattr(self, name, func). For example, with mydll.sum, the `sum` function pointer is cached. WinDLL subclasses CDLL to change the default function pointer _flags_ value to _FUNCFLAG_STDCALL because the Windows API uses the stdcall calling convention, as do many shared libraries on Windows. ctypes also defines a LibraryLoader class, of which ctypes.cdll and ctypes.windll are instances, i.e. cdll = LibraryLoader(CDLL). The constructor simply sets the shared library class as the attribute self._dlltype. In turn, the LoadLibrary method returns self._dlltype(name). This is silly because it's extra typing, an extra call, and prevents passing constructor arguments (e.g. use_last_error). Where this goes from silly to smelly is the __getattr__ method of LibraryLoader. This method makes the mistake of caching loaded libraries. For example, ctypes.windll.kernel32 caches a WinDLL('kernel32') instance on ctypes.windll. Remember that __getattr__ is a fallback method called by __getattribute__. Thus subsequent references to ctypes.windll.kernel32 will use the cached library instance. This is bad because multiple packages will end up sharing the same library instance. Recall that the __getattr__ method of CDLL caches function pointers. That's a good thing because function pointer look-up and instantiation is relatively expensive. I can understand the good intentions of carrying this over to caching entire library instances across multiple packages. But the savings is not worth the ensuing chaos of super-global cached state, especially for commonly used Windows libraries such as kernel32.dll. The problem is that every function pointer defines a prototype that consists of its errcheck, restype, and argtypes properties, but not every package or script will define these exactly the same, for various reasons. Thus the last one to define the prototype wins. All other users of the library in the process will be broken. In short, avoid ctypes.[cdll | windll].LoadLibrary because it's silly, and avoid ctypes.[cdll | windll].[library name] because it's smelly. _______________________________________________ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor