4

I'm having trouble converting a C++ .dll function to C#.

The function is this:

    void funct(void*(*handler)(void*));

I think this means passing a pointer to function taking a void pointer and returning a void pointer, as explained here:

Passing Function Pointer.

What I'm trying to do is do the same thing in C#, but I have do idea how. I tried to use delegates, but I am both unsure how to and also if they can even do what I am trying to do.

Thanks for the help!

EDIT:

Here are the C++ functions for register_message_handler and message_handler:

void register_message_handler(void*(*handler)(void*));
void *message_handler(void *raw_message);

EDIT: xanatos has the exact explanation and conversion to C# below. Thanks a ton xanatos!

9
  • No, it returns void. So it is a method with no parameters and no return value. Commented Mar 27, 2015 at 12:18
  • see this codeproject.com/Articles/27298/… or stackoverflow.com/questions/13280323/… Commented Mar 27, 2015 at 12:19
  • 2
    void funct(Action handler) Commented Mar 27, 2015 at 12:19
  • 2
    void*(handler)(void) should be void (*handler)(void) Commented Mar 27, 2015 at 12:24
  • 1
    @Tropicana55 Can you show us the C (the ones in the .h file) signature of register_message_handler and message_handler? It isn't very clear how they should work together Commented Mar 27, 2015 at 12:50

1 Answer 1

9
void funct(void*(*handler)(void*));

is a function that accepts a pointer and returns a pointer.

In C# it would be:

IntPtr MyFunc(IntPtr ptr);

So you'll need a delegate like:

public IntPtr delegate MessageHandlerDelegate(IntPtr ptr);

[DllImport("mydll.dll")]
public static extern void register_message_handler(MessageHandlerDelegate del);

Note that P/Invoke (calling native methods) is one of the rare cases where Action<...> and Func<...> delegates don't work, and you have to build "specific" delegate.

BUT to call it, it's a little complex, because you must save a "copy" of the delegate so that if the C functions calls this method at any time, this copy is still "alive" and hasn't been GC (see for example https://stackoverflow.com/a/5465074/613130). The most common way to do it is to encapsulate everything in a class, and put the copy in a property/field of the class:

class MyClass
{
    public delegate IntPtr MessageHandlerDelegate(IntPtr ptr);

    [DllImport("mydll.dll")]
    public static extern void register_message_handler(MessageHandlerDelegate del);

    [DllImport("mydll.dll")] 
    public static extern IntPtr message_handler(IntPtr message);

    public MessageHandlerDelegate Del { get; set; }

    public void Register()
    {
        // Make a copy of the delegate
        Del = Handler;
        register_message_handler(Del);            
    }

    public IntPtr Handler(IntPtr ptr)
    {
        // I don't know what ptr is
        Console.WriteLine("Handled");
        return IntPtr.Zero; // Return something sensible
    }
}

Note that if you use IntPtr then you don't need the unsafe.

If you want to pass message_handler to register_message_handler the safest way is to

// Make a copy of the delegate
Del = message_handler;
register_message_handler(Del);

There is a possibility that you can do directly no there isn't, checked

register_message_handler(message_handler);

and that the CLR will solve this, BUT I'm not sure of this... I can't easily test it, and I wouldn't do it. (if you want to test it, add a GC.Collect() just after the register_message_handler. If after some time you receive a CallbackOnCollectedDelegate error then you know you can't do it :-) )

Mmmh... checked. You can't do the raw register_message_handler(message_handler), you have to use MyClass.

Be very aware of something: it's better to always specify the calling convention C-side and C#-side even in function pointers. C# uses stdcall, while C uses cdecl. In x86 mode you can get very awful silent crashes (in x64 there is a single calling convention)

void __stdcall register_message_handler(void* (__stdcall *handler)(void*));
void * __stdcall message_handler(void *raw_message);

and C# side

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate IntPtr MessageHandlerDelegate(IntPtr ptr);

[DllImport("Win32Project1.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void register_message_handler(MessageHandlerDelegate del);

[DllImport("Win32Project1.dll", CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr message_handler(IntPtr message);

(or everywhere cdecl)

Sign up to request clarification or add additional context in comments.

5 Comments

The question is about void *(f)(void) , none of your examples is an exact match.
Holy cow! Thanks a ton xanatos! Sorry for the returning void or void pointer confusion, I forgot to turn the c++ function into code and the * dropped out. I just edited in the 2 c++ functions and I will try your recommendation now.
Hey xanatos, I implemented your code and I had another question as to how to call it correctly later. In addition to the code inside of MyClass above, I added in the message_handler function: [DllImport("mydll.dll")] public unsafe static extern void message_handler(void *message); Then, I tried calling it later on with this call: MyClass.register_message_handler(MyClass.message_handler); and I got a "convert from void* to IntPtr" error. I tried to convert the void* using the IntPtr() conversion call, but I think I'm missing the point of the error.
@Tropicana55 The signature is wrong. Right one: [DllImport("mydll.dll")] public static extern IntPtr message_handler(IntPtr message). And see the added paragraphs to the answer. When you see a void*, use IntPtr
Oh that's right, I'm sorry. You did mention that void* to IntPtr point. Thank you so much! It now works perfectly. I wish I could do more than just thank you; hell, I can't even upvote you because I'm too new. But you have been an incredible help.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.