Library tutorials & articles

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.

Comments

  1. 10 Nov 2004 at 11:56
    Hi,

    have also tried to convert the ambigous #define MAKEINTRESOURCEW(i) (LPWSTR)((ULONG_PTR)((WORD)(i))) in C++ into C# but with no luck.
    So I compiled a C++ program using MAKEINTRESOURCE and traced it to see what TYPE is really passed to UpdateResource(..) or similar resource accessing functions. What I hav efound that it actaully passes the interger value (Resource ID). For example if you have a Resource with "MY_RES_ID" with ID 129
    the compiled code actaully just passes the 129 as a WORD data.

    So in C# while doing DllImport, I just chaged the Data Type for the Resource Id parameter which uses MAKEINTRESOURCE.
    Here is what I did with UpdateResource

    [DllImport("kernel32.dll")]
    static extern bool UpdateResource(IntPtr hUpdate, string lpType, int lpName,  ushort wLanguage, IntPtr lpData, uint cbData);

    Notice the Third param which has been chaged from String to int. And It works!!!  Now you will just pass (using my example as mentioned above) 129 instead of MY_RES_ID, or directly MY_RES_ID if it is defined somewhere to reprent 129.



    ----
    Rezaul Kabir
    shuvro@yahoo.com
  2. 16 Apr 2004 at 17:20
    Sorry, I had a comment, but it was a mis-understanding of the original post.
  3. 24 Mar 2004 at 10:03
    This article is very useful to understand how to call unmanaged function from C#.net. I have to do the same thing for my project but it seems little complex as it is passing user defined data type as an argument to the calling function. I will appreciate help with this.
    Unmanaged C++ function which I am trying to call from C#.net app. is as below: (from the header file)

    static void LibAction(const libString& actionXML, libString& returnXML, libError& libError);

    libSting is a reference to the container class - wraps Standard Teplate Library String type. declared in the header file as below:

    class LIB_CLASS_EXPORT LibString : public std::string
    {
     typedef std::string STL_STRING;
    public:

     // --- Constructors/Destructor ---
     
     LibString() { ; }
     LibString(const LibString& S);
     LibString(const char* s);
     LibString(char s);
     LibString(istream&);
     LibString(const STL_STRING & S) : STL_STRING(S) { ; }
     virtual ~LibString();


    libError is a reference to the container class which is used for the error handling.

    How do I call this function from C# windows application.

    Below is how I am trying to declare above function in my C# app.

    [DllImport(@"C:\Test\dlls\Lib.dll", EntryPoint="LibAction")]
    public static extern void LibAction(string actionXML, string returnXML, string libError);

    Instead of string I should be passing ref to the libString. I am not sure how do I do that in C#.

    Please help.




  4. 01 Jan 1999 at 00:00

    This thread is for discussions of Hosting Control Panel Applets using C#/C++.

Leave a comment

Sign in or Join us (it's free).

Mark Belles
AddThis

Related discussion

Related podcasts

  • Object-Oriented Programming in Ruby

    In this episode, I talk with Scott Bellware about object-oriented programming in Ruby, and Ruby's object model. This is taken from a private conversation, and the audio quality suffers at times. Much thanks to Scott for allowing this to be released.This episode of the Alt.NET Podcast is bro...

Want to stay in touch with what's going on? Follow us on twitter!