Python関数を呼び出すCコードがあります。このPython関数はアドレスを受け取り、最終的にPythonが呼び出せる関数に変換するためにWINFUNCTYPEを使います。 Python関数へのパラメータとして送信されるC関数は、最終的に別のPython関数を呼び出します。クラッシュを引き起こすのはこの最後のステップです。ですから、私はC - > Python - > C - > Pythonに行きます。最後のC - > Pythonはクラッシュします。私は問題を理解しようとしてきましたが、私はできませんでした。PythonをC言語に埋め込む:Pythonコードで呼び出されるCコールバックでPythonコードを呼び出そうとするとエラーが発生する
誰かが私の問題を指摘できますか?
Cコードは、Visual Studio 2010でコンパイルされ、引数で実行 "C:\ ... \ crash.py" と "関数func1":
#include <stdlib.h>
#include <stdio.h>
#include <Python.h>
PyObject* py_lib_mod_dict; //borrowed
void __stdcall cfunc1()
{
PyObject* py_func;
PyObject* py_ret;
int size;
PyGILState_STATE gil_state;
gil_state = PyGILState_Ensure();
printf("Hello from cfunc1!\n");
size = PyDict_Size(py_lib_mod_dict);
printf("The dictionary has %d items!\n", size);
printf("Calling with GetItemString\n");
py_func = PyDict_GetItemString(py_lib_mod_dict, "func2"); //fails here when cfunc1 is called via callback... will not even go to the next line!
printf("Done with GetItemString\n");
py_ret = PyObject_CallFunction(py_func, 0);
if (py_ret)
{
printf("PyObject_CallFunction from cfunc1 was successful!\n");
Py_DECREF(py_ret);
}
else
printf("PyObject_CallFunction from cfunc1 failed!\n");
printf("Goodbye from cfunc1!\n");
PyGILState_Release(gil_state);
}
int wmain(int argc, wchar_t** argv)
{
PyObject* py_imp_str;
PyObject* py_imp_handle;
PyObject* py_imp_dict; //borrowed
PyObject* py_imp_load_source; //borrowed
PyObject* py_dir; //stolen
PyObject* py_lib_name; //stolen
PyObject* py_args_tuple;
PyObject* py_lib_mod;
PyObject* py_func;
PyObject* py_ret;
Py_Initialize();
//import our python script
py_dir = PyUnicode_FromWideChar(argv[1], wcslen(argv[1]));
py_imp_str = PyString_FromString("imp");
py_imp_handle = PyImport_Import(py_imp_str);
py_imp_dict = PyModule_GetDict(py_imp_handle); //borrowed
py_imp_load_source = PyDict_GetItemString(py_imp_dict, "load_source"); //borrowed
py_lib_name = PyUnicode_FromWideChar(argv[2], wcslen(argv[2]));
py_args_tuple = PyTuple_New(2);
PyTuple_SetItem(py_args_tuple, 0, py_lib_name); //stolen
PyTuple_SetItem(py_args_tuple, 1, py_dir); //stolen
py_lib_mod = PyObject_CallObject(py_imp_load_source, py_args_tuple);
py_lib_mod_dict = PyModule_GetDict(py_lib_mod); //borrowed
printf("Calling cfunc1 from main!\n");
cfunc1();
py_func = PyDict_GetItem(py_lib_mod_dict, py_lib_name);
py_ret = PyObject_CallFunction(py_func, "(I)", &cfunc1);
if (py_ret)
{
printf("PyObject_CallFunction from wmain was successful!\n");
Py_DECREF(py_ret);
}
else
printf("PyObject_CallFunction from wmain failed!\n");
Py_DECREF(py_imp_str);
Py_DECREF(py_imp_handle);
Py_DECREF(py_args_tuple);
Py_DECREF(py_lib_mod);
Py_Finalize();
fflush(stderr);
fflush(stdout);
return 0;
}
Pythonコード:
from ctypes import *
def func1(cb):
print "Hello from func1!"
cb_proto = WINFUNCTYPE(None)
print "C callback: " + hex(cb)
call_me = cb_proto(cb)
print "Calling callback from func1."
call_me()
print "Goodbye from func1!"
def func2():
print "Hello and goodbye from func2!"
出力:
Calling cfunc1 from main!
Hello from cfunc1!
The dictionary has 88 items!
Calling with GetItemString
Done with GetItemString
Hello and goodbye from func2!
PyObject_CallFunction from cfunc1 was successful!
Goodbye from cfunc1!
Hello from func1!
C callback: 0x1051000
Calling callback from func1.
Hello from cfunc1!
The dictionary has 88 items!
Calling with GetItemString
PyObject_CallFunction from wmain failed!
私は最後までPyErr_Printを()を添加し、これは結果であった:
Traceback (most recent call last):
File "C:\Programming\crash.py", line 9, in func1
call_me()
WindowsError: exception: access violation writing 0x0000000C
編集:abarnertが指摘したバグを修正しました。出力は影響を受けません。
EDIT:バグを解決したコードに追加されました(cfunc1でGILロックを取得しました)。もう一度abarnertに感謝します。 the docsとして
py_func = PyDict_GetItemString(py_lib_mod_dict, "func2"); //fails here when cfunc1 is called via callback... will not even go to the next line!
printf("Done with GetItemString\n");
py_ret = PyObject_CallFunction(py_func, 0);
Py_DECREF(py_func);
言う、借用参照を返しPyDict_GetItemString
:
私が言ったように、私はPOSIXに移植するときに同じ問題がありました。そのPy_DECREFを取り除くだけで私の問題は解決しました...私の答えでも述べたGILの問題でしょうか? 'PyGILState_Ensure' /' Release'ペアを入れてみて、何が起こるか見てみましょう。その間、 'PyObject_Print'文の束を入れて、すべてが正しいものであることを確認してください。だから私は 'func1'がdecref'dであるという問題を発見しました。 – abarnert
この問題は、シングルスレッド環境で発生します。 GILロックは、2つのスレッドが同じPythonインタプリタインスタンス内にある可能性のあるマルチスレッド環境でのみ必要です。 – Dennis
(私は新しい行のためにenterを押し続けます...そして、readintヘルプの後でさえ、改行を行う方法を知らない、申し訳ありません)実際にはPyDict_GetItemStringで失敗点があります。これは、AVを引き起こすPythonの標準ライブラリです。標準のPython関数の中にプリントを置く方法がわかりません。次の行にもAVする必要がありますが、決してそこには到達しません。興味深いのは、このコードはLinuxでは失敗せず、Windowsで失敗するということです(途中で指摘していただきありがとうございます)。私はLinux VMをセットアップして、他に何かを理解できるかどうかを確認しています。 – Dennis