3

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")
e9wikner
  • 51
  • 5
  • 1
    `const_cast(spec.c_str())` looks horrible. If that function is modifying what that points to in any way then the program behaviour is undefined. – Bathsheba Nov 06 '14 at 11:06
  • @Bathsheba replacing that with `itemid` according to the following buit it didn't solve the access violation: `char itemid[PCMS_L_ID+1] = { '\0' }; strncpy(itemid,spec.c_str(), PCMS_L_ID);` – e9wikner Nov 06 '14 at 12:30

0 Answers0