9

Cライブラリであるlibupnpを利用するPythonアプリケーションに取り組んでいます。私は十分に簡単なライブラリを使用するために CTypes を使用しています。私が抱えている問題は、読み取り要求のコールバック関数を登録するときです。関数には、次の形式のプロトタイプがあります。

int read_callback(void *pFileHandle, char *pBuf, long nBufLength);

pFileHandle は、単なるファイル ハンドル タイプです。pBuf は、書き込み可能なメモリ バッファです。ここにデータが出力されます。nBufLength は、ファイルから読み取るバイト数です。ステータス コードが返されます。

これには Python 関数ポインタがあります。これは簡単に作成できましたが、このコールバックを処理する Python 関数を定義すると、Python 文字列は不変であり、割り当てまたは変更すると新しいインスタンスが作成されるため、pBuf が書き込まれないことがわかりました。関数が要求されたファイル データで終了すると、C ライブラリは char ポインターが返されることを期待しているため、これは大きな問題を引き起こします。ただし、Python 文字列が原因で、バッファは毎回空になります。C ライブラリを変更せずにこれを回避する方法はありますか?

ハンドラーは、指定されたバッファーパラメーターを変更する必要がありますが、これは私の問題です。

したがって、私が実現したいのは、Python 関数が呼び出されてファイルの読み取りを実行することです (メモリ内、ファイル システム ハンドル、またはその中間にある可能性があります)。pBuf パラメーターは、ストリームの読み取りによって入力されます (これも Python で)。その後、コールバックは、pBuf に書き込まれた C コードに戻ります。

4

3 に答える 3

4

コールバックは、pBuf と nBufLength で呼び出されます。pBuf には既に書き込み可能なメモリが割り当てられていますが、pBuf.value を要求すると、これは不変の python 文字列に変換されます。

代わりに、直接変更できるオブジェクトに pBuf を変換します。

## If pBuf is c_char_p, then convert to writable object
c = ctypes.cast(pBuf, ctypes.POINTER(ctypes.c_char))
## At this point, you can set individual bytes
## with c[i] = x, but this is dangerous and there's a safer way:

## get address of string
addr = ctypes.addressof(c.contents)

## convert to char[] for safe writing
c2 = (c_char*nBufLength).from_address(addr)

## see how many bytes to write
nb = min(len(msg), nBufLength-1)

c2[:nb] = msg[:nb]
c2[nb+1] = '\0'
于 2013-01-14T02:59:31.097 に答える
3

ctypes は、C ライブラリが書き込み可能なバッファ オブジェクトを割り当てることができます。

import ctypes
init_size = 256
pBuf = ctypes.create_string_buffer(init_size)

参照: http://docs.python.org/2/library/ctypes.html#ctypes.create_string_buffer

于 2013-01-14T02:57:53.610 に答える
0

pBuf を として宣言しないでくださいc_char_pctypesその型を不変の Python 文字列に変換すると、C ポインター アドレスにアクセスできなくなります。代わりに として宣言し、POINTER(c_char)それを使用ctypes.memmoveしてデータをコピーすることができます。ウィンドウの例:

DLL コード (MSVC で としてコンパイルcl /LD test.c)

#ifdef _WIN32
#   define API __declspec(dllexport)
#else
#   define API
#endif

typedef int (*CALLBACK)(void *pFileHandle, char *pBuf, long nBufLength);
char g_buf[10] = "012345678";
CALLBACK g_callback;

API void set_callback(CALLBACK callback) {
    g_callback = callback;
}

API int call_callback() {
    return g_callback(0, g_buf, 10);
}

API const char* get_buf() {
    return g_buf;
}

Python 3 コード:

import ctypes as ct

# Declare the callback type, argument types and return types
CALLBACK = ct.CFUNCTYPE(ct.c_int,ct.c_void_p,ct.POINTER(ct.c_char),ct.c_long)
dll = ct.CDLL('./test')
dll.set_callback.argtypes = CALLBACK,
dll.set_callback.restype = None
dll.call_callback.argtypes = ()
dll.call_callback.restype = ct.c_int
dll.get_buf.argtypes = ()
dll.get_buf.restype = ct.c_char_p

# Decorating a Python function as a callback
#  makes it usable as a ctypes parameter.
@CALLBACK
def callback(handle, buf, length):
    data = b'ABCD\0'
    if length < len(data):
        return 0
    ct.memmove(buf,data,len(data))
    return 1

dll.set_callback(callback)
print(dll.call_callback())
print(dll.get_buf())

出力。get_bufが a を返し、c_char_pそれがバイト文字列であることに注意してください。const char*値が失われます。

1
b'ABCD'
于 2013-01-18T06:32:00.000 に答える