python ctypes and C++ pointers - python

C++ funtion:
DLLENTRY int VTS_API
SetParamValue( const char *paramName, void *paramValue ) //will return zero(0) in the case of an error
{
rtwCAPI_ModelMappingInfo* mmi = &(rtmGetDataMapInfo(PSA_StandAlone_M).mmi);
int idx = getParamIdx( paramName );
if (idx<0) {
return 0; //Error
}
int retval = capi_ModifyModelParameter( mmi, idx, paramValue );
if (retval == 1 ) {
ParamUpdateConst();
}
return retval;
}
and my Python code:
import os
from ctypes import *
print(os.getcwd())
os.chdir(r"C:\MY_SECRET_DIR")
print(os.getcwd())
PSAdll=cdll.LoadLibrary(os.getcwd()+"\PSA_StandAlone_1.dll")
setParam=PSAdll.SetParamValue
setParam.restype=c_int
setParam.argtypes=[c_char_p, c_void_p]
z=setParam(b"LDOGenSpdSetpoint", int(20) )
returns
z=setParam(b"LDO_PSA_GenSpdSetpoint01", int(20) )
WindowsError: exception: access violation reading 0x00000020
Any idea what can help?
I already tried POINTER and byref(), but I am getting the same output

SetParamValue expects a pointer (void* or c_void_p) as the 2nd argument, but you're passing an int. The function will interpret it as a pointer (memory address), and when attempting to dereference it (to get its content), it will segfault (Access Violation), as the process doesn't have permissions on that address.
To fix the problem, pass the proper arguments to the function:
z = setParam(b"LDOGenSpdSetpoint", pointer(c_int(20)))
You can find more details on [Python 3]: ctypes - A foreign function library for Python.

Related

Calling C++ function which accepts and returns std::string from Python

I'm trying to call C++ function using ctypes. The function declaration looks like
std::string some_function( const std::string &param )
As Python can not interact with C++ directly, I have created a C-wrapper for this function as follows
const char *my_function( const char *param )
{
std::string result = some_function( std::string( param ) );
char *ret = new char[result.length() + 1];
strcpy(ret, result.c_str());
return ret;
}
And Python wrapper
def my_function(param):
func = lib.my_function
func.argtypes = [ctypes.c_char_p]
func.restype = ctypes.c_char_p
result = func(ctypes.c_char_p(param))
return result
But when I try to call Python wrapper with some bytes passed as param, for example
my_function(b'GP\x00\x01\xe6\x10\x00\x00\x01\x01\x00\x00\x00\x1ex\xcb\xa16l\xf1\xbfp\xe6\xaa\xc89\x81\xdd?')
It fails with the following error
terminate called after throwing an instance of 'std::length_error'
what(): basic_string::_M_create
I'm not very good in C and C++. Can you help me to figure out how to create correct wrapper? Thanks.
Since your data contains embedded nulls, c_char_p won't work as it assumes the returned char* is null-terminated and converts the data up to the first null found to a bytes object. std::string as used also makes that assumption when pass a char* only, so it needs the data length as well.
To manage a data buffer with null content, the size of the input data must be passed in and the size of the output data must be returned. You'll also have to manage freeing the data allocated in the C++ code.
The below code demonstrates everything:
test.cpp - compiled with cl /EHsc /W4 /LD test.cpp with Microsoft Visual Studio.
#include <string>
#include <string.h>
// Windows DLLs need functions to be explicitly exported
#ifdef _WIN32
# define API __declspec(dllexport)
#else
# define API
#endif
std::string some_function(const std::string& param) {
return param + std::string("some\x00thing", 10);
}
// pLength is used as an input/output parameter.
// Pass in input length and get output length back.
extern "C" API
const char* my_function(const char* param, size_t* pLength) {
auto result = some_function(std::string(param, *pLength));
auto reslen = result.length();
auto res = new char[reslen];
// Use memcpy to handle embedded nulls
memcpy_s(res, reslen, result.data(), reslen);
*pLength = reslen;
return res;
}
extern "C" API
void my_free(const char* param) {
delete [] param;
}
test.py
import ctypes as ct
dll = ct.CDLL('./test')
# Note: A restype of ct.c_char_p is converted to a bytes object
# and access to the returned C pointer is lost. Use
# POINTER(c_char) instead to retain access and allow it
# to be freed later.
dll.my_function.argtypes = ct.c_char_p, ct.POINTER(ct.c_size_t)
dll.my_function.restype = ct.POINTER(ct.c_char)
dll.my_free.argtypes = ct.c_char_p,
dll.my_free.restype = None
def my_function(param):
# wrap size in a ctypes object so it can be passed by reference
size = ct.c_size_t(len(param))
# save the returned pointer
p = dll.my_function(param, ct.byref(size))
# slice pointer to convert to an explicitly-sized bytes object
result = p[:size.value]
# now the pointer can be freed...
dll.my_free(p)
# and the bytes object returned
return result
result = my_function(b'data\x00more')
print(result)
Output:
b'data\x00moresome\x00thing'

