Reputation: 2858
I can:
I cannot Step 2 in opposite direction.
Here is my C++ code (compiled with clion and cygwin as a shared library using C++14).
#include <iostream>
wchar_t aa[2];
extern "C" {
int DoA()
{
return 10;
}
int DoB(wchar_t * in)
{
if (in[1] == 'a')
{
return 25;
}
return 30;
}
wchar_t * DoC()
{
aa[0] = 'a';
aa[1] = 'b';
return aa;
}
}
Here is my python 3.6.1 code that shows what I can and what I cannot do. So how should I get my string and do things with it in python? I expect to use the address with wstring_at to get the value, but it is not working.
from ctypes import *
import os.path
print('Hello')
itExist = os.path.exists('C:/Users/Daan/CLionProjects/stringproblem/cmake-build-release/cygstringproblem.dll')
print(itExist)
lib = cdll.LoadLibrary('C:/Users/Daan/CLionProjects/stringproblem/cmake-build-release/cygstringproblem.dll')
print('dll loaded')
A = lib.DoA()
print(A)
Bx = lib.DoB(c_wchar_p('aaa'))
print(Bx)
By = lib.DoB(c_wchar_p('bbb'))
print(By)
Ca = lib.DoC()
print(Ca)
print('Issue is coming')
Cb = wstring_at(Ca,2)
print(Cb)
Here is the output with error.
Hello
True
dll loaded
10
25
30
-1659080704
Issue is coming
Traceback (most recent call last):
File "ShowProblem.py", line 19, in <module>
Cb = wstring_at(Ca,2)
File "C:\Users\Daan\AppData\Local\Programs\Python\Python36\lib\ctypes\__init__.py", line 504, in wstring_at
return _wstring_at(ptr, size)
OSError: exception: access violation reading 0xFFFFFFFF9D1C7000
Upvotes: 2
Views: 2117
Reputation: 177461
If you set the .argtypes
and .restype
of your wrapped functions, you can call them more naturally. To handle an output string, it will be thread safe if you allocate the buffer in Python instead of using a global variable, or just return a wide string constant. Here's an example coded for the Microsoft compiler:
test.c
#include <wchar.h>
#include <string.h>
__declspec(dllexport) int DoA(void) {
return 10;
}
__declspec(dllexport) int DoB(const wchar_t* in) {
if(wcslen(in) > 1 && in[1] == 'a') // Make sure not indexing past the end.
return 25;
return 30;
}
// This version good if variable data is returned.
// Need to pass a buffer of sufficient length.
__declspec(dllexport) int DoC(wchar_t* aa, size_t length) {
if(length < 3)
return 0;
aa[0] = 'a';
aa[1] = 'b';
aa[2] = '\0';
return 1;
}
// Safe to return a constant. No memory leak.
__declspec(dllexport) wchar_t* DoD(void) {
return L"abcdefg";
}
test.py
from ctypes import *
# Set up the arguments and return type
lib = CDLL('test')
lib.DoA.argtypes = None
lib.DoA.restype = c_int # default, but just to be thorough.
lib.DoB.argtypes = [c_wchar_p]
lib.DoB.restype = c_int
lib.DoC.argtypes = [c_wchar_p,c_size_t]
lib.DoC.restype = c_int
lib.DoD.argtypes = None
lib.DoD.restype = c_wchar_p
# Map to local namespace functions
DoA = lib.DoA
DoB = lib.DoB
DoD = lib.DoD
# Do some pre- and post-processing to hide the memory details.
def DoC():
tmp = create_unicode_buffer(3) # Writable array of wchar_t.
lib.DoC(tmp,sizeof(tmp))
return tmp.value # return a Python string instead of the ctypes array.
print(DoA())
print(DoB('aaa'))
print(DoB('bbb'))
print(DoC())
print(DoD())
Output:
10
25
30
ab
abcdefg
Upvotes: 1
Reputation: 8218
I reproduced your problem on Linux and corrected it by defining the return type from your DoC
function:
from ctypes import *
print('Hello')
lib = cdll.LoadLibrary(PATH_TO_TOUR_LIB)
print('dll loaded')
# this line solved the issue for me
lib.DoC.restype = c_wchar_p
A = lib.DoA()
print(A)
Bx = lib.DoB(c_wchar_p('aaa'))
print(Bx)
By = lib.DoB(c_wchar_p('bbb'))
print(By)
Ca = lib.DoC()
print(Ca)
print('Issue is coming')
Cb = wstring_at(Ca,2)
print(Cb)
I also allocated the memory dynamically (some Python expert might comment on this, I guess that this causes a memory leak):
extern "C" {
int DoA()
{
return 10;
}
int DoB(wchar_t * in)
{
if (in[1] == 'a')
{
return 25;
}
return 30;
}
wchar_t * DoC()
{
wchar_t* aa = new wchar_t[2];
aa[0] = 'a';
aa[1] = 'b';
return aa;
}
}
Let me know if it works on Windows.
Upvotes: 2