Passing an array of structs to unmanaged dll

G

Guest

Hi, I have a C# program that uses an unmanaged dll that has a function
similar to the signature below :

void f(out MyStruct[] arr, out int num); // num = actual array length returned

The array must be allocated (with known max length = 10) before the call to
the dll function (the dll just fills it ,with no allocations).

The definitions of Mystruct and :


public struct Mystruct TimeTag
{
Int32 Id;
Int32 Type;
TimeTag StartTime;
TimeTag EndTime;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
Int32[] Reserved;
}

public struct TimeTag
{
int Day;
Int Hour;
}


I allocate the array , init all fields , allocate and init all needed struct
fields , pass it to the dll and get an exception:

"The runtime has encountered a fatal error. The address of the error was at
0x79e95b95, on thread 0x28348. The error code is 0xc0000005. This error may
be a bug in the CLR or in the unsafe or non-verifiable portions of user code.
Common sources of this bug include user marshaling errors for COM-interop or
PInvoke, which may corrupt the stack".

I can't change nor the dll neither it's API.

What is the problem and what should I do to avoid it?
 
N

Nicholas Paldino [.NET/C# MVP]

Alexanderfe,

Can you give the original function and structure declaration in C? The
reason I ask is because I want to know if the Reserved field on the MyStruct
structure is embedded in the structure, or a pointer to another array.
 
G

Guest

Thanks for the reply,
It's embedded

typedef struct Mystruct{
LONGINT Id;
LONGINT Type;
TimeTag StartTime;
TimeTag EndTime;
LONGINT Reserved[2];
};



Nicholas Paldino said:
Alexanderfe,

Can you give the original function and structure declaration in C? The
reason I ask is because I want to know if the Reserved field on the MyStruct
structure is embedded in the structure, or a pointer to another array.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Alexanderfe said:
Hi, I have a C# program that uses an unmanaged dll that has a function
similar to the signature below :

void f(out MyStruct[] arr, out int num); // num = actual array length
returned

The array must be allocated (with known max length = 10) before the call
to
the dll function (the dll just fills it ,with no allocations).

The definitions of Mystruct and :


public struct Mystruct TimeTag
{
Int32 Id;
Int32 Type;
TimeTag StartTime;
TimeTag EndTime;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
Int32[] Reserved;
}

public struct TimeTag
{
int Day;
Int Hour;
}


I allocate the array , init all fields , allocate and init all needed
struct
fields , pass it to the dll and get an exception:

"The runtime has encountered a fatal error. The address of the error was
at
0x79e95b95, on thread 0x28348. The error code is 0xc0000005. This error
may
be a bug in the CLR or in the unsafe or non-verifiable portions of user
code.
Common sources of this bug include user marshaling errors for COM-interop
or
PInvoke, which may corrupt the stack".

I can't change nor the dll neither it's API.

What is the problem and what should I do to avoid it?
 
G

Guest

I've forgot to mention that when I use structs that consist only of Int32
fields in the same manor, all works fine and no exception is thrown.

Nicholas Paldino said:
Alexanderfe,

Can you give the original function and structure declaration in C? The
reason I ask is because I want to know if the Reserved field on the MyStruct
structure is embedded in the structure, or a pointer to another array.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Alexanderfe said:
Hi, I have a C# program that uses an unmanaged dll that has a function
similar to the signature below :

void f(out MyStruct[] arr, out int num); // num = actual array length
returned

The array must be allocated (with known max length = 10) before the call
to
the dll function (the dll just fills it ,with no allocations).

The definitions of Mystruct and :


public struct Mystruct TimeTag
{
Int32 Id;
Int32 Type;
TimeTag StartTime;
TimeTag EndTime;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
Int32[] Reserved;
}

public struct TimeTag
{
int Day;
Int Hour;
}


I allocate the array , init all fields , allocate and init all needed
struct
fields , pass it to the dll and get an exception:

"The runtime has encountered a fatal error. The address of the error was
at
0x79e95b95, on thread 0x28348. The error code is 0xc0000005. This error
may
be a bug in the CLR or in the unsafe or non-verifiable portions of user
code.
Common sources of this bug include user marshaling errors for COM-interop
or
PInvoke, which may corrupt the stack".

I can't change nor the dll neither it's API.

What is the problem and what should I do to avoid it?
 
W

Willy Denoyette [MVP]

Alexanderfe said:
Hi, I have a C# program that uses an unmanaged dll that has a function
similar to the signature below :

void f(out MyStruct[] arr, out int num); // num = actual array length
returned

The array must be allocated (with known max length = 10) before the call
to
the dll function (the dll just fills it ,with no allocations).

The definitions of Mystruct and :


public struct Mystruct TimeTag
{
Int32 Id;
Int32 Type;
TimeTag StartTime;
TimeTag EndTime;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
Int32[] Reserved;
}

public struct TimeTag
{
int Day;
Int Hour;
}


I allocate the array , init all fields , allocate and init all needed
struct
fields , pass it to the dll and get an exception:

"The runtime has encountered a fatal error. The address of the error was
at
0x79e95b95, on thread 0x28348. The error code is 0xc0000005. This error
may
be a bug in the CLR or in the unsafe or non-verifiable portions of user
code.
Common sources of this bug include user marshaling errors for COM-interop
or
PInvoke, which may corrupt the stack".

I can't change nor the dll neither it's API.

What is the problem and what should I do to avoid it?



The caller allocates the array of structs, however, you pass it as "out
MyStruct[]", this ain't gonna work, by this you are expecting the callee to
create the struct array and pass a pointer back to the caller (what you
effectively pass is a null pointer). You need to pass your struct array by
ref.

f(ref MyStruct[] arr, out int num);

....
int num = 0;
MyStruct[] ms = new MyStruct[10]; // allocate array of structs
f(ref ms, out int num); // pass the array address, callee fills the array
...

Willy.
 
N

Nicholas Paldino [.NET/C# MVP]

Alexanderfe,

Given that, your structure declarations should look like this:

[StructLayout(LayoutKind.Sequential)]
public struct TimeTag
{
public int Day;
public int Hour;
}

[StructLayout(LayoutKind.Sequential)]
public struct Mystruct
{
public int Id;
public int Type;
public TimeTag StartTime;
public TimeTag EndTime;
public fixed int Reserved[2];
}

Notice the fixed keyword on the array field in the Mystruct declaration.
This causes the array to be embedded in the struct, and not a pointer to the
array. This is why it works when you declare two separate integers in the
struct, because the layout in memory is the same (one integer after
another).

Also note Willy's comments as well.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

LONGINT Id;
LONGINT Type;
TimeTag StartTime;
TimeTag EndTime;
LONGINT Reserved[2];
};

Alexanderfe said:
Thanks for the reply,
It's embedded

typedef struct Mystruct{
LONGINT Id;
LONGINT Type;
TimeTag StartTime;
TimeTag EndTime;
LONGINT Reserved[2];
};



Nicholas Paldino said:
Alexanderfe,

Can you give the original function and structure declaration in C?
The
reason I ask is because I want to know if the Reserved field on the
MyStruct
structure is embedded in the structure, or a pointer to another array.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Alexanderfe said:
Hi, I have a C# program that uses an unmanaged dll that has a function
similar to the signature below :

void f(out MyStruct[] arr, out int num); // num = actual array length
returned

The array must be allocated (with known max length = 10) before the
call
to
the dll function (the dll just fills it ,with no allocations).

The definitions of Mystruct and :


public struct Mystruct TimeTag
{
Int32 Id;
Int32 Type;
TimeTag StartTime;
TimeTag EndTime;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
Int32[] Reserved;
}

public struct TimeTag
{
int Day;
Int Hour;
}


I allocate the array , init all fields , allocate and init all needed
struct
fields , pass it to the dll and get an exception:

"The runtime has encountered a fatal error. The address of the error
was
at
0x79e95b95, on thread 0x28348. The error code is 0xc0000005. This error
may
be a bug in the CLR or in the unsafe or non-verifiable portions of user
code.
Common sources of this bug include user marshaling errors for
COM-interop
or
PInvoke, which may corrupt the stack".

I can't change nor the dll neither it's API.

What is the problem and what should I do to avoid it?
 
W

Willy Denoyette [MVP]

Marshaling arrays of structs is problematic (unless they are holding
blit-able types only) in .NET, so you'll have to take care about the
marshaling yourself.
Basically what you need to do is pass the struct array as an array of
IntPtr's each pointing to an unmanaged struct, on return from unmanaged code
you'll have to marshal the IntPtr's back to their managed struct
representation.

Following illustrates the process.

// C function, takes a pointer to a pointer of type MyStruct and a ref int
holding the size of the input array
extern "C" void __declspec(dllexport) __stdcall F(Mystruct **p, int &size)
{
int n;
Mystruct * ps;
for(int n = 0; n < size; n++)
{
ps = *p++;
//set the struct fields using pointer arithmetics
ps->Id = 1;
..
}
size = n;
}


// C# snip

...
[StructLayout(LayoutKind.Sequential)]
public struct TimeTag
{
public int Day;
public int Hour;
}

[StructLayout(LayoutKind.Sequential)]
public struct Mystruct
{
public int Id;
public int Type;
public TimeTag StartTime;
public TimeTag EndTime;
public fixed int Reserved[2];
}
....
[DllImport("dllname"), SuppressUnmanagedCodeSecurity] static extern void
F( IntPtr[] p, ref int size);
....


int size = 10;
IntPtr[] ps = new IntPtr[size ];
for(int n = 0; n <size ; n++)
ps[n] =
Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Mystruct )));
GetEmbeddedArray2(ps2, ref size );
Mystruct [] s = new Mystruct [size];
for(int i = 0; i < size ; i++)
s = (Mystruct )Marshal.PtrToStructure(ps,
typeof(Mystruct ));

