views:

184

answers:

3

Yet another one of my P/Invoke questions! I have this C function:

int _ei_x_new(ei_x_buff* x);

Essentially, it initializes a new buffer struct. In C#, I have this:

[DllImport(EIDLL, EntryPoint = "_ei_x_new")]
public static extern int ei_x_new(out ei_x_buff x);

ei_x_buff is pretty simple:

typedef struct ei_x_buff_TAG {
    char* buff;
    int buffsz;
    int index;
} ei_x_buff;

[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct ei_x_buff {
    [MarshalAsAttribute(UnmanagedType.LPStr)]
    public string buff;
    public int buffsz;
    public int index;
}

But when I do this:

ei_x_buff buffer;
Ei.ei_x_new(out buffer);

I get an AccessViolationException:

Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

Do I need to alloc some memory or something? It's such a simple piece of code that I can't see any glaring problems with it.

EDIT: Native code for _ei_x_new:

// In my wrapper library
DLL_EXPORT int _ei_x_new(ei_x_buff* x) {
    return ei_x_new(x);
}

// In external library being wrapped
int ei_x_extra = 100;

int ei_x_new(ei_x_buff* x)
{
    x->buff = malloc(ei_x_extra);
    x->buffsz = ei_x_extra;
    x->index = 0;
    return x->buff != NULL ? 0 : -1;
}
A: 

Couple of things

  • Use out not ref in the PInvoke signature as you want data marshalled in both directions (into and out of the native function)
  • Can you post the native signature as their could be an issue with the definition. In particular the buff member

EDIT

Just to eliminate one possibility, switch the buff member to be typed IntPtr with no attributes. If that doesn't cause a crash then it's likely a marshalling issue with the string type.

JaredPar
The P/Invoke signature uses out already. If you meant "ref not out", it produces the same result. The native signature is given in the question: int _ei_x_new(ei_x_buff* x);
David Brown
Oh, you mean the struct's native signature? I've edited my question to show it.
David Brown
Changing it to IntPtr stopped the exception. Can I leave it like that, or do I need to use another type?
David Brown
@David, I would go that route and add a wrapper property which calls Marshal.PtrToStringAnsi on the value. Curious, how is the string allocated in native code? The memory I mean
JaredPar
I've updated the question with the full code for _ei_x_new. Doesn't look like it's working with a string at all, really...
David Brown
A: 

Do yo have mark the buffer as pinned in memory using the fixed statement:

example:

// assume class Point { public int x, y; }
// pt is a managed variable, subject to garbage collection.
Point pt = new Point();
// Using fixed allows the address of pt members to be
// taken, and "pins" pt so it isn't relocated.
fixed ( int* p = &pt.x )
{
    *p = 1; 
}
Preet Sangha
Shouldn't be relevant in this scenario
JaredPar
A: 

I think it will work with string too, if you just initialisze the buffer properly on the C side. Keep in mind that the buffer malloc returns may contain garbage, so you should add something like

if (x->buff) x->buff[0] = '\0';

Another thing you may want to double check is that you're using the same calling convention on the native and managed side. The CLR assumes _stdcall unless you specify something else in the DllImport attribute.

Mattias S