V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
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
Sponsored by
LinkedIn
不坐班的神仙工作 · 去任何你想去的地方远程,赚一线城市的工资
2000 个不用出门 Social 的全球远程工作,帮助 V2EX 的小伙伴开启全新的工作方式。
Promoted by LinkedIn
1462326016
V2EX  ›  Python

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

  •  
  •   1462326016 · 2019-11-14 14:34:51 +08:00 · 3159 次点击
    这是一个创建于 1051 天前的主题,其中的信息可能已经有所发展或是发生改变。

    先贴停止工作的截图

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