我有一个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是一个如下所示的结构:(变量名被修改)
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;
我通过ctypes在python中使用这个dll。我创建了一个类来覆盖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)
]
接下来,我创建了一个类来覆盖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)
这是我如何使用DLL的init函数的示例。 首先创建一个Param实例,然后输入一些值。 问题值 - 我将在稍后到达 - 是参数 LOGFILENAME。
parameter = Param()
parameter.a = 0
parameter.b = 0
...
parameter.LogFileName = 'IAmGoingNuts'
接下来,我创建DLL的一个实例并调用该函数。
mylittleDLL = MyDLL()
mylittleDLL.init(parameter)
问题是,因为LogFileName
函数调用
由于访问违规而给我一个例外。当我从C ++和Python的结构中删除日志文件时,问题就消失了。
这里有例外:
Windows Error(None): exception: access violation reading 0xC54D7C00
如果我修改python和c ++代码并从两者中删除LogFileName
,它可以正常工作。
无论如何,如果我通过单引号或双引号给出LogFilename,或者在它前面放置一个'b',就像这样: b"SomeFileName"
我已经通过提供logfilename作为ctypes.by_ref进行了实验(这肯定不起作用)。我尝试了不同版本的ctypes.create_string_buffer(b"filename")
。来自stackoverflow和codeproject.com的重新创建的例子..然而..我被卡住了。
我很确定我犯的是一个非常愚蠢的错误,但我不知道它在哪里。我只是想提供一个从python到dll的文件名..这实际上就是我想要的。
任何帮助都受到欢迎和赞赏!
祝你好运, 托拜厄斯
更新
在eryksun的帮助下,我找到了不同位置的偏移量 Python中的类(52Bytes)和C(51Bytes)中的结构中的LogFileName。 我还发现了一些#pramga pack(push,1),它将打包设置为一个字节。我已经删除了pragma用于测试目的,但是偏移量仍然不同(与之前相同)并且肯定异常仍然发生..我再次检查过在python类和c struct中具有相同的结构并且还验证了数据类型..一切都很好。它可能因为一些编译器优化而发生....至少我会通过禁用优化来尝试。
第二次更新
即使使用禁用的pragma pack指令和禁用的优化,它也会在Param结构中出现python(52Bytes)和C ++(51Bytes)中的不同偏移量。 好吧..至少我可以做一些指针算法,但实际上我想避免这个...我怎样才能确保相同的偏移?在CTypes以及C ++中我使用std。像uint32_t这样的数据类型。我按结构中的大小排序数据(除了char *它是一个4Bytes类型i quess)。我将重新排序LogFileName poniter只是为了检查是否有任何差异并将结果放在这里..也许这就是诀窍..
第3次更新 - &gt;溶液<!/强>
问题在于结构中char *(Pointer 4Byte Type)的位置!我已经改变了结构如下(这里是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)
]
因此我将4Byte类型直接放在8Byte类型int64_t
之后。
因此char*
直接位于4Byte和8Byte寄存器上(Byte allignment)。
我的猜测:在前一个struct / class中,char* LogFileName
是最后一个元素,我怀疑{a 1a}类型为“aaaa”的1字节填充因此在C ++中为51Bytes和Python中的52Bytes。
上次更新:
我发现了一个uint8_t
,由于c struct Param中元素的顺序,这也可能导致C ++端缺少字节填充。
此更改后pragma pack(push,1)
在C ++和Python中都有32Bytes的偏移量。
非常感谢eryksun !!!
答案 0 :(得分:0)
问题在于结构中char *(Pointer 4Byte Type)的位置!我已经改变了结构如下(这里是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)
]
因此我将4Byte类型直接放在8Byte类型int64_t
之后。因此char*
直接位于4Byte和8Byte寄存器上(Byte allignment)。
我猜测:在前结构/类中,char* LogFileName
是最后一个元素,我怀疑uint8_t类型为“aaaa”的1字节填充因此它在C ++中为51Bytes,在Python中为52Bytes
上次更新:我发现了一个pragma pack(push,1)
,由于c struct Param
中元素的顺序,这也可能导致C ++端缺少字节填充。
此更改后char* LogFileName
在C ++和Python中都有32Bytes的偏移量。
非常感谢eryksun !!!