Ctypes calling Go Dll with arguments (C string)

How to pass a string as argument from Python to a Go Dll using ctypes:
Go-code:
package main
import "C"
import "fmt"
//export GetInt
func GetInt() int32 {
return 42
}
//export GetString
func GetString() {
fmt.Println("Foo")
}
//export PrintHello
func PrintHello(name string) {
// if name == "hello" { ... }
fmt.Printf("From DLL: Hello, %s!", name)
}
func main() {
// Need a main function to make CGO compile package as C shared library
}
Compiled on MacOs using: GOOS=windows GOARCH=386 CGO_ENABLED=1 CC=i686-w64-mingw32-gcc go build -buildmode=c-shared -o ./dist/perf_nlp.dll
Python code:
import ctypes
def getString():
nlp = ctypes.windll.LoadLibrary("H:/perf_nlp.dll")
dllFunc = nlp.GetString
dllFunc.restype = ctypes.c_char_p
return dllFunc()
def getInt():
nlp = ctypes.windll.LoadLibrary("H:/perf_nlp.dll")
dllFunc = nlp.GetInt
dllFunc.restype = int
return dllFunc()
def readString():
nlp = ctypes.windll.LoadLibrary("H:/perf_nlp.dll")
dllFunc = nlp.ReadString
dllFunc.argtypes = [ctypes.c_char_p]
dllFunc.restype = ctypes.c_char_p
return dllFunc(b'Foo')
print(getInt())
print(getString())
print(readString()). # Fails
Out:
42
Foo
None
unexpected fault address 0x871000
fatal error: fault
[signal 0xc0000005 code=0x0 addr=0x871000 pc=0x623e501f]
goroutine 17 [running, locked to thread]:
runtime.throw(0x6245b592, 0x5)
/Users/foobar/.goenv/versions/1.14.15/src/runtime/panic.go:1116 +0x64 fp=0x1242fda4 sp=0x1242fd90 pc=0x623be404
runtime.sigpanic()
/Users/foobar/.goenv/versions/1.14.15/src/runtime/signal_windows.go:249 +0x1ed fp=0x1242fdb8 sp=0x1242fda4 pc=0x623ceb8d
runtime.memmove(0x12500011, 0x800588, 0x32efe4)
/Users/foobar/.goenv/versions/1.14.15/src/runtime/memmove_386.s:89 +0x7f fp=0x1242fdbc sp=0x1242fdb8 pc=0x623e501f
fmt.(*buffer).writeString(...)
/Users/foobar/.goenv/versions/1.14.15/src/fmt/print.go:82
fmt.(*fmt).padString(0x124a60b0, 0x800588, 0x32efe4)
/Users/foobar/.goenv/versions/1.14.15/src/fmt/format.go:110 +0x6c fp=0x1242fdfc sp=0x1242fdbc pc=0x6241576c
fmt.(*fmt).fmtS(0x124a60b0, 0x800588, 0x32efe4)
/Users/foobar/.goenv/versions/1.14.15/src/fmt/format.go:359 +0x4d fp=0x1242fe14 sp=0x1242fdfc pc=0x6241664d
fmt.(*pp).fmtString(0x124a6090, 0x800588, 0x32efe4, 0x73)
/Users/foobar/.goenv/versions/1.14.15/src/fmt/print.go:450 +0x188 fp=0x1242fe38 sp=0x1242fe14 pc=0x62418f58
fmt.(*pp).printArg(0x124a6090, 0x62447c80, 0x1248c110, 0x73)
/Users/foobar/.goenv/versions/1.14.15/src/fmt/print.go:698 +0x776 fp=0x1242fe80 sp=0x1242fe38 pc=0x6241ad56
fmt.(*pp).doPrintf(0x124a6090, 0x6245e0a5, 0x14, 0x1242ff48, 0x1, 0x1)
/Users/foobar/.goenv/versions/1.14.15/src/fmt/print.go:1030 +0x12b fp=0x1242fef0 sp=0x1242fe80 pc=0x6241d81b
fmt.Fprintf(0x62476550, 0x1248c0d8, 0x6245e0a5, 0x14, 0x1242ff48, 0x1, 0x1, 0x623e2fe7, 0x0, 0x12488030)
/Users/foobar/.goenv/versions/1.14.15/src/fmt/print.go:204 +0x52 fp=0x1242ff20 sp=0x1242fef0 pc=0x62417bd2
fmt.Printf(...)
/Users/foobar/.goenv/versions/1.14.15/src/fmt/print.go:213
main.ReadString(...)
/Users/foobar/__projects__/heine/db_dll/perf_nlp.go:19
main._cgoexpwrap_c3579cea1e16_ReadString(0x800588, 0x32efe4)
_cgo_gotypes.go:71 +0x8d fp=0x1242ff54 sp=0x1242ff20 pc=0x6241e9bd
runtime.call16(0x0, 0x32ef1c, 0x32ef68, 0x8, 0x0)
/Users/foobar/.goenv/versions/1.14.15/src/runtime/asm_386.s:565 +0x30 fp=0x1242ff68 sp=0x1242ff54 pc=0x623e3020
runtime.cgocallbackg1(0x0)
/Users/foobar/.goenv/versions/1.14.15/src/runtime/cgocall.go:332 +0x149 fp=0x1242ffb0 sp=0x1242ff68 pc=0x62393f59
runtime.cgocallbackg(0x0)
/Users/foobar/.goenv/versions/1.14.15/src/runtime/cgocall.go:207 +0xb5 fp=0x1242ffe0 sp=0x1242ffb0 pc=0x62393d85
runtime.cgocallback_gofunc(0x0, 0x0, 0x0, 0x0)
/Users/foobar/.goenv/versions/1.14.15/src/runtime/asm_386.s:806 +0x7e fp=0x1242fff0 sp=0x1242ffe0 pc=0x623e419e
runtime.goexit()
/Users/foobar/.goenv/versions/1.14.15/src/runtime/asm_386.s:1337 +0x1 fp=0x1242fff4 sp=0x1242fff0 pc=0x623e4651
Working solution:
//export ReadString
func ReadString(name *C.char) *C.char {
res := "";
goName := C.GoString(name);
if goName == "Foo" {
res = "From DLL: Hello, Foo"
}else{
res = "From DLL: Hello!"
}
return C.CString(res)
}
Python:
def readString():
nlp = ctypes.windll.LoadLibrary("H:/perf_nlp.dll")
dllFunc = nlp.ReadString
dllFunc.argtypes = [ctypes.c_char_p]
dllFunc.restype = ctypes.c_char_p
return dllFunc(b'cFoo')
A Go string and a C string are entirely unrelated (except in that both are called strings, which is a lie for at least one of them).
Here Python is sending a C string because you've told it to, but Go expects a Go string, which has a completely diffrent layout so it blows up. And if it didn't blow up at the callsite it'd probably blow up when the GC tries to handle the string, which it can't, because it's not a Go string.
You want to look at the magical "C" pseudo-package: you need to take in a *C.char and copy that to a Go string using C.GoString before you can pass it to anything expecting a go String. Or something along those lines, my experience with cgo (especially calling into it) is limited to avoiding this as a bad idea.
Regardless you probably want to at the very least read the cgo documentation in full, FFI is tricky at the best of time, and FFI between two managed languages much more so.

