任何调用 c/c++ 的方式来自 python 的代码,无需更改 c/c++代码?

发布于 2024-10-20 19:23:39 字数 5693 浏览 2 评论 0原文

编辑完成: 我正在包装一个自定义 python dll,目的是最终删除自定义代码并使用最近的 python 而不是现在使用的古老版本。其中一个函数初始化一个 python 扩展,并调用

PyObject* _PyObject_New(PyTypeObject *type)

在我的包装器中,此调用生成的 (c) 对象在 PyErr_Print() 上崩溃(是的,我也包装了它)。

我想尝试将 Repr 或 Str 调用替换为我选择的某些内容,看看它们是否有问题。另外,我不能使用胶囊,因为这些对象即使在 python 代码上也应该可用。

根据请求:真正的问题发生在两个 c 本机指针之一被调用的地方。我采用了 python 2.7.1 源代码,在 VS 2003 上打开(项目在 PC/VS7.1 上),在那里打开 pythonrun.c 并在最后添加了三驾马车函数的存根:

    static CHAR gameHome[MAX_PATH + 1] = { 0 };
    __declspec (dllexport) void  Py_SetGameInterface(){
        //Set the path here
        unsigned long nChars;
        nChars = GetModuleFileName(NULL, gameHome, MAX_PATH);
        if(nChars <= 0){
            printf("Failed getting the exe path\n");
            return;
        }
        for( ; nChars > 0; nChars--){
            //think windows accepts both
            if(gameHome[nChars]=='\\' || gameHome[nChars]=='/'){
                break;
            }
        }
        if(nChars == 0){
            printf("Failed getting the python path\n");
            return;
        }
        strcpy(&gameHome[nChars+1],"Bin\\python\0");
        Py_SetPythonHome(gameHome);
    }   


        /* this was deprecated in python 2.3 but vampire needs it */
#undef _PyObject_Del
        __declspec (dllexport) void _PyObject_Del(PyObject *op)
        {
            PyObject_FREE(op);
        }


        //original returns the number of chars output
        //if you give it a char* youll receive what was written but that 
        //looks like a accident of the stack
        __declspec (dllexport) int Py_FlushConsoleOutput(){
            return 0;
        }

        __declspec (dllexport) int PyRun_ConsoleString(char * str, int typeOfExpression, PyObject * globals, PyObject * locals){
            PyObject* ret = NULL;
            printf("Console String: %s, size %d\n", str, strlen(str));

            if(PyErr_Occurred() != NULL){
                printf("WTF ERROR\n%s", PyString_AsString(PyObject_Str(PyErr_Occurred())));
                PyErr_Clear();
            }
            //trying to run dir(cvar) crashes here
            ret = PyRun_String(str, typeOfExpression, globals, locals);
            if(ret != NULL){
                Py_DECREF(ret);
            }
            //ret crashes with non null ret
            return 0;
        }

        #undef PyRun_String
        PyAPI_FUNC(PyObject *)
            PyRun_String(char *str, int s, PyObject *g, PyObject *l)
        {
            PyObject* r;
            if(PyErr_Occurred() != NULL){
                printf("WTF ERROR\n%s", PyString_AsString(PyObject_Str(PyErr_Occurred())));
                PyErr_Clear();
            }
            r = PyRun_StringFlags(str, s, g, l, NULL);
            if(r != NULL && PyBool_Check(r)){
                //newer python defined a bool value but bloodlines is 
                //not accepting it somehow (though bool is a subclass of int)
                if(r == Py_True)
                    return PyInt_FromLong(1L);
                else
                    return PyInt_FromLong(0L);
            }
            return r;
        }

构建解决方案但删除了不起作用的python子项目(它们不需要),

将此bash文件放在Game/Bin目录(python dll所在的目录)中

cp -f /home/paulo/OS\ Images/shared/Python-2.7.1/PC/VS7.1/python27.dll ./vampire_python21.dll
cp -f /home/paulo/OS\ Images/shared/Python-2.7.1/PC/VS7.1/unicodedata.pyd ./python/unicodedata.pyd
cp -f /home/paulo/OS\ Images/shared/Python-2.7.1/PC/VS7.1/_ctypes.pyd ./python/_ctypes.pyd
cp -f /home/paulo/OS\ Images/shared/Python-2.7.1/PC/VS7.1/select.pyd ./python/select.pyd
cp -f /home/paulo/OS\ Images/shared/Python-2.7.1/PC/VS7.1/winsound.pyd ./python/winsound.pyd
cp -f /home/paulo/OS\ Images/shared/Python-2.7.1/PC/VS7.1/python.exe ./python/python.exe
cp -f /home/paulo/OS\ Images/shared/Python-2.7.1/PC/VS7.1/pythonw.exe ./python/pythonw.exe
cp -f /home/paulo/OS\ Images/shared/Python-2.7.1/PC/VS7.1/w9xpopen.exe ./python/w9xpopen.exe
rm -R ./python/Lib
cp -R /home/paulo/OS\ Images/shared/Python-2.7.1/Lib ./python/Lib

