Question

Hi @eryksun and Python experts:

I am trying to import a dll file into python with cffi with the following commands:

from cffi import FFI
ffi=FFI()
lib=ffi.dlopen('ATC3DG.DLL')

From a previous question about ctypes, I know that DLL are all cdecl (thanks to @eryksun's hints), and I was able to access its functions in the following fashion:

from ctypes import *
lib=windll.LoadLibrary('ATC3DG.DLL')
lib._InitializeBIRDSystem

However, I am not sure what would be the equivalent operations in cffi. lib._InitializeBIRDSystem works under ctypes but not cffi. Any suggestion?

Thanks for looking into this,

Erik

Was it helpful?

Solution

The symbols exported by atc3dg.dll use a leading underscore, which is unusual for cdecl. You'll have to add the underscore in the definitions used in ffi.cdef, just as you did with ctypes.

import cffi

ffi = cffi.FFI()
ffi.cdef('''
enum MESSAGE_TYPE
{
    SIMPLE_MESSAGE,     // short string describing error code
    VERBOSE_MESSAGE,    // long string describing error code
};

int _InitializeBIRDSystem(void);

int _GetErrorText(
    int                 errorCode,
    char                *pBuffer,
    int                 bufferSize,
    enum MESSAGE_TYPE   type
    );
''')

lib = ffi.dlopen('atc3dg.dll')
buf = ffi.new('char[100]')

err = lib._InitializeBIRDSystem()
lib._GetErrorText(err, buf, len(buf), lib.SIMPLE_MESSAGE)
print(ffi.string(buf).decode('ascii'))

# output:
# System         : No BIRDs were found anywhere

If you have a C compiler configured, you can use ffi.verify(). I'm not very experienced with cffi (not yet, but it looks promising), so take this with a grain of salt.

I had to make a small change to the header. In the definition of COMMUNICATIONS_MEDIA_PARAMETERS, line 500 needs to add enum as follows:

enum COMMUNICATIONS_MEDIA_TYPE mediaType;

Also, just as an FYI, the compiler printed a few warnings, such as "conversion from 'unsigned __int64' to 'unsigned short', possible loss of data". I haven't followed up on this in the generated source for the extension module.

import os
import cffi
import subprocess

pth = os.path.abspath(os.path.dirname(__file__))
os.environ['PATH'] += ';%s' % pth

# preprocess: modify this for your compiler
# Using Microsoft's cl.exe that comes with VC++.
# cdef chokes on __declspec, so define DEF_FILE.
cmd = 'cl.exe /DDEF_FILE /EP %s' % os.path.join(pth, 'atc3dg.h')
hdr = subprocess.check_output(cmd, universal_newlines=True)

ffi = cffi.FFI()
ffi.cdef(hdr)

# using __declspec(dllimport) links more efficiently,
# but skipping it still works fine
lib = ffi.verify(hdr, library_dirs=[pth], libraries=['atc3dg'])
buf = ffi.new('char[100]')

err = lib.InitializeBIRDSystem()
lib.GetErrorText(err, buf, len(buf), lib.SIMPLE_MESSAGE)
print(ffi.string(buf).decode('ascii'))
# output:
# System         : No BIRDs were found anywhere

On the plus side, this approach avoids the leading underscore. It's an ABI detail handled by the linker when the extension module is compiled.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top