Chris
Chris

Reputation: 10101

pulling return parameters using ctypes

Foreword: I'm pretty familiar with Python but had never touched C until a week ago... now I'm trying to speak to a motor controller using ctypes.

So i've been playing around with this particular function (VCS_GetProtocolStackSettings), and was unable to get it to work until just recently. My problem is that, although it's returning a '1' (ie is sucessful per the spec below), I seem unable to access the return parameters.

Here is the my code. You can see that I am storing the return parameters in pointers to uint32 objects, I just have no idea how to get at them.

lib=ctypes.WinDLL('C:\\Program Files (x86)\\maxon motor ag\\EPOS Positioning Controller\\EPOS2\\04 Programming\\Windows DLL\\EposCmd64.dll')

typeDict={  'char': ctypes.c_char,
        'char*': ctypes.c_char_p,
        '__int8': ctypes.c_int8,
        'BYTE': ctypes.c_uint8,
        'short': ctypes.c_int16,
        'WORD': ctypes.c_uint16,
        'long': ctypes.c_int32,
        'DWORD': ctypes.c_uint32,
        'BOOL': ctypes.c_int32,
        'HANDLE': ctypes.POINTER(ctypes.c_uint32)
        }

def VCS_GetProtocolStackSettings(KeyHandle):
    '''Returns the communication parameters 'baudrate and 
        'timeout'
    '''

    func=lib['VCS_GetProtocolStackSettings']
    func.argtypes
    func.restype=typeDict['BOOL']   

    pBaudrate=ctypes.pointer(typeDict['DWORD']())
    pTimeout=ctypes.pointer(typeDict['DWORD']())
    pErrorCode=ctypes.pointer(typeDict['DWORD']())

    cKeyHandle=typeDict['HANDLE'](KeyHandle)

    return func(KeyHandle,pBaudrate,pTimeout,pErrorCode)

enter image description here

Upvotes: 1

Views: 2037

Answers (2)

user1129665
user1129665

Reputation:

If are testing ctypes, it's better to make a dummy DLL. For example, the prototype of your function is (I found it here):

     Initialisation_DllExport BOOL __stdcall VCS_GetProtocolStackSettings(HANDLE KeyHandle, DWORD* pBaudrate, DWORD* pTimeout, DWORD* pErrorCode); 

The dummy DLL would be:

/**
 * file : lib.c
 * build: cl /LD lib.c
 */
#include <windows.h>
#include <stdio.h>
#define DLL_EXPORT __declspec(dllexport)

#ifdef __cplusplus
extern "C" {
#endif
DLL_EXPORT BOOL __stdcall VCS_GetProtocolStackSettings(HANDLE KeyHandle,
                                                       DWORD* pBaudrate,
                                                       DWORD* pTimeout,
                                                       DWORD* pErrorCode);
#ifdef __cplusplus
};
#endif

DLL_EXPORT BOOL __stdcall VCS_GetProtocolStackSettings(HANDLE KeyHandle,
                                                       DWORD* pBaudrate,
                                                       DWORD* pTimeout,
                                                       DWORD* pErrorCode)
{
    printf("%lu\n", (unsigned long) KeyHandle);
    *pBaudrate  = 1;
    *pTimeout   = 2;
    *pErrorCode = 3;

    return 4;
}

So our python code would be:

#!python

from ctypes import *

BOOL   = c_int
DWORD  = c_ulong
HANDLE = c_void_p

lib = WinDLL('./lib.dll')

# BOOL __stdcall VCS_GetProtocolStackSettings(HANDLE KeyHandle,
#                                             DWORD* pBaudrate,
#                                             DWORD* pTimeout,
#                                             DWORD* pErrorCode);

VCS_GetProtocolStackSettings = lib['VCS_GetProtocolStackSettings']
VCS_GetProtocolStackSettings.restype  = BOOL
VCS_GetProtocolStackSettings.argtypes = [HANDLE,
                                         POINTER(DWORD),
                                         POINTER(DWORD),
                                         POINTER(DWORD)]

KeyHandle = HANDLE(123)
Baudrate  = DWORD(0)
Timeout   = DWORD(0)
ErrorCode = DWORD(0)

result = BOOL(VCS_GetProtocolStackSettings(KeyHandle,
                                           byref(Baudrate),
                                           byref(Timeout),
                                           byref(ErrorCode)))

print(Baudrate.value)
print(Timeout.value)
print(ErrorCode.value)
print(result.value)

If you run it:

> python example.py
123
1
2
3
4
>

Upvotes: 1

Mark Tolonen
Mark Tolonen

Reputation: 178254

pBaudrate.contents.value will access the value in the pointer, but you only really need a c_uint() and pass it by reference to the function. Below is an example. It is also an example of making a function more "Pythonic" by throwing an exception on failure.

import ctypes
from ctypes import wintypes

lib = ctypes.WinDLL(dllnamehere)
lib.VCS_GetProtocolStackSettings.argtypes = [wintypes.HANDLE,wintypes.PDWORD,wintypes.PDWORD,wintypes.PDWORD]
lib.VCS_GetProtocolStackSettings.restype = wintypes.BOOL

def VCS_GetProtocolStackSettings(KeyHandle):
    baudrate = wintypes.DWORD()
    timeout = wintypes.DWORD()
    errorcode = wintypes.DWORD()
    result = lib.VCS_GetProtocolStackSettings(KeyHandle,ctypes.byref(baudrate),ctypes.byref(timeout),ctypes.byref(errorcode))
    if not result:
        raise RuntimeError('error code = {}'.format(errorcode.value))
    return baudrate.value,timeout.value

P.S. Double check that WinDLL is appropriate. Windows system DLLs use the __stdcall calling convention, but many DLLs use __cdecl calling convention and you would use CDLL instead.

Upvotes: 2

Related Questions