My environment: Windows Vista 64, Python3.1, Visual Studio 2008 sp1
Error Description: I am trying to wrap a dll (eg. Lib.dll) using ctype, when proceeding to one of the function (eg. func()) in Lib.dll, an error occurred:
"WindowsError: exception: access violation reading"
I think the different between func() and previous functions of Lib.dll
written in my '.py' file is that func()
contains a DOUBLE type parameter, but I am sure that the parameter are passed correctly, since I have used c_double()
to cast it. And it seems the error occurred when entering func()
, since the first code (printf()
) inside func()
does not execute.
I also tried to run the same dll and its functions in C environment, it runs smoothly.
Further Information:
the lib.dll is compiled in MSVC (with extern "C"), and I am using CDLL for the calling type. The problem is actually happened on a function in another dll (lib2.dll). I use lib.dll only because the lib2.dll is written in C++ and I wrap the functions that I want in lib2. It looks like this:
///////////////////////////////////
// lib.cpp
lib2 l;
void func(char * c1, char * c2, double d)
{
printf("%s, %s, %f\n", c1, c2, d); // All the parameters are passed correctly
l.func(c1, c2, d); // THIS IS where the error occurred
}
///////////////////////////////////
// lib2.cpp
void lib2::func(char * c1, char * c2, double d)
{
printf(); // The error happened before this line being executed.
...
}
///////////////////////////////////
And python script looks like this:
// my.py
dll = cdll.LoadLibrary("lib.dll")
dll.some_func1(c_char_p('a'))
dll.some_func2(c_char_p('b'))
func(c_char_p('c1'), c_char_p('c2'), c_double(1.1))
////////////////////////////////////
it is also weird that lib2.dll cannot work when I use ctype to load it. It shows the function is not found. So I have to use lib.dll to call functions in lib2.dll.
Could anyone give me some hints? Thanks
ctypes
is for C, but you can write a wrapper to expose a C++ class. Since you mentioned you use Python 3.1, I also noted you have c_char_p('c1')
where 'c1'
is a Unicode string. Since the example provided is not a complete example that can be used as is to reproduce the problem, it's difficult to tell what problem you are having.
Below is a complete, working example. You can build it from a Visual Studio command prompt by running "nmake".
This wrapper "flattens" the C++ object into a C API.
#include "lib2.h"
extern "C" {
__declspec(dllexport) lib2* lib2_new() { return new lib2; }
__declspec(dllexport) void lib2_delete(lib2* p) { delete p; }
__declspec(dllexport) void lib2_func(lib2* p, char* c1, char* c2, double d) {
p->func(c1,c2,d);
}
}
#ifdef LIB2_EXPORTS
# define LIB2_API __declspec(dllexport)
#else
# define LIB2_API __declspec(dllimport)
#endif
class LIB2_API lib2
{
public:
void func(char * c1, char * c2, double d);
};
#include <stdio.h>
#include "lib2.h"
void lib2::func(char * c1, char * c2, double d)
{
printf("%s %s %f\n",c1,c2,d);
}
all: lib1.dll lib2.dll
lib1.dll: lib1.cpp lib2.dll
cl /nologo /LD /W4 lib1.cpp -link lib2.lib
lib2.dll: lib2.cpp lib2.h
cl /nologo /LD /W4 /D LIB2_EXPORTS lib2.cpp
#!python3
from ctypes import *
class lib2:
lib1 = CDLL('lib1')
# It's best to declare all arguments and types, so Python can typecheck.
lib1.lib2_new.argtypes = []
lib1.lib2_new.restype = c_void_p # Can use this for an opaque pointer.
lib1.lib2_func.argtypes = [c_void_p,c_char_p,c_char_p,c_double]
lib1.lib2_func.restype = None
lib1.lib2_delete.argtypes = [c_void_p]
lib1.lib2_delete.restype = None
def __init__(self):
self.obj = self.lib1.lib2_new()
def __del__(self):
self.lib1.lib2_delete(self.obj)
def func(self,c1,c2,d):
self.lib1.lib2_func(self.obj,c1,c2,d)
o = lib2()
o.func(b'abc',b'123',1.2) # Note byte strings
C:\temp>nmake
Microsoft (R) Program Maintenance Utility Version 11.00.50727.1
Copyright (C) Microsoft Corporation. All rights reserved.
cl /nologo /LD /W4 /D LIB2_EXPORTS lib2.cpp
lib2.cpp
Creating library lib2.lib and object lib2.exp
cl /nologo /LD /W4 lib1.cpp -link lib2.lib
lib1.cpp
Creating library lib1.lib and object lib1.exp
C:\temp>test.py
abc 123 1.200000
Since writing wrappers can be tedious, it is better to use something like boost::Python
, Cython
, or SWIG
. I'm most familiar with SWIG, so here's another example:
all: _lib2.pyd lib2.dll
PYTHON_ROOT = c:\python33
lib2_wrap.cxx: lib2.i
@echo Generating wrapper...
swig -c++ -python lib2.i
_lib2.pyd: lib2_wrap.cxx lib2.dll
cl /nologo /EHsc /MD /LD /W4 /I$(PYTHON_ROOT)\include lib2_wrap.cxx -link lib2.lib /LIBPATH:$(PYTHON_ROOT)\libs /OUT:_lib2.pyd
lib2.dll: lib2.cpp lib2.h
cl /nologo /LD /W4 /D LIB2_EXPORTS lib2.cpp
%module lib2
%begin %{
#pragma warning(disable:4127 4211 4706)
%}
%{
#include "lib2.h"
%}
%include <windows.i>
%include "lib2.h"
#!python3
import lib2
o = lib2.lib2()
o.func('abc','123',1.2) #Note SWIG encodes Unicode strings by default
C:\temp>nmake /las
Generating wrapper...
lib2.cpp
Creating library lib2.lib and object lib2.exp
lib2_wrap.cxx
Creating library lib2_wrap.lib and object lib2_wrap.exp
C:\temp>test
abc 123 1.200000