Is there a way to receive the return value in Python through the OUT parameter in C++?

I have a C++ dll, one of the export function is defined as follows:
OPERATEMYSQLSTDCALL_API int __stdcall CheckMac(char * pcMac, OUT LPSTR errorInfo);
I use it in python, use the ctypes library,i read some of the information and call it as follws:
from ctypes import *
lib = WinDLL('OperateMysqlStdcall.dll')
CheckMac = lib.CheckMac
CheckMac.argtypes = [c_char_p, POINTER(c_wchar_p)]
CheckMac.restype = c_int
p1=c_wchar_p()
value = CheckMac('88888888',byref(p1));
print p1.value
but when i execute it,it return None,i'm sure the value "OUT LPSTR errorInfo" in C++ is not NULL,i print it in console and it shows Correctly.could anyone tells me why it can't work in python.Thank U very much!
The type of LPSTR is char* so you should use c_char_p for its type as well. As an output parameter, though, you need a writable string buffer. Ideally, the API should indicate the size of the buffer passed so a buffer overrun could be checked.
Here's some test DLL code:
#include <windows.h>
extern "C" __declspec(dllexport)
int __stdcall CheckMac(char* pcMac, LPSTR errorInfo)
{
strcpy(errorInfo, pcMac);
return 1;
}
And Python:
from ctypes import *
lib = WinDLL('test.dll')
CheckMac = lib.CheckMac
CheckMac.argtypes = [c_char_p, c_char_p]
CheckMac.restype = c_int
errorInfo = create_string_buffer(1024)
value = CheckMac('88888888',errorInfo);
print errorInfo.value
Output:
88888888