// use array of structs here...
....

Willy.


Alexanderfe said:
I've forgot to mention that when I use structs that consist only of Int32
fields in the same manor, all works fine and no exception is thrown.

Nicholas Paldino said:
Alexanderfe,

Can you give the original function and structure declaration in C?
The
reason I ask is because I want to know if the Reserved field on the
MyStruct
structure is embedded in the structure, or a pointer to another array.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Alexanderfe said:
Hi, I have a C# program that uses an unmanaged dll that has a function
similar to the signature below :

void f(out MyStruct[] arr, out int num); // num = actual array length
returned

The array must be allocated (with known max length = 10) before the
call
to
the dll function (the dll just fills it ,with no allocations).

The definitions of Mystruct and :


public struct Mystruct TimeTag
{
Int32 Id;
Int32 Type;
TimeTag StartTime;
TimeTag EndTime;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
Int32[] Reserved;
}

public struct TimeTag
{
int Day;
Int Hour;
}


I allocate the array , init all fields , allocate and init all needed
struct
fields , pass it to the dll and get an exception:

"The runtime has encountered a fatal error. The address of the error
was
at
0x79e95b95, on thread 0x28348. The error code is 0xc0000005. This error
may
be a bug in the CLR or in the unsafe or non-verifiable portions of user
code.
Common sources of this bug include user marshaling errors for
COM-interop
or
PInvoke, which may corrupt the stack".

I can't change nor the dll neither it's API.

What is the problem and what should I do to avoid it?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top