Welcome, guest | Sign In | My Account | Store | Cart

When I wanted to use Python to call functions in Windows .DLL I was surprised that I had a difficult time locating the necessary information for making this happen. This is a base class that you use to define your class (and methods for each function). It uses Sam Rushings calldll, cstring and membuf modules but I think it will make interfacing with any DLL much easier for the beginner (especially for the first time).

Python, 162 lines
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# CLASS NAME: DLLInterface
#
# Author: Larry Bates
#
# Written: 08/14/2002
#
# Description: This is a base class for implementing a class interface
# to call general DLL libraries.  You should build your class using
# this class as the base class.  Define methods for each function of
# the DLL that you wish to implement.
#
import os, sys
import calldll
from dynwin.windll import cstring, membuf

class DLLInterface:
    '''
    class DLLInterface is used as a base class as a general interface class to
    Microsoft Windows DLLs.  None  of the methods of this class should be
    called directly.  Subclass this class and have subclass' methods
    make calls to _dllcall to call functions from the DLL.
    '''

    #--------------------------------------------------------------------------------------------
    # Example usage:
    #
    # class anydll(DLLInterface):
    #    def function1 (self, arg1, arg2, ..., argn)
    #        #
    #        # code goes here to implement function1 from the DLL
    #        # as a method of anydll class.
    #        #
    #        # Syntax of _dllcall function is (function in .DLL,
    #        #                                 argument types,    ['llh' is long, long, short]
    #        #                                 return code type,  ['h' is short]
    #        #                                 arguments in a tuple)              
    #        #
    #        result=self._dllcall('function1',
    #                             'lll',
    #                             'h',
    #                             (arg1,
    #                              arg2,
    #                              arg3,
    #                               .
    #                               .
    #                               .
    #                              argn))
    #        return result
    #
    # this would then be executed using:
    #
    # D=anydll(DLLPath)
    # result=d.function(arg1, arg2, ..., argn)
    #
    # argument types = 'h'-short, 'l'-long, 's'-string (null terminated using helper function
    #                                                   cstring in dynwin)
    #--------------------------------------------------------------------------------------------
       
    def __init__(self, DLLPath, ext='.dll'):
        self._debug=0
        self._trace=0
        self._loaded=0
        self._funcs={}
        self._DLLName=os.path.split(DLLPath)[1]+ext  # Save DLL filename
        if self._trace: print "creating _handle"
        #
        # Load the DLL library (if not already loaded) and save a
        # handle to it in self._handle
        #
        if not self._loaded:
            self._handle=calldll.load_library(DLLPath+ext)
            #
            # If I didn't get a handle, DLL wasn't loaded
            #
            if not self._handle:
                raise SystemError, "couldn't load module '%s'" % self._DLLName
            else:
                self._loaded=1
            
        if self._trace: print "handle=",self._handle

        if self._trace: print "Leaving __init__"
        return

    def unload(self):
        if self._loaded and self._handle:
            calldll.free_library(self._handle)
            
        self._loaded=0
        self._handle=0
        self._funcs={}
        return

    def _dlladdr(self, func_name):
        #
        # This method uses the function name (func_name) and looks into the .DLL
        # to find the address that should be used to call that function.
        #
        if self._trace: print "self._handle=",self._handle
        if self._trace: print "_dlladdr to get address of %s func" % func_name
        if not self._handle:
            raise SystemError, "No handle found for %s" % self._DLLName
        #
        # See if I've called this func_name before.  If I have, I have saved
        # the address in self._funcs dictionary object so I don't have to call
        # calldll.get_proc_address repeatedly.  A method of "caching" the addresses.
        # to hopefully speed things up a little.
        #
        try:
            addr=self._funcs[func_name]
        except:
            addr=calldll.get_proc_address(self._handle, func_name)
            self._funcs[func_name]=addr
            
        if self._trace:
            print "leaving _dlladdr, function %s has calling address=%i" % (func_name, addr)
            print ""
        #
        # Return the address of this function so it can be called by calldll
        #
        if addr: return addr
        else:    raise SystemError, "%s has no function named '%s'" % (self._DLLName, func_name)

    def _dllcall(self, function_name,arg_format_string, result_format_string, args):
        #
        # This method is used to make the call to the DLL function
        # Arguments are:
        #
        # function_name - String containing the name of the function to call
        # arg_format_string - this is a string that contains the format of the
        #                     arguments that are to be passed to the function.
        #                     These can be one of:
        #                     l - long integer (or address)
        #                     h - short integer (half)
        #                     s - string
        #
        #                     Example: "lllh"
        #
        # result_format_string - The result will be either l or h, returned by
        #                        function
        # args - This is a tuple (even if there is only one argument)
        #
        #                     Example: (x,) or (a,b,c,d)
        #
        if self._trace: print "Entering _dllcall to call function=",function_name
        result=calldll.call_foreign_function(self._dlladdr(function_name),
                                                           arg_format_string,
                                                           result_format_string,
                                                           args)
        #
        # If result_format_string is "h" mask out high bits of result
        #
        if result_format_string in ('h','H'):
            result=result & 0xffff
            
        #
        # If result_format_string is "s", strip off trailing null character
        #
        if result_format_string in ('s','S'):
            result=result[:-1]

        return result          

After about 2 weeks of trying to get Python to talk to a .DLL based toolkit that I wanted to use, I finally came up with a general solution to interfacing Python with .DLLs. This has been tested with ExperVisions RTK OCR toolkit and Castelle's CPI32 fax toolkit. I'm still a beginner, but I hope this saves someone some time/effort. It still takes some effort to figure out how to pass arguments (pointers vs values) and how to handle the structures some .DLLs employ, but at least this part of the problem is solved.

4 comments

David Moody 21 years, 7 months ago  # | flag

GREAT JOB--------Thanks!! Words can not express how much you have done for me with this code. Thank You

Gene Chiaramonte 20 years, 6 months ago  # | flag

Another module for calling dll functions. You may want to look at ctypes from Tom Heller.

http://starship.python.net/crew/theller/ctypes/

Gene

marie tsutsumi 18 years, 2 months ago  # | flag

Function Without Arguments. What do I have to do when my functions from DLL that don't have arguments?

result=self._dllcall("RevMensagens","","h",('')) ==> it doesn't work!!

Larry Bates (author) 16 years, 10 months ago  # | flag

Depricated. Today you are much better using ctypes (which is now part of the Standard Library) than this routine.