Marshalling dynamic array to C# delegate (callback) - c#

I have some native C/C++ code that calls a C# delegate as callback. What's the best practice to pass a dynamic array argument? Actually the C pointer is a data member of a struct and I pass the struct to the callback.
Is it OK to do something like this using IntPtr?
struct Data {
... (other data members)
double* array;
int size;
};
Array is a pointer to an array allocated in my C++ code (just a call to new or malloc). On the C# side the delegate would expect
struct Data {
... (other data members)
IntPtr array;
int size;
}
My concern is... should IntPtr be memory allocated using Marshal.AllocHGlobal or is it also safe if it's memory allocated in my C++ code (new or malloc)?

Using IntPtr is correct. The memory is allocated, and deallocate on the unmanaged side. You should therefore do nothing related to allocation and deallocation on the managed side.
Simply read from or write to the array using Marshal.Copy. Or if you prefer use an unsafe block and interpret the IntPtr as a double*.

Related

copy from managed class to native struct

I need to fill native structs from managed classes. Following technique (1. copy managed data into managed byte array, 2. use memcopy to fill native struct) I found to be a common solution for this.
I assume the reason why following code doesn't work is that I use managed classes not managed structs. Having managed classes is a requirement in my project. Can I make this code work using managed classes, or must I switch to managed structs ?
this is the c# managed class:
[StructLayout(LayoutKind.Sequential)]
public class man_s
{
public man_s()
{
// (do something which i can't do in a struct!)
}
// should go into a one-byte native bool
[MarshalAs(UnmanagedType.I1)]
public bool flag1;
public Int32 a;
public Int32 b;
};
... the native cpp struct:
struct nat_s
{
public:
bool flag1;
__int32 a;
__int32 b;
};
... the code which should copy the managed data into the native struct:
// setup some managed data
man_s^ mng = man_s();
mng->flag1 = true;
mng->a = 10;
mng->b = 20;
nat_s nat;
int s = sizeof(nat);
// size check is ok!
System::Diagnostics::Debug::Assert(sizeof(nat) == System::Runtime::InteropServices::Marshal::SizeOf(mng));
// copy into managed byte array
array<byte>^ byteArray = gcnew array<byte>(s);
System::Runtime::InteropServices::Marshal::Copy(IntPtr((void*)(&mng)), byteArray, 0, s);
// this doesn't bring up the expected results
pin_ptr<byte> start = &byteArray[0];
memcpy(&nat, start, s);
// does not work either
System::Runtime::InteropServices::Marshal::Copy(byteArray, 0, IntPtr((void*)(&nat)), s);enter code here
Marshal.Copy is for copying data between managed arrays and unmanaged arrays. That's not what you have here: You have a managed object, and an unmnaged struct. For that, you want the PtrToStructure and StructureToPtr methods. Those methods target copying between a managed object and unmanaged memory of some sort.
// Despite the name, man_s is a managed class, not a managed struct.
// This means it gets the ^ (which you had correct),
// but it also means it gets gcnew (which you were missing).
man_s^ mng = gcnew man_s();
nat_s nat;
// You had this code is correct.
Debug::Assert(sizeof(nat) == Marshal::SizeOf(mng));
// StructureToPtr copies to unmanaged memory.
// An unmanaged array (i.e., allocated with `malloc` or `new byte[]`)
// would work, but a pointer to the unmanaged struct will also work just fine.
// The `false` means "Don't destroy the object that's already at the destination",
// which I believe does not apply here.
Marshal::StructureToPtr(mng, &nat, false);
// You can go the other way as well.
Marshal::PtrToStructure(&nat, mng);
// or
man_s = Marshal::PtrToStructure<man_s>(&nat);
Note: I'm not at a compiler right now. You might need to cast &nat to an IntPtr.
No. AFAIK you can't do that. You know nothing about the memory layout of a managed ref class. You may allocate an inner value type inside the class. You may pin this and copy this as a whole block.
Also I don't understand your code. You copy a managed memory with Marshal::Copy. And you do this in converting a managed class to a native pointer. Than you copy this into managed memory and copy this managed memory again to a native memory! Why at all? If you have a pin pointer that works as a native pointer.

C to C# PInvoke with structs with pointers

