views:

162

answers:

2

I have a closed source unmanaged DLL coded in C++ that I wanted to use in a C# solution so I created a wrapper managed DLL that use P/Invoke to call the closed source DLL function. That works pretty well for no param function and int variables. However I get a System.ExecutionEngineException when running a more complex function that take an array of struct as parameter which contains array of char for strings. Here is what I had:

[StructLayout(LayoutKind.Sequential)]
public struct Target
{
    public int targetID;

    public string Label;
}

[DllImport("tyrfde.dll", EntryPoint = "tyrfdeGetTarget")]
public static extern int GetTarget(ref Target[] targets);

Below is the information I have from the header file of the DLL:

#define TARGET_LBL_SIZE   (256l)

typedef struct _tyrfdeTarget
{
    TInt32 TargetID;                   // integer signed 32bits
    TCharA Label[TARGET_LBL_SIZE];     // caracter
} tyrfdeTarget;

TInt32 __stdcall tyrfdeGetTargets(tyrfdeTarget* pTargets);

Not quite sure why the array size is specified as long but anyway SizeConst only take int. After some search here is what I tried to fix.

[StructLayout(LayoutKind.Sequential, Size = 260), Serializable]
public struct Target
{
    public int targetID;

    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 256)]
    public string Label;
}

[DllImport("tyrfde.dll", EntryPoint = "tyrfdeGetTargets")]
public static extern int GetTarget(ref Target[] targets);

But I still have the problem. I have read that this exception can throw if the functions clear part of the memory used by CLR. Unfortunately I cannot verify that. Is there something in my code that is obviously wrong and could cause the problem?

+2  A: 

Hm, I think your problem is with the ref Target[] targets parameter. AFAIR this is a reference to a reference, which is probably not what you actually want.

I'd try this:

[DllImport("tyrfde.dll", EntryPoint = "tyrfdeGetTargets")]
public static extern int GetTarget([Out, MarshalAs(UnmanagedType.LPArray)] Target[] targets);

Maybe this article helps you to find the correct declaration.

Note that the size of the array is not clear here, usually in such cases there is also a ref int length parameter, which can then be referenced in the MarshalAs attribute via the SizeParameterIndex property.

Lucero
Your [MarshalAs(UnmanagedType.LPArray)] stop the error throwing, but my array isn't filled by the function. Wasn't why the ref was there?
lucian.jp
Ah, the array is filled by the function... in this case you need to add a [Out] attribute. Try the edited code.
Lucero
Edited version works. Thanks, Any idea why the syntax isn't: GetTarget([MarshalAs(UnmanagedType.LPArray)] out Target[] targets);
lucian.jp
Because `out` (not as attribute, but as modifier) passes a reference to the array reference (e.g. `tyrfdeTarget**` in your case), the out attribute however states that the marshaller must marshal the memory block back into the array after the call.
Lucero
Every time I see that exception, it's the result of the CLR moving an object around during a run of the GC. Marshaling pins the object in the managed heap, keeping it from moving around, which is why the exception would go away.
ajs410
A: 

1) Are you sure that TCharA is 16-bit? Otherwise I think you should also specify whar CharSet to use.

2) Writing this kind of wrappers is way way simpler in C++/CLI.

danbystrom
For 1: I don't have the type definition, so I am not absolutly sure, but is a fair guess.For 2: from the previousle mentioned article : If the unmanaged API is packaged as a DLL and the source code is not available, P/Invoke is the only option
lucian.jp
1. OK, 2. I think you got that wrong. A C++/CLI assembly allows you to include .h-files and/or link to .lib files while still living in "managed land". It you still prefer to name the calls the C++/CLI cde makes "P/Invoke" is up to you... :-)
danbystrom