Discussion:
Marshalling dynamic byte array
(too old to reply)
Mihajlo Cvetanović
2010-01-20 10:50:29 UTC
Permalink
Raw Message
I'm creating a C++ dll and C# code that uses it. At the moment (and
subject to change if there is better solution) functions in DLL have one
input and one output structure. Output structure has pointer to dynamic
byte array, like this:

struct DataBlock {
UCHAR* data;
int size;
}

struct Person {
wchar_t name[50];
int name_size;
wchar_t surname[50];
int surname_size;
}

bool WINAPI DeleteBlock(DataBlock* block);
bool WINAPI CreatePersonBlock(DataBlock* block, const Person* person);
bool WINAPI CreateOtherBlock(DataBlock* block, const Other* other);


Function DeleteBlock is supposed to delete arrays created from other
functions (maximum array size is unknown). If you have a suggestion how
to transfer data better, I'll gladly hear it.

My main question is how to marshal DataBlock? I'm new to C#, especially
to interop, but AFAIK marshaling implies that original structure is
temporary, which means there'll be memory leak and DeleteBlock is
useless. So, how would I go about this?
Fábio Chicout
2010-01-21 02:02:26 UTC
Permalink
Raw Message
Hi Mihajlo,

Maybe the information you need is in this link:
http://msdn.microsoft.com/en-us/library/aa288468(VS.71).aspx
On the section: <b>Specifying Custom Marshaling for User-Defined Structs</b>

Hope it helps
Post by Mihajlo Cvetanović
I'm creating a C++ dll and C# code that uses it. At the moment (and
subject to change if there is better solution) functions in DLL have one
input and one output structure. Output structure has pointer to dynamic
struct DataBlock {
UCHAR* data;
int size;
}
struct Person {
wchar_t name[50];
int name_size;
wchar_t surname[50];
int surname_size;
}
bool WINAPI DeleteBlock(DataBlock* block);
bool WINAPI CreatePersonBlock(DataBlock* block, const Person* person);
bool WINAPI CreateOtherBlock(DataBlock* block, const Other* other);
Function DeleteBlock is supposed to delete arrays created from other
functions (maximum array size is unknown). If you have a suggestion how to
transfer data better, I'll gladly hear it.
My main question is how to marshal DataBlock? I'm new to C#, especially to
interop, but AFAIK marshaling implies that original structure is
temporary, which means there'll be memory leak and DeleteBlock is useless.
So, how would I go about this?
Mihajlo Cvetanović
2010-01-21 12:31:52 UTC
Permalink
Raw Message
Post by Fábio Chicout
Hi Mihajlo,
http://msdn.microsoft.com/en-us/library/aa288468(VS.71).aspx
On the section: <b>Specifying Custom Marshaling for User-Defined Structs</b>
Hope it helps
Thanks for the link. I've been there already, but at the time failed to
grasp the neatness of IntPtr. I made a wrapper function that uses
structure with IntPtr, calls mine CreatePersonBlock, system's
Marshal.Copy and mine DeleteBlock, before returning just byte[]. Seems
to work for now.
Mihajlo Cvetanović
2010-02-09 15:10:33 UTC
Permalink
Raw Message
After some struggles I settled for this DLL interface (made in C++)

struct Person {
wchar_t name[50];
int name_size;
}

int WINAPI WritePerson(UCHAR* block, int* block_size, const Person* person);
int WINAPI ReadPerson(const UCHAR* block, int* block_size, const Person*
person);

Here's C# part that uses DLL:

[StructLayout(LayoutKind.Sequential, Charset = CharSet.Unicode)]
public class Person
{
public const int max_name_size = 50;

private int name_size;
[MarshalAs(UnanagedType.ByValArray, SizeConst = max_name_size)]
private char[] name;

public String Name {
get { return new String(name, 0, name_size); }
set { name = value.PadRight(max_name_size, '\0').ToCharArray();
name_size = value.Length; }
}

public byte[] CreatePerson(Person person)
{
int block_size = 0;
MyDll.WritePerson(IntPtr.Zero, ref block_size, this);

byte[] block = new byte[block_size];
MyDll.WritePerson(
Marshal.UnsafeAddrOfPinnedArrayElement(block, 0),
ref block_size, this);

return block;
}
}

public class MyDll
{
[DllImport("MyDll.dll")]
public static extern int WritePerson(
IntPtr block,
[In, Out] ref int block_size,
Person person);

[DllImport("MyDll.dll")]
public static extern int ReadPerson(
IntPtr block,
[In, Out] ref int block_size,
[Out] Person person);
}

Now, this stuff works, and data are being marshaled, but it all looks
like a kludge to me. Is there a better way to pass array of variable
size as function parameter (better than "IntPtr block", and that
Marshal.Unsafe function)? I tried SizeParamIndex, but can't seem to use
"ref int" for the size, only clean int.

More importantly, in C# I'd like to use String directly instead of
through a property with padding it to appropriate size, but DLL's
Person::name is not zero terminated, and using String directly gives me
only 49 characters for the name.

Does anybody know of a more elegant way to bind this C-style DLL with C#?
Loading...