2011-06-20 21 views
3

Windows環境でdllからCコールバック を呼び出す際に呼び出す必要のあるPythonコールバックを作成しようとしています。この問題を理解するには、以下のコードをご覧ください。CコールバックへのPythonラッパー

from ctypes import * 

#---------qsort Callback-------------# 
IntArray5 = c_int * 5 
ia = IntArray5(5,1,7,33,99) 
libc = cdll.msvcrt 
qsort = libc.qsort 
qsort.restype = None 

CMPFUNC = CFUNCTYPE(c_int,POINTER(c_int),POINTER(c_int)) 
test = 0 
def py_cmp_func(a,b): 
    #print 'py_cmp_func:',a[0],b[0] 
    global test 
    test = 10000 
    return a[0]-b[0] 

cmp_func = CMPFUNC(py_cmp_func) 
qsort(ia, len(ia), sizeof(c_int), cmp_func) 
print "global test=",test 
for item in ia : print item 

#----------Load DLL & Connect ------------# 
gobiDLL = WinDLL("C:\LMS\QCWWAN2k.dll") 
print 'Output of connect : ',gobiDLL.QCWWANConnect() 

#----------SetByteTotalsCallback----------# 
tx = POINTER(c_ulonglong) 
rx = POINTER(c_ulonglong) 
proto_callback = WINFUNCTYPE(c_void_p,tx,rx) 

gtx = grx = 0 # Used to copy the response in the py_callback 
def py_callback(t,r): 
    sleep(10) 
    print 'python callback ...' 
    print "tx=",t,"rx=",r 
    global gtx,grx 
    gtx = 5000 # gtx = t 
    grx = 2000 # grx = r 
    #return 0 

callback = proto_callback(py_callback) 
gobiDLL.SetByteTotalsCallback.restype = c_ulong 
gobiDLL.SetByteTotalsCallback.argtypes = [proto_callback,c_byte]      

print "SetByteTotalsCallback = ",gobiDLL.SetByteTotalsCallback(callback, c_byte(256)) 
print "gtx = ",gtx 
print "grx = ",grx 

DLL以下に示すように、プロトタイプとSetByteTotalsCallback()メソッドのコールバックについて説明します。

プロトタイプ:

ULONG QCWWANAPI2K SetSessionStateCallback(tFNSessionState pCallback); 

コールバック:

void ByteTotalsCallback(ULONGLONG txTotalBytes, ULONGLONG rxTotalBytes); 

OUTPUT:

>>> 
global test= 10000 
1 
5 
7 
33 
99 
Output of connect : 0 
SetByteTotalsCallback = 0 
gtx = 0 
grx = 0 
>>>> 

現在の問題は、全体のプログラムが正常に呼び出されるということです が、Pythonのコールバックがありませんまったく呼ばれる。プログラムは のgobiDLL.SetByteTotalsCallback(コールバック、c_byte(256))メソッドから0のステータスで終了しますが、コール中に というpythonで書かれたcallback()メソッドは呼び出されません。

あなたは、Pythonコールバックを入力するのに役立つかもしれない点を教えてください。 他のサンプルqsort()メソッドは、ポインタをPython関数ポインタにすばやく渡します。 ここで問題の根本的な原因を得ることはできません。

TIA、

アンソニー

+0

問題を最小限の再現可能なシナリオに減らすことができれば、はるかに簡単です。ありがとう! – rafalotufo

+0

私はこの例をさらに短くすることを提案します(コードの半分をもう一度することができると思われます)。しかし、これは一般的な状況ではないので、おそらく誰かがコンパイルして自分自身で見ることができるビジュアルスタジオソリューションを提供することを検討してください。 – Arafangion

答えて

6

あなたがすることはできません。 C/C++関数はPython関数に直接アクセスすることはできません。関数プロトタイプはおそらくCへのポインタを期待しています。Pythonはその特定の関数の内部データ構造体へのポインタを渡します。