Handler Issues Applying cdll in Python

I'm trying to porting some C dll(FANUC FOCAS Library - for CNC) code via Python using ctypes, so I wrote porting code. (as belows), but get a very strange result when loading the DLL and calling the function. In my case, I don't understand using handler in python.
I would like to apply the following c code in python.
Declaration(for c)
#include "fwlib64.h"
FWLIBAPI short WINAPI cnc_allclibhndl3(const char *ipaddr,unsigned short port,
long timeout, unsigned short *FlibHndl);
Example Code(in focas library manual for c)
#include "fwlib64.h"
void example( void )
{
unsigned short h;
short ret;
ODBST buf;
ret = cnc_allclibhndl3( "192.168.0.100", 8193, 1, &h ) ;
//
if ( !ret ) {
cnc_statinfo( h, &buf ) ;
cnc_freelibhndl( h ) ;
} else {
printf( "ERROR!(%d)\n", ret ) ;
}
}
Testfocas.py
from ctypes import *
mylib = cdll.LoadLibrary('./Fwlib64.dll')
class ODBSYS(Structure):
pass
_fields_ =[
("dummy", c_ushort),
("max_axis", c_char*2),
("cnc_type", c_char*2),
("mt_type",c_char*2),
("series",c_char*4),
("version",c_char*4),
("axes",c_char*2),]
h=c_ushort()
pt=pointer(h)
ret=c_short()
buf=ODBSYS()
ret=mylib.cnc_allclibhndl3('192.168.0.100',8193,1,pt)
mylib.cnc_statinfo(h,buf)
mylib.cnc_freelibhndl(h)
I want the function to return 0 or -16 but, in my case the function return is
cnc_allclibhndl3 = 65520 (i guess open port)
cnc_statinfo = -8
cnc_freelibhndl -8
Return Status of Data Window Functions
EW_OK(0) Normal termination
EW_SOCKET(-16) Socket communication error Check the power supply of CNC, Ethernet I/F board, Ethernet connection cable.
EW_HANDLE(-8) Allocation of handle number is failed.
I do not know what I was wrong with.
CDLL is for __cdecl calling convention. cdll is not recommended for use because it is a shared instance across modules.
WINAPI is defined as __stdcall, so use WinDLL:
mylib = WinDLL.LoadLibrary('./Fwlib64.dll')
Next, define argtypes and restype for your argument and result types for your function:
mylib.cnc_allclibhndl3.argtypes = c_char_p,c_ushort,c_long,POINTER(c_ushort)
mylib.cnc_allclibhndl3.restype = c_short
Finally, pass the output parameter by reference. It is more efficient than creating a pointer:
h = c_ushort()
ret = mylib.cnc_allclibhndl3('192.168.0.100',8193,1,byref(h))
Prototypes for cnc_statinfo and cnc_freelibhndl were not provided. Define argtypes and restype for them as well.

