7

I'm not truly a CS guy, so if any of you geniuses on here can point me in the right direction I'll be eternally grateful.

I have a c-code command line function that used to write its results to file. I converted it to return it's data via a float* array to a C++ program like such (to avoid constant file I/O):

float * mgrib(int argc, char **argv)

This worked perfectly. I now need to get this into a C# program, and here's where things go haywire.

The first thing I did to avoid the char ** was to make the arguments a series of bool. That worked fine if I allow it to still dump to file.

The problem is juggling the c-style float array in C#. Within the c-code it was allocated with malloc.

So here's everything I've tried with no success(I know the size of the array):

  1. Make a "free" function to export to call from C# to release the memory when I'm done with it. After a few loops the C# crashes with no warning.

  2. Release the malloc from C# with Marshal.FreeCoTaskMem. Same result.

  3. Move the float* to an argument and remove the c-code malloc. (void mgrib(..., float* data,...)

__a)Allocate it with Marshal.AllocCoTaskMem. Free it with Marshal.FreeCoTaskMem.

__b)Use Marshal.Copy to allocate. Free it with Marshal.FreeCoTaskMem (Maybe this is wrong?)

I've dabbled in just about everything I could find in the internet. Please let me know if more info is necessary. I'm hoping this just a simple concept that I'm missing.

Nate
  • 30,286
  • 23
  • 113
  • 184
Anthony
  • 7,638
  • 3
  • 38
  • 71
  • 5
    Forget the C prototype and write a clean one with C#. Return a float array (float[]) instead. – stefan Mar 08 '11 at 18:30
  • 1
    Please post your c# code – Gabe Moothart Mar 08 '11 at 18:39
  • 2
    Are you trying to do this well, or easily? The easy way would be to leave both programs nearly intact, but have the communicate via a pipe instead of a file. To do it well, you're probably better off writing the whole program in one language. – Jerry Coffin Mar 08 '11 at 19:12
  • I'm quite good at c# at this point, but much too weak in c-code to convert the full (very large) program from c to c#. I do wish that was an option. I'll try the below suggestion and if that is unsuccessful post some of the c# (It's a pain since the machines are not connected so I would need to sneaker net it), though it's mostly just the Dllimport call within a loop. – Anthony Mar 08 '11 at 21:39

1 Answers1

7

Use this signature for your C function (replace the mgrib.dll with real library name).

[DllImport( "mgrib.dll", EntryPoint = "mgrib" )]
public static extern IntPtr mgrib(
    int argc,
    [MarshalAs( UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr )]
    StringBuilder[] argv );

Call the mgrib function as follows:

// Prepare arguments in proper manner
int argc = 0;
StringBuilder[] argv = new StringBuilder[ argc ];
IntPtr pointer = mgrib( argc, argv );

When the call is done, you can get the result like this:

float[] result = new float[ size ];
Marshal.Copy( pointer, result, 0, size );

EDIT:

Since the mgrib is allocating the memory using malloc, we need to free the memory using free function. However, you will have to wrap the call to free function in another function that will be exported from native library.

extern "C" __declspec(dllexport) void mgrib_free( float* pointer );

void mgrib_free( float* pointer )
{
    free( result );
}

And then import it into like this:

[DllImport( "mgrib.dll", EntryPoint = "mgrib_free",
            CallingConvention = CallingConvention.Cdecl )]
public static extern void mgrib_free( IntPtr pointer );

And invoke it as follows:

mgrib_free( pointer );
  • Rest, Thanks a lot for your detailed reply. This is actually exactly what I did for one of my many attempts. Your answer gave me the confidence to drill down a little deeper and step through each iteration (ugh). I discovered that no matter what the program crashes without warning after accessing 'mgrib' 256 times. That's a little too much of a coincidence. I was using Marshal.FreeCoTaskMem. Marshal.FreeHGlobal throws a handle error. So I think it's pretty clear now that FreeCoTaskMem isn't quite cutting it, somehow the memory isn't being freed. Any ideas anyone? – Anthony Mar 09 '11 at 02:13
  • That was the first thing I tried, but before I learned how to use IntPtr. I'll give it a try. Thanks for coming back to this. – Anthony Mar 10 '11 at 20:33
  • Turns out after all that I was cutting off the destruction of another pointer in the c-code when I returned the data I was interested in. Thanks a lot trying to help me solve my own stupidity ;)! – Anthony Mar 27 '11 at 06:28