vard
vard

Reputation: 2302

Marshalling structure with embedded string buffer

I have some of unmanaged C++ dynamic library and C# GUI application, using it. I need to pass a pointer to structure with embedded buffer and some integral meta data members. Structure allocated on C# side to C++ library. Then library populates it, returns to C# application and application uses it in some ways, releasing finally.

My first approach was using StringBuilder as embedded string buffer, but i receive an exception on runtime which says it is not allowed to use StringBuilder as structure member. Also I got advice to use string preinited with suitable buffer size. I've tryed this approach, but it seems bufefr passed via string cannot be modified on C++ part. Is it right?

// C++
typedef struct SomeStruct{
    wchar_t* stringBuffer; // buffer
    uint64_t size;  // some additional integral fields
    int64_t  mTime; // some additional integral fields
};

// Native library API method
MYCDLL_API uint8_t __stdcall getSomeStructures(uint32_t index,
                                              SomeStruct* struct,
                                              uint64_t stringBufferSize);


// C# part
[DllImport(myLibraryname, CallingConvention = CallingConvention.StdCall)]
static extern RetCode getSomeStructures(UInt32 index,
                                        ref SomeStruct someStruct,
                                        UInt64 stringBufferSize);

How this task could be solved? As, I mentioned I need to pass a reference to structure with embedded modifyable character buffer from C# to C++.

Upvotes: 0

Views: 783

Answers (2)

fastobject
fastobject

Reputation: 1430

Try to use following declaration of structure:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct SomeStruct
{
    [MarshalAs(UnmanagedType.LPWStr)]
    public string stringBuffer;
    public UInt64 size;
    public Int64 mTime;
};

[DllImport("Win32Project1.dll", EntryPoint = "GetSomeStructures")]
static extern int GetSomeStructures(
        uint index, ref SomeStruct str, ulong stringBufferSize);

static void Main(string[] args)
{
    var a = new SomeStruct(){
        stringBuffer = "asd",
        size=10,
        mTime = 20
    };



    Console.WriteLine(GetSomeStructures(1, ref a, 1));
}

Upvotes: 0

David Heffernan
David Heffernan

Reputation: 612854

I'd declare that field as IntPtr.

struct SomeStruct
{
    IntPtr stringBuffer;
    ulong size;
    long mTime;
}

Declare the function like this:

[DllImport(myLibraryname)]
static extern byte getSomeStructures(
    uint index,
    ref SomeStruct someStruct,
    ulong stringBufferSize
);

Use Marshal.AllocHGlobal() and Marshal.FreeHGlobal() to allocate and free the buffer. Use Marshal.PtrToStringUni() to create a string from the pointer.

SomeStruct someStruct;
someStruct.stringBuffer = Marshal.AllocHGlobal(1024);
try
{
    // initialize the rest of someStruct
    byte retval = getSomeStructures(index, ref someStruct, 1024);
    // check retval for errors
    string str = Marshal.PtrToStringUni(someStruct.stringBuffer);        
}
finally
{
    Marshal.FreeHGlobal(someStruct.stringBuffer);
}

I've assume that the parameter named size is a byte size. If it is actually a length, then you need to account for the characters being 16 bytes wide, and you should change the parameter name to length rather than size.

Upvotes: 1

Related Questions