首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python 学习手册
Python Cookbook
Python 基础教程
Python Sites
PyPI - Python Package Index
http://www.simple-is-better.com/
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
Coding
V2EX  ›  Python

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

  •  
  •   1462326016 · 29 天前 · 979 次点击

    先贴停止工作的截图

    MtRCmF.png

    具体的函数定义是这样的

    //获取二维码
    //参数
    //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 文档, 没有源码,只能通过文档去调用。

    15 回复  |  直到 2019-11-15 08:59:22 +08:00
        1
    zfj1441   29 天前 via iPhone
    换 32 位 python 试试
        2
    no1xsyzy   29 天前   ♥ 1
    大概:你需要注意 result 的要求是 char** ,而你给了个 char*
    我猜测,它应该是自己在堆上写字符串,然后把这个字符串的指针通过一个可写的 char* 弹回给你,而不是往你提供的内存写。
    C 语言代码类似这样:
    char* result=NULL;
    void* object=...;
    GetQRCode(&object, &result);
        3
    no1xsyzy   29 天前
        4
    1462326016   29 天前
    @zfj1441 抱歉,忘了贴环境了,环境是 win7 64 位旗舰版,python 是 32 位的,因为 dll 也是 32 位的,所以必须要用 32 位的 python 来调用
        5
    Kisesy   29 天前
    一般来说,往外传都要用 byref()
        6
    1462326016   29 天前
    @Kisesy 同样也已经试过 byref,各种组合方式都试过了,花了一天多时间试遍了。。。哭。。
        7
    1462326016   29 天前
    @no1xsyzy 感谢提示,我参考下
        8
    ysc3839   29 天前 via Android
    这异常代码和“Segmentation fault”一样——都说明不了具体错误是啥。
        9
    no1xsyzy   29 天前
    @ysc3839 所以最好就是去看标准做法,看看哪有区别
    我就是直接搜索 python ctypes get result from char**
    就突然有了头猪
        10
    1462326016   29 天前
    @ysc3839 对的,只是提供给大家做一个参考,因为也没有其他的信息可以提供,只能贴一下截图了
        11
    1462326016   29 天前
    @no1xsyzy 因为 dll 不是我写的,文档也是别人提供的,所以我猜想可能 dll 的作者没有按照标准做法来使用传入的参数导致了这个问题,但是因为没有源码所以无从考证,只能多参考下其他人的做法,多尝试下,看能不能找到一些线索。官方文档我也看了好多遍,各种 ctypes 下的函数也都试了好几次,还是同样的报错,目前来说没有什么头绪。
        12
    ysc3839   29 天前 via Android
    @1462326016 发一下你改过的代码来看看?如果可以的话发一下 dll ?
        13
    no1xsyzy   29 天前
    @1462326016 不过 C# 的需要更多信息,尤其是 *result 是否发生变化,从什么变化到什么。
    既然传的是 “内存地址的整数形式”,那就肯定应该看 *result 是否变化,作为整型的和作为 char[] 的都要看。
    而且确实如果能有一个最小复现环境是最好的。
    最后的杀手锏:用 C# 再包一层
        14
    1462326016   28 天前
    @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 这种形式的。
    两个值在函数调用过程中都没有变过
        15
    1462326016   28 天前
    @ysc3839 已发
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1464 人在线   最高记录 5043   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 24ms · UTC 00:17 · PVG 08:17 · LAX 16:17 · JFK 19:17
    ♥ Do have faith in what you're doing.