1

I'm calling an external C library from a C# wrapper that I'm writing. One of the functions I'm calling is to register a callback function. I get no errors when I call the setCallback function but when the callback function is later called I get errors, unfortunately unspecified by the C library.

Here's the C method (from the header file)

DLLExport int setCallbacks(Client handle, void* context, connectionLost* cl, messageArrived* ma, deliveryComplete* dc); 

Here's my C# declaration of the method

[DllImport("some.dll", CharSet = CharSet.Auto, ExactSpelling = false, CallingConvention = CallingConvention.Cdecl)] private static extern int setCallbacks(IntPtr client, IntPtr context, MulticastDelegate connectionLost, MulticastDelegate messageArrived, MulticastDelegate messageDelivered); 

These are my delegates

private delegate int ConnectionLostDelegate(IntPtr context, [MarshalAs(UnmanagedType.LPStr)] String cause); private delegate int MessageArrivedDelegate(IntPtr context, [MarshalAs(UnmanagedType.LPStr)] String title, Int32 titleLength, MessageObject message); private delegate int MessageDeliveredDelegate(IntPtr context, int deliveryToken); 

This is how I call the method

ConnectionLostDelegate conLost = new ConnectionLostDelegate(ConnectionLost); MessageArrivedDelegate mesArr = new MessageArrivedDelegate(MessageArrived); MessageDeliveredDelegate mesDel = new MessageDeliveredDelegate(MessageDelivered); result = setCallbacks(client, IntPtr.Zero, conLost, mesArr, mesDel); 

Here's the struct that is used by one of the callbacks

[StructLayout(LayoutKind.Sequential)] public class MessageObject { [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.U1)] public byte[] struct_id; Int32 struct_version; Int32 payloadlen; IntPtr payload; Int32 qos; Int32 retained; Int32 dup; Int32 msgid; public MessageObject() { struct_id = Encoding.ASCII.GetBytes("WXYZ"); struct_version = 0; payload = IntPtr.Zero; } } 

C Headers for callback functions as follows..

typedef int messageArrived(void* context, char* title, int titleLen, MessageObject* message); typedef void deliveryComplete(void* context, int token); typedef void connectionLost(void* context, char* cause); 
2
  • Can you also show signature from c headers for connectionLost, messageArrived, deliveryComplete? Commented Apr 13, 2012 at 11:03
  • Hi Nikolay, I've added these now. Commented Apr 13, 2012 at 11:16

1 Answer 1

1

I don't have a lot of experience with p/invoke but it looks like you should correct signatures of delegates to return void there it is void in c. And also use ref for pointer parameters like context and message

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

3 Comments

Thanks for the heads up on the delegate signature return types, that won't have helped. I shouldn't need a ref for the parameters as they are of type IntPtr and a class which will be by reference anyway.
It is IntPtr for context, but not for message. I think it should be private delegate int MessageArrivedDelegate(IntPtr context,..., ref MessageObject message);
If MessageObject was a struct and not a class I would agree with you. I have tried it with a ref prefix and it still doesn't work.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.