1

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?

jpsimard-nyx
  • 8,587
  • 6
  • 32
  • 48
  • You should also post an example where you actually allocate the array and call the function. The problem could be there. It seems strange that if the function accepts a pointer to `N` elements that it also doesn't require the number of elements in the array as the second parameter. – arul Dec 02 '09 at 15:04

2 Answers2

2

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
  • 59,176
  • 9
  • 122
  • 152
  • Your [MarshalAs(UnmanagedType.LPArray)] stop the error throwing, but my array isn't filled by the function. Wasn't why the ref was there? – jpsimard-nyx Dec 02 '09 at 15:30
  • Ah, the array is filled by the function... in this case you need to add a [Out] attribute. Try the edited code. – Lucero Dec 02 '09 at 15:32
  • Edited version works. Thanks, Any idea why the syntax isn't: GetTarget([MarshalAs(UnmanagedType.LPArray)] out Target[] targets); – jpsimard-nyx Dec 02 '09 at 15:40
  • 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 Dec 02 '09 at 15:44
  • 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 Feb 09 '10 at 21:47
  • This is the exact same problem I was having with another DLL. The trick was overzealous definition of size constants, and using Out attribute instead of ref. – drharris Nov 01 '10 at 18:59
0

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.

Dan Byström
  • 9,067
  • 5
  • 38
  • 68
  • 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 – jpsimard-nyx Dec 02 '09 at 15:27
  • 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... :-) – Dan Byström Dec 02 '09 at 16:07