I'm working on a wrapper module in Python for an api that's written in c++. The api itself is a wrapper for a c library/api for a Windows (7) application that's installed locally on my computer. I have access to the c++ api sourcecode but not the c library. I build the Cython module with distutils in Python 2.7.5 (i.e. VS 9.0) and also tried 3.4.1 (i.e. VS 10.0) both 32bit installations.
What happens when accessing the module is that there is a popup saying that python.exe has stopped working
and then Process finished with exit code -1073741819 (0xC0000005)
when pressing close. When debugging (I have Visual Studio 2012) I get: Unhandled exception at 0x64ED1EB2 (apiclient10m.dll) in python.exe: 0xC0000005: Access violation reading location 0x7C03B776
. I also sometimes get Unhandled exception at 0x1E0EFECF (python27.dll) in python.exe: 0xC0000005: Access violation writing location 0x5753422C.
depending on how I call the api.
I think it's related to an object "ObjStruct" that's defined in the c++ api and sent to the c library for updating. This object is then read in c++ and an int is sent back to Cython. Somewhere in the call stack when returning some memory location is access that is not allowed.
This is what it looks like:
mymodule.pyx:
# distutils: language = c++
# distutils: extra_compile_args = ["/EHsc"]
# distutils: sources = [connection.cpp, object.cpp]
# distutils: include_dirs=['C:\\Program Files (x86)\\App\\api']
# distutils: library_dirs=['C:\\Program Files (x86)\\App\\api']
from libcpp.string cimport string
from cython.operator cimport dereference as deref
cdef extern from "connection.h" namespace "pvcs":
cdef cppclass connection:
connection() except +
...
cdef class PyConnection:
cdef connection *_cpp_connection_instance # Pointer to wrapped connection instance
def __cinit__(self):
self._cpp_connection_instance = new connection()
def __dealloc__(self):
del self._cpp_connection_instance
cdef extern from "object.h" namespace "pvcs":
cdef int get_baseline_uid(connection c, string spec)
def get_uid(PyConnection connection, bytes specification):
return get_baseline_uid(
deref(connection._cpp_connection_instance), specification)
The get_uid method is the one that fails.
c++ api:
namespace pvcs {
class connection {
public:
connection();
~connection();
int get_baseline_uid(connection & c, std::string & spec) {
ObjStruct os = { 0 };
//Fills the struct os with the information
if(InitSpec(c.uid(), const_cast<char *>(spec.c_str()), BASELINE, &os) == 1) {
int uid = os.uid;
ObjFree(&os);
return uid; // Somewhere after this point the error occurs.
}
return -1;
}
The connection part works fine where a window is shown for logging in to the application. It's the get_baseline_uid that fails. parts of the c api h-file:
typedef struct ObjStruct
{
int uid;
int objType;
int typeUid;
_TCHAR typeName[(L_TYPE_NAME + 1)*CHARSIZEMAX];
_TCHAR productId[(L_PRODUCT_ID + 1)*CHARSIZEMAX];
_TCHAR objId[(MAX_L_ID + 1)*CHARSIZEMAX];
_TCHAR variant[(L_VARIANT + 1)*CHARSIZEMAX];
_TCHAR revision[(L_REVISION + 1)*CHARSIZEMAX];
_TCHAR description[(L_DESCRIPTION + 1)*CHARSIZEMAX];
_TCHAR userName[(L_USER + 1)*CHARSIZEMAX];
_TCHAR status[(L_STATUS + 1)*CHARSIZEMAX];
_TCHAR dateTime[(L_DATE_TIME + 1)*CHARSIZEMAX];
_TCHAR isExtracted;
int noAttrs; /* The number of attributes for this object. */
ObjAttrStruct *attrs; /* Pointer to the array of attributes. */
int specUid;
} ObjStruct;
extern int APIFUN CCONV InitSpec(int, _TCHAR*, int, PcmsObjStruct*);
The h-file resides in the directory specified to distutils C:\Program Files (x86)\App\api
together with a bunch of static libraries (.lib-files). These libraries seem to access dll files in the C:\Program Files (x86)\App\prog
folder. This is where the dll apiclient10m.dll
that causes the access violation is.
distutils setup.py:
from distutils.core import setup
from Cython.Build import cythonize
METADATA = {'name': 'mymodule',
'ext_modules': cythonize("mymodule.pyx"),}
if __name__ == '__main__':
metadata = METADATA.copy()
setup(**metadata)
Everything compiles and links fine but when running e.g. this it fails in the last row according to what I explained above:
import mymodule
con = mymodule.PyConnection()
uid = mymodule.get_uid(con, "item specification")