5

I need to set a callback function in a C library equal to a C# function, and can't figure out a way to do it without either dlopen or kernel32, which seems windows/unix specific. Does anyone know of a way to do this?

The problem: C shared library exposes function pointers, whose values should be set by over-writing them. E.g.

//C Code
extern void (*ptr_R_ShowMessage) (const char *)

The current c# code creates a delegate to a function that matches this signature, uses the marshal class to get a pointer to that delegate, and then overwrites the C pointer with this value.

//call to libdl or kernel32.dll 
IntPtr showPointer = GetFunctionAddress(hndl,"ptr_R_ShowMessage");
IntPtr newShowPointer = Marshal.GetFunctionPointerForDelegate(matchingDelegate);
Marshal.WriteIntPtr(showPointer, newShowPointer);

The requirement on libdl and kernel32.dll causes all kinds of problems... and ideally would be avoided.

Does anyone know how I can make the C libraries pointer point to the C# code, without modifying the C code or using the GetFunctionAddress dynamic loading? I think this might be impossible, but it seems like it could be.

3 Answers 3

5

I'm not 100% sure if this is what you're looking for.

In our case, we have a third party C Sdk that provides several hooks for callback messages. The callback call is initiated in C code and expects to invoke a C function, but we are able to hook it to a C# method using the following steps. (This code is kind of old and probably there are more succint and modern methods to accomplish the same in newer iterations of C#).

First we declare inside a C# class a Callback delegate, compatible with the signature of the C function pointer the SDK is expecting.

For example, if the C code is expecting to call back a function having the following signature:

void DecCallBack(int nPort, void* pBuf, int nSize, FRAME_INFO *pInfo, int nReserved1, int reserved 2);

Here's the matching C# delegate definition. Note that the "magic" happens thanks to the UnmanagedFunctionPointer attribute:

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate void DecCallBack(int nPort, IntPtr pBuf, int nSize, ref FRAME_INFO frameInfo, int nReserved1, int nReserved2);

Once we have this, we declare a class member of type DecCallBack that will hold a delegate instance in our class.

static DecCallBack _decodeCallBack = null;

Next we code a handler method compatible with the delegate signature. In this case we name it HandleDecData()

private static void HandleDecData(int nPort, IntPtr pBuf, int nSize, ref FRAME_INFO frameInfo, int nReserved1, int nReserved2) {

   // Here we handle the callback,  this code is being called from an external C library

}       

Next, somewhere in the C# class, we need to initialize the delegate member and hook our callback Handler (In this case it is the static method HandleDecData()

_decodeCallBack += new DecCallBack(HandleDecData);

Finally, we pass the delegate member as if it were a function pointer to C. In our case, the third party SDK has a C PlayM4_SetDecCallBack() function that expects the function pointer to call when the callback is invoked - we can safely pass the delegate member of our class instead.

 if( !PlayM4_SetDecCallBack(channel, _decodeCallBack) ) 
  throw new InvalidOperationException("Error setting DecCallBack");

I hope this is helpful.

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

2 Comments

Oh man, definitely helpful! Had no idea about that UnmanagedFunctionPointerAttribute. That said, unfortunately the SDK I am using does not expose a "Set" with which to set the value, but only the function pointer variable itself. That is, I need to figure out a way to get the address of the function pointer in the shared library, so I can re-write it with the new function address.
Oh, I see - I think that the method you are currently using GetProcAddress() is fine. You can encapsulate the 'hacky' code under a nice C# property ;)
1

C shared library DO NOT exposes function pointers in that way, extern means that this function is defined in another C file and linker during creation of binary object will use actual code from the function in final executable

to fix this you need to include C# file somehow in C project in way the C compiler will link both into binary object, but it is impossible

what current code is trying to do is called "hack", if you want to achieve similar functionality without hacks you need:

  1. In C code provide function which will accept pointer to function to execute
  2. In C code where ptr_R_ShowMessage is called use pointer from p1
  3. In C# code call function from P1 and provide method to call as argument

Here is sample I've found which executes function from external library and provide it C# function as callback: http://tutorials.csharp-online.net/CSharp_Delegates_and_Events%E2%80%94Win32_callbacks

8 Comments

Hi Ilya, thanks for giving a response! I am going to edit above to clarify some things, agreed this is a hack, definitely not mine...
Okay, so the C code does call the address of ptr_R_ShowMessage when it wants to call the ShowMessage function. This pointer is also defined somewhere else in the C file. However, to embed this library, this function pointer needs to be rewritten with a new function address, by assigning to it a new function address value (in this case for a c# function with the same signature).
do you know how I can get the address of the original function pointer from C#?
@evolvedmicrobe you do not need this, check my answer again, you need to provide mediator function which will get pointer from C# and store it in variable in C
The problem is I don't control the SDK I am trying to set, and they don't expose a function that will store the R variable, they only expose the place to store it (as a value, but not as a function). I can get the pointer to the C# function no problem, but setting the C function is...
|
-3

I confirmed that this is not possible in C# directly, so interfacing with the libary load function is required. Thanks to both for answers though, definitely useful.

Comments

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.