Need help calling a proprietary C DLL from Python

Craig craigm3604 at gmail.com
Sat Mar 22 02:21:48 EDT 2008


On Mar 21, 4:04 am, Dennis Lee Bieber <wlfr... at ix.netcom.com> wrote:
> On Thu, 20 Mar 2008 16:50:18 -0700 (PDT), Craig <craigm3... at gmail.com>
> declaimed the following in comp.lang.python:
>
>
>
> > I received a direct email from someone, and I came up with the
> > following after implementing his advice:
>
>         Just for completeness... I'm the "someone"... And this is a copy of
> the message in question:
>
>
>
>
>
> >         Please forgive my going direct -- I do not have Usenet posting privileges from work.
>
> > From your message:
>
> > -=-=-=-=-=-=-
> > And, this is what printed:
> > After - X = 4325376, CacheSize = 0, OpenMode = 3, vHandle = 17586736
>
> > The printf shows that the values of X and vHandle have changed, but I
> > am not sure it is working properly as X should be a MUCH smaller
> > number (< 256). I would have expected a 0 (VIS_OK), 13 (VIS_DOS_ERROR)
> > or 14(VIS_DISK_ERROR) if it had worked.
>
> > Next, I changed:
> > fName = windll.oleaut32.SysAllocStringByteLen("u:\\msdb\\dcod\x00",
> > 13)
> > so that I knew it would not find the file, and got:
> > After - X = 2555917, CacheSize = 0, OpenMode = 3, vHandle = 0
>
> > The vHandle staying 0 tells me that it did not find the file, but the
> > value of X seems random.
> > -=-=-=-=-=-=-=-
>
> >         X does NOT seem random to me -- but I took the effort to render it in hexadecimal. X seems to be coming back as a 32-bit integer. Your <256 code appears to be in the second half of that integer -- observe:
>
> > >>> "%8.8X" % 4325376
> > '00420000'
> > >>> "%8.8X" % 2555917
> > '0027000D'
>
> >         Also observe that x0D => 13 decimal... your stated "VIS_DOS_ERROR" code.
>
> > >>> 0x0042
> > 66
> > >>> 0x0027
> > 39
> > >>> 0x000d
> > 13
>
> >         I have no explanation for the leading 16-bits... But under the rules of Python, "X = ..." is commonly a rebinding operation, so presetting X as a ctypes short integer may not be having any effect (I suspect those are more important for calls where the input parameters need to be mutable and of a known C format).
>
> > >>> import ctypes
> > >>> x = ctypes.c_short(123)
> > >>> dir(x)
> > ['__class__', '__ctype_be__', '__ctype_le__', '__ctypes_from_outparam__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__nonzero__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__', '_as_parameter_', '_b_base_', '_b_needsfree_', '_objects', '_type_', 'value']
>
> > >>> x
> > c_short(123)
> > >>> x.value
> > 123
>
> > >>> x.value = 13
> > >>> x
> > c_short(13)
>
> >         Note that in this I have assigned to the "value" component of a c_short object. Plugging in your results values:
>
> > >>> x.value = 4325376
> > >>> x
> > c_short(0)
> > >>> x.value = 2555917
> > >>> x
> > c_short(13)
>
> > Gives... ta da... 0 and 13 respectively. Perhaps you should, after creating X as a c short object, be assigning to the value, not to the object...
>
> > X.value = windll. ...
>
> > {Replies are being directed to my home email}
>
> --
>         Wulfraed        Dennis Lee Bieber               KD6MOG
>         wlfr... at ix.netcom.com             wulfr... at bestiaria.com
>                 HTTP://wlfraed.home.netcom.com/
>         (Bestiaria Support Staff:               web-a... at bestiaria.com)
>                 HTTP://www.bestiaria.com/


Sorry, I wasn't trying to exclude any credit from Dennis, I just
wasn't sure if he wanted to be listed.


As it turns out, I am running into more of these BSTR and pointer
problems.

I have moved on to the next function to convert:
short FAR PASCAL VmxInfo(LPHANDLE lpDatasetNumber, LPVSTATS lpvstats);
which uses a structure:
typedef struct tagVSTATS
  {
    LONG   nrecords;
    WORD   gps_used;
    WORD   gps_unused;
    WORD   max_key_len;
    LONG   grp_size;
    WORD   init_alloc;
    WORD   cr_opts;
    WORD   free_prop_area;
    BSTR   format;
    BSTR   reserved;
  } VSTATS;
typedef VSTATS FAR * LPVSTATS;

This is what I have tried:
from ctypes import *
from ctypes.util import *

libc = cdll.msvcrt
printf = libc.printf

LPBSTR = POINTER(c_void_p)
HANDLE = POINTER(POINTER(c_long))
LPHANDLE = POINTER(HANDLE)
LPSHORT = POINTER(c_short)
LPVSTATS = POINTER(c_void_p)

