0

Explanation:

I am attempting to access a function in a DLL using python 2.7 on Windows 7 that requests a pointer to a structure containing a void pointer array. At the moment, I do not have the source code for this DLL, but I do have the header file which contains the function prototype for the function in question as well as the definitions of the structures.

The Structures:

    typedef struct {
        unsigned long      PagesPerMark[MAX_NUM_DMA_CHANNELS];
        void               *VirtDMAAdr[MAX_NUM_DMA_CHANNELS][MAX_DMA_PAGES_PER_CH];
        unsigned long long PhysDMAAdr[MAX_NUM_DMA_CHANNELS][MAX_DMA_PAGES_PER_CH];
    } s_DMAConfig;

    typedef struct {
        s_DMAConfig DMA;
        void        *User;
    } s_Adapter;

The Function Prototype:

    #ifdef WIN32
    #define EXPORTULONG __declspec(dllexport) unsigned long
    #else
    #define EXPORTULONG unsigned long
    #endif  
    EXPORTULONG Adapter_Zero(s_Adapter *p_Adapter);

My attempt at accessing the function using Python:

    import ctypes

    MAX_NUM_DMA_CHANNELS = 4
    MAX_DMA_PAGES_PER_CH = 1024

    class s_DMAConfig(ctypes.Structure):
        _fields_ = [("PagesPerMark", ctypes.c_ulong * MAX_NUM_DMA_CHANNELS),
                   ("VirtDMAAddr", ctypes.POINTER(ctypes.c_void_p * MAX_NUM_DMA_CHANNELS * MAX_DMA_PAGES_PER_CH)),
                   ("PhysDMAAdr", ctypes.c_ulonglong * MAX_NUM_DMA_CHANNELS * MAX_DMA_PAGES_PER_CH)]

    class s_Adapter(ctypes.Structure):
        _fields_ = [("DMA", s_DMAConfig),
                   ("User", ctypes.c_void_p)]

    uLongArray = ctypes.c_ulong * MAX_NUM_DMA_CHANNELS
    pagePerMark = uLongArray()
    voidPtrArr = ctypes.c_void_p * MAX_NUM_DMA_CHANNELS * MAX_DMA_PAGES_PER_CH
    virtDMAAddr = voidPtrArr()
    uLongLongArray = ctypes.c_ulonglong * MAX_NUM_DMA_CHANNELS * MAX_DMA_PAGES_PER_CH
    physDMAAddr = uLongLongArray()
    dmaConfig = s_DMAConfig(pagePerMark, ctypes.pointer(voidPtrArr()), physDMAAddr)

    userInput = ctypes.c_void_p()
    adapter = s_Adapter(dmaConfig, userInput)

    ## Load DLL
    RRadapter = ctypes.cdll.LoadLibrary("myLib.dll")
    Adapter_Zero = RRadapter.Adapter_Zero
    Adapter_Zero.restype = ctypes.c_ulong   
    Adapter_Zero.argtypes = [ctypes.POINTER(s_Adapter),]
    Adapter_Zero(ctypes.byref(adapter))

When I attempt to run this solution it crashes the python interpreter right after the DLL function call is made. There are no return errors, so I'm not sure what course of action I should take next. I'm pretty new to Python, so any advice would be greatly appreciated.

I've looked at other somewhat similar questions such as Python ctypes pointer to pointer to structure as well as Python & Ctypes: Passing a struct to a function as a pointer to get back data but to no avail. Additionally, I could not find any good examples of using a void ptr array, so I hope this question will aid others who may be in a similar situation.

My questions for you:

1) Did I correctly implement the call to the DLL using Python's ctypes?

2) In the case that I did correctly implement the Python script (most likely I didn't,) what free tools could I use to investigate the reason for the failure?

Community
  • 1
  • 1
Glynz
  • 1
  • 3
  • `VirtDMAAddr` is not a pointer; it's a 2D array of `void *` pointers. You also have the row and column size flipped. It should be `("VirtDMAAdr", (ctypes.c_void_p * MAX_DMA_PAGES_PER_CH) * MAX_NUM_DMA_CHANNELS)`. – Eryk Sun Jun 23 '15 at 23:07
  • You don't have to initialize `adapter` by manually assigning the fields. It's just a block of memory that ctypes initializes to all zeros. Use `adapter = s_Adapter()`. – Eryk Sun Jun 23 '15 at 23:23
  • @eryksun Thanks for the heads up! Based on your two comments I don't even need to initialize the fields, but rather pass in the default constructor. Am I understanding you correctly? – Glynz Jun 24 '15 at 03:15
  • Your initialization code is pointless, but harmless. In the first comment I gave you the correct definition of `VirtDMAAdr`. You also have the row and column order reversed in `PhysDMAAdr`. It should be `("PhysDMAAdr", (ctypes.c_ulonglong * MAX_DMA_PAGES_PER_CH) * MAX_NUM_DMA_CHANNELS)`. – Eryk Sun Jun 24 '15 at 03:23
  • @eryksun It worked! In my naiveté, I didn't know that Python flipped the rows and columns as compared to C. Would you mind posting your comment as an answer? I want to make sure you get the deserved recognition and points for successfully answering the question. – Glynz Jun 24 '15 at 15:20
  • The buffer is the same number of elements either way. The main problem was defining `VirtDMAAddr` as a pointer, which is either 4 or 8 bytes (i.e. 32-bit or 64-bit), when it should have been a 2D array of pointers. Thus `DMAConfig` was considerably too small. If you have some free time, you may benefit from reading the [C FAQ](http://c-faq.com). – Eryk Sun Jun 24 '15 at 16:45

1 Answers1

0

Have you any crash reports?

Have you tried:

RRadapter = ctypes.CDLL('lib.dll')

This worked well for my last project.

Sorry no right to comment yet.

fgoettel
  • 351
  • 2
  • 6
  • Judging from the documentation it seems that either form of loading the library is acceptable, although, yours seems to better follow the documentation. Regardless, I tried using that method, and it still resulted in a crash. In terms of logging crash reports, do you mean something similar to this answer [stackoverflow link](http://stackoverflow.com/questions/15111249/how-to-log-a-python-crash)? It's ok to post an answer-- I'm just thankful for a response! – Glynz Jun 23 '15 at 18:54