Marshalling structs



  • I am having the hardest time trying to marshal some C structs.

    <FONT face="Courier New">typedef struct _FileInfo
    {
        char* Name;
        char* DisplayName;
        unsigned int Status;
    } FileInfo;</FONT>

    <FONT face="Courier New">typedef struct _FileResult
    {
        unsigned int FileCount;
        FileInfo FileInfos[1]; // why???
    } FileResult;</FONT>

    There is a function that returns a pointer (to a pointer) to a FileResult.

    <FONT face="Courier New">unsigned int GetFileResult(...stuff..., FileResult** ppFileResult);</FONT>

    In C, I do this:

    <FONT face="Courier New">FileResult* pFileResult;
    unsigned int r = GetFileResult(...stuff..., &pFileResult);
    for(int i = 0; i < pFileResult->FileCount; i++)
    {
        FileInfo* pFileInfo = &(pFileResult->FileInfos[i]);
        //do something with pFileInfo
    }</FONT>

    I cannot figure out how to get this working in C#.  Here are my C# struct declarations:

    <FONT face="Courier New">public struct FileInfo
    {
        public string Name;
        public string DisplayName;
        public uint Status;
    }</FONT>

    <FONT face="Courier New">public struct FileResult
    {
        public uint FileCount;
        public IntPtr FileInfos;
    }</FONT>

    <FONT face="Courier New">public uint GetFileResult(...stuff..., out IntPtr ppFileResult);</FONT>

    <FONT face="Courier New">IntPtr pFileResult;
    uint r = GetFileResult(...stuff..., out pFileResult);
    FileResult fileResult = new FileResult();
    PtrToStructure(pFileResult, fileResult);
    for(int i = 0; i < fileResult.FileCount; i++)
    {
        // how to marshal fileResult.FileInfos ???
    }</FONT>

    I know the IntPtr FileInfos is not right; as expected, it contains the address of the string Name in the first FileInfo struct.  I cannot modify the C source, so I am stuck with that.  FileInfo is dynamically allocated by the callee.  Is there a way to do this without using unsafe code?  I think it is impossible, but I'm not the most knowledgeable when it comes to marshalling.



  • First, I'll answer the easy question:
    @luke727 said:

    <FONT face="Courier New">    FileInfo FileInfos[1]; // why???</FONT>

    That tells C that there is an array of FileInfos structures.  The upper bound of 1 can be violated as traditional C doesn't check array bounds anyways.

    Second, try declaring the C# structures like this:

    <FONT face="Courier New">using System.Runtime.InteropServices;</FONT>

    <FONT face="Courier New">[StructLayout(LayoutKind.Sequential, Pack=2)]
    public struct FileInfo
    {
        [MarshalAs(UnmanagedType.LPStr)]
        public string Name;
        </FONT><FONT face="Courier New">[MarshalAs(UnmanagedType.LPStr)]
        public string DisplayName;
        public uint Status;
    }

    <FONT face="Courier New">[StructLayout(LayoutKind.Sequential, Pack=2)]
    public struct FileResult
    {
        public uint FileCount;
        [MarshalAs(UnmanagedType.AsAny)]
        public FileInfo[] FileInfos;
    }</FONT>

    </FONT>

    You can allocate an instance of these guys and use Marshal.ReadByte to see where it is storing data if it needs some tweaking.

     



  • @luke727 said:

    <FONT face="Courier New"></FONT>

    ...FileInfo is dynamically allocated by the callee.  Is there a way to do this without using unsafe code?  I think it is impossible, but I'm not the most knowledgeable when it comes to marshalling.

    BTW, this would disturb me greatly if I had to deal with it.  If the library allocates the memory for the FileInfos, who frees it?  Sure, you know the minimum amount of memory that it allocated, but it probably allocated more than it needs.  It is generally bad practice for a library to allocate memory and will cause memory leaks in your application if the library doesn't deallocate it or you don't use the same library to deallocate the memory that the library used to allocate it.

    You may be reduced to taking the second member of the FileResult struct as an IntPtr and using unsafe code or the Marshal class to pick out the data.  Yipee.  At least you can do it as a member method of the struct.  Make FileInfos private and build an accessor method that returns a C# array of FileInfos.



  • The library does have a function to free the memory; I just didn't think it was relevant to the discussion.

    I also have all the appropriate StructLayout and MarshalAs attributes set correctly; I just didn't want to clutter things.

    The problem is with the array of FileInfo structs.  Since it is dynamically allocated, I cannot use any of the MarshalAs options because for arrays they require SizeConst to bet set.  I tried using just an IntPtr, but that didn't work: it contains the address of the Name string for the first FileInfo rather than the address of the struct itself.  I think I will just have to use unsafe code.



  • In that case, there's probably no Managed way to solve the problem.  The Marshal class also requires a size of memory to be allocated.  The only reasonable way to access an arbitrary number of arbitrarily sized strings is unsafe code. 

     

    You should need a license to write C code that uses strings.


Log in to reply