I'm trying to create a C# interface which receives a callback from an external C DLL.
The callback's parameters contain pointers to C structs, which themselves have a pointer to a different struct.
The callback signature:
typedef application_event_result (*application_event_ptr)(abuffertype* read_buffer, abuffertype* write_buffer);
The buffer struct definitions in C:
typedef struct {
uint16 size;
uint8* data;
} anotherbuffertype;
typedef struct {
anotherbuffertype *buffer;
uint16 position;
} abuffertype;
I know that the C# signature of the callback should use "ref" for the pointer type of the parameter. But how can the pointer inside the "abuffertype" struct be defined in C#?
So far I have this definition of the two structs in C#:
[StructLayout(LayoutKind.Sequential)]
public struct anotherbuffer
{
UInt16 size;
IntPtr data;
}
[StructLayout(LayoutKind.Sequential)]
public struct abuffer
{
anotherbuffer buffer;
UInt16 position;
}
But that doesn't work. The contents of "abuffer" in C# is not what was there before the callback in the C code.
Do I need to unmarshal the internal struct pointer manually, and if so, how?
You will not get help from the marshaller, this normally causes a major memory management problem. But can work in the specific case of a callback since it is the calling C program that manages the data.
You have to convert the data yourself. Declare the pointers as IntPtr and use Marshal.PtrToStructure() to retrieve the data.
The anotherbuffertype.data member looks like an array, use Marshal.Copy() to copy its content into your own byte[] array. If you don't mind the unsafe keyword then you can keep it declared as byte* and access the elements with data[index], avoids the cost of the copy. It is not very unsafe, pretty easy to keep index constrained to [0..size).

COM interop passing wrong pointers, with only a single byte of my data

