C to C# PInvoke with structs with pointers - c#

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).

Related

Pointer to a struct that has a fixed array of another struct

I'm trying to write C# bindings for a native DLL. In the DLL are structs defined as
typedef struct Foo {
Bar bars[32];
} Foo;
and
typedef struct Bar {
uint32_t data;
} Bar;
and a function
void Baz(Foo* foo);
Ideally, I would like to define the C# structs as
public unsafe struct Foo {
public unsafe fixed Bar bars[32]
}
and
public struct Bar {
public uint data;
}
and the function as
public unsafe void BazDelegate(Foo* foo);
This would allow me to use pointers to manipulate the structs. However, this is not allowed because fixed arrays can only be used on primitive types, so I can't have a fixed array of the second type. The API requires that the first type be passed in via pointer to a certain function.
The only solution I've found is by changing the type of the field to a managed array, Bar[], and letting the marshaller take care of it. But then that means that I can' take a pointer the the struct. So I would have to change the function's signature from unsafe pointer to ref.
The bindings are not written using [DllImport]. They are loaded at runtime through Marshal.GetDelegateForFunctionPointer. Could I change the structs to use [MarshalAs(UnmanagedType.ByValArray)] and the function use ref Foo instead of Foo*?
Is using a function created from Marshal.GetDelegateForFunctionPointer considered P/Invoke? Do managed arrays still get marshalled if they aren't passed through [DllImport]? Do they get marshalled if passed by ref?

Marshalling dynamic array to C# delegate (callback)

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*.

What happens to a byte array in C# when passed to unmanaged code in a struct?

I'm working on a .NET DLL to access a C++ library that I was given. In the C++ library, there is this struct:
typedef struct FOO
{
DWORD DataSize;
BYTE *pData;
}
I've recreated it in C# thus:
[StructLayout(LayoutKind.Sequential)]
public struct FOO
{
public uint DataSize;
public byte[] pData;
}
My import from the C++ DLL is next. I'm going to include the header from the C++ side as well. The method in C++ takes a pointer to the structure I'm passing in, so from what I've been able to gather passing a reference in would work in this case:
// C++ Header
HRESULT CallFoo(FOO * pFoo);
[DllImport("SomeLibrary.DLL", EntryPoint = "CallFoo")]
private static extern uint CallFoo(ref FOO rFoo);
When I step into my code in the C++ side, I'm getting the structure but the value in pData is a memory address. This appears to be fouling the code in the C++ library but I can't make sense of the HRESULT it's returning to me (I've put in a question to the owner of the C++ library as to what the error message is).
Another approach I took based upon this question's answer, was to try passing an IntPtr instead of a byte array. I modified the struct:
[StructLayout(LayoutKind.Sequential)]
public struct FOO
{
public uint DataSize;
public IntPtr pData;
}
and to call it:
FOO fooParm = new Foo();
var ptr = IntPtr.Zero;
byte[] bArr = MethodThatReturnsAByteArray();
ptr = Marshal.AllocHGlobal(bArr.Length);
Marshal.Copy(bArr, 0, ptr, bArr.Length);
fooParm.pData = ptr;
fooParm.DataSize = bArr.Length;
uint i = CallFoo(fooParm);
This unfortunately is not working either. I get the same error code as the original approach.
Your whole approach is not good. There is some info on the layout of the managed structs/classes, but it is better not to use it. Try to do this:
[DllImport("SomeLibrary.DLL", EntryPoint = "CallFoo")]
private static extern uint CallFoo(uint DataSize, IntPtr pData);
The more simple parameters you pass back and forth, the better the result will be.
I don't have the C++ library in front of me, so it's hard to answer this question accurately, I feel, but it sounds like you're getting a pointer and the library is having problems with that. Would it be possible to use Marshal::PtrToStructure to map that byte[] pointer to a proper struct?
Here is a link to the particular method that I'm talking about. The second code example near bottom (in C++) might apply: http://msdn.microsoft.com/en-US/library/4ca6d5z7.aspx

C# import C Dll. Array pointer in struct. How to?

I need your help.
I am trying to import a C Dll into a C# project. While doing that, I need pass a struct between the Dll and C# project in both directions.
Here is C definition:
struct mwBITMAP
{
int bmWidth;
int bmHeight;
BYTE* bmData;
};
Here is C# definition:
[StructLayout(LayoutKind.Sequential)]
public struct MwRemoteBmp
{
public int Width;
public int Height;
public byte[] Data;
}
I tried to pass the a struct (the Data is well initialized) from C# to a dll's test function by reference. The width and height are both right. But the Data is all wrong.
Where did I make mistakes?
Yes, the array gets marshaled as a SAFEARRAY. Not crashing the pinvoke marshaller is pretty unusual. Declare the Data member as IntPtr, then use Marshal.Copy() to copy the data.
Beware that this would be hard to use in C as well. There's a memory management problem, it isn't clear who owns the array. Most typically, the C function would use malloc() to allocate the array. That's a big problem, you cannot release that array in C#, no way to call free(). You'll have an unpluggable memory leak. If you can't rewrite the C code then you'll need to write a wrapper in the C++/CLI language so that you can call free(). Even that is tricky if the C dll doesn't use the same CRT as the C++/CLI code. You have to compile the C code with the /MD option.
Use the IntPtr type instead of byte[] type.
In your example:
[StructLayout(LayoutKind.Sequential)]
public struct MwRemoteBmp
{
public int Width;
public int Height;
public IntPtr Data;
}

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