Python and ctypes: how to correctly pass "pointer-to-pointer" into DLL?

Question:

I have a DLL that allocates memory and returns it. Function in DLL is like this:

void Foo( unsigned char** ppMem, int* pSize )
{
  * pSize = 4;
  * ppMem = malloc( * pSize );
  for( int i = 0; i < * pSize; i ++ ) (* ppMem)[ i ] = i;
}

Also, i have a python code that access this function from my DLL:

from ctypes import *
Foo = windll.mydll.Foo
Foo.argtypes = [ POINTER( POINTER( c_ubyte ) ), POINTER( c_int ) ]
mem = POINTER( c_ubyte )()
size = c_int( 0 )
Foo( byref( mem ), byref( size ) ]
print size, mem[ 0 ], mem[ 1 ], mem[ 2 ], mem[ 3 ]

I’m expecting that print will show “4 0 1 2 3” but it shows “4 221 221 221 221” O_O. Any hints what i’m doing wrong?

Asked By: grigoryvp

||

Answers:

Post actual code. The C/C++ code doesn’t compile as either C or C++. The Python code has syntax errors (] ending function call Foo). The code below works. The main issue after fixing syntax and compiler errors was declaring the function __stdcall so windll could be used in the Python code. The other option is to use __cdecl (normally the default) and use cdll instead of windll in the Python code.

mydll.c (cl /W4 /LD mydll.c)


#include <stdlib.h>

__declspec(dllexport) void __stdcall Foo(unsigned char** ppMem, int* pSize)
{
    char i;
    *pSize = 4;
    *ppMem = malloc(*pSize);
    for(i = 0; i < *pSize; i++)
        (*ppMem)[i] = i;
}

demo.py (Python 2/3 and 32-/64-bit compatible)

from __future__ import print_function
from ctypes import *

Foo = WinDLL('./mydll').Foo
Foo.argtypes = POINTER(POINTER(c_ubyte)), POINTER(c_int)
Foo.restype = None

mem = POINTER(c_ubyte)()
size = c_int()
Foo(byref(mem),byref(size))
print(size.value, mem[0], mem[1], mem[2], mem[3])

Output

4 0 1 2 3
Answered By: Mark Tolonen
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.