python在c dll中

发布于 2025-01-29 03:00:01 字数 698 浏览 3 评论 0原文

我刚刚创建了一个带有python的dll。 当我用python函数导出功能时,我无法在Python代码中调用它 但是,当我内部没有python代码的情况下导出经典的C函数时,它可以很好地工作。我不明白

c dll

#include <stdio.h>

#define PY_SSIZE_T_CLEAN
#include <Python.h>

__declspec(dllexport) PyObject* getList()
{
    PyObject *PList = PyList_New(0);
    PyList_Append(PList, Py_BuildValue("i", 1));

    return PList;
}

python代码

import ctypes

lib = ctypes.cdll.LoadLibrary("EasyProtect.dll")

getList = lib.getList
getList.argtypes = None
getList.restype = ctypes.py_object

print(getList())

我的错误

print(getList())
OSError: exception: access violation reading 0x0000000000000010

I just created a DLL in C with Python in it.
When I export my function with python functions in it, I can't call it in my Python code
But when I export a classic C function without Python code inside, it works perfectly. I don't understand

C DLL

#include <stdio.h>

#define PY_SSIZE_T_CLEAN
#include <Python.h>

__declspec(dllexport) PyObject* getList()
{
    PyObject *PList = PyList_New(0);
    PyList_Append(PList, Py_BuildValue("i", 1));

    return PList;
}

Python Code

import ctypes

lib = ctypes.cdll.LoadLibrary("EasyProtect.dll")

getList = lib.getList
getList.argtypes = None
getList.restype = ctypes.py_object

print(getList())

My Error

print(getList())
OSError: exception: access violation reading 0x0000000000000010

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

还在原地等你 2025-02-05 03:00:01

根据 [python.docs]:class ctypes.pypes.pydll(name,mode = default = default_mode = default_mode = default_mode = default_mode = default_mode = default_mode = default_mode = default_mode = default_mode = default_mode ,hander = none)强调是我的):

此类实例的行为就像 cdll python gil是 在函数调用过程中发布的,在函数执行后,检查了python错误标志。如果设置了错误标志,则提出了一个python异常。

因此,这仅对呼叫Python C API函数直接有用

因此,您应该用 pydll pydll )替换 cdll cdll )。
我加强了您的示例。

dll00.c

#include <stdio.h>
#define PY_SSIZE_T_CLEAN
#include <Python.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 PyObject* createList(unsigned int size, int *pValues);

#if defined(__cplusplus)
}
#endif


PyObject* createList(unsigned int size, int *pValues)
{
    PyObject *pList = PyList_New(size);
    for (unsigned int i = 0; i < size; ++i) {
        PyList_SetItem(pList, i, Py_BuildValue("i", pValues[i]));
    }
    return pList;
}

code00.py

#!/usr/bin/env python

import ctypes as ct
import sys


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


def main(*argv):
    
    dll00 = ct.PyDLL(DLL_NAME)
    create_list = dll00.createList
    create_list.argtypes = (ct.c_uint, IntPtr)
    create_list.restype = ct.py_object

    dim = 5
    int_arr = (ct.c_int * dim)(*range(dim, 0, -1))  # Intermediary step: create an array
    res = create_list(dim, ct.cast(int_arr, IntPtr))
    print("\n{:s} returned: {:}".format(create_list.__name__, res))


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.")
    sys.exit(rc)

输出

  [cfati@cfati-5510-0:e:\ work \ work \ dev \ stackoverflow \ q072231434]&gt; Sopr.Bat
###设置较短的提示,在粘贴stackoverflow(或其他)页面###时,以更好地适应

[提示]&gt; “ c:\ install \ pc032 \ microsoft \ visualstudiocommunity \ 2019 \ vc \ auxiliary \ build \ vcvarsall.bat” x64&gt; nul

[提示]&gt; dir /b
code00.py
dll00.c

[提示]&gt;
[提示]&gt; cl /nologo /md /ddll /i "c: \ install \ pc064 \ python \ pypython \03.09 \ include“ dll00.c /link /link /nologo /nologo /dll /out:dll00.dll 00.dll /libpath: \ python \ python \ 03.09 \ libs'
dll00.c
   创建库dll00.lib和对象dll00.exp

[提示]&gt;
[提示]&gt; dir /b
code00.py
dll00.c
dll00.dll
dll00.exp
dll00.lib
dll00.obj