find ../ -name '*.pyc' -delete
export PYTHONVERBOSE=1; wine ../vampire.exe -console 1

编译并运行。游戏使用控制台打开,如果您输入 dir(cvar) ,它会在运行/打印代码期间创建 AST 后崩溃(抱歉 wine 不理解 VS 调试符号)。奇怪的是,另一个本机指针“dir(ccmd)”不会崩溃,但可能有一些关于另一个问题的线索:

[]
Console String: __dict__, size 8
Traceback (most recent call last):
  File "<string>", line 1, in <module>
NameError: name '__dict__' is not defined
Console String: __members__, size 11
Traceback (most recent call last):
  File "<string>", line 1, in <module>
NameError: name '__members__' is not defined
Console String: __methods__, size 11
Traceback (most recent call last):
  File "<string>", line 1, in <module>
NameError: name '__methods__' is not defined
Console String: __class__, size 9
Traceback (most recent call last):
  File "<string>", line 1, in <module>
NameError: name '__class__' is not defined

注意它之前崩溃了(编辑:实际上,它正在以某种方式再次被调用。python 中的异常C api?需要调查。EDIT2:实际上这只发生在“dir(ccmd)”命令中,而不是“dir(cvar)”命令中,它会立即崩溃)返回并且该函数是特定于控制台的。很可能还有更多我不知道的改变。

控制台上的“print ccmd”、“str(ccmd)”或“repr(ccmd)”给出:

<cvar object at 0x0245E4A8>

并且“print cvar”、“str(cvar)”或“repr(cvar)”给出

<cvar object at 0x0245E4A0>

另请注意,我不知道ConsoleString 函数的返回是什么,但我知道它正在返回程序集上测试 0 (NULL) 以分支 if。

编辑:好的,通过在崩溃时附加调试器来找到崩溃点: 这是 python 2.7.1 源中 typeobject.c 的第 3963 行,

if (base && base->tp_dict == NULL)

Base 不为 null,但访问该字典会使解释器崩溃。我应该发送错误报告吗?

Edit done:
I am wrapping a custom python dll with the purpose of removing the custom code eventually and using a recent python instead of the ancient version that is being used now. One of the functions initializes a python extension, and calls

PyObject* _PyObject_New(PyTypeObject *type)

