
I've thrown together a simple foreign function interface, using some
code lifted from MIT Scheme.

You can use it to call functions in arbitrary DLL's.  It should work
for both __stdcall and __cdecl functions.

[Note: You can't use this with 16-bit DLL's.  For that you need to
 use the thunking library, which I thankfully know nothing about.]

[Note: calldll.call_foreign_function does not yet support
 returning 64-bit quantities (like C doubles).  They are returned
 using a different convention]

===========================================================================
There are a few very standard windows functions represented in this
module, which allow you to get at DLL's and function pointers:

>>> calldll.load_library (library_name)

will attempt to load a dll (map it into your address space), and
return a module handle or zero.

>>> calldll.get_module_handle (library_name)

will return the handle of a library if it is already loaded, or zero.

>>> calldll.get_proc_address (module_handle, procedure_name)

will return the address of a procedure if it's exported by the module,
or zero.

Many standard windows functions have two entry points, with either an
'A' or a 'W' appended to the name.  These are the 'ascii' and 'wide
char' variants of those functions that deal with strings.

If you have the 'dumpbin' utility (comes with visual c++), you can see
what symbols are exported by a 32-bit dll like this:

d:\winnt\system32> dumpbin /exports user32.dll

To call a function, once you have its address:

calldll.call_foreign_function (
  function_address,
  in_args_format_string,
  result_format_string,
  argument_tuple
  )

where <in_args_format_string> is a standard PyArg_ParseTuple string,
and <result_format_string> is a single-char string which is used to
build a python return value from the result of the function call.

===========================================================================
calldll has a simple memory buffer object, which can be used to pass
buffers to dll calls.  You can read and write strings from a <membuf>
object.  There are also a few functions for reading from arbitrary
memory addresses.

# create a 64-byte buffer
>>> my_struct = calldll.membuf (64)
# get the address of the start of the buffer:
>>> my_struct.address()

===========================================================================
There are a few utility modules which make <calldll> a little
easier to use.

[Note, the previous 'windll.WIN' interface has been removed. I've
 found that I prefer using the windll.module object directly]

The following sequence shows how you might use 'windll.py' to call
a function in a DLL:

>>> import windll
>>> kernel32 = windll.module ('kernel32')
>>> kernel32.Beep (1000, 50)

windll automatically gets the procedure's address, and caches
everything, so the next time you use 'kernel32.Beep' it'll just be a
dictionary lookup.

Your win32 documentation should tell you which library a particular
win32 function is in... If you're using Visual C++, click on the
'Quick Info' button in the InfoViewer Topic window.

===========================================================================

I've now added a callback-generating facility.  This removes the final
barrier to writing full-blown win32 applications completely in python
- many win32 functions require the addresses of a callback function
in order to work.  A window procedure would be the most obvious
example of this.  But you can't pass the address of a Python function
object, can you! 8^)

The module 'gencb.py' will generate a snippet of x86 assembly code
that will act as a wrapper for a python function object.  The code
translates the given arguments into a tuple (using Py_BuildValue) and
then calls PyEval_CallObject().

See 'cbdemo.py' for a demonstration.  There are also a few other
modules (in various stages of completeness) that might eventually form
the core of a Python 'Class Library' for programming Windows.

===========================================================================

An alternate version of the 'npstruct' module is available, implemented
as a C extension module.  If your bit-twiddling needs speeding up, look
for that package here:

  http://www.nightmare.com/software.html

===========================================================================
(July 24, 1998)

With the assistance of Christian Tismer, Gordon McMillan, and Mark Hammond
I have now (I believe) made calldll and gencb thread-safe.

Multiple GUI threads are still not working; if you try to create other
windows in other threads it will _start_ to work, and then crash.  If
someone can figure this out please let me know!

This isn't much of a real restriction, though, since the GDI itself is
not thread safe, it's quite a snake's nest (ahem) to make it work.  I
believe that MFC makes a similar restriction on the use of threads.

'Worker Threads' seem to work just fine.

-Sam
rushing@nightmare.com
