python windows dll ctypes

Python ctypes cdll.LoadLibrary, crea una instancia de un objeto, ejecuta su método, se trunca la dirección de la variable privada



windows (1)

Escribí una biblioteca dll en c, compilo con vs2017 de 64 bits, y trato de cargarla con python3.6 de 64 bits. Sin embargo, la dirección de la variable miembro del objeto se truncó a 32 bits.

Aquí está mi archivo sim.c, que se compila a 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); } }

Aquí está mi script de 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)

Aquí está la dirección de la member_var en addr_debug.txt:

member_var init address: 0000025259E123C4 member_var process address: 0000000059E123C4

Así que accediendo a ella desencadena error de acceso a memoria:

OSError: exception: access violation reading 0000000059E123C4

Algunos intentos intenté entender el problema:

  • Defina member_var como público en lugar de privado, no ayuda, la dirección aún está truncada.
  • Defina member_var como variable global, entonces la dirección está bien. Así que supongo que el truncamiento de la dirección member_var ocurre cuando se devuelve el objeto a Python, o se devuelve el objeto a dll.

Siempre (CORRECTAMENTE) especifique argtypes y vuelva a escribir para las funciones definidas en C , de lo contrario (estilo C89 ) se predeterminarán a int ( 32 bits ), generando !!! Comportamiento indefinido !!! . En 64 bits , puede provocar un truncamiento (que es exactamente lo que está experimentando).

Además, cuando tenga problemas, no se olvide de [Python 3.Docs]: ctypes - Una biblioteca de funciones ajena a Python .

A continuación se muestra una versión adaptada de su código.

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()

Notas :

  • Como dije al principio, el problema fue que no se especificaron los argumentos y los tipos de reingreso (por ejemplo, para DetectorNew : comment detector_new_func.restype = c_void_p , y volverá a encontrar el problema)
  • En el código de la pregunta faltan partes ( #include s, import s, ...), también hay algunos errores de sintaxis, por lo que no se compila, y por lo tanto no sigue [SO]: Cómo crear un Mínimo, Ejemplo completo, y verificable (mcve) . Por favor, cuando esté seguro de tener mcve al preguntar
  • El objeto que asigne ( new Detector() ), también debe ser desasignado (de lo contrario, generará una pérdida de memoria ), así que agregué una función ( DetectorDelete - para hacer eso), que se llama desde ( Python ) Detector ''s incinerador de basuras
  • Otros cambios (no críticos) (cambio de nombre de identificadores, un poco de refactorización, impresión en stdout , ...)

Salida :

(py35x64_tes1) e:/Work/Dev//q052268294>"c:/Install/x86/Microsoft/Visual Studio Community/2015/vc/vcvarsall.bat" x64 (py35x64_test) e:/Work/Dev//q052268294>dir /b code.py detector.cpp (py35x64_test) e:/Work/Dev//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//q052268294>dir /b code.py detector.cpp detector.obj sim.dll sim.exp sim.lib (py35x64_test) e:/Work/Dev//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