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] none wrote:
> 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,
>