[英]Python 2.7 ctypes interfacing DLL providing char* as c_char_p leads to exception
i got a Dll where i have exported following function: 我得到了Dll,其中我已导出以下函数:
extern "C" __declspec(dllexport) uint32_t __stdcall init(Param* InitParameter);
extern "C" __declspec(dllexport) uint32_t __stdcall init(Param* InitParameter)
{
std::cerr << "Output test! " << std::endl;
std::string xy(InitParameter->log_filename);
std::cerr << xy << std::endl;
...
}
Param is a struct looking like this: (Variable names are modified) 参数是一个看起来像这样的结构:(变量名被修改)
typedef struct {
int64_t a;
int64_t b;
int64_t c;
int64_t d;
uint32_t aa;
uint32_t bb;
uint32_t cc;
uint16_t aaa;
uint16_t bbb;
uint8_t aaaa;
bool Switch1
bool Switch2
char* LogFileName
}Param;
I use this dll within python by ctypes. 我通过ctypes在python中使用此dll。 I created a class to cover the Param structure. 我创建了一个类来介绍Param结构。
class Param(ctypes.Structure):
_fields_ = [
("a", ctypes.c_int64),
("b", ctypes.c_int64),
("c", ctypes.c_int64),
("d", ctypes.c_int64),
("aa", ctypes.c_uint32),
("bb", ctypes.c_uint32),
("cc", ctypes.c_uint32),
("aaa", ctypes.c_uint16),
("bbb", ctypes.c_uint16),
("aaaa", ctypes.c_uint8),
("Switch1", ctypes.c_bool),
("Switch2", ctypes.c_bool),
("LogFileName", ctypes.c_char_p)
]
Next i have created a class to cover the DLL and its function: 接下来,我创建了一个涵盖DLL及其功能的类:
class MyDLL():
def __init__(self): #Constructor
self.dll = ctypes.WinDLL('MyDll.dll')
self.pf_init = self.dll['init']
self.pf_init.restype = ctypes.c_uint32
self.pf_init.argtypes = [ctypes.POINTER(Param)]
def init(self, parameter_object):
self.parameter = parameter_object
try:
self.pf_init(self.parameter)
print "It works!"
except WindowsError as e:
print "Windows Error({0}): {1}".format(e.errno, e.message)
This is an example how I use the init function of the DLL. 这是我如何使用DLL的init函数的示例。 First a create an instance of Param, and put in some values. 首先创建一个Param实例,并放入一些值。 The problematic value - i will come later to that point - is the parameter LogFileName. 问题值-我稍后会讲到-参数LogFileName。
parameter = Param()
parameter.a = 0
parameter.b = 0
...
parameter.LogFileName = 'IAmGoingNuts'
Next i create an instance of the DLL and call the function. 接下来,我创建DLL的实例并调用该函数。
mylittleDLL = MyDLL()
mylittleDLL.init(parameter)
The problem is, because of the LogFileName
the function call is giving me an exception because of access violation. 问题是,由于LogFileName
该函数调用由于访问冲突而给我一个异常。 When i remove the logfile from the structure in C++ and Python, the problem is gone. 当我从C ++和Python中的结构中删除日志文件时,问题就消失了。
Here the exception: 这里是例外:
Windows Error(None): exception: access violation reading 0xC54D7C00
If i modify python and c++ code and remove the LogFileName
from both, it works fine. 如果我修改python和c ++代码并从两者中删除LogFileName
,它将正常工作。
Somehow it does not matter if i give the LogFilename by singlequotes or doublequotes or putting a 'b' in front of it like: b"SomeFileName"
无论如何,如果我通过单引号或双引号给LogFilename或在其前面放一个'b' b"SomeFileName"
关系,例如: b"SomeFileName"
I have experimented by providing the logfilename as ctypes.by_ref (which won't work for sure). 我已经尝试通过将日志文件名提供为ctypes.by_ref(肯定不会起作用)来进行实验。 I have tried ctypes.create_string_buffer(b"filename")
in different variations. 我尝试了ctypes.create_string_buffer(b"filename")
的不同版本。 Recreated examples from stackoverflow and codeproject.com.. however.. i am stuck. 从stackoverflow和codeproject.com重新创建的示例。
I am quite sure I am doing a very silly mistake, but i do not get the hang of it where it is. 我很确定自己犯了一个非常愚蠢的错误,但是我不明白它的所在。 I just want to provide a filename from python to the dll.. that is actually all I want. 我只想提供一个从python到dll的文件名..实际上就是我想要的。
Any help is highly welcomed and appreciated! 任何帮助都将受到高度欢迎和赞赏!
Best regards, Tobias 最好的问候,托比亚斯
UPDATE UPDATE
With the help of eryksun, i have found different offsets of the location of LogFileName in the class in Python (52Bytes) and in the struct in C (51Bytes). 借助于eryksun,我在Python(52Bytes)类和C(51Bytes)结构中发现了LogFileName位置的不同偏移量。 I have also found some #pramga pack(push, 1) which sets the packaging to one byte. 我还发现了一些#pramga pack(push,1),它将包装设置为一个字节。 I have removed the pragma for testing purpose, but still the offsets are different (the same as before) and for sure the exception still happens.. I have checked once more for having the same structure in python class as well as in the c struct and also verified the datatypes.. All is fine. 我已经删除了用于测试目的的编译指示,但是偏移量仍然不同(与以前相同),并且可以确定仍然发生异常。.我再次检查了python类以及c结构中是否具有相同的结构并验证了数据类型。一切都很好。 It might happen because of some compiler optimization.... at least i will give a try by disabling optimization. 这可能是由于某些编译器优化而发生的。。。至少我会尝试禁用优化。
2nd UPDATE 第二次更新
Even with disabled pragma pack directives and disabled optimization it comes to different offsets in python (52Bytes) and C++(51Bytes) in the Param structure. 即使禁用了pragma pack指令和禁用了优化,它在Param结构中的python(52Bytes)和C ++(51Bytes)中也会出现不同的偏移量。 Well.. at least I could do some pointer arithmetic, but actually I want to avoid this... How can i ensure same offsets? 好吧..至少我可以做一些指针算术,但实际上我想避免这种情况。我如何确保相同的偏移量? In CTypes as well in C++ I am using std. 在CTypes和C ++中,我也在使用std。 data-types like uint32_t. 像uint32_t这样的数据类型。 I ordered the data by size in the struct (Except char* it is a 4Bytes type i quess). 我在结构中按大小排序了数据(char *除外,它是4字节类型的队列)。 I will reorder the LogFileName poniter just to check for any differences and put the results here.. maybe this does the trick.. 我将对LogFileName Poniter重新排序,以检查是否有任何差异并将结果放在此处。也许这可以解决问题。
3rd UPDATE -> Solution! 第三次更新->解决方案!
The problem has been the location of the char* (Pointer 4Byte Type) in the structure! 问题出在结构中char *(指针4Byte类型)的位置! I have changed the structure as follows (here Python, but the same is done in C++): 我更改了结构,如下所示(此处为Python,但在C ++中也是如此):
class Param(ctypes.Structure):
_fields_ = [
("a", ctypes.c_int64),
("b", ctypes.c_int64),
("c", ctypes.c_int64),
("d", ctypes.c_int64),
("LogFileName", ctypes.c_char_p),
("aa", ctypes.c_uint32),
("bb", ctypes.c_uint32),
("cc", ctypes.c_uint32),
("aaa", ctypes.c_uint16),
("bbb", ctypes.c_uint16),
("aaaa", ctypes.c_uint8),
("Switch1", ctypes.c_bool),
("Switch2", ctypes.c_bool)
]
Thus i have put the 4Byte Type directly after the 8Byte Types int64_t
. 因此,我将4Byte Type直接放在8Byte Types int64_t
。 So the char*
is directly on a 4Byte and 8Byte boarder (Byte allignment). 因此, char*
直接位于4Byte和8Byte边界(字节分配)上。
My wild guess: In the former struct/class, the char* LogFileName
has been the last element and i suspect 1 byte padding for the uint8_t
type of "aaaa" thus it has been 51Bytes in C++ and 52Bytes in Python. 我的猜测:在以前的struct / class中, char* LogFileName
是最后一个元素,我怀疑uint8_t
类型的“ aaaa”填充了1个字节,因此在C ++中为51Bytes,在Python中为52Bytes。
Last update on this: I have found one more pragma pack(push,1)
, this could also have caused the missing byte padding on the C++ side because of the order of the elements in the c struct Param. 对此的最新更新:我发现了另一个pragma pack(push,1)
,这也可能由于c struct Param中元素的顺序而导致C ++端缺少字节填充。
After this change char* LogFileName
has offset of 32Bytes both in C++ and Python. 更改之后,在C ++和Python中char* LogFileName
偏移量均为32Bytes。
Thank you soo much eryksun!!! 太谢谢你了!
The problem has been the location of the char* (Pointer 4Byte Type) in the structure! 问题出在结构中char *(指针4Byte类型)的位置! I have changed the structure as follows (here Python, but the same is done in C++): 我更改了结构,如下所示(此处为Python,但在C ++中也是如此):
class Param(ctypes.Structure):
_fields_ = [
("a", ctypes.c_int64),
("b", ctypes.c_int64),
("c", ctypes.c_int64),
("d", ctypes.c_int64),
("LogFileName", ctypes.c_char_p),
("aa", ctypes.c_uint32),
("bb", ctypes.c_uint32),
("cc", ctypes.c_uint32),
("aaa", ctypes.c_uint16),
("bbb", ctypes.c_uint16),
("aaaa", ctypes.c_uint8),
("Switch1", ctypes.c_bool),
("Switch2", ctypes.c_bool)
]
Thus i have put the 4Byte Type directly after the 8Byte Types int64_t
. 因此,我将4Byte Type直接放在8Byte Types int64_t
。 So the char*
is directly on a 4Byte and 8Byte boarder (Byte allignment). 因此, char*
直接位于4Byte和8Byte边界(字节分配)上。
My wild guess: In the former struct/class, the char* LogFileName
has been the last element and i suspect 1 byte padding for the uint8_t type of "aaaa" thus it has been 51Bytes in C++ and 52Bytes in Python. 我的猜测:在以前的struct / class中, char* LogFileName
是最后一个元素,我怀疑uint8_t类型的“ aaaa”填充了1个字节,因此在C ++中为51Bytes,在Python中为52Bytes。
Last update on this: I have found one more pragma pack(push,1)
, this could also have caused the missing byte padding on the C++ side because of the order of the elements in the c struct Param
. 对此的最新更新:我发现了另一个pragma pack(push,1)
,由于c struct Param
元素的顺序,这也可能导致C ++端缺少字节填充。
After this change char* LogFileName
has offset of 32Bytes both in C++ and Python. 更改之后,在C ++和Python中char* LogFileName
偏移量均为32Bytes。
Thank you soo much eryksun!!! 太谢谢你了!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.