Load a C-Dll in C# with struct array as parameter - c#

I try to import functions from my C-Dll. A function has a struct array as a parameter. The struct will be filled in the function.
struct test
{
int test1;
int test2;
};
void FillStruct( struct test stTest[], int size)
{
if(size == 2){
stTest[0].test1 = 5;
stTest[0].test2 = 5;
stTest[1].test1 = 2;
stTest[1].test2 = 2;
}
}
The method FillStruct should be used in C#.
I think I have to create the struct in C#. Must I marshal the struct if I use memcpy in the Fillstruct?

struct Test
{
public int test1;
public int test2;
}
[DllImport("mydll", CallingConvention = Cdecl)]
public static extern void FillStruct( Test[] stTest, int size);
[...]
var test = new Test[n];
FillStruct(test, test.Length);
I don't see the problem here. It does not matter what you do with the memory in your c code: as long as you don't cause buffer overflows, you can read, write an copy all you want. c# arrays are just the type and length of the array, followed by the data. When you use p/invoke with simple structs, a pointer to the first element in the original array will be passed to your c code.

Related

How to mashall a return of an array of structs from C to C#

Please could I ask for help regarding the following?
I have a function in a C dll which returns an array of structs. I can't change the source code. I have, as a test, written my own C dll to return an array of structs to see, as a first test, how to call this from C#.
So, the stucture is really simple:
typedef struct
{
int version;
char name[1024];
double top_depth;
float latitude;
}Info_Test;
Here's my C code of the function I wish to call:
Info_Test* GetInfoArray_Test()
{
static Info_Test dit[2];
dit[0].version = 1;
sprintf_s(dit[0].name, 6, "%s", "One");
dit[0].top_depth = 1.1;
dit[0].latitude = -1.1f;
dit[1].version = 2;
sprintf_s(dit[1].name, 6, "%s", "Two");
dit[1].top_depth = 2,2;
dit[1].latitude = -2.2f;
return &(dit[0]);
}
I can call this form a C client with no problem as follows:
Info_Test* pDIT = _GetInfoArray_Test();
Where _GetInfoArray_Test is a function pointer loaded by dynamically linking to the C dll. Works fine, can reference pDIT[0] and pDIT[1] no problem.
Now, how to call this from C#?
I have the following, but it only gives me access to the first of the two elements in the returned array:
[DllImport("MyMathDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr GetInfoArray_Test();
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Info_Test
{
public int version;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)]
public string name;
public double top_depth;
public float latitude;
}
IntPtr ptr3 = GetInfoArray_Test();
Info_Test dit = (Info_Test)Marshal.PtrToStructure(ptr3,typeof(Info_Test));
So. now I have a copy of the first of the two structs but not the second?
How can I correctly call this function so that I get the array of structs (all 2 of them in this case), not just the first one?
Thanks for any help,
Mitch.

correct marshalling of c++ structure in c#

I have this structure in C++ that I need to be converted to C#, so I can create this structure from a byte[].
struct TRANS_RECORD_DATA {
int size, code;
int ThrowItemCount;
int ItemCount;
int ItemSubStart;
int DataSize;
BYTE Data[sizeof(sRECORD_ITEM) * 200]; // sizeof(sRECORD_ITEM) = 548
};
The C# version:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct TRANS_RECORD_DATA {
public int size, code;
public int ThrowItemCount;
public int ItemCount;
public int ItemSubStart;
public int DataSize;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 548*200)]
public byte[] Data;
};
I am using this generic function to give me the structure from the byte array:
T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
T stuff = (T) Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof (T));
handle.Free();
return stuff;
}
But it is giving me:
AccessViolation Exception.
I belive I know why but I don't know how to solve it. The byte[] I have, that needs to be mapped into the struct, does not always have the Data member with size of 548*200. This number is a maximum. But seems that that GenericMethod I use, always try to create the struct with that Data always 548*200, and then it will obviously throw an AccessViolation because the data to be mapped has ended already.
For example this code:
var bytes = new byte[26];
var structure = ByteArrayToStructure<TRANS_RECORD_DATA>(bytes);
Should return a TRANS_RECORD_DATA with all those int members with 0 value, and finally that byte[] Data would have only the remaining two bytes. (26 - 24 = 2). But it seems that it tries to create the full 548*200 byte[] all the time, and then cause the access violation.
Is there a way to solve this ?
So as you explaining it, regardless of C definition for Data array being 200*548 bytes long, it is not actually fully allocated or filled by an external unmanaged code you call.
this way your only workaround is NOT to define Data in your C# struct definition:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct TRANS_RECORD_DATA {
public int size, code;
public int ThrowItemCount;
public int ItemCount;
public int ItemSubStart;
public int DataSize;
};
and after reading DataSize re-interpret remainder of the byte array as that data structure.
you can still use series of calls to Marshal.PtrToStructure:
GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
TRANS_RECORD_DATA stuff = (TRANS_RECORD_DATA) Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof (TRANS_RECORD_DATA));
var items = new List<sRECORD_ITEM>(stuff.ItemCount);
for (int i = 0; i < stuff.ItemCount; ++i)
{
var ptr = handle.AddrOfPinnedObject().Add(i*548)
sRECORD_ITEM item = (sRECORD_ITEM)Marshal.PtrToStructure(ptr,typeof(sRECORD_ITEM));
}
I am pretty sure this should do the trick.

