all groups > dotnet interop > november 2005 >
You're in the

dotnet interop

group:

Marshaling a link list structure


Marshaling a link list structure none
11/25/2005 5:37:32 PM
dotnet interop:
I'm dealing with a legacy C DLL and I need some help & guidance with one
of the structures and functions. We have a link list structure defined
as:


typedef struct OurLinkList
{
OurLinkList* pNext
char chValue[1];
} OurLinkList;


To allocate a node in our C code, we have:

void CreateNode(char* pchString)
{
if(pchString == NULL)
return;

size_t len = strlen(pchString);
OurLinkList* pNode
= (OurLinkList *)malloc(len + sizeof(OurLinkList));
pNode->pNext = NULL;
memcpy(pNode-> chValue, pchString, len + 1);
return pNode;
}


In my C# code, I currently have the following:

[StructLayout(LayoutKind.Sequential), CharSet = CharSet.Ansi)]
public class OurLinkList
{
public IntPtr pNext;
[MarshalAs(UnmanagedType.ByvalTStr, SizeConst = 2000)]
public string chValue;
}



This seems to work but I have two questions about my C# version of
OurLinkList.

First, in order to get the string (chValue) to work in C#, I tried a
number of things, but only

[MarshalAs(UnmanagedType.ByvalTStr, SizeConst =
_SOME_NUMBER_LARGE_ENOUGH_TO_HOLD ALL_OF_THE_CHARS_)]


With our code, the string created in CreateNode() is usually less than
50 chars, so setting SizeConst = 2000 will be large enough for the
strings we'll have, but is there a better way of specifying how to
marshal chValue?

It's not a char* so I wasn't able to use UnmanagedType.LPWStr, and it's
not a fixed-sze array so UnmanagedType.ByvalTStr, SizeConst = 2000 is
wrong as well (but at least it's working).

Am I introducing a memory leak or potentially trashing memory when I set
the SizeConst to something larger than the actual size?





My second issue is with my C# pNext pointer: I have it declared as
IntPtr. This works OK, but I don't like having to write the extra code
to marshal the IntPtr into a OurLinkList. When I tried to declare it as:

public class OurLinkList
{
public OurLinkList pNext;

or

public class OurLinkList
{
unsafe public OurLinkList* pNext;



I got some compile errors. How should pNext be defined?


Thanks,

Re: Marshaling a link list structure None
1/18/2006 9:14:49 PM
I never received a response from anyone on this issue but here is the
solution. The issue is that that company I work at have some legacy
C-DLLs which I'm using with my C# project. Some of the functions in one
of the DLLs use a linked list structure, but the link list structure is
declared in a slightly unusual way. Normally, you'd define a link list
like this:

typedef struct Node
{
Node* pNext;
char* pchValue;
} Node;

....and allocate a new node like this:

Node* pNode = (Node *)malloc(sizeof(Node));
pNode->pNext = NULL;
pNode->pchValue = (char *)malloc(strlen("foo") + 1);
strcpr(pNode->pchValue, "foo");

(or something along those lines)

However the structure I'm using is defined as:
typedef struct Node
{
Node* pNext;
char chValue[1];
} Node;

....and you allocate a new node like this:
size_t len = strlen("foo");
Node* pNode = (Node *)malloc(len + sizeof(Node));
pNode->pNext = NULL;
strcpr(pNode->chValue, "foo");



Now, here is where I got all fowled up: The character array isn't a
pointer, so I can't use IntPtr to directly marshal the char array, and
the char array isn't a fixed-sized array so I can't use
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = _SOME_NUMBER_)] either.

The solution is
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class Node
{
public IntPtr next;
public IntPtr chValue;
}

// ...and in the C-DLL we have void CreateNode(Node** ppNode);


IntPtr pNode = IntPtr.Zero;
CreateNode(ref pNode);
Node node = (Node)Marshal.PtrToStructure(pNode, typeof(Node));
string value = Marshal.PtrToStringAnsi(
new IntPtr(pNode.ToInt32() + Marshal.SizeOf(pNode)));



So, if you ever come across a structure with a variable-sized member of
[1], this is how you marshal the structure.




[quoted text, click to view]
AddThis Social Bookmark Button