2016-04-28 15 views
2

Ich habe einen Wrapper für das Corsair Utility Engine SDK geschrieben, aber es gibt eine Funktion, die ich nicht umbrechen konnte. Es ist eine asynchrone Funktion, die eine Callback-Funktion akzeptiert, aber ich kann nicht herausfinden, wie ich diesen Rückruf geben soll.Wie erstelle ich eine Callback-Funktion mit Ctypes in Python?

Die Funktion sieht wie folgt aus:

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

Dies sind die Implementierungen, die ich versucht habe, so weit:

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) 

sowie

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) 

Der Code I teste mit ist

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) 

Die time.sleep() ist nur da, um das Programm am Leben zu erhalten.

Beim Ausführen stürzt es unter Windows mit dem Fehlercode 3221225477 (STATUS_ACCESS_VIOLATION) ab.

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

+0

@ IgnacioVazquez-Abrams Ich bin nicht sicher, was du meinst, können Sie das näher erläutern? – 10se1ucgo

+0

Wie definieren Sie 'CallbackType'? – tynn

+0

@tynn [Dies ist die Dokumentation für die Funktion] (http://i.imgur.com/ZRsrSPW.png), CallbackType wird an keiner anderen Stelle im Handbuch erwähnt. – 10se1ucgo

Antwort

0

Ich habe das Problem mit der Müllabfuhr völlig vergessen, bis mich Eryksun erinnerte. Er schlug vor, dass ich einen permanenten Callback-Handler erstellen würde, der alle Callbacks speichern und sie bei Bedarf aufrufen und aufrufen würde. Das habe ich getan.

Der Funktionsprototyp sieht wie folgt aus:

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

Die eigentliche Funktion wie folgt aussieht:

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] 

Die _callback_handler Funktion sieht wie folgt aus.

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 ist die tatsächliche CFUNCTYPE dass der Permanent Rückruf hüllt (_callback_handler), die eine von dem Prototyp des argtype ist. Wenn SetLedsColorsAsync aufgerufen wird, wird der Parameter callback in ein Wörterbuch eingegeben (wobei der Kontext oder die ID der Funktion der Schlüssel ist). Anstatt den Callback in die Funktion zu stellen, wird stattdessen der permanente Callback übergeben. Sobald der permanente Rückruf aufgerufen wird, ruft er die richtige Funktion auf und entfernt sie aus dem Wörterbuch. Ich

Der verwendete Test:

#!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) 

Beispiel Ausgabe:

2969710418936 True 0 

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

+0

Der Benutzer 'Kontext' darf nicht eindeutig sein. Ich schlage vor, ein '(Callback, Kontext)' Tupel zu erstellen und seine 'ID' als' Kontext' zu setzen. Der permanente Rückruf wird dann "aufspringen" und das Tupel entpacken. – eryksun

+0

Die Optimierung, um den Kontext als 'NULL' (' None') zu übergeben, wenn der Benutzer keinen 'Callback' bereitstellt, ist gut, aber es muss eine Fehlerprüfung durchgeführt werden. Frühe Ausnahmen sind klarer als asynchrone Ausnahmen im permanenten Rückruf. Wenn 'callback'' None' ist, dann rufe 'ValueError' auf, wenn' context' nicht auch 'None' ist. Else raise 'TypeError' wenn' not callable (callback) '. Andernfalls speichern Sie das Tupel in '_callbacks' durch seine' id', die zum 'context' für den permanenten Callback wird. – eryksun

+0

Ich empfehle auch '_libcue',' _callback' und '_callbacks' für die Klasse selbst zu speichern. Es gibt keinen Grund, diese in jeder Instanz festzulegen. – eryksun