Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning string from C++ function with ctypes gives large int, not char pointer

I am trying to call C++ functions from a dll using ctypes in python. My current issue is the function seems to return a large int, either positive or negative, instead of the char pointer I expect it to. If I convert that int to a c_char_p and call .value on it, it kills my kernel every single time. I've looked all over this site and in the docs too and can't figure this out. A lot of the things I have seen on this site even throw errors for me, like passing in strings to ctypes opjects and functions when they should be byte objects or something similar. Below is my c++ code that is turned into a dll and the python code I am using to call functions from the dll. Please if anyone can help me, that would be awesome. SaySomething is the function in question. Thanks.

TestLibrary.h

#pragma once

#ifdef TESTLIBRARY_EXPORTS
#define TESTLIBRARY_API __declspec(dllexport)
#else
#define TESTLIBRARY_API __declspec(dllexport)
#endif

#include <windows.h>
#include <cstring>

#ifdef __cplusplus
extern "C"
{
#endif

    TESTLIBRARY_API char* SaySomething(const char* phrase);

#ifdef __cplusplus
};
#endif

TestLibrary.cpp

#include "stdafx.h"
#include "TestLibrary.h"
#include <iostream>

TESTLIBRARY_API char* SaySomething(const char* phrase)
{
    char* p = new char [100];
    p = "string something";
    return p;
}

tester2.py

import ctypes

dbl = ctypes.c_double
pChar = ctypes.c_char_p
pVoid = ctypes.c_void_p

libName = (r"D:\Documents\Coding Stuff\Visual Studio 2017\Projects\TestLibrary"
           r"Solution\x64\Debug\TestLibrary.dll")
x = ctypes.windll.LoadLibrary(libName)

x.SaySomething.argtypes = [pChar]
x.SaySomething.restypes = pChar

phrase = b"Hi"
phrase = pChar(phrase)

res = x.SaySomething(phrase)
like image 871
ZachR Avatar asked Mar 08 '23 14:03

ZachR


1 Answers

While you can make an API that does what you are trying to do, you currently will have a memory leak. A better solution is to have Python allocate and manage the memory for the result.

I also fixed the dllimport as mentioned in the comments and defined TESTLIBRARY_EXPORTS in the .cpp file so the function would export from the DLL. restype was also fixed.

TesterLibrary.h

#pragma once

#ifdef TESTLIBRARY_EXPORTS
#define TESTLIBRARY_API __declspec(dllexport)
#else
#define TESTLIBRARY_API __declspec(dllimport)
#endif

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#ifdef __cplusplus
extern "C" {
#endif

TESTLIBRARY_API char* SaySomething(const char* phrase, char* result, size_t resultMaxLength);

#ifdef __cplusplus
}
#endif

TesterLibrary.cpp

#define TESTLIBRARY_EXPORTS
#include "TestLibrary.h"
#include <stdio.h>

TESTLIBRARY_API char* SaySomething(const char* phrase, char* result, size_t resultMaxLength)
{
    _snprintf_s(result,resultMaxLength,_TRUNCATE,"Decorated <%s>",phrase);
    return result;
}

tester2.py

import ctypes

libName = (r"TestLibrary.dll")
x = ctypes.CDLL(libName)

x.SaySomething.argtypes = [ctypes.c_char_p,ctypes.c_char_p,ctypes.c_size_t]
x.SaySomething.restype = ctypes.c_char_p

phrase = b"Hi"
result = ctypes.create_string_buffer(100)
res = x.SaySomething(phrase,result,ctypes.sizeof(result))
print(res)
print(result.value)

Output

b'Decorated <Hi>'
b'Decorated <Hi>'

Python will automatically free the result buffer when there are no more references to it.

like image 88
Mark Tolonen Avatar answered Mar 12 '23 01:03

Mark Tolonen