Hosting Control Panel Applets using C#/C++

Calling unmanaged function pointers from managed c

Here now we are faced with a rather tricky problem. How do you call an unmanaged function pointer from managed code? At first glance the answer seems simple, we use delegates. However correct that solution may seem, I have not been able to uncover a means of creating a delegate in managed code to an unmanaged function pointer. Several methods in the Marshal class look promising, namely GetUnmanagedThunkForManagedMethodPtr . Here I will admit defeat because I cannot for the life of me figure out how to work this method. The docs are no help, and I simply got tired of racking my brain to figure it out. I am hoping that someone will read this article and come up with a solution for what I am about to do next. Part of this article, by the way, I am assigning to the rest of you interop gurus to help me figure that method out. I'm certain it can be done, it's just not worth my time to waste any more time trying to figure it out. If someone does, please let me know!

Enter our own trickery. I decided the easiest way to do this would be to create a small unmanaged C++ DLL that could call it for us. Calling function pointers in C++ is as easy as declaring integers to the rest of the managed world. So breaking open a Win32 project and setting its project type to dynamic link library, I created a DLL to do this work for me. I called it AppletProxy.dll for lack of a better term, because it will act as a proxy between our managed code in C# and the unmanaged function exported by the applet. I am not going to cover how to create unmanaged DLLs here, that is also beyond the scope of the article. If you are really interested, the source code should provide you with a very simple example to learn from, and as always, I'm around for questioning if you get stuck. Here is what the unmanaged function looks like that will be the key to calling the unmanaged function pointers for us.

LONG APIENTRY ForwardCallToApplet(APPLET_PROC pAppletProc, HWND hwndCpl, UINT msg, LPARAM lParam1, LPARAM lParam2)
{
    if (pAppletProc != NULL)
        return pAppletProc(hwndCpl, msg, lParam1, lParam2);
    // call the unmanaged function pointer, this is the same as calling a regular function, except we’re using the variable instead of a function name
    return 0L;
}

Ok, I know a lot of you are looking at this and thinking, what in the heck is this guy doing? I don't understand the syntax of this stuff, and what's with all the funky data types. I'm hoping that's not the case, because if it is, you should really go open MSDN and look up the data types. They are all readily available in the docs.

This method will accept an unmanaged function pointer and call the method it points to and return us the result.

Now that we have our proxy function to call the unmanaged function pointer, you might be wondering how we call that from our managed code. We simple use P/Invoke and define the entry point just like any other API. Here is how to do just that.

[DllImport("AppletProxy")]
public static extern int ForwardCallToApplet(IntPtr appletProc, IntPtr hWndCpl, AppletMessages message, IntPtr lParam1, IntPtr lParam2);

One of the problems I encountered when I started trying to call API functions were the difference in data types. I had a real problem trying to figure out what an HWND or LPARAM translated to in managed code. Here is a quick reference for you newbies that will help you out when trying to translate functions from C/C++ to managed code.

  • HWND , HANDLE , HINSTANCE , HICON , any Pointer type maps to System.IntPtr
  • DWORD , LONG , BOOL map to System.Int32 or int in C#
  • LPSTR , LPTSTR , LPCTSTR map to System.String and System.Text.StringBuilder
  • Use System.String most times, but if the API requires a pre-initialized buffer of any length, then use a System.Text.StringBuilder

I hope this helps, because I know for a while I was constantly heading off to the C header files to find the underlying definitions and then doing some research on MSDN to figure out what the data type was supposed to be declared as in managed code. The main thing to remember in my opinion is that if it starts with "H", it's most likely a handle of some sort which maps nicely to System.IntPtr . The specific implementations of course may vary from time to time, but as a general guideline these have worked out just fine.

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.” - Antoine de Saint Exupéry