class VSTATS(Structure):
      _fields_ = [
                   ("nrecords", c_long),
                   ("gps_used", c_short),
                   ("gps_unused", c_short),
                   ("max_key_len", c_short),
                   ("grp_size", c_long),
                   ("init_alloc", c_short),
                   ("cr_opts", c_short),
                   ("free_prop_area", c_short),
                   ("format", c_void_p),
                   ("reserved", c_void_p)
                 ]
vStats = VSTATS(1, 2, 3, 4, 5, 6, 7, 8)

VmxInfo = windll.vbis5032.VmxInfo
VmxInfo.restype = c_short
VmxInfo.argtypes = [LPHANDLE, LPVSTATS]

print "\n(2) VB/ISAM testing:"
hwmcb = HANDLE()

print "\nVmxInfo test:"
res = c_short(255)
printf ("Before - res = %#x (%d), hwmcb = %d\n", res, res, hwmcb)
res = VmxInfo( byref(hwmcb), byref(c_void_p(vStats)) )
printf ("After - res = %#x (%d), hwmcb = %d\n", res, res, hwmcb)
printf ("nrecords = %d, gps_used = %d, gps_unused = %d, max_key_len =
%d\n", vStats.nrecords, vStats.gps_used, vStats.gps_unused,
vStats.max_key_len)
printf ("grp_size = %d, init_alloc = %d, cr_opts = %d, free_prop_area
= %d\n", vStats.grp_size, vStats.init_alloc, vStats.cr_opts,
vStats.free_prop_area)
printf ("format = \x22%s\x22\n", vStats.format)
printf ("reserved = \x22%s\x22\n", vStats.reserved)

This is what I get:
Traceback (most recent call last):
  File "C:\temp\vbisam_test_2.py", line 99, in <module>
    res = VmxInfo( byref(hwmcb), byref(c_void_p(vStats)) )
TypeError: cannot be converted to pointer


And, on the last variation of this, this function needs to receive
BSTR values passed back from the dll:
short FAR PASCAL VmxGet(LPHANDLE lpDatasetNumber, LPSHORT lpSecIndex,
                        LPSHORT lpOption, BSTR *SrchKey,
                        BSTR  *SecKey, BSTR  *PriKey, LPSTR
lpTypeDef);
SrchKey is provided by me, SecKey and PriKey are returned by the dll,
and TypeDef is defined by me and filled by the dll.

For this, I have added:
VmxGet = windll.vbis5032.VmxGet
VmxGet.restype = c_short
VmxGet.argtypes = [LPHANDLE, LPSHORT, LPSHORT, LPBSTR, LPBSTR, LPBSTR,
LPBSTR]
hwmcb = HANDLE()
SecIndex = c_short(0)
Option = c_short(17)
SrchKey =
windll.oleaut32.SysAllocStringByteLen("MSD19PH
\x00", 41)
SecKey =
windll.oleaut32.SysAllocStringByteLen("1234567890123456789012345678901234567890\x00",
41)
PriKey =
windll.oleaut32.SysAllocStringByteLen("1234567890123456789012345678901234567890\x00",
41)
TypeDef =
windll.oleaut32.SysAllocStringByteLen(c_char_p("X".center(129,
"\x00")), 129)
print "\nVmxGet test:"
res = c_short(255)
printf ("Before - res = %#x (%d), SecIndex = %d, Option = %d, hwmcb =
%d\n", res, res, SecIndex, Option, hwmcb)
printf ("SrchKey = \x22%s\x22\nSeckey = \x22%s\x22\nPriKey = \x22%s
\x22\nTypeDef = \x22%s\x22\n", SrchKey, SecKey, PriKey, TypeDef)
print "Ready to call (Library = " + find_library("vbis5032") + ") ..."
res = VmxGet( byref(hwmcb), byref(SecIndex), byref(Option),
byref(c_void_p(SrchKey)), byref(c_void_p(SecKey)),
byref(c_void_p(PriKey)), byref(c_void_p(TypeDef)) )
printf ("After - res = %#x (%d), SecIndex = %d, Option = %d, hwmcb = %d
\n", res, res, SecIndex, Option, hwmcb)
printf ("SrchKey = \x22%s\x22\nSeckey = \x22%s\x22\nPriKey = \x22%s
\x22\nTypeDef = \x22%s\x22\n", SrchKey, SecKey, PriKey, TypeDef)

The last message I get is:
Ready to call (Library = C:\Windows\vbis5032.dll) ...
and then I get a Windows popup box:
python.exe has stopped working
so, I know I am passing something very wrong.


Once these two problems are resolved, I am sure I can process the rest
of the C prototypes myself, as they are simply variations on these
themes.


As before, any help is appreciated.



More information about the Python-list mailing list