C to Python via SWIG: can't get void** parameters to hold their value

I have a C interface that looks like this (simplified):
extern bool Operation(void ** ppData);
extern float GetFieldValue(void* pData);
extern void Cleanup(p);
which is used as follows:
void * p = NULL;
float theAnswer = 0.0f;
if (Operation(&p))
{
theAnswer = GetFieldValue(p);
Cleanup(p);
}
You'll note that Operation() allocates the buffer p, that GetFieldValue queries p, and that Cleanup frees p. I don't have any control over the C interface -- that code is widely used elsewhere.
I'd like to call this code from Python via SWIG, but I was unable to find any good examples of how to pass a pointer to a pointer -- and retrieve its value.
I think the correct way to do this is by use of typemaps, so I defined an interface that would automatically dereference p for me on the C side:
%typemap(in) void** {
$1 = (void**)&($input);
}
However, I was unable to get the following python code to work:
import test
p = None
theAnswer = 0.0f
if test.Operation(p):
theAnswer = test.GetFieldValue(p)
test.Cleanup(p)
After calling test.Operation(), p always kept its initial value of None.
Any help with figuring out the correct way to do this in SWIG would be much appreciated. Otherwise, I'm likely to just write a C++ wrapper around the C code that stops Python from having to deal with the pointer. And then wrap that wrapper with SWIG. Somebody stop me!
Edit:
Thanks to Jorenko, I now have the following SWIG interface:
% module Test
%typemap (in,numinputs=0) void** (void *temp)
{
$1 = &temp;
}
%typemap (argout) void**
{
PyObject *obj = PyCObject_FromVoidPtr(*$1, Cleanup);
$result = PyTuple_Pack(2, $result, obj);
}
%{
extern bool Operation(void ** ppData);
extern float GetFieldValue(void *p);
extern void Cleanup(void *p);
%}
%inline
%{
float gfv(void *p){ return GetFieldValue(p);}
%}
%typemap (in) void*
{
if (PyCObject_Check($input))
{
$1 = PyCObject_AsVoidPtr($input);
}
}
The python code that uses this SWIG interface is as follows:
import test
success, p = test.Operation()
if success:
f = test.GetFieldValue(p) # This doesn't work
f = test.gvp(p) # This works!
test.Cleanup(p)
Oddly, in the python code, test.GetFieldValue(p) returns gibberish, but test.gfv(p) returns the correct value. I've inserting debugging code into the typemap for void*, and both have the same value of p! The call Any ideas about that?
Update: I've decided to use ctypes. MUCH easier.
I agree with theller, you should use ctypes instead. It's always easier than thinking about typemaps.
But, if you're dead set on using swig, what you need to do is make a typemap for void** that RETURNS the newly allocated void*:
%typemap (in,numinputs=0) void** (void *temp)
{
$1 = &temp;
}
%typemap (argout) void**
{
PyObject *obj = PyCObject_FromVoidPtr(*$1);
$result = PyTuple_Pack(2, $result, obj);
}
Then your python looks like:
import test
success, p = test.Operation()
theAnswer = 0.0f
if success:
theAnswer = test.GetFieldValue(p)
test.Cleanup(p)
Edit:
I'd expect swig to handle a simple by-value void* arg gracefully on its own, but just in case, here's swig code to wrap the void* for GetFieldValue() and Cleanup():
%typemap (in) void*
{
$1 = PyCObject_AsVoidPtr($input);
}
Would you be willing to use ctypes? Here is sample code that should work (although it is untested):
from ctypes import *
test = cdll("mydll")
test.Operation.restype = c_bool
test.Operation.argtypes = [POINTER(c_void_p)]
test.GetFieldValue.restype = c_float
test.GetFieldValue.argtypes = [c_void_p]
test.Cleanup.restype = None
test.Cleanup.argtypes = [c_void_p]
if __name__ == "__main__":
p = c_void_p()
if test.Operation(byref(p)):
theAnswer = test.GetFieldValue(p)
test.Cleanup(p)

Categories

Resources