Is there any way to know which functions are exported from the dll
through python foreign function library ctypes
?
And if possible to know details about the exported functions through ctypes
.
If yes, could someone provide a snippet of code?
Is there any way to know which functions are exported from the dll
through python foreign function library ctypes
?
And if possible to know details about the exported functions through ctypes
.
If yes, could someone provide a snippet of code?
I don't think ctypes offers this functionality. On Windows with visual studio:
DUMPBIN -EXPORTS XXX.DLL
Or for mingw on windows:
objdump -p XXX.dll
If you are on Linux, there is a handy utility nm
to list the content of a shared library (there is always a handy utility on Linux, especially for C stuff).
Here is the question about it.
You use it with the -D
flag: nm -D ./libMyLib.so
In general, this is not possible, because, again in general, dynamically loaded libraries do not carry the meta-information you require. It may be possible to obtain that information in certain special cases through system-specific ways, but ctypes
itself does not fetch that information. You can record such info via ctypes
(see e.g. the restype and argtypes
attributes of function pointers), but only after you have obtained it by different means.
@Mark's answer uses Visual Studio tools.
On windows you can also use Dependency Walker to get the function names of dll exports.
Sometimes names are mangled and can't be used as a valid python function name.
You can use getattr
to get a handle to mangled functions, e.g:
mylib = ctypes.cdll('mylib.dll')
my_func = getattr(mylib, '_my_func@0')
my_func()
If you've also got the source for said library, and you're looking for a fully automated all-python way, you could use pycparser
for the file: prog.c
typedef short int ret_t;
typedef short int param_t;
ret_t add(param_t a, param_t b) {
return (ret_t)(a + b);
}
ret_t passthrough(ret_t (* func)(param_t a, param_t b), param_t a, param_t b) {
// parameter intentionally altered.
// if this isn't done, compiler will deem entire function redundant
return func(a, b + 1);
}
compiling with gcc
gcc -I. -E ./prog.c > prog-preproc.c
gives us the pre-processed c file: prog-preproc.c
then in python:
import pycparser
parser = pycparser.c_parser.CParser()
with open('prog-preproc.c', 'r') as fh:
ast = parser.parse(fh.read())
class FunctionVisitor(pycparser.c_ast.NodeVisitor):
def visit_FuncDef(self, node):
print("found function: %s" % node.decl.name)
#node.show()
FunctionVisitor().visit(ast)
yields
found function: add
found function: passthrough
To dig further you can also get parameter and return types.
Uncomment node.show()
for more information from within the Abstract Syntax Tree (AST)
I'll be releasing a library for this soon (I'll try to remember to come back and drop a link)
Internally ctypes uses functions provided by dynamic link library (dlopen/dlsym on unix, LoadLibrary/GetProcAddress on windows) to load library and find address of function specified by function name; and then use cffi library to pass parameter dynamically.
Problem is that the dynamic link library that ctypes depends on doesn't include function to list symbol from the shared library, that's why you can't list symbol by ctypes.
To do that, you have to use specific tools to dump elf file (readelf on unix) and pe file for dll (dumpbin on windows).
YES! there is a very clever native method to do it.
let's say you are using Python ctypes. put something like this in your C code:
1) in your C code:
#define PYEXPORT extern "C" __declspec(dllexport)
now put PYEXPORT above the function you want to export:
PYEXPORT
int myfunc(params){
2) After compiling, go back into Python and open your .c file, and parse it similar to this:
source1_ = open(cfile_name + '.c')
source1 = source1_.read()
source1_.close()
fn = source1.split('PYEXPORT')[-1].split('(')[0].split(' ')[1]
shell input: fn
shell output: 'myfunc'
3) Now here's the clever part: define a new function in a string:
a1 = """
global get_c_fn
def get_c_fn(dll):
func = dll."""
a2 = """
return func"""
a3 = a1 + fn + a2
print(a3)
global get_c_fn
def get_c_fn(dll):
func = dll.myfunc
return func
NOW do exec(a3) and it will turn that string into a function that you can use.
4) do the usual:
mydll = ctypes.CDLL(cfile_name + '.dll')
c_fn = get_cuda_fn(mydll)
c_fn.argtypes = func_params (an array of C-converted inputs you need)
c_fn( *[params] )
and there you have a python wrapper for a C script without having to modify ten different things every time something changes.