Python ctypes cdll.LoadLibrary,实例化一个对象,执行它的方法,私有变量地址被截断
Python ctypes cdll.LoadLibrary, instantiate an object, execute its method, private variable address truncated
我用c写了一个dll库,用vs2017 64位编译,用python3.6 64位加载试试。但是,对象的成员变量地址被截断为 32 位。
这是我的 sim.c 文件,它被编译为 sim.dll:
class Detector {
public:
Detector();
void process(int* pin, int* pout, int n);
private:
int member_var;
};
Detector::Detector()
{
memset(&member_var, 0, sizeof(member_var));
myfile.open("addr_debug.txt");
myfile << "member_var init address: " << &member_var << endl;
}
void Detector::process(int* pin, int* pout, int n);
{
myfile << "member_var process address: " << &member_var << endl;
myfile.close();
}
#define DllExport __declspec( dllexport )
extern "C" {
DllExport Detector* Detector_new() { return new Detector(); }
DllExport void Detector_process(Detector* det, int* pin, int* pout, int n)
{
det->process(pin, pout, n);
}
}
这是我的 python 脚本:
from ctypes import cdll
lib = cdll.LoadLibrary(r'sim.dll')
class Detector(object):
def __init__(self):
self.obj = lib.Detector_new()
def process(self,pin, pout, n):
lib.Detector_process(self.obj,pin, pout, n)
detector = Detector()
n = 1024
a = np.arange(n, dtype=np.uint32)
b = np.zeros(n, dtype=np.int32)
aptr = a.ctypes.data_as(ctypes.POINTER(ctypes.c_int))
bptr = b.ctypes.data_as(ctypes.POINTER(ctypes.c_int))
detector.process(aptr, bptr, n)
这是addr_debug.txt中member_var的地址:
member_var init address: 0000025259E123C4
member_var process address: 0000000059E123C4
因此访问它会触发内存访问错误:
OSError: exception: access violation reading 0000000059E123C4
我试图理解这个问题的一些尝试:
- 将 member_var 定义为 public 而不是私有地址,无济于事,地址仍然被截断。
- 定义member_var为全局变量,地址就可以了。所以我猜 member_var 地址截断发生在将对象返回到 python 或将对象传递回 dll 时。
始终(正确地)为 C[= 中定义的函数指定 argtypes 和 restype 66=],否则(C89风格)它们会默认为int(一般32bit),生成!!!未定义的行为 !!!。在 64 位 上,地址(大于 2 GiB)将被 t运行 分类(这正是你正在经历的)。查看 了解更多详情。
此外,当 运行遇到问题时,不要忘记 [Python 3.Docs]: ctypes - A foreign function library for Python。
下面是您的代码的改编版本。
detector.cpp:
#include <stdio.h>
#include <memory.h>
#include <fstream>
#define C_TAG "From C"
#define PRINT_MSG_2SP(ARG0, ARG1) printf("%s - [%s] (%d) - [%s]: %s: 0x%0p\n", C_TAG, __FILE__, __LINE__, __FUNCTION__, ARG0, ARG1)
using std::endl;
std::ofstream outFile;
class Detector {
public:
Detector();
void process(int *pIn, int *pOut, int n);
private:
int m_var;
};
Detector::Detector()
: m_var(0) {
outFile.open("addr_debug.txt");
outFile << "m_var init address: " << &m_var << endl;
PRINT_MSG_2SP("&m_var", &m_var);
}
void Detector::process(int *pIn, int *pOut, int n) {
outFile << "m_var process address: " << &m_var << endl;
outFile.close();
PRINT_MSG_2SP("&m_var", &m_var);
}
#define SIM_EXPORT __declspec(dllexport)
#if defined(__cplusplus)
extern "C" {
#endif
SIM_EXPORT Detector *DetectorNew() { return new Detector(); }
SIM_EXPORT void DetectorProcess(Detector *pDet, int *pIn, int *pOut, int n) {
pDet->process(pIn, pOut, n);
}
SIM_EXPORT void DetectorDelete(Detector *pDet) { delete pDet; }
#if defined(__cplusplus)
}
#endif
code.py:
import sys
from ctypes import CDLL, POINTER, \
c_int, c_void_p
import numpy as np
sim_dll = CDLL("./sim.dll")
detector_new_func = sim_dll.DetectorNew
detector_new_func.restype = c_void_p
detector_process_func = sim_dll.DetectorProcess
detector_process_func.argtypes = [c_void_p, POINTER(c_int), POINTER(c_int), c_int]
detector_delete_func = sim_dll.DetectorDelete
detector_delete_func.argtypes = [c_void_p]
class Detector():
def __init__(self):
self.obj = detector_new_func()
def process(self, pin, pout, n):
detector_process_func(self.obj, pin, pout, n)
def __del__(self):
detector_delete_func(self.obj)
def main():
detector = Detector()
n = 1024
a = np.arange(n, dtype=np.uint32)
b = np.zeros(n, dtype=np.int32)
aptr = a.ctypes.data_as(POINTER(c_int))
bptr = b.ctypes.data_as(POINTER(c_int))
detector.process(aptr, bptr, n)
if __name__ == "__main__":
print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
main()
备注:
- 正如我在开头所说,问题是 argtypes 和 restype 没有被指定(例如 DetectorNew :评论
detector_new_func.restype = c_void_p
,你会运行再次陷入问题)
- 问题中的代码缺少部分(#includes,imports,...),还有一些语法错误,因此它无法编译,因此不遵循 [SO]: How to create a Minimal, Complete, and Verifiable example (mcve) 准则。请在询问
时确保有mcve
- 你分配的对象(
new Detector()
),也必须被释放(否则会产生内存泄漏),所以我添加了一个函数(DetectorDelete - 这样做),这是从 (Python) Detector 调用的析构函数
- 其他(非关键)更改(标识符重命名、一些重构、打印到 stdout、...)
输出:
(py35x64_tes1) e:\Work\Dev\Whosebug\q052268294>"c:\Install\x86\Microsoft\Visual Studio Community15\vc\vcvarsall.bat" x64
(py35x64_test) e:\Work\Dev\Whosebug\q052268294>dir /b
code.py
detector.cpp
(py35x64_test) e:\Work\Dev\Whosebug\q052268294>cl /nologo /DDLL /EHsc detector.cpp /link /DLL /OUT:sim.dll
detector.cpp
Creating library sim.lib and object sim.exp
(py35x64_test) e:\Work\Dev\Whosebug\q052268294>dir /b
code.py
detector.cpp
detector.obj
sim.dll
sim.exp
sim.lib
(py35x64_test) e:\Work\Dev\Whosebug\q052268294>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" ./code.py
Python 3.5.4 (v3.5.4:3f56838, Aug 8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)] on win32
From C - [detector.cpp] (28) - [Detector::Detector]: &m_var: 0x0000020CE366E270
From C - [detector.cpp] (34) - [Detector::process]: &m_var: 0x0000020CE366E270
我用c写了一个dll库,用vs2017 64位编译,用python3.6 64位加载试试。但是,对象的成员变量地址被截断为 32 位。
这是我的 sim.c 文件,它被编译为 sim.dll:
class Detector {
public:
Detector();
void process(int* pin, int* pout, int n);
private:
int member_var;
};
Detector::Detector()
{
memset(&member_var, 0, sizeof(member_var));
myfile.open("addr_debug.txt");
myfile << "member_var init address: " << &member_var << endl;
}
void Detector::process(int* pin, int* pout, int n);
{
myfile << "member_var process address: " << &member_var << endl;
myfile.close();
}
#define DllExport __declspec( dllexport )
extern "C" {
DllExport Detector* Detector_new() { return new Detector(); }
DllExport void Detector_process(Detector* det, int* pin, int* pout, int n)
{
det->process(pin, pout, n);
}
}
这是我的 python 脚本:
from ctypes import cdll
lib = cdll.LoadLibrary(r'sim.dll')
class Detector(object):
def __init__(self):
self.obj = lib.Detector_new()
def process(self,pin, pout, n):
lib.Detector_process(self.obj,pin, pout, n)
detector = Detector()
n = 1024
a = np.arange(n, dtype=np.uint32)
b = np.zeros(n, dtype=np.int32)
aptr = a.ctypes.data_as(ctypes.POINTER(ctypes.c_int))
bptr = b.ctypes.data_as(ctypes.POINTER(ctypes.c_int))
detector.process(aptr, bptr, n)
这是addr_debug.txt中member_var的地址:
member_var init address: 0000025259E123C4
member_var process address: 0000000059E123C4
因此访问它会触发内存访问错误:
OSError: exception: access violation reading 0000000059E123C4
我试图理解这个问题的一些尝试:
- 将 member_var 定义为 public 而不是私有地址,无济于事,地址仍然被截断。
- 定义member_var为全局变量,地址就可以了。所以我猜 member_var 地址截断发生在将对象返回到 python 或将对象传递回 dll 时。
始终(正确地)为 C[= 中定义的函数指定 argtypes 和 restype 66=],否则(C89风格)它们会默认为int(一般32bit),生成!!!未定义的行为 !!!。在 64 位 上,地址(大于 2 GiB)将被 t运行 分类(这正是你正在经历的)。查看
此外,当 运行遇到问题时,不要忘记 [Python 3.Docs]: ctypes - A foreign function library for Python。
下面是您的代码的改编版本。
detector.cpp:
#include <stdio.h>
#include <memory.h>
#include <fstream>
#define C_TAG "From C"
#define PRINT_MSG_2SP(ARG0, ARG1) printf("%s - [%s] (%d) - [%s]: %s: 0x%0p\n", C_TAG, __FILE__, __LINE__, __FUNCTION__, ARG0, ARG1)
using std::endl;
std::ofstream outFile;
class Detector {
public:
Detector();
void process(int *pIn, int *pOut, int n);
private:
int m_var;
};
Detector::Detector()
: m_var(0) {
outFile.open("addr_debug.txt");
outFile << "m_var init address: " << &m_var << endl;
PRINT_MSG_2SP("&m_var", &m_var);
}
void Detector::process(int *pIn, int *pOut, int n) {
outFile << "m_var process address: " << &m_var << endl;
outFile.close();
PRINT_MSG_2SP("&m_var", &m_var);
}
#define SIM_EXPORT __declspec(dllexport)
#if defined(__cplusplus)
extern "C" {
#endif
SIM_EXPORT Detector *DetectorNew() { return new Detector(); }
SIM_EXPORT void DetectorProcess(Detector *pDet, int *pIn, int *pOut, int n) {
pDet->process(pIn, pOut, n);
}
SIM_EXPORT void DetectorDelete(Detector *pDet) { delete pDet; }
#if defined(__cplusplus)
}
#endif
code.py:
import sys
from ctypes import CDLL, POINTER, \
c_int, c_void_p
import numpy as np
sim_dll = CDLL("./sim.dll")
detector_new_func = sim_dll.DetectorNew
detector_new_func.restype = c_void_p
detector_process_func = sim_dll.DetectorProcess
detector_process_func.argtypes = [c_void_p, POINTER(c_int), POINTER(c_int), c_int]
detector_delete_func = sim_dll.DetectorDelete
detector_delete_func.argtypes = [c_void_p]
class Detector():
def __init__(self):
self.obj = detector_new_func()
def process(self, pin, pout, n):
detector_process_func(self.obj, pin, pout, n)
def __del__(self):
detector_delete_func(self.obj)
def main():
detector = Detector()
n = 1024
a = np.arange(n, dtype=np.uint32)
b = np.zeros(n, dtype=np.int32)
aptr = a.ctypes.data_as(POINTER(c_int))
bptr = b.ctypes.data_as(POINTER(c_int))
detector.process(aptr, bptr, n)
if __name__ == "__main__":
print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
main()
备注:
- 正如我在开头所说,问题是 argtypes 和 restype 没有被指定(例如 DetectorNew :评论
detector_new_func.restype = c_void_p
,你会运行再次陷入问题) - 问题中的代码缺少部分(#includes,imports,...),还有一些语法错误,因此它无法编译,因此不遵循 [SO]: How to create a Minimal, Complete, and Verifiable example (mcve) 准则。请在询问 时确保有mcve
- 你分配的对象(
new Detector()
),也必须被释放(否则会产生内存泄漏),所以我添加了一个函数(DetectorDelete - 这样做),这是从 (Python) Detector 调用的析构函数 - 其他(非关键)更改(标识符重命名、一些重构、打印到 stdout、...)
输出:
(py35x64_tes1) e:\Work\Dev\Whosebug\q052268294>"c:\Install\x86\Microsoft\Visual Studio Community15\vc\vcvarsall.bat" x64 (py35x64_test) e:\Work\Dev\Whosebug\q052268294>dir /b code.py detector.cpp (py35x64_test) e:\Work\Dev\Whosebug\q052268294>cl /nologo /DDLL /EHsc detector.cpp /link /DLL /OUT:sim.dll detector.cpp Creating library sim.lib and object sim.exp (py35x64_test) e:\Work\Dev\Whosebug\q052268294>dir /b code.py detector.cpp detector.obj sim.dll sim.exp sim.lib (py35x64_test) e:\Work\Dev\Whosebug\q052268294>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" ./code.py Python 3.5.4 (v3.5.4:3f56838, Aug 8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)] on win32 From C - [detector.cpp] (28) - [Detector::Detector]: &m_var: 0x0000020CE366E270 From C - [detector.cpp] (34) - [Detector::process]: &m_var: 0x0000020CE366E270