I'm having a problem passing pointers to native code using COM. I want to build up a byte array in managed (C#) code and pass that array to native (C++) code. I'm handling the managed-code side, and my colleague owns the native side. Note that I'm much stronger on the managed side, and I've pretty much been using the COM object as written.
The COM signature (simplified) looks this:
unsafe void DoSomething([In] byte* buffer, [In] uint length)
And I call it like this:
var arr = File.ReadAllBytes(#"c:\temp\foo.bar");
fixed (byte* p = arr)
{
// let's see what we're actually pointing at
IntPtr ip = new IntPtr(p);
Console.WriteLine(ip.ToInt32().ToString("x"));
interop.DoSomething(p, arr.Length);
}
Debugging on the managed side, I see the data I expect at the memory location printed out (using Visual Studio's Memory view). When I debug the unmanaged side, I see the right data at that location as well. But, the pointer doesn't point to the right place! It points to a completely different memory location. That location contains the correct first byte of my data, but garbage for the rest of it. And then, of course, lots of wonderful crashy badness happens.
So, for instance, I see:
managed side: pointer (p) is 0x1234567, Memory at 0x1234567 contains the contents of my file.
unmanaged side: pointer (buffer) is 0x5678901, first byte of memory at that location contains the first byte of my file, rest is garbage. Memory at 0x1234567 contains the contents of my file.
I've also tried automatic marshaling:
void DoSomething([In] [MarshalAs(UnmanagedType.LPArray,
ArraySubType = UnmanagedType.U1)]
byte[] buffer,
[In] uint length)
... using straight byte arrays on the managed side; the same result.
And I've tried allocating memory with Marshal.AllocHGlobal, copying data into that with Marshal.Copy and passing the resulting IntPtr, cast to a byte*. Same result.
What the heck am I missing here?
This is a 32-bit process. I've tried smallish (300B) and large (10MB) buffer sizes. C# 3.5, VS 2010.
The core problem is that your COM method is not Automation compatible. A byte* is ambiguous. It can mean either a single byte passed by reference (ref byte in C#) or it can mean an array pointer passed by value (byte[] in C#). A type library cannot express the difference. Automation requires that you pass arrays as a SAFEARRAY.
You'll get away with this when you use the method from a C++ program, you simply pass a pointer to an array. But this goes wrong after Tlbimp.exe translates the type library into an interop library, the interop stub will have the argument declared as ref byte. Which is the exact reason you see only one byte copied.
Fixing this is painful if you can't fix the COM server. You'll have to disassemble the interop library with ildasm.exe /out. Then edit the method declaration in the IL file, then put humpty-dumpty back together with ilasm.exe. Use a little test program to know the exact way the IL should be edited.
Have you tried:
void DoSomething([In] [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)]
byte[] buffer, [In] uint length)
?

Will struct modifications in C# affect unmanaged memory?

My gut reaction is no, because managed and unmanaged memory are distinct, but I'm not sure if the .NET Framework is doing something with Marshaling behind the scenes.
What I believe happens is:
When getting a struct from my unmanaged DLL, it is the same as making that call gets an IntPtr and then uses it and the Marshal class to copy the struct into managed memory (and changes made to the struct in managed memory do not bubble up).
I can't seem to find this documented anywhere on MSDN. Any links would be appreciated.
Here is what my code looks like:
[DllImport("mydll.dll", BestFitMapping=false, CharSet=CharSet.Ansi)]
private static extern int GetStruct(ref MyStruct s);
[StructLayout(LayoutKind.Sequential, Pack=0)]
struct MyStruct
{
public int Field1;
public IntPtr Field2;
}
public void DoSomething()
{
MyStruct s = new MyStruct();
GetStruct(ref s);
s.Field1 = 100; //does unmanaged memory now have 100 in Field1 as well?
s.Field2 = IntPtr.Zero; //does unmanaged memory now have a NULL pointer in field Field2 as well?
}
No, the P/Invoke marshaller copied the unmanaged structure member values into the managed version of the structure. In general, the managed version of a structure is not in any way compatible with the unmanaged version of it. The memory layout is not discoverable, something the CLR uses to reorder fields to make the structure smaller. Marshaling is essential, you have to create a copy.
Modifying the structure is not possible with the given function signature since you let fill in the memory that's passed to it. The function itself already copies the structure. You can however party on the Field2 value since it is a raw pointer. If that points to a structure then marshal it yourself with Marshal.PtrToStructure(). Modify the managed copy of it and copy it back to unmanaged memory with Marshal.StructureToPtr(). Or access it directly with Marshal.ReadXxx() and WriteXxx().
CSharp Language Specification.doc pg 26
Struct constructors are invoked with the new operator, but that does not imply that memory is being allocated. Instead of dynamically allocating an object and returning a reference to it, a struct constructor simply returns the struct value itself (typically in a temporary location on the stack), and this value is then copied as necessary.
Since, there is nothing special about a 'struct' backing store, so one would not expect there to be annonymous marshalling operations going on behind the member assignments.

Ways To Marshal A Pointer of Array of Struct

I'm calling functions from C++ that returns a pointer to an array of struct and I'm having problems since I'm new to this operation/implementation.
My C++ codes:
// My C++ Structs
typedef struct _MainData {
double dCount;
DataS1 *DS1;
int iCount1;
DataS2 *DS2;
int iCount2;
}MainData;
typedef struct _DataS1 {
unsigned int uiCount1;
unsigned int uiCount2;
int iCount;
void *pA;
void *pB;
} DataS1;
typedef struct _DataS2 {
unsigned int uiCount1;
unsigned int uiCount2;
unsigned int uiCount3;
unsigned int uiCount4;
double dCount;
int iCount1;
char strLbl[64];
} DataS2;
// My C++ Function
MainData* GetData(const int ID)
{
MainData* mData;
int iLength = Get_Count();
mData = new MainData[iLength];
for(int x = 0;x < VarCounter; x++)
{
// Codes here assign data to mData[x]
}
return mData;
}
Question:
How can I call the C++ function GetData to C#?
My current codes in C# are:
[DllImport(".\\sdata.dll")]
[return: MarshalAs(UnmanagedType.LPArray)]
private static unsafe extern MainData[] GetData(int ID);
// The struct MainData in my C# side is already "Marshalled"...
//My function call is here:
MainData[] SmpMapData = GetData(ID);
When I compiled it, there's an exception:
"Cannot marshal 'return value': Invalid managed/unmanaged type combination."
Sorry for the poor coding... Please help...
First, you need to remember that MarshalAs (explicit or implicit) for the return value essentially means "copy native structures content into managed structures".
Second, since the CLR marshaler only copies the data, if you do not free the memory you're allocated in the C++ function, you've got a memory leak to manage.
Third, this error is mainly due to the fact that the CLR marshaler has no way of knowing the length of the array returned by the native code, since you're basically returning a memory pointer and no length.
If you want to keep these memory structures as-is, I strongly suggest you to look into C++/CLI. You'll be able to wrap those complex types into mixed native/managed classes that will avoid you to copy the data around. This will help you keep the data marshaling to the bare minimum between native and managed code.
If you still want to use C# and no C++/CLI, you'll have to write a somehow smarter piece of code to unmarshal the data returned by the native code into managed data. You can look into Custom Marshaling for that.
I don't see how the .NET runtime could possibly know how many MainData are allocated in GetData(...).
Refactor your C++ code to consume an array to populate or return single MainDatas.

Categories