1

I'm trying to pass an pointer to an enum. I found a tutorial for ctypes and enum here [ctypes structures ][1] but my application is a little different. the pseudocode for my DLL looks like this

#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>

#ifdef SOME_API_EXPORTS
#define SOME_API __declspec(dllexport)
#else
#define SOME_API __declspec(dllimport)
#endif

typedef enum   
{
    off,
    on    
} MyEnum;

SOME_API int32_t get_param(MyEnum* param1)
{
    int status = error;
    if (param1 == NULL)
    {
        return status;
    }
    //do some processing
    status = done;
    return status;
}

what I did in python looks similar to this:

    import ctypes
    from enum import IntEnum
   
    test = ctypes.WinDLL('Project.dll')
    
    if (test == 0):
        print( " Could not open DLL")
    
    class CtypesEnum(IntEnum):
        @classmethod
        def from_param(cls, obj):
            return int(obj) 
    
    class myEnum(CtypesEnum):
    
        off = 0                
        on = 1                     
    
    
    getParam = test.get_param
    getParam.argtypes = [ctypes.POINTER(ctypes.c_int)]
    getParam.restype= ctypes.c_uint
    getParam(myEnum.on)

The error I get now is

     Traceback (most recent call last):
      File "<pyshell#5>", line 1, in <module>
        getParam(myEnum.on)
    ctypes.ArgumentError: argument 1: <class 'TypeError'>: expected LP_c_long instance instead of myEnum

What's the correct way of passing an enum pointer using ctypes.I couldn't find an example and I'm kinda new to python :/

  [1]: https://v4.chriskrycho.com/2015/ctypes-structures-and-dll-exports.html

This was my solution for the python part. I read 1 and 0 respectively.

import ctypes
from ctypes import *

test = ctypes.WinDLL('Project.dll')

if (test == 0):
    print( " Could not open DLL")
 
class myEnum(c_int):

    off = 0                
    on = 1

    
getParam = test.get_param
getParam.argtype =  myEnum()
getParam.restype= ctypes.c_int
result = test.get_param(myEnum.on)
print(result)
result = test.get_param(myEnum.off)
print(result)
CristiFati
  • 38,250
  • 9
  • 50
  • 87
pekoms
  • 47
  • 1
  • 8
  • 1
    Please submit a [\[SO\]: How to create a Minimal, Reproducible Example (reprex (mcve))](https://stackoverflow.com/help/minimal-reproducible-example). What is *CtypesEnum*? Note that the function takes an *MyEnum* **pointer**, so you should have `getParam.argtypes = [ctypes.POINTER(ctypes.c_int)]` (or use the enum if it doesn't *TypeError*). Most likely, a *dupe* of: [\[SO\]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer)](https://stackoverflow.com/questions/58610333/c-function-called-from-python-via-ctypes-returns-incorrect-value/58611011#58611011). – CristiFati Aug 18 '21 at 03:39
  • I apologize. I forgot to add the CtypesEnum part. I edited my code to your suggestion but I still got the same error listed above. – pekoms Aug 18 '21 at 03:52
  • The code you posted here does **not** yield these results. There's an *ctypes.ArgumentError* involved. Please post the correct code (in one snippet, not one line between comments), and the output. (whole traceback) – CristiFati Aug 18 '21 at 04:06
  • That's all the code there is for my test. And I updated the error as well. – pekoms Aug 18 '21 at 04:34

1 Answers1

0

Listing [Python.Docs]: ctypes - A foreign function library for Python.
As I stated in my comment, the 1st error is (most likely) the same thing as [SO]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer), then after the edit, another one (which occurs before the 1st) pops up.
Here's how to get rid of all.

dll00.c:

#include <stdio.h>
#include <stdint.h>

#if defined(_WIN32)
#  define DLL00_EXPORT_API __declspec(dllexport)
#else
#  define DLL00_EXPORT_API
#endif


typedef enum {
    Off,
    On,
} Enum;


#if defined(__cplusplus)
extern "C" {
#endif

DLL00_EXPORT_API int32_t get_param(Enum *pparam);

#if defined(__cplusplus)
}
#endif


int32_t get_param(Enum *pparam) {
    int status = -1;
    if (pparam == NULL) {
        printf("NULL arg\n");
        return status;
    }
    printf("C - arg: %d\n", *pparam);
    if (pparam == NULL) {
        return status;
    }
    //do some processing
    status = 1;
    return status;
}

code00.py:

#!/usr/bin/env python

import sys
import ctypes as ct
from enum import IntEnum


DLL_NAME = "./dll00.{:s}".format("dll" if sys.platform[:3].lower() == "win" else "so")


class CtypesEnum(IntEnum):
    @classmethod
    def from_param(cls, obj):
        return int(obj)


class OOEnum(CtypesEnum):
    Off = 0
    On = 1



def main(*argv):
    dll = ct.CDLL(DLL_NAME)  # Use ct.WinDLL for Win 32bit
    get_param = dll.get_param
    get_param.argtypes = (ct.POINTER(ct.c_int),)
    get_param.restype = ct.c_int

    for en in [
        OOEnum.Off,
        OOEnum.On,
    ]:
        get_param(ct.pointer(ct.c_int(en.value)))


if __name__ == "__main__":
    print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
                                                   64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    rc = main(*sys.argv[1:])
    print("\nDone.")
    sys.exit(rc)

Output:

[cfati@cfati-5510-0:/mnt/e/Work/Dev/StackOverflow/q068826175]> ~/sopr.sh
### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###

[064bit prompt]> ls
code00.py  dll00.c
[064bit prompt]> 
[064bit prompt]> gcc -shared -m64 -fPIC -o dll00.so dll00.c
[064bit prompt]> ls
code00.py  dll00.c  dll00.so
[064bit prompt]> 
[064bit prompt]> ${PY_VENVS_HOME}/py_pc064_03_08_test0/bin/python code00.py 
Python 3.8.10 (default, Jun  2 2021, 10:49:15) [GCC 9.4.0] 064bit on linux

C - arg: 0
C - arg: 1

Done.

Things are the same on Win, only build (and run) commands differ.

CristiFati
  • 38,250
  • 9
  • 50
  • 87
  • Your solution works. I also added my solution. I get the same results when I print on the screen. Maybe I just got lucky. Thanks again. – pekoms Aug 18 '21 at 05:52
  • You're welcome! :) There were several errors (without the code necessarily reflecting them). Anyway, this should get rid of all. – CristiFati Aug 18 '21 at 06:20