How use pinvoke for C struct array pointer to C#

I am trying to use pinvoke to marshall an array of structures inside another structure from C to C#. AFAIK, no can do.
So instead, in the C structure I declare a ptr to my array, and malloc. Problems: 1) How do I declare the equivalent on the C# side? 2) How do I allocate and use the equivalent on the C# side?
//The C code
typedef struct {
int a;
int b; } A;
typedef struct {
int c;
// A myStruct[100]; // can't do this, so:
A *myStruct; } B;
//The c# code:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class A{
int a;
int b;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class B{
int c;
// can't declare array of [100] A structures...
?
}
[EDIT]: Somehow I misinterpreted what I read elsewhere about fixed array of objects on the c# side.
And I can fix the array size in C So compiled ok, but then I get "object reference not set to an instance of an object" when using:
data.B[3].a = 4567; So, reading elsewhere about what this error might be, I added this method:
public void initA()
{
for (int i = 0; i < 100; i++) { B[i] = new A(); }
}
Again, compiled OK, but same error msg.
To marshal "complex" structures like this between C and C#, you have a couple of options.
In this case, I would highly recommend that you try to embed a fixed array into your C-side structure, as it will simplify the C# side a lot. You can use the MarshalAs attribute to tell C# how much space it needs to allocate in the array at run-time:
// In C:
typedef struct
{
int a;
int b;
} A;
typedef struct
{
int c;
A data[100];
} B;
// In C#:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct A
{
int a;
int b;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct B
{
int c;
[MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
A[] data = new data[100];
}
If you don't know, or can't specify, a fixed size for your array, then you'll need to do what you did and declare it as a pointer in C. In this case, you cannot tell C# how much memory the array is going to use at run-time, so you're pretty much stuck with doing all of the marshalling by hand. This question has a good rundown of how that works, but the basic idea is:
You should add a field to your structure that includes a count of the array elements (this will make your life much easier)
Declare the field in C# as: IntPtr data; with no attributes.
Use Marshal.SizeOf(typeof(A)) to get the size of the struct in unmanaged memory.
Use Marshal.PtrToStructure to convert a single unmanaged structure to C#
Use IntPtr.Add(ptr, sizeofA) to move to the next structure in the array
Loop until you run out.

Getting Array of struct from IntPtr

I have some struct like this
struct MyStruct
{
public int field1;
public int field2;
public int field3;
}
and I have pointer to array of this struct.
So, I need to get array from this pointer.
I'm tried to using Marshal.PtrToStructure, but i had memory reading error.
This is my methode:
public MyStruct[] GetArrayOfStruct(IntPtr pointerToStruct, int length)
{
var sizeInBytes = Marshal.SizeOf(typeof(TCnt));
MyStruct[] output = new MyStruct[length];
for (int i = 0; i < length; i++)
{
IntPtr p = new IntPtr((pointerToStruct.ToInt32() + i * sizeInBytes));
output[i] = (MyStruct)System.Runtime.InteropServices.Marshal.PtrToStructure(p, typeof(MyStruct));
}
return output;
}
So, what am i doing wrong ?
This function worked for me, assuming that the size of the struct is fixed:
public static void MarshalUnmananagedArray2Struct<T>(IntPtr unmanagedArray, int length, out T[] mangagedArray)
{
var size = Marshal.SizeOf(typeof(T));
mangagedArray = new T[length];
for (int i = 0; i < length; i++)
{
IntPtr ins = new IntPtr(unmanagedArray.ToInt64() + i * size);
mangagedArray[i] = Marshal.PtrToStructure<T>(ins);
}
}
Two problems. You use TCnt instead of MyStruct in the Marshal.SizeOf() call. Your IntPtr arithmetic cannot work on a 64-bit machine, you must use IntPtr.ToInt64() or cast to (long).
Just getting the wrong IntPtr or length is certainly a possibility too of course. Use Debug + Windows + Memory + Memory 1 and put "pointerToStruct" in the Address box for basic verification.
Structs in C and C# are not the same thing. One of the differences is that in C# you have to explicitly demand that your struct should be sequentially laid out. If you didn't write
[StructLayout(LayoutKind.Sequential)] or [StructLayout(LayoutKind.Explicit)] attribute to your structure I don't believe that you can manage it in this way. Microsoft states that PtrToStructure is to be used to convert structures from unmanaged to managed memory
You should test if adding this attributes to your struct helps, If it doesn't yet help try allocating memory with Marshal.AllocHGlobal(IntPtr) and use Marshal.Copy to init your structure and then try using PtrToStructure. If this works then you can't use PtrToStructure with managed memory

Marshalling an array of structures from C++ to C#?

In my C# code I'm trying to fetch an array of structures from a legacy C++ DLL (the code I cannot change).
In that C++ code, the structure is defined like this:
struct MyStruct
{
char* id;
char* description;
};
The method that I'm calling (get_my_structures) returns a pointer to an array of MyStruct structures:
MyStruct* get_my_structures()
{
...
}
There is another method that returns the number of stuctures so I do know how many structures get returned.
In my C# code, I have defined MyStruct like this:
[StructLayout(LayoutKind.Sequential)]
public class MyStruct
{
[MarshalAsAttribute(UnmanagedType.LPStr)] // <-- also tried without this
private string _id;
[MarshalAsAttribute(UnmanagedType.LPStr)]
private string _description;
}
The interop signature looks like this:
[DllImport("legacy.dll", EntryPoint="get_my_structures")]
public static extern IntPtr GetMyStructures();
Finally, the code that fetches the array of MyStruct structures looks like this:
int structuresCount = ...;
IntPtr myStructs = GetMyStructures();
int structSize = Marshal.SizeOf(typeof(MyStruct)); // <- returns 8 in my case
for (int i = 0; i < structuresCount; i++)
{
IntPtr data = new IntPtr(myStructs.ToInt64() + structSize * i);
MyStruct ms = (MyStruct) Marshal.PtrToStructure(data, typeof(MyStruct));
...
}
The trouble is, only the very first structure (one at the offset zero) gets marshaled correctly. Subsequent ones have bogus values in _id and _description members. The values are not completely trashed, or so it seems: they are strings from some other memory locations. The code itself does not crash.
I have verified that the C++ code in get_my_structures() does return correct data. The data is not accidentally deleted or modified during or after the call.
Viewed in a debugger, C++ memory layout of the returned data looks like this:
0: id (char*) <---- [MyStruct 1]
4: description (char*)
8: id (char*) <---- [MyStruct 2]
12: description (char*)
16: id (char*) <---- [MyStruct 3]
...
[Update 18/11/2009]
Here is how the C++ code prepares these structures (the actual code is much uglier, but this is a close enough approximation):
static char buffer[12345] = {0};
MyStruct* myStructs = (MyStruct*) &buffer;
for (int i = 0; i < structuresCount; i++)
{
MyStruct* ms = <some other permanent address where the struct is>;
myStructs[i].id = (char*) ms->id;
myStructs[i].description = (char*) ms->description;
}
return myStructs;
Admittedly, the code above does some ugly casting and copies raw pointers around, but it still does seem to do that correctly. At least that's what I see in the debugger: the above (static) buffer does contain all these naked char* pointers stored one after another, and they point to valid (non-local) locations in memory.
Pavel's example shows that this is really the only place where things can go wrong. I will try to analyze what happens with those 'end' locations where the strings really are, not the locations where the pointers get stored.
I cannot reproduce your problem, which leads me to suspect that it's really on C++ side of things. Here's the complete source code for my attempt.
dll.cpp - compile with cl.exe /LD:
extern "C" {
struct MyStruct
{
char* id;
char* description;
};
__declspec(dllexport)
MyStruct* __stdcall get_my_structures()
{
static MyStruct a[] =
{
{ "id1", "desc1" },
{ "id2", "desc2" },
{ "id3", "desc3" }
};
return a;
}
}
test.cs - compile with csc.exe /platform:x86:
using System;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
public class MyStruct
{
[MarshalAsAttribute(UnmanagedType.LPStr)]
public string _id;
[MarshalAsAttribute(UnmanagedType.LPStr)]
public string _description;
}
class Program
{
[DllImport("dll")]
static extern IntPtr get_my_structures();
static void Main()
{
int structSize = Marshal.SizeOf(typeof(MyStruct));
Console.WriteLine(structSize);
IntPtr myStructs = get_my_structures();
for (int i = 0; i < 3; ++i)
{
IntPtr data = new IntPtr(myStructs.ToInt64() + structSize * i);
MyStruct ms = (MyStruct) Marshal.PtrToStructure(data, typeof(MyStruct));
Console.WriteLine();
Console.WriteLine(ms._id);
Console.WriteLine(ms._description);
}
}
}
This correctly prints out all 3 structs.
Can you show your C++ code that fills the structs? The fact that you can call it from C++ directly and get correct results does not necessarily mean it's correct. For example, you could be returning a pointer to a stack-allocated struct. When doing a direct call, then, you'd get a technically invalid pointer, but the data would likely remain preserved. When doing P/Invoke marshalling, the stack could be overwritten by P/Invoke data structures by the point it tries to read values from there.
I would change the structure. Instead of strings etc. , use IntPtr:
[StructLayout(LayoutKind.Sequential)]
public class MyStruct
{
private IntPtr _id;
private IntPtr _description;
}
Then each value of the C# array could be manually marshalled to string using Marshal.PtrToString taking into account charset etc.
I usually end up working these things out by trial and error. Make sure you have the CharSet property set on your StructLayout, and I would try UnmanagedType.LPTStr, seems to work better for char *, even though I am not sure why.
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
public class MyStruct
{
[MarshalAsAttribute(UnmanagedType.LPTStr)]
private string _id;
[MarshalAsAttribute(UnmanagedType.LPTStr)]
private string _description;
}
I think, also, in addition to the answers given, that you need to supply the length as well, ie
[MarshalAsAttribute(UnmanagedType.LPTStr), SizeConst = , ArraySubType = System.Runtime.InteropServices.UnmanagedType.AnsiBStr)]
This is a trial and error to get this right, also, another thing to consider, in some WinAPI calls that expect a string parameter, usually a ref parameter, it might be worth your while to try the StringBuilder class also...Nothing else comes to mind on this other than the points I have mentioned here... Hope this helps, Tom
You have to use UnmanagedType.LPTStr for char*. Also a StringBuilder is recommended for a non const char*:
And a CharSet specification:
[StructLayout(LayoutKind.Sequential, Charset = CharSet.Auto)]
public class MyStruct
{
[MarshalAsAttribute(UnmanagedType.LPTStr)]
private StringBuilder _id;
[MarshalAsAttribute(UnmanagedType.LPTStr)]
private StringBuilder _description;
}
As for the DllImport declaration, have you tried
[DllImport("legacy.dll", EntryPoint="get_my_structures")]
public static extern MarshalAs(UnmanagedType.LPArray) MyStruct[] GetMyStructures();
?
Also, if the previous doesn't work, leave it at IntPtr and try to Mashal the returned structs like this:
for (int i = 0; i < structuresCount; i++)
{
MyStruct ms = (MyStruct) Marshal.PtrToStructure(myStructs, typeof(MyStruct));
...
myStructs += Marshal.SizeOf(ms);
}

Categories