Greg Dolley’s Weblog

A Blog about Graphics Programming, Game Programming, Tips and Tricks

Part 1: How to Make Native Calls from Managed Code in C++/CLI

Posted by gregd1024 on January 30, 2008

Sometimes when working in a .NET environment we inevitably need to call a native function. With most .NET languages the programmer doesn’t need to have knowledge of the marshalling going on behind the scenes. However, programming in C++/CLI happens to be the exception.

While C++/CLI certainly does a better job at abstraction than the old managed C++ language in .NET 1.0, it doesn’t compare to the ease of C# or VB.NET.

This tutorial will be split into two posts: basic marshalling and advanced marshalling. This one will cover the basics – how simple data types are marshalled such as strings and character arrays.

The Marshal Class

Take a look at the following namespace:

  • System::Runtime::InteropServices

This namespace contains a class called “Marshal.” It is very versatile and will handle all the marshalling techniques presented in this tutorial. For most cases of managed to native calls, you’ll never have to look anywhere else.

Functions Which Take String Arguments

First, let’s look at a function which takes a series of native strings. We’re going to look at “printf()”. Since native strings are really just character buffer pointers, we need to convert a .NET “String” object into a character buffer pointed to by an IntPtr struct. Once we have an IntPtr, grabbing the native pointer comes down to calling one of its member functions. To do the first step we use the following function in Marshal:

  • System::IntPtr Marshal::StringToHGlobalAnsi(System::String ^s)

This function will allocate a character buffer and return an address via the IntPtr struct. Converting this into a native pointer is a simple matter of calling its “ToPointer()” member function. The “ToPointer()” function looks like this:

  • void *IntPtr::ToPointer(void)

Now we put both functions together like in the following code snippet:

// Get .NET version string, convert to native char *, and print to screen with printf()

 

Version ^v = Environment::Version;

String ^ver_string = String::Format(“{0}.{1}.{2}”, v->Major, v->Minor, v->Build);

 

char *native_string = (char *)Marshal::StringToHGlobalAnsi(ver_string).ToPointer();

printf(“.NET version: %s\r\n”, native_string);

Marshal::FreeHGlobal(IntPtr((void *)native_string));

We must call “FreeHGlobal()” because the previous call to “StringToHGlobalAnsi()” allocated a buffer of characters on the unmanaged heap (just like calling “malloc()” in C or C++). Otherwise we would end up with a memory leak.

Functions Which Return Strings

OK, now we’re going to look at native functions which return strings. This time, we need to do the opposite of what we did in the last section – take a char pointer buffer (or unicode buffer) and convert it into a .NET String object. We do this via the “PtrToStringAnsi()” function inside the Marshal class. It looks like this:

  • System::String ^Marshal::PtrToStringAnsi(System::IntPtr ptr)

You must pass it a managed pointer wrapper instead of a native pointer, but the IntPtr object has a constructor that will do this conversion automatically. You’ll see how to use it in the following example.

We’re going to read some characters from a file via the native “fread()” function. This function fills in a buffer pointed to by the first parameter and returns the number of bytes read. If the function had instead returned a pointer directly via its return value, the conversion technique would be no different.

char file_buffer[256];

FILE *fp = fopen(“c:\\Temp\\test.txt”, “rb”);

 

memset(file_buffer, 0, sizeof(char)*256);

fread(file_buffer, sizeof(char), 10, fp);

String ^file_buffer_string = Marshal::PtrToStringAnsi(IntPtr((void *)file_buffer));

 

fclose(fp);

Objects and Structures

Strings and character buffers may not be the only thing you need to marshal between a managed context and a native call. You may come across a function that takes some predefined object – such as a native struct or class. These cases will be covered in part two of this tutorial which I will be posting in the next couple days.

As always you can get new posts automatically using this RSS feed. Or if you prefer email updates, click here.

-Greg Dolley

Advertisement

