通过python ctypes差异python2和python3调用带有char* 参数的C函数

xvw2m8pv  于 2022-12-21  发布在  Python
关注(0)|答案(1)|浏览(144)

我正在把一个文件从python2转换成python3,这个文件使用ctypes模块调用一个C函数,下面这个最小的例子在python2中是有效的,但是在python3(3.11)中会产生如下错误:
错误:异常:写入0x0000000000094E0时发生访问冲突

// mydll.c
#include <stdio.h>

void myfunc(char* c, int i, char* c2) {
    printf("Hello World");
}

int main() {
    return 0;
}
# foo.py
import ctypes
import sys

PY3 = sys.version_info.major == 3

if PY3:
    clibrary = ctypes.WinDLL("mydll.dll", winmode=1)
else:
    clibrary = ctypes.WinDLL("mydll.dll")

prototype = ctypes.CFUNCTYPE(None, ctypes.c_char_p, ctypes.c_int, ctypes.c_char_p)

c1 = (ctypes.c_char * 512)()
i = ctypes.c_int(0)
c2 = (ctypes.c_char * (600 * 8))()
func = prototype(("myfunc", clibrary))

func(c1, i, c2)

我认为这与不同python版本之间字符串的unicode和bytes表示有关.从我收集的信息来看,这看起来像是取消引用一个空指针或类似的东西.我试过使用ctypes.create_string_buffer(),但遇到了同样的错误.
我希望相同的代码在python2和python3中都能工作。是什么导致了python3错误?

sqougxex

sqougxex1#

  • 根据[Python.Docs]:ctypes -加载共享库(强调是我的):

在Windows上,winmode 参数用于指定如何装入库(因为忽略了 mode)。它采用对Win32 API LoadLibraryEx flags参数有效的任何值。如果省略,默认值是使用导致最安全的DLL加载的标志,以避免诸如DLL劫持之类的问题。传递DLL的完整路径是确保加载正确的库和依赖项的最安全方法。

  • 然后从[MS.Learn]: LoadLibraryExA function (libloaderapi.h)(* 不要解析DLL引用 * - * 0x 00000001 )(***强调仍然是我的):
    注意
    不要使用此值;它仅用于向后兼容**。如果您计划仅访问DLL中的数据或资源,请使用LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVELOAD_LIBRARY_AS_IMAGE_RESOURCE或同时使用这两个函数。否则,请使用LoadLibrary函数将库作为DLL或可执行模块加载。

所以,去掉 winmode(或者将其设置为支持的值),应该就可以了。
你的例子(稍微调整了一下):

  • dll00.c*:
#include <stdio.h>

#if defined(_WIN32)
#  define DLL00_EXPORT_API __declspec(dllexport)
#else
#  define DLL00_EXPORT_API
#endif

#if defined(__cplusplus)
extern "C" {
#endif

DLL00_EXPORT_API void func(char *pc0, int i, char *pc1);

#if defined(__cplusplus)
}
#endif

void func(char *pc0, int i, char *pc1)
{
    printf("Hello World\n");
}
  • 代码00.py *:
#!/usr/bin/env python

import ctypes as cts
import sys

DLL_NAME = "./dll00.{:s}".format("dll" if sys.platform[:3].lower() == "win" else "so")

def main(*argv):
    dll = cts.WinDLL(DLL_NAME)

    prototype = cts.WINFUNCTYPE(None, cts.c_char_p, cts.c_int, cts.c_char_p)
    c0 = (cts.c_char * 512)()
    i = 0
    c1 = (cts.c_char * (600 * 8))()
    func = prototype(("func", dll))

    func(c0, i, c1)

if __name__ == "__main__":
    print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
                                                   64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    rc = main(*sys.argv[1:])
    print("\nDone.\n")
    sys.exit(rc)

输出

(py_pc064_03.11_test0) [cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q074867788]> sopr.bat
### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###

[prompt]> "c:\Install\pc032\Microsoft\VisualStudioCommunity\2019\VC\Auxiliary\Build\vcvarsall.bat" x64 > nul

[prompt]> dir /b
code00.py
dll00.c

[prompt]> cl /nologo /MD /DDLL dll00.c  /link /NOLOGO /DLL /OUT:dll00.dll
dll00.c
   Creating library dll00.lib and object dll00.exp

[prompt]> dir /b
code00.py
dll00.c
dll00.dll
dll00.exp
dll00.lib
dll00.obj

[prompt]>
[prompt]> "e:\Work\Dev\VEnvs\py_pc064_02.07.18_test0\Scripts\python.exe" ./code00.py
Python 2.7.18 (v2.7.18:8d21aa21f2, Apr 20 2020, 13:25:05) [MSC v.1500 64 bit (AMD64)] 064bit on win32

Hello World

Done.

[prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.11_test0\Scripts\python.exe" ./code00.py
Python 3.11.0rc1 (main, Aug  8 2022, 11:30:54) [MSC v.1932 64 bit (AMD64)] 064bit on win32

Hello World

Done.

备注

相关问题