w00dLAN
w00dLAN

Reputation: 1

How to call a C function with an output parameter

A have code in my dll (on C++). The function must accept a pointer to long as an output variable:

int add(int a, long *ddd)
{   
    *ddd = 1758;
    return a;
}

I am using Python 3:

from ctypes import *
import os

adr = os.getcwd()
dll = CDLL(adr + '\mydllcode.dll')

myint = 999
mylong = c_long(8888)
p_myint = c_int(myint)
p_mychar = c_wchar_p(mychar)
p_mylong = POINTER(mylong)

dll.add.argtypes = [c_int, POINTER(c_long)]
ret = dll.sub(p_myint, p_mylong)
print(ret)
print(str(mylong) + ' - ' + p_mylong.value)

And I get the error:

Traceback (most recent call last):
File "C:\PythonProjects\Shield\testdll.py", line 12, in <module>
p_mylong = POINTER(mylong)
TypeError: unhashable type

Upvotes: 0

Views: 905

Answers (2)

Mark Tolonen
Mark Tolonen

Reputation: 177675

POINTER(ctypes-type) declares a pointer type and its parameter must be a ctypes type, so below was incorrect:

mylong = c_long(8888)        # Creates a ctypes instance, not a type.
p_mylong = POINTER(mylong)  # error

When you create a ctype to hold the output parameter, simply create an instance of the type:

mylong = c_long()  # defaults to a value of zero, but for output parameter doesn't matter

Then pass it as a parameter by reference with:

byref(mylong)

Also, wrapping input values is generally unncessary if you set .argtypes. ctypes knows to wrap Python values as declared, so you can just pass them directly.

Example:

test.c

__declspec(dllexport)
int add(int a, long *ddd)
{   
    *ddd = 1758;
    return a;
}

test.py

import ctypes as ct

dll = ct.CDLL('./test') # look in current directory for DLL.
dll.add.argtypes = ct.c_int,ct.POINTER(ct.c_long)
dll.add.restype = ct.c_int

mylong = ct.c_long() # instance to store output variable
ret = dll.add(999,ct.byref(mylong))  # pass by reference
print(ret,mylong.value)  # .value extracts the result as a Python object.

Output:

999 1758

You can create pointers directly with pointer(instance) as well, so below also works, but is less efficient than byref:

import ctypes as ct

dll = ct.CDLL('./test')
dll.add.argtypes = ct.c_int,ct.POINTER(ct.c_long) # pointer type. Note upper case!
dll.add.restype = ct.c_int

mylong = ct.c_long()          # create ctypes instance
p_mylong = ct.pointer(mylong) # create pointer to instance.  Note lower case!
ret = dll.add(999,p_mylong)
print(ret,mylong.value)

Upvotes: 1

Paul Brennan
Paul Brennan

Reputation: 2696

I modified your code a little to Change the definitions to match. Following the ideas in the answer to Passing pointers to DLL function in Python , this may help

from ctypes import *
import os

adr = os.getcwd()
dll = CDLL(adr + '\mydllcode.dll')

myint = 999
mylong = c_long(8888)
p_myint = c_int(myint)
p_mychar = c_wchar_p(mychar)
p_mylong_p = ctypes.POINTER(ctypes.c_long)

dll.add.argtypes = [c_int, p_mylong_p]
ret = dll.sub(p_myint, ctypes.byref(mylong))
print(ret)
print(str(mylong) + ' - ' + p_mylong.value)

Upvotes: 0

Related Questions