これはPythonのC拡張をビルドして、そのDLLをラップしてPythonに公開するときです。あなたがやるべきことは、本質的にCコールバックがPythonコールバックを呼び出すことです。それはになることができるからです。明確にするために、何を達成したいことは次のとおりです。

   | This side is C land i.e. "real" addresses 
       | 
Python objects --> C extension -- register callback with --> DLL 
       |            | 
in the python |          Calls callback 
       |            | 
    interpreter <-------------- Callback in C extension <------- 
       | 

次はあなたがMSVC(または代替ツールで、このコードをビルドする必要がありますC.から呼び出すPythonの機能を構築するための非常に簡単に説明されPythonディストリビューションをビルドするために使われたものです。 depends.exeを使用して、それがリンクされているmsvcXX.dllを見つけてください。

グローバル状態は、一般的に悪いと考えられますが、簡単にするために、私が使用したものだということです。

static PyObject* pyfunc_event_handler = NULL; 
static PyObject* pyfunc_event_args = NULL; 

私は、簡単にコールバックを設定する処理を行うために、設定されたハンドラ関数を追加しました。しかし、あなたがいることをする必要はありません、あなただけの

static PyObject* set_event_handler(PyObject *self, PyObject *args) 
{ 
    PyObject *result = NULL; 
    PyObject *temp; 

に必要な次の行は重要なものである - 私はPyArg_ParseTupleへの2つのPythonオブジェクト(O引数の通過許可一つの目的は、機能が含まれています、。他のパラメータ。

if (PyArg_ParseTuple(args, "OO", &temp, &pyfunc_event_args)) { 

     if (!PyCallable_Check(temp)) { 
      PyErr_SetString(PyExc_TypeError, "parameter must be a function"); 
      return NULL; 
     } 

参照を並べ替える。Pythonはこれを行う必要があります。

 Py_XINCREF(temp);     /* Add a reference to new func */ 
     Py_XDECREF(pyfunc_event_handler); /* Dispose of previous callback */ 
     pyfunc_event_handler = temp;   /* Remember new callback */ 

     /* Boilerplate to return "None" */ 
     Py_INCREF(Py_None); 
     result = Py_None; 
    } 
    return result; 
} 

あなたはその後、別の場所でこれを呼び出すことができます。

PyObject* arglist = Py_BuildValue("(O)", pyfunc_event_args); 
pyobjresult = PyObject_CallObject(pyfunc_event_handler, arglist); 
Py_DECREF(arglist); 

DECREFを忘れないでください、あなたはPythonは引数リストをGCする必要があります。

    :あなたはおそらく上に読みたい

    def func(obj): 
        /* handle obj */ 
    

    物事を:funcはそうのようなパラメータに一致している

    set_event_handler(func, some_tuple) 
    

    :これは同様に簡単です使用してのpythonから

  • LoadLibrary(CからDLLをロード)。
  • GetProcAddress(呼び出す関数を見つける)。
  • Extending Python with C or C++ Python docsから。
+0

あなたの答えは9Fingersありがとう!私たちはコールバックアクセス用のCエクステンションの作成を評価します。これが私たちの質問を解決することを願っています。 qsort(cdll.msvcrt dllから)のpythonコールバックが呼び出されているのと同様に、関数ポインタ呼び出しのctypesメカニズムの変更を期待していました。 – Anthony

+0

理解を明確にするのに役立つ質問はほとんどありません。1. ctypesを使用して作成およびマッピングされたPythonコールバック/ラッパーは登録されません。私はcallabcksをサポートする拡張子があるので、それが動作すると推測します。 2. qsortの例には、正しく呼び出される2つのポインタを持つ関数があります。なぜ外部dllのメソッドがコールバックを登録しないのでしょうか?コールバックを処理するために存在しないc拡張が原因で失敗しましたか? – Anthony

関連する問題