ctypes callback works (more or less) while debugging but not independently

1

The pycanon package does not work with Canon's current ED SDK, therefore I have started from scratch making a new one. Progress comes step by step, but now I am stuck on catching the camera's events, which invoke a callback mechanism. There are at least two issues with it, and they may well be interrelated.

To start, here is the callback function that I am having called:

def propertyChanged(*args):
    print(args)
    return window.propertyChanged(*args)

In the header file, this is defined as:

#if defined( BUILD_EDSDK_DLL )
    #define EDSAPI  EDSEXPORT EDSSTDCALL
#else
    #define EDSAPI  EDSIMPORT EDSSTDCALL
#endif
/*-----------------------------------------------------------------------------
//
//  Function:   EdsSetPropertyEventHandler
//              
//  Description:
//       Registers a callback function for receiving status 
//          change notification events for property states on a camera.
//
//  Parameters:
//       In:    inCameraRef - Designate the camera object. 
//              inEvent - Designate one or all events to be supplemented.
//              inPropertyEventHandler - Designate the pointer to the callback
//                      function for receiving property-related camera events.
//              inContext - Designate application information to be passed by 
//                      means of the callback function. Any data needed for
//                      your application can be passed. 
//      Out:    None
//
//  Returns:    Any of the sdk errors.
-----------------------------------------------------------------------------*/
EdsError EDSAPI EdsSetPropertyEventHandler(
            EdsCameraRef                    inCameraRef, 
            EdsPropertyEvent                inEvnet,           
            EdsPropertyEventHandler         inPropertyEventHandler,
            EdsVoid*                        inContext );

It works, more or less, in that it is called with (sometimes) proper arguments. What's wrong is that when I am not in the debugger, the arguments seem to be offsetted by two longInts.

Look at the full test program listed below, especially the run()-method. What I would expect is that camera.setAEMode(5) would trigger the callback. It doesn't, but camera.getBodyID() two lines above it does so, whereas I would not expect this. I suppose that is only my understanding of the working of the API and not the issue I am having.

When the camera.getBodyID() is being executed (with testCallback True) and a breakpoint in propertyChanged, I see i is called with the arguments (257, 3, 0, 129568680), which make sense:

  • 257 is the integer value of the enum EdsPropertyEvents.PropertyChanged;
  • 3 the integer value of the enum EdsPropID.bodyID;
  • 0 is the optional parameter that I don't use and assign zero
  • the last parameter I assume to be the context pointer, that I don't use while I wouldn't know how to convert it back to something meaningful in Python.

However, when running exactly the same while not debugging, the parameters passed to propertyChanged are (107020331, 106511131, 257, 3).

And there is this second issue: when I don't assign the callback function, the program runs just fine. But when I assign True to testCallback, then the program crashes after returning from the callback (exception: access violation reading 0x0000002B when not debugging, or PROPERTIES_UNAVAILABLE in response to getBodyID()). When the program is run without triggering the callback function (# camera.getBodyID()), but still with assigning it, then python.exe crashes after leaving the run() -method and thinks it is a good idea telling Microsoft about that.

What should I do to make this work?

from PyQt5.Qt import *  # @UnusedWildImport
from canonSDK import edSDK
from canonSDK.edSDK import EDSDK
import traceback
from canonSDK.EDSDKtypes import *  # @UnusedWildImport
from ctypes import py_object


# copy of self of the main-window object; 
# necessary since it is exceedingly hard to pass self thru the callback
window = None


def propertyChanged(*args):
    print(args)
    return window.propertyChanged(*args)


class RCmain(QWidget):

    def __init__(self):
        super().__init__()
        global window
        window = self

        QVBoxLayout(self)        
        self.console = QTextBrowser()
        self.layout().addWidget(self.console)
        pnl = QFrame(); self.layout().addWidget(pnl)
        pnl.setFixedHeight(30)
        btn = QPushButton("run", parent = pnl)
        btn.clicked.connect(self.run)
        sys.excepthook = self.exceptionHook


    def show(self):
        super().show()

        # initialize dll connection:
        self.edsdk = EDSDK()
        self.edsdk.initializeSDK()
        self.camera = None

        # show success by listing the connected cameras:
        l = self.edsdk.getCameraList()
        n = self.edsdk.getChildCount(l)

        self.print(n, "cameras:")
        for c in self.edsdk.cameras:
            self.print(c.index, ":", c.name)


    def close(self):
        self.edsdk.terminateSDK()
        if self.camera is not None:
            self.camera.close()
        super().close()


    def exceptionHook(self, type, value, tback):
        traceback.print_exception(type, value, tback)


    def print(self, *args):
        print(*args)
        self.console.append(" ".join([str(a) for a in args]))


    #WINFUNCTYPE (EdsError,  EdsPropertyEvent, EdsPropertyID, EdsUInt32, EdsVoid)        
    def propertyChanged(self, inEvent, inPropertyID, inParam, inContext):
        self.print(inEvent, inPropertyID, inParam, inContext)                  
        self.print(EdsPropID(inPropertyID).name, "has changed") # EdsPropID is an IntEnum
        return 0


    def run(self):
        # test sequence

        camera       = self.edsdk.getCamera(0)
        testCallback = True

        if testCallback:
            self.edsdk.setPropertyEventHandler(camera.reference, 
                                               EdsPropertyEvents.All, 
                                               EdsPropertyEventHandler(propertyChanged))

        self.print(camera.getProductName())
        self.print(camera.getBodyID())
        self.print(camera.getAEMode())
        camera.setAEMode(5)
        self.print(camera.getAEMode())

        camera.startLiveView()        
#       camera.downloadEvfData() # 2b impl'd
        camera.endLiveView()

        self.camera  = camera



if __name__ == '__main__':
    import sys

    edSDK.setDllPath('D:/python3/remote shooting/EDSDKv2.7/EDSDK/Dll')
    app = QApplication(sys.argv)

    w = RCmain()
    w.show()


    app.exec_() 
python
callback
ctypes
asked on Stack Overflow Mar 1, 2018 by user508402 • edited Mar 1, 2018 by Cong Ma

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0