Need help calling a proprietary C DLL from Python

Craig craigm3604 at gmail.com
Sun Mar 23 17:24:52 EDT 2008


On Mar 23, 4:48 pm, Dennis Lee Bieber <wlfr... at ix.netcom.com> wrote:
> On Sat, 22 Mar 2008 19:05:31 -0700 (PDT), Craig <craigm3... at gmail.com>
> declaimed the following in comp.lang.python:
>
> > I got back exactly what I expected for TypeDef, but SecKey and PriKey
> > were what I initialized them to , not what should have been returned.
>
> > Do you mean that I should change any string that is expected to be
> > changed by the dll to a create_string_buffer type (LPSTR) instead of a
> > windll.oleaut32.SysAllocStringByteLen type (LPBSTR) and then simply
> > list it in the call instead of byref, as I did with TypeDef?
>
>         Based upon my interpretation of the C prototype and the ctypes
> documentation (sample code), that IS what I expect... (I didn't expect
> it to work with the TypeDef field though...)
>
>         There are two ways in which a C language function call can handle
> returning string data. (Let's see if I remember how to code C; been 6
> years)
>
> char pre_alloc_buffer[] = "This is a fixed nul-terminated buffer space";
> char *dynamic_buffer;   /*      this is an uninitialized pointer to char */
> char *dynamic_too;
>
>         The function can either modify a buffer passed in:
>
> int func1(char *bfr);
>
> or it can allocate a buffer internally and return that either as a
> function result, or via a reference to a pointer variable
>
> char *func2(char *bfr[]);       /*      ugh, my memory is fading        */
>         /* If I did this right, it is a pointer to array of character */
>
> res = func1(pre_alloc_buffer);
> or
> res = func1(&pre_alloc_buffer[0]); /* long/explicit form */
>
>         The address of the start of the buffer space is passed, and func1
> performs its magic in the pre-allocated buffer.
>
> dynamic_too = func2(&dynamic_buffer);
>
>         Here, we pass the address of just a pointer variable (which
> currently points to garbage). func2 is expected to perform something
> wherein IT allocates memory and that memory address is what is put into
> the argument/return
>
>         ...
>         *bfr = malloc(...);
>         return (bfr);
>
>         Based on my interpretation of the prototypes, I'd have expected the
> two BSTR to be the func1 style passing of a buffer, but the last
> argument /might/ have been the second func2 style.
>
>         I really can't be more definitive; I've not used ctypes -- only read
> the documentation; the only DLLs I've had to use were covered by the
> win32 extension library which comes as part of the ActiveState Python
> installation.
>
>
>
> > Can it really be that simple?
>
> > As for the structure:
> > 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)
> > can I just use a create_string_buffer (LPSTR) and parse things up via
> > substrings after the call returns?
>
>         It may be possible, though the library seems to have gone out of its
> way to mask such efforts from the user.
> --
>         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/


This dll was designed to be used from either C or Visual Basic 6.

I have the declare statements for VB6, if that helps.

Based on the results I have so far (and I have tried MANY permutations
like trying to use the LPSTR for SecKey and PriKey which are returned
as is TypeDef), it looks like SecKey and PriKey are being used as data
instead of pointers.

For this, I try:
LPSTR = c_char_p
VmxGet.argtypes = [LPHANDLE, LPSHORT, LPSHORT, LPBSTR, LPSTR, LPBSTR,
LPSTR]
SrchKey =
windll.oleaut32.SysAllocStringByteLen("MSD19DN
\x00", 41)
SecKey = create_string_buffer(41)
SecKey.raw = "1234567890123456789012345678901234567890"
PriKey =
windll.oleaut32.SysAllocStringByteLen("ABCDEFGHIJKLMNOPQRSTUVWXYZ12345678901234\x00",
41)
TypeDef = create_string_buffer(128)
TypeDef.raw = "X".center(128, "X")
res = VmxGet( byref(hwmcb), byref(SecIndex), byref(Option),
byref(c_void_p(SrchKey)), SecKey, byref(c_void_p(PriKey)), TypeDef )
and I get:
Traceback (most recent call last):
  File "C:\temp\vbisam_test_2.py", line 158, in <module>
    res = VmxGet( byref(hwmcb), byref(SecIndex), byref(Option),
byref(c_void_p(S
rchKey)), SecKey, byref(c_void_p(PriKey)), TypeDef )
WindowsError: exception: access violation reading 0x3433322D

I notice that SecKey.raw starts with "1234" and the exception address
is 0x3433322D, which is "432-".

And, changing to:
VmxGet.argtypes = [LPHANDLE, LPSHORT, LPSHORT, LPBSTR, LPBSTR, LPSTR,
LPSTR]
PriKey = create_string_buffer(41)
PriKey.raw = "ABCDEFGHIJKLMNOPQRSTUVWXYZ12345678901234"
res = VmxGet( byref(hwmcb), byref(SecIndex), byref(Option),
byref(c_void_p(SrchKey)), byref(c_void_p(SecKey)), PriKey, TypeDef )
I then get:
Traceback (most recent call last):
  File "C:\temp\vbisam_test_2.py", line 159, in <module>
    res = VmxGet( byref(hwmcb), byref(SecIndex), byref(Option),
byref(c_void_p(S
rchKey)), byref(c_void_p(SecKey)), PriKey, TypeDef )
WindowsError: exception: access violation reading 0x4443423D

I notice that PriKey.raw starts with "ABCD" and the exception address
is 0x4443423D, which is "DBC=".


So, I can see that there is data where pointers are expected.

I just don't see how to pass those pointers. When they were passed as
LPBSTR (and oleaut32.SysAllocString), the calls "worked" in that they
didn't get exceptions and they did return the correct record, but the
SecKey and PriKey fields were not correct, and they were BSTR
(pointers).

I did get the VmxInfo to work by:
VSTATS_fmt = "lhhhlhhhlll"
VsamInfo = create_string_buffer(36)
temp = unpack(VSTATS_fmt, VsamInfo.raw)
vStats = VSTATS(temp[0], temp[1], temp[2], temp[3], temp[4], temp[5],
temp[6], temp[7])


Any idea on how to jump the last hurdle on VmxGet?

This is the very last thing that is stopping my project.



More information about the Python-list mailing list