2016-04-28 110 views
2

我爲Corsair實用工具引擎SDK編寫了一個包裝器,但有一個函數我一直無法包裝。這是一個接受回調函數的異步函數,但我似乎無法弄清楚如何給它回調。如何用Python中的ctypes創建回調函數?

功能如下:

bool CorsairSetLedsColorsAsync(int size, CorsairLedColor* ledsColors, void (*CallbackType)(void* context, bool result, CorsairError error), void *context) 

這些是我迄今爲止嘗試過的實現:

def SetLedsColorsAsync(self, size, led_color, callback, context): 
    c_func = CFUNCTYPE(c_void_p, c_void_p, c_bool, c_int) 
    c_callback = c_func(callback) 
    self._libcue.CorsairSetLedsColorsAsync.restype = c_bool 
    self._libcue.CorsairSetLedsColorsAsync.argtypes = [c_int, POINTER(CorsairLedColor), c_void_p, c_void_p] 
    return self._libcue.CorsairSetLedsColorsAsync(size, led_color, c_callback, context) 

以及

def SetLedsColorsAsync(self, size, led_color, callback, context): 
    c_func = CFUNCTYPE(None, c_void_p, c_bool, c_int) 
    c_callback = c_func(callback) 
    self._libcue.CorsairSetLedsColorsAsync.restype = c_bool 
    self._libcue.CorsairSetLedsColorsAsync.argtypes = [c_int, POINTER(CorsairLedColor), c_func, c_void_p] 
    return self._libcue.CorsairSetLedsColorsAsync(size, led_color, c_callback, context) 

的代碼我正在測試的是

from cue_sdk import * 
import time 


def test(context, result, error): 
    print context, result, error 
    return 0 

Corsair = CUE("CUESDK.x64_2013.dll") 
Corsair.RequestControl(CAM_ExclusiveLightingControl) 
Corsair.SetLedsColorsAsync(1, CorsairLedColor(CLK_H, 255, 255, 255), test, 1) 

while True: 
time.sleep(1) 

time.sleep()只是爲了讓程序保持活動狀態。

運行時,它在Windows上崩潰,錯誤代碼爲3221225477STATUS_ACCESS_VIOLATION)。

If you need to see the actual wrapper, you can find it here

+0

@ IgnacioVazquez - 艾布拉姆斯我不知道你的意思,你能詳細點嗎? – 10se1ucgo

+0

你如何定義'CallbackType'? – tynn

+0

@tynn [這是該函數的文檔](http://i.imgur.com/ZRsrSPW.png),在手冊的其他地方沒有提及CallbackType。 – 10se1ucgo

回答

0

我完全忘記了垃圾收集的問題,直到eryksun提醒我。他建議我創建一個永久回調處理程序,用於存儲所有回調,並在必要時調用+彈出它們。這就是我所做的。

的函數原型如下:

self._callback_type = CFUNCTYPE(None, c_void_p, c_bool, c_int) 
self._callback = self._callback_type(self._callback_handler) 
self._libcue.CorsairSetLedsColorsAsync.restype = c_bool 
self._libcue.CorsairSetLedsColorsAsync.argtypes = [c_int, POINTER(CorsairLedColor), self._callback_type, c_void_p] 

_callback_handler功能如下:

def _callback_handler(self, context, result, error): 
    if context is not None and context in self._callbacks: 
     self._callbacks.pop(context)(context, result, error) 

的實際功能如下所示。

def SetLedsColorsAsync(self, size, led_color, callback=None, context=None): 
    if callback: 
     if context is None: 
      context = id(callback) 
     self._callbacks[context] = callback 
    return self._libcue.CorsairSetLedsColorsAsync(size, led_color, self._callback, context) 

_callback_type是實際CFUNCTYPE封裝了永久回調(_callback_handler),這是原型的argtype之一。當調用SetLedsColorsAsync時,參數callback被放入字典中(上下文或函數的ID是鍵)。而不是將回調提供給函數,而是永久回調傳遞。一旦永久回調被調用,它將調用正確的函數並將其從字典中移除。

測試我使用:

#!python3 
import time 

from cue_sdk import * 


def test(context, result, error): 
    print(context, result, error) 
    assert context == id(test) 


Corsair = CUE("CUESDK.x64_2013.dll") 
Corsair.RequestControl(CAM_ExclusiveLightingControl) 
Corsair.SetLedsColorsAsync(1, CorsairLedColor(CLK_H, 255, 255, 255), test) 

while True: 
    time.sleep(1) 

輸出示例:

2969710418936 True 0 

If I'm not making sense, the commit is here.

+0

用戶'上下文'可能不是唯一的。我建議創建一個'(回調,上下文)'元組並將其''id'設置爲'context'。永久回調將彈出並解開元組。 – eryksun

+0

當用戶沒有提供「回調」時,優化傳遞上下文爲'NULL'('None')是好的,但它需要錯誤檢查。在永久回調中,早期異常將比異步異常更清晰。如果'callback'是'None',那麼如果'context'不是'None',則引發'ValueError'。如果'不可調用(回調)',則引發'TypeError'。否則,通過它的'id'將元組存儲在'_callbacks'中,這將成爲永久回調的''context''。 – eryksun

+0

我還建議在類本身上存儲'_libcue','_callback'和'_callbacks'。沒有理由在每個實例上設置它們。 – eryksun

相關問題