In my wrapper the (c) object produced by this call is crashing on PyErr_Print() (yes i've wrapped it too).

I'd like to experiment with replacing the Repr or Str calls to something of my choosing to see if those are guilty. Also, i can't use capsules because the objects are supposed to be available even on python code.

By request: the real problem is happening wherever one of two c native pointers gets called. I took python 2.7.1 source, opened in on VS 2003 (project is on PC/VS7.1), opened pythonrun.c there and added stubs of the troika functions at the end:

    static CHAR gameHome[MAX_PATH + 1] = { 0 };
    __declspec (dllexport) void  Py_SetGameInterface(){
        //Set the path here
        unsigned long nChars;
        nChars = GetModuleFileName(NULL, gameHome, MAX_PATH);
        if(nChars <= 0){
            printf("Failed getting the exe path\n");
            return;
        }
        for( ; nChars > 0; nChars--){
            //think windows accepts both
            if(gameHome[nChars]=='\\' || gameHome[nChars]=='/'){
                break;
            }
        }
        if(nChars == 0){
            printf("Failed getting the python path\n");
            return;
        }
        strcpy(&gameHome[nChars+1],"Bin\\python\0");
        Py_SetPythonHome(gameHome);
    }   


        /* this was deprecated in python 2.3 but vampire needs it */
#undef _PyObject_Del
        __declspec (dllexport) void _PyObject_Del(PyObject *op)
        {
            PyObject_FREE(op);
        }


        //original returns the number of chars output
        //if you give it a char* youll receive what was written but that 
        //looks like a accident of the stack
        __declspec (dllexport) int Py_FlushConsoleOutput(){
            return 0;
        }

        __declspec (dllexport) int PyRun_ConsoleString(char * str, int typeOfExpression, PyObject * globals, PyObject * locals){
            PyObject* ret = NULL;
            printf("Console String: %s, size %d\n", str, strlen(str));

            if(PyErr_Occurred() != NULL){
                printf("WTF ERROR\n%s", PyString_AsString(PyObject_Str(PyErr_Occurred())));
                PyErr_Clear();
            }
            //trying to run dir(cvar) crashes here
            ret = PyRun_String(str, typeOfExpression, globals, locals);
            if(ret != NULL){
                Py_DECREF(ret);
            }
            //ret crashes with non null ret
            return 0;
        }

        #undef PyRun_String
        PyAPI_FUNC(PyObject *)
            PyRun_String(char *str, int s, PyObject *g, PyObject *l)
        {
            PyObject* r;
            if(PyErr_Occurred() != NULL){
                printf("WTF ERROR\n%s", PyString_AsString(PyObject_Str(PyErr_Occurred())));
                PyErr_Clear();
            }
            r = PyRun_StringFlags(str, s, g, l, NULL);
            if(r != NULL && PyBool_Check(r)){
                //newer python defined a bool value but bloodlines is 
                //not accepting it somehow (though bool is a subclass of int)
                if(r == Py_True)
                    return PyInt_FromLong(1L);
                else
                    return PyInt_FromLong(0L);
            }
            return r;
        }

Build the solution but deleted the python subprojects that don't work (they are not needed)

put this bash file on the Game/Bin directory (where the python dll lives)

cp -f /home/paulo/OS\ Images/shared/Python-2.7.1/PC/VS7.1/python27.dll ./vampire_python21.dll
cp -f /home/paulo/OS\ Images/shared/Python-2.7.1/PC/VS7.1/unicodedata.pyd ./python/unicodedata.pyd
cp -f /home/paulo/OS\ Images/shared/Python-2.7.1/PC/VS7.1/_ctypes.pyd ./python/_ctypes.pyd
cp -f /home/paulo/OS\ Images/shared/Python-2.7.1/PC/VS7.1/select.pyd ./python/select.pyd
cp -f /home/paulo/OS\ Images/shared/Python-2.7.1/PC/VS7.1/winsound.pyd ./python/winsound.pyd
cp -f /home/paulo/OS\ Images/shared/Python-2.7.1/PC/VS7.1/python.exe ./python/python.exe
cp -f /home/paulo/OS\ Images/shared/Python-2.7.1/PC/VS7.1/pythonw.exe ./python/pythonw.exe
cp -f /home/paulo/OS\ Images/shared/Python-2.7.1/PC/VS7.1/w9xpopen.exe ./python/w9xpopen.exe
rm -R ./python/Lib
cp -R /home/paulo/OS\ Images/shared/Python-2.7.1/Lib ./python/Lib

find ../ -name '*.pyc' -delete
export PYTHONVERBOSE=1; wine ../vampire.exe -console 1

compiled and ran. The game open with a console, and if you type dir(cvar) it crashes after creating the AST during running/printing of the code (sorry wine doesn't understand the VS debug symbols). Curiously the other native pointer "dir(ccmd)" doesn't crash, but may have some clues to what is wrong with the other:

[]
Console String: __dict__, size 8
Traceback (most recent call last):
  File "<string>", line 1, in <module>
NameError: name '__dict__' is not defined
Console String: __members__, size 11
Traceback (most recent call last):
  File "<string>", line 1, in <module>
NameError: name '__members__' is not defined
Console String: __methods__, size 11
Traceback (most recent call last):
  File "<string>", line 1, in <module>
NameError: name '__methods__' is not defined
Console String: __class__, size 9
Traceback (most recent call last):
  File "<string>", line 1, in <module>
NameError: name '__class__' is not defined

Notice that it crashes BEFORE (EDIT: actually, it is being called again somehow. Exceptions in the python C api? Need to investigate. EDIT2: actually this only happens in the "dir(ccmd)" command not on "dir(cvar)", that crashes right away) the return and that this function is console specific. There may very well be more alterations i'm not aware of.

"print ccmd", "str(ccmd)" or "repr(ccmd)" on the console gives :

<cvar object at 0x0245E4A8>

and "print cvar", "str(cvar)" or "repr(cvar)" gives

<cvar object at 0x0245E4A0>

Also notice i don't know what the return of the ConsoleString function is, but i know it is testing against 0 (NULL) on the return assembly to branch a if.

EDIT: OK, found the crashing point by applying a debbuger to attached when crashed:
It is line 3963 of typeobject.c on the python 2.7.1 source,

if (base && base->tp_dict == NULL)

Base is not null, yet accessing that dictionary crashes the interpreter. Should i send a bug report?

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

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

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文