Python 通过 ctypes 调用 dll 中的函数时遇到了 Python 停止工作的问题,异常代码为 0xc0000005

2019-11-14 14:34:51 +08:00
 1462326016

先贴停止工作的截图

具体的函数定义是这样的

//获取二维码
//参数
//object	接口指针对象
//result	二级指针,返回执行结果 on 字符串,返回执行结果
int WINAPI GetQRCode(void** object, char **result);

我的 python 代码

# 之前的代码就忽略了,wx_user 已经创建好,没有问题。
buffer = create_string_buffer(3000)
result = lib.GetQRCode(wx_user, buffer)
# result = lib.GetQRCode(wx_user, pointer(buffer))
# result = lib.GetQRCode(wx_user, addressof(buffer))

因为传入的第二个参数是要可写的,所以创建了一个可写的缓冲区,希望 dll 将字符串写到这个指针所指向的内存中,之后我再通过 python 读取出来,实现将一个字符串传递出来,而不是通过返回值的方式。

但是我试了好多种办法,包括代码中的三种,还有设置参数和返回值类型的方式都不行,都会在写的时候导致 python 停止运行。

查询了异常代码,可能是在写内存的时候(越界或者其他的错误)导致的问题。

可是这个创建缓冲区的方式是官方文档里找到的,只有这一个函数来创建可以写的内存。

也找了其他语言调用这个这个函数的方式,C# 是直接传入一个整形的值,调试了下看得出来应该是内存地址的整数形式,易语言是传入了一个整形变量的地址,这两种都可以成功调用,我用 python 也模拟了两个方式,都不能成功调用。

所以想问问有没有 ctypes 用的数量的大佬,帮忙看看。万分感谢。

附:dll 只有 api 文档, 没有源码,只能通过文档去调用。

3588 次点击
所在节点    Python
15 条回复
zfj1441
2019-11-14 19:49:41 +08:00
换 32 位 python 试试
no1xsyzy
2019-11-14 19:53:23 +08:00
大概:你需要注意 result 的要求是 char** ,而你给了个 char*
我猜测,它应该是自己在堆上写字符串,然后把这个字符串的指针通过一个可写的 char* 弹回给你,而不是往你提供的内存写。
C 语言代码类似这样:
char* result=NULL;
void* object=...;
GetQRCode(&object, &result);
no1xsyzy
2019-11-14 19:53:54 +08:00
1462326016
2019-11-14 19:54:12 +08:00
@zfj1441 抱歉,忘了贴环境了,环境是 win7 64 位旗舰版,python 是 32 位的,因为 dll 也是 32 位的,所以必须要用 32 位的 python 来调用
Kisesy
2019-11-14 20:07:12 +08:00
一般来说,往外传都要用 byref()
1462326016
2019-11-14 20:38:59 +08:00
@Kisesy 同样也已经试过 byref,各种组合方式都试过了,花了一天多时间试遍了。。。哭。。
1462326016
2019-11-14 20:40:09 +08:00
@no1xsyzy 感谢提示,我参考下
ysc3839
2019-11-14 20:42:25 +08:00
这异常代码和“Segmentation fault”一样——都说明不了具体错误是啥。
no1xsyzy
2019-11-14 21:08:54 +08:00
@ysc3839 所以最好就是去看标准做法,看看哪有区别
我就是直接搜索 python ctypes get result from char**
就突然有了头猪
1462326016
2019-11-14 21:14:35 +08:00
@ysc3839 对的,只是提供给大家做一个参考,因为也没有其他的信息可以提供,只能贴一下截图了
1462326016
2019-11-14 21:17:41 +08:00
@no1xsyzy 因为 dll 不是我写的,文档也是别人提供的,所以我猜想可能 dll 的作者没有按照标准做法来使用传入的参数导致了这个问题,但是因为没有源码所以无从考证,只能多参考下其他人的做法,多尝试下,看能不能找到一些线索。官方文档我也看了好多遍,各种 ctypes 下的函数也都试了好几次,还是同样的报错,目前来说没有什么头绪。
ysc3839
2019-11-14 21:22:50 +08:00
@1462326016 发一下你改过的代码来看看?如果可以的话发一下 dll ?
no1xsyzy
2019-11-14 22:12:08 +08:00
@1462326016 不过 C# 的需要更多信息,尤其是 *result 是否发生变化,从什么变化到什么。
既然传的是 “内存地址的整数形式”,那就肯定应该看 *result 是否变化,作为整型的和作为 char[] 的都要看。
而且确实如果能有一个最小复现环境是最好的。
最后的杀手锏:用 C# 再包一层
1462326016
2019-11-15 08:43:38 +08:00
@no1xsyzy
贴下关键代码。
C#的导入函数定义,传入了两个整形的变量
```
[DllImport("pad.dll", EntryPoint = "GetQRCode")]
public static extern int GetQRCode(int objects, int result);
```
```
int pushStr;
fixed (PushStr = &pushStr)
//未执行下边代码时 Marshal.PtrToStringAnsi(new IntPtr(Convert.ToInt32(pushStr)))字符串为 null
Dll.GetQRCode(User, (int)PushStr);
var msg = Marshal.PtrToStringAnsi(new IntPtr(Convert.ToInt32(pushStr)));
//上边这句话 msg 获取到了字符串,所以根据函数定义和上述代码猜测是传入了整形的地址。
```
pushStr 是 0x028aa1e0 这种形式的
(int)PushStr 传入的参数是 42639840 这种形式的。
两个值在函数调用过程中都没有变过
1462326016
2019-11-15 08:59:22 +08:00
@ysc3839 已发

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/619528

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX