我得到了一个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;
...
}
参数是一个看起来像这样的结构:(变量名称被修改(
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 的初始化函数的示例。 首先创建一个参数的实例,并输入一些值。 有问题的值 - 我稍后会谈到这一点 - 是参数 日志文件名。
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"
我已经通过提供日志文件名作为ctypes.by_ref进行了实验(这肯定不起作用(。我尝试了不同变体的ctypes.create_string_buffer(b"filename")
。从堆栈溢出和 codeproject.com 重新创建示例。然而。。我卡住了。
我很确定我犯了一个非常愚蠢的错误,但我不明白它在哪里。我只想提供一个从python到dll的文件名。这实际上就是我想要的。
任何帮助都非常欢迎和赞赏!
此致敬意 托比亚斯
更新
在 eryksun 的帮助下,我发现了位置的不同偏移 LogFileName 在 Python 中的类中(52Bytes(和在 C 中的结构中(51Bytes(。 我还发现了一些 #pramga 包(push,1(,它将打包设置为一个字节。我已经删除了编译指示以进行测试,但偏移量仍然不同(与以前相同(,并且肯定异常仍然会发生。.我再次检查了python类和c结构中是否具有相同的结构,并验证了数据类型。一切都很好。这可能是由于某些编译器优化而发生的。至少我会通过禁用优化来尝试一下。
第二次更新
即使禁用了编译指示包指令和禁用优化,它也会在 python(52Bytes(和 C++(51Bytes( 结构中出现不同的偏移量。 井。。至少我可以做一些指针算术,但实际上我想避免这种情况......如何确保相同的偏移量?在 CTypes 中,我也在C++中使用 std. 数据类型,如 uint32_t。我在结构中按大小对数据进行排序(除了 char* 它是 4Bytes 类型的 i quess(。我将重新排序 LogFileName poniter 只是为了检查是否有任何差异并将结果放在这里。.也许这能解决问题..
第三次更新 ->解决方案!
问题在于结构中字符*(指针 4Byte 类型(的位置!我更改了结构如下(这里是 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++侧缺少字节填充,因为 c 结构 Param 中元素的顺序。
在此更改之后char* LogFileName
在C++和Python中都有32Bytes的偏移量。
非常感谢你!!
问题在于结构中字符*(指针 4Byte 类型(的位置!我更改了结构如下(这里是 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++侧缺少字节填充,因为 cstruct Param
中元素的顺序。
在此更改之后,char* LogFileName
在C++和Python中的偏移量均为32Bytes。
非常感谢你!!