pythonctypesdllexport

c# dll using unmanagedexports calling from python


i'm trying to use an old library i made with c# from python.

python code

import ctypes

dll : ctypes.WinDLL = ctypes.windll.LoadLibrary("my.dll")

Name = dll.Name

c_text = ctypes.create_string_buffer(128)
c_text.value = b'Hello World'

Name.argtypes = (ctypes.c_char_p,)
Name.restype = ctypes.c_char_p
result = Name(c_text).decode('utf-8')

c# code

[DllExport("Name", CallingConvention = CallingConvention.StdCall)]
private static string Name(string name){
    return name
  }

Works well, now my question is when i need to pass intPtr and in python loaded an image with PIL.Image.open().

c# code

[DllExport("Image", CallingConvention = CallingConvention.StdCall)]
private static string Image(IntPtr ScreenCapture){
    try{
        img = (Bitmap)Image.FromHbitmap(ScreenCapture);
        return "Ok";
    }
    catch{
        return "Error";
    }
 }

The argtypes will be c_void_p? how can a get that c_void_p from image?

i know the intPtr is like void* but i no idea how to use ctypes.POINTER(ctypes.c_void_p) and PIL.Image.open().


Solution

  • If [MS.Learn]: Image.FromHbitmap Method is what the .dll is using inside, then we have an XY Problem ([SE.Meta]: What is the XY problem?).
    If not, then I'll just delete the answer.

    Image.FromHbitmap (and surely other methods as well) work with HBITMAP, which is an opaque GDI HANDLE (check [MS.Learn]: Bitmaps (Windows GDI)), and no assumption should be made on what it actually holds, only use functions that work with it.

    The assumption that PIL.Image.open result is HBITMAP compatible, is false. Also Image.FromHbitmap just "decodes" the information in the HBITMAP (yielding bitmap properties like width, height, pixel data, colors, ...). But that's exactly what PIL is also doing, rendering the .dll function call useless. You don't need to call it as you already have that information.

    If for some reason you'd fell that you need to call the .dll function, then you must also wrap some WinAPIs that return a HBITMAP (e.g.: [MS.Learn]: LoadBitmapA function (winuser.h), but I doubt that it will fit you needs).
    While we're on the wrapping topic, I must point [GitHub]: mhammond/pywin32 - Python for Windows (pywin32) Extensions which is a Python wrapper over WinAPIs (and contains lots of boilerplate code that Python programmers would not have to write). Documentation (WiP) can be found at [GitHub.MHammond]: Python for Win32 Extensions Help (or [ME.TimGolden]: Python for Win32 Extensions Help).

    Now, strictly answering to your question, the argument type should indeed be a c_void_p (or, for clarity just use the ctypes.wintypes.HBITMAP alias), but again you should cast something compatible (and PIL.ImageFile isn't).

    Might be useful to read (probably):



    Update #0

    Here's a sample program:

    code00.py:

    #!/usr/bin/env python
    
    import ctypes as cts
    import sys
    from ctypes import wintypes as wts
    
    import win32gui as wgui
    import win32ui as wui
    
    
    DLL_NAME = "./my.dll"
    
    
    def main(*argv):
        dll = cts.WinDLL(DLL_NAME)
        image = dll.Image
        image.argtypes = (wts.HBITMAP,)
        image.restype = cts.c_char_p
    
        bmp = wui.CreateBitmap()
        # ...
        print(bmp.GetHandle())  # @TODO - cfati: Check for NULL (has some data)
    
        ret = image(bmp.GetHandle())
    
        # ...
        wgui.DeleteObject(bmp.GetHandle())
    
    
    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.\n")
        sys.exit(rc)