[提示]&gt;
[提示]&gt; ”
Python 3.9.9(标签/v3.9.9:CCB0E6A,2021,18:08:50)[MSC V.1929 64 BIT(AMD64)] 064BIT


造物主义者返回:[5,4,3,2,1]

完毕。
 

According to [Python.Docs]: class ctypes.PyDLL(name, mode=DEFAULT_MODE, handle=None) (emphasis is mine):

Instances of this class behave like CDLL instances, except that the Python GIL is not released during the function call, and after the function execution the Python error flag is checked. If the error flag is set, a Python exception is raised.

Thus, this is only useful to call Python C api functions directly.

So, you should replace cdll (CDLL) by pydll (PyDLL).
I enhanced your example a bit.

dll00.c:

#include <stdio.h>
#define PY_SSIZE_T_CLEAN
#include <Python.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 PyObject* createList(unsigned int size, int *pValues);

#if defined(__cplusplus)
}
#endif


PyObject* createList(unsigned int size, int *pValues)
{
    PyObject *pList = PyList_New(size);
    for (unsigned int i = 0; i < size; ++i) {
        PyList_SetItem(pList, i, Py_BuildValue("i", pValues[i]));
    }
    return pList;
}

code00.py:

#!/usr/bin/env python

import ctypes as ct
import sys


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


def main(*argv):
    
    dll00 = ct.PyDLL(DLL_NAME)
    create_list = dll00.createList
    create_list.argtypes = (ct.c_uint, IntPtr)
    create_list.restype = ct.py_object

    dim = 5
    int_arr = (ct.c_int * dim)(*range(dim, 0, -1))  # Intermediary step: create an array
    res = create_list(dim, ct.cast(int_arr, IntPtr))
    print("\n{:s} returned: {:}".format(create_list.__name__, res))


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.")
    sys.exit(rc)

Output:

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q072231434]> 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]>
[prompt]> cl /nologo /MD /DDLL /I"c:\Install\pc064\Python\Python\03.09\include" dll00.c  /link /NOLOGO /DLL /OUT:dll00.dll /LIBPATH:"c:\Install\pc064\Python\Python\03.09\libs"
dll00.c
   Creating library dll00.lib and object dll00.exp

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

[prompt]>
[prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.09_test0\Scripts\python.exe" code00.py
Python 3.9.9 (tags/v3.9.9:ccb0e6a, Nov 15 2021, 18:08:50) [MSC v.1929 64 bit (AMD64)] 064bit on win32


createList returned: [5, 4, 3, 2, 1]

Done.
無心 2025-02-05 03:00:01

当您使用Python C API时,必须保留GIL(全球解释器锁)。为此使用pydll。加载您的DLL:

lib = ctypes.PyDLL("EasyProtect.dll")

顺便说一句,您的DLL具有参考泄漏。 py_buildvalue返回一个新对象,pylist_append将其添加到列表时会增加引用。 PY_DECREF应在py_buildvalue返回的对象上调用

但是,在这种情况下,使用所需的大小创建列表,并使用pylist_set_item,该列表窃取了新对象的引用以初始化列表,并已对初始化新列表项目进行了优化:

#include <stdio.h>

#define PY_SSIZE_T_CLEAN
#include <Python.h>

__declspec(dllexport) PyObject* getList()
{
    PyObject *PList = PyList_New(1);                # size 1 list
    PyList_SET_ITEM(PList, 0, PyLong_FromLong(1));  # assign offset 0
    return PList;
}

When you use the Python C API, the GIL (global interpreter lock) must be held. Use PyDLL for that. Load your DLL with:

lib = ctypes.PyDLL("EasyProtect.dll")

As an aside, your DLL has a reference leak. Py_BuildValue returns a new object, and PyList_Append increments the reference when adding it to the list. Py_DECREF should be called on the object returned by Py_BuildValue.

In this case, though, create the list with the size you want and use PyList_SET_ITEM which steals the new object's reference to initialize the list and is optimized for initializing new list items:

#include <stdio.h>

#define PY_SSIZE_T_CLEAN
#include <Python.h>

__declspec(dllexport) PyObject* getList()
{
    PyObject *PList = PyList_New(1);                # size 1 list
    PyList_SET_ITEM(PList, 0, PyLong_FromLong(1));  # assign offset 0
    return PList;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文