Recipe17.4.Calling Functions from a Windows DLL


Recipe 17.4. Calling Functions from a Windows DLL

Credit: Stefano Spinucci

Problem

You want to avoid writing a Python extension in C, by directly calling from Python functions that already exist in a Windows DLL.

Solution

The third-party ctypes extension makes this task pretty easy:

from ctypes import windll, c_int, c_string, byref # load 'Ehllapi.dll' (from current dir), and function 'hllapi' from the DLL Ehllap32 = windll.ehllapi hllapi = Ehllap32.hllapi # prepare the arguments with types and initial values h_func = c_int(1) h_text = c_string('A') h_len = c_int(1) h_ret = c_int(999) # call the function hllapi(byref(h_func), h_text, byref(h_len), byref(h_ret)) # print the resulting values of all arguments after the call print h_func.value, h_text.value, h_len.value, h_ret.value

Discussion

I needed the code in this recipe specifically to call a C function whose prototype is:

void FAR PASCAL hllapi(int FAR *, char FAR *, int FAR *, int FAR *);

from a DLL named Ehllapi.DLL (an implementation of the IBM 3270 HLLAPI for an Italian 3270 terminal emulator, as it happens). Thomas Heller's ctypes extension, found at http://sourceforge.net/projects/ctypes, made the task very easy. In particular, ctypes makes mincemeat of problems related to representing function arguments that must belong to a certain C type and possibly get passed "by reference" (i.e., via a pointer).

In the past, I used another extension, known as calldll, which was (and still is) available from http://www.nightmare.com/software.html. While once very useful, calldll cannot rely on some of the modern techniques that ctypes uses internally, because these possibilities were introduced only in relatively recent versions of Python. calldll, using a single membuf Python type to represent all possible C types, tends to be much more cumbersome than ctypes when they are both used to perform the same tasks.

Judge for yourself: here is a working calldll version of the same script that I just showed how to code with ctypes:

import calldll, struct # some helpful auxiliary functions def myPrintLong(vVar):     ''' print a long contained in a membuf '''     print calldll.read_long(vVar.address( )) def myPrintString(vVar):     ''' print a string contained in a membuf '''     a = calldll.read_string(vVar.address( ))     print a, len(a) def mySetLong(vMemBuf, vValueToSet):     ''' set to an unsigned long the value of a membuf with len == 4 '''     vMemBuf.write(struct.pack('L', vValueToSet)) def mySetString(vMemBuf, vValueToSet):     ''' set to a string (with \0 terminator) the value of a membuf '''     pack_format = "%ds" % 1+len(vValueToSet)              # +1 for the \0     string_packed = struct.pack(pack_format, vValueToSet) # pack( ) adds the \0     vMemBuf.write(string_packed) # load 'Ehllapi.dll' (from current dir), and function 'hllapi' from the DLL dll_handle = calldll.load_library ('.\\Ehllapi') function_address = calldll.get_proc_address (dll_handle, 'HLLAPI') # allocate and init three membufs with the size to hold an unsigned long Lsize = struct.calcsize('L') vFunction = calldll.membuf(Lsize) mySetLong(vFunction, 1) vTextLen = calldll.membuf(Lsize) vResult = calldll.membuf(Lsize) mySetLong(vResult, 1) # allocate a membuf as large as the DLL requires; in this case, space # for 24 x 80 characters + 1 for a \0 terminator vText = calldll.membuf(1921) # init the text and text-length variables based on string of interest string_value_to_write = 'A' mySetString(vText, string_value_to_write) mySetLong(vTextLen, len(string_value_to_write)) # call the function, print the results, and clean up calldll.call_foreign_function(function_address, 'llll', 'l',   (vFunction.address( ), vText.address( ), vTextLen.address( ), vResult.address( ))) myPrintLong(vResult) myPrintString(vText) calldll.free_library(dll_handle)

To be honest, I can't quite be sure whether all of these gyrations are truly indispensable to making this calldll-based version work. Whenever I try to simplify this version a bit, something or other always breaks noisily, so I've stopped messing with it. One reason the ctypes-based version is cleaner and simpler is that ctypes has never given me trouble, so I've been encouraged to continue working on that version to improve it.

See Also

ctypes is at http://sourceforge.net/projects/ctypes; calldll is at http://www.nightmare.com/software.html.



Python Cookbook
Python Cookbook
ISBN: 0596007973
EAN: 2147483647
Year: 2004
Pages: 420

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net