2 Responses to “Part 1: How to Make Native Calls from Managed Code in C++/CLI”

  1. Tom O'Connell said

    Love the article, but I have a question. I am working on a similar structure (Native 3rd Party C++ .lib and .hpp files, Wrapped in C++/CLI, used in C#) and I have hit a brick wall with my work. Everything seemed to work in debug, but on switch to release mode, I get access violation errors. I am not sure if it’s an overrun from poor packing, or something worse. Attached is my post:

    The code from the original hpp files for callback function format:

    typedef int (*CallbackFunction) (void *inst, const void *data);

    Code from the C++/CLI Wrapper for callback function format: (I’ll explain why I declared two in a moment)


    public delegate int ManagedCallbackFunction (IntPtr oInst, const IntPtr oData);
    public delegate int UnManagedCallbackFunction (void* inst, const void* data);

    –Quickly, the reason I declared a second “UnManagedCallbackFunction” is that I tried to create an “intermediary” callback in the wrapper, so the chain changed from Native C++ > C# to a version of Native C++ > C++/CLI Wrapper > C#…Full disclosure, the problem still lives, it’s just been pushed to the C++/CLI Wrapper now on the same line (the return).

    And finally, the crashing code from C#:


    public static int hReceiveLogEvent(IntPtr pInstance, IntPtr pData)
    {
    Console.WriteLine("in hReceiveLogEvent...");
    Console.WriteLine("pInstance: {0}", pInstance);
    Console.WriteLine("pData: {0}", pData);

    // provide object context for static member function
    helloworld hw = (helloworld)GCHandle.FromIntPtr(pInstance).Target;
    if (hw == null || pData == null)
    {
    Console.WriteLine("hReceiveLogEvent: received null instance pointer or null data\n");
    return 0;
    }

    // typecast data to DataLogger object ptr
    IntPtr ip2 = GCHandle.ToIntPtr(GCHandle.Alloc(new DataLoggerWrap(pData)));
    DataLoggerWrap dlw = (DataLoggerWrap)GCHandle.FromIntPtr(ip2).Target;

    //Do Logging Stuff

    Console.WriteLine("exiting hReceiveLogEvent...");
    Console.WriteLine("pInstance: {0}", pInstance);
    Console.WriteLine("pData: {0}", pData);
    Console.WriteLine("Setting pData to zero...");
    pData = IntPtr.Zero;
    pInstance = IntPtr.Zero;
    Console.WriteLine("pData: {0}", pData);
    Console.WriteLine("pInstance: {0}", pInstance);
    return 1;
    }

    All writes to the console are done and then we see the dreaded crash on the return:

    Unhandled exception at 0x04d1004c in helloworld.exe: 0xC0000005: Access violation reading location 0x04d1004c.

    If I step into the debugger from here, all I see is that the last entry on the call stack is: > “04d1004c()” which evaluates to a decimal value of: 80805964

    Which is only interesting if you look at the console which shows:


    entering registerDataLogger
    pointer to callback handle: 790848
    fp for callback: 2631370
    pointer to inst: 790844
    in hReceiveLogEvent...
    pInstance: 790844
    pData: 80805964
    exiting hReceiveLogEvent...
    pInstance: 790844
    pData: 80805964
    Setting pData to zero...
    pData: 0
    pInstance: 0

    Now, I know that between debug and release some things are quite different in the Microsoft world. I am, of course worried about byte padding and initialization of variables, so if there is something I am not providing here, just let me know and I’ll add to the (already long) post. I also think the managed code may NOT be releasing all ownership and then the native C++ stuff (which I don’t have the code for) may be trying to delete or kill off the pData object, thus crashing the app.

    More full disclosure, it all works fine (seemingly) in Debug mode!

    A real head scratch issue that would appreciate any help!

  2. Pass C++/CLI delegate as function pointer into native C++ class…

    To Pass C++/CLI delegate as function pointer into native C++ class, you can make use of this .net method: System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate, the key point is: define the delagate (MyDelegate^ m_myDelegate in my ex…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

 
%d bloggers like this: