Need help calling a proprietary C DLL from Python

Craig craigm3604 at gmail.com
Mon Mar 24 17:27:36 CET 2008


On Mar 23, 7:59 pm, Dennis Lee Bieber <wlfr... at ix.netcom.com> wrote:
> On Sun, 23 Mar 2008 14:24:52 -0700 (PDT), Craig <craigm3... at gmail.com>
> declaimed the following in comp.lang.python:
>
>
>
> > This dll was designed to be used from either C or Visual Basic 6.
>
> > I have the declare statements for VB6, if that helps.
>
>         Probably not that much -- I'd bet it's full of variant records <G>
>
>
>
> > 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-".
>
>         0x2D is 4 less than the expected 0x31...
>
>
>
> > 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=".
>
>         ... and 0x3D is 4 less than the expected 0x41
>
>         Which leads me to suspect that BSTR are a structure, not a plain
> string, in which the address given is supposed to be prefaced with a
> length value. IOWs, something like:
>
> |----|--------------------------------------|
> ^    ^  C-string data
> |    |Address to be passed
> |(address - 4) to get to a length count field
>
> Confirmed:http://msdn2.microsoft.com/en-us/library/ms221069(VS.85).aspx
>
> (the URL is from HTTP through to the end, including .aspx)
>
>         Creating such may not be difficult -- but passing it to the DLL
> could be. I don't know if ctypes allows pointer arithmetic.
>
> ctypes OLESTR is a c_wchar_p, but that is still a c-style string with no
> length prefix.
>
>         The only mention of BSTR in the win32 extensions is in text that
> implies that the win32 extension library takes care of converting
> from/to BSTR and Python strings transparently -- but that won't work for
> your third-party library I suspect.
>
>         Might have to beg the author(s) of ctypes to add a BSTR type to the
> list of those supported... as I can find no means of tweaking the
> address computed by byref() to point somewhere into a structure rather
> than the beginning of the structure.
>
> >>> pk = ctypes.windll.oleaut32.SysAllocStringByteLen("1234567890", 12)
> >>> pk
> 1373844
> >>> id(pk)
> 18941724
> >>> ctypes.string_at(pk)
> '1234567890'
> >>> ctypes.string_at(pk-4, 20)
>
> '\x0c\x00\x00\x001234567890\x00\x01\x00\x00\x00\x00'
>
>
>
>         Okay, the return value from SysAlloc... IS the integer representing
> the address of a BSTR structure (IE, the address four bytes in from the
> real memory start)... Note how backing up 4 bytes reveals the BSTR
> length field
>
>         What happens if you just pass that item as-is, no byref(), no
> conversion to ctypes pointer types...
>
> PriKey = ctypes.windll.oleaut32.SysAllocStringByteLen("1234567890", 41)
> SecKey = ctypes.windll.oleaut32.SysAllocStringByteLen("1234567890", 41)
>
> res = VmxGet(   byref(hwmcb),
>                                 byref(SecIndex),
>                                 byref(Option),
>                                 byref(c_void_p(SrchKey)),
>                                 SecKey,
>                                 PriKey,
>                                 TypeDef )
>
> >>> PriKey = ctypes.windll.oleaut32.SysAllocStringByteLen("1234567890", 41)
> >>> ctypes.string_at(PriKey, 41)
>
> "1234567890\x00\x01:\x00\x00\x00\xb6\x01\x00\x000\x99#\x01\xe0\x94\x1a\x1e\x0b\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00'">>> ctypes.string_at(PriKey-4, 41+4)
>
> ")\x00\x00\x001234567890\x00\x01:\x00\x00\x00\xb6\x01\x00\x000\x99#\x01\xe0\x94\x1a\x1e\x0b\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00'"
>
> >>> ord(")")
> 41
>
>         You'll need to use the .string_at() or  .wstring_at() functions to
> extract any data changed by the call. Should not be a Python
> immutability problem as all "you" allocated in Python is the integer
> holding the address. You may need to call a Sys... function to free the
> memory that had been allocated -- Python doesn't know about it.
> --
>         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/

The VB6 Declare is:
Declare Function VmxGet Lib "vbis5032.DLL" (DatasetHandle&,
SecIndexField%, Options%, SelectorKey$, RSecondaryKey$, RPrimaryKey$,
RRecordVariable As Any) As Integer

The only variant is the RRecordVariable, which is actually meant to be
a Type structure.

Anyway, I thought maybe there was a clue in here that I am missing.


Back to Python. I ran with the following:
VmxGet.argtypes = [LPHANDLE, LPSHORT, LPSHORT, LPBSTR, LPSTR, LPSTR,
LPSTR]
SrchKey =
windll.oleaut32.SysAllocStringByteLen("MSD19DN
\x00", 41)
SecKey =
windll.oleaut32.SysAllocStringByteLen("1234567890123456789012345678901234567890\x00",
41)
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, PriKey, TypeDef )
And, now I get:
Traceback (most recent call last):
  File "C:\temp\vbisam_test_2.py", line 156, in <module>
    res = VmxGet( byref(hwmcb), byref(SecIndex), byref(Option),
byref(c_void_p(S
rchKey)), SecKey, PriKey, TypeDef )
ctypes.ArgumentError: argument 5: <type 'exceptions.TypeError'>: wrong
type

So, now the problem is with:
VmxGet.argtypes = [LPHANDLE, LPSHORT, LPSHORT, LPBSTR, LPSTR, LPSTR,
LPSTR]
I am not sure what to put for arguments 5 and 6 now.

I realized the need to release the BSTRs myself, and already included:
windll.oleaut32.SysFreeString(SrchKey)
windll.oleaut32.SysFreeString(SecKey)
windll.oleaut32.SysFreeString(PriKey)
at the end of the program.

I have no problem using .string_at() to extract the returned values -
once the .argtypes let the call execute.


In case this helps, here is an excerpt from a C program that was
provided with the dll:
typedef struct tagMYREC
  {
    char    name[51];        //  1 xref generated name key

    char    lastname[31];    //  2      last name
    char    firstname[21];   //  3      first name
    char    address[41];     //  4      address
    char    city[31];        //  5 xref city key
    char    state[3];        //  6 xref state
    char    zip[11];         //  7 xref zip code
    char    phone[21];       //  8 xref phone number
    BSTR    notes;           //  9      notes

  } MYREC;
typedef MYREC FAR * LPMYREC;

short             rc;  // return code (from VB/ISAM function calls)
static HANDLE     nDatasetNumber;
static short      cur_index;
static short      GetOpt;
static BSTR       Dummy;
static BSTR       Throwaway;
static BSTR       PrimaryKey;
static MYREC      myrec;

rc = VmxGet(&nDatasetNumber, // int    DatasetNumber  // VIS_BUSY ?
            &cur_index,      // int    SelectedIndex
            &GetOpt,         // int    OptionParameter
            &Dummy,            // Don't need a Selector param with
these options
            &Throwaway,      // Don't need returned index entry in
this case
            &PrimaryKey,     // LPBSTR  lpPriKey
            (void *) &myrec);         // LPVOID lpRecordStructure

For someone who knows C and Python, this may provide a clue to the
BSTR passing/return problem.



More information about the Python-list mailing list