Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to marshal a struct with char * data but data is null

I have a struct in my C# as follows:

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct UserProfileData
{
    int userProfileRevision;
    [MarshalAs(UnmanagedType.LPStr)] 
    public String firstName;
    [MarshalAs(UnmanagedType.LPStr)] 
    public String lastName;
    [MarshalAs(UnmanagedType.LPStr)] 
    public String memberids;
    [MarshalAs(UnmanagedType.LPStr)] 
    public String emailAddress;
}

I pass a reference to this

typedef struct userProfile
{
    int profileRevision;
    char *firstName;
    char *lastName;
    char *memberids;
    char *emailAddress;
} userProfile_t;

My C .dll has function like this

int getUserProfileData(userProfile_t *pUserProfile);

to get the values for the strings in the struct above. I call this function from C# code and the int value 'profileRevision' is properly populated. The strings like 'firstname' are properly dynamically allocated and filled within the above C function but when the code returns to the C# environment all the strings in the struct are null. What's the best way to handle this?

like image 470
user1660675 Avatar asked Nov 04 '22 16:11

user1660675


1 Answers

The way you have written it, the char* buffers are allocated on the managed side. But that's the wrong place. The allocation happens on the unmanaged side. Declare the struct in C# like this:

[StructLayout(LayoutKind.Sequential)]
public struct UserProfileData
{
    int userProfileRevision;
    public IntPtr firstName;
    public IntPtr lastName;
    public IntPtr memberids;
    public IntPtr emailAddress;
}

Then call getUserProfileData, passing the struct as an out parameter. Or possibly a ref parameter. I can't tell from here which it should be.

Your DllImport will look like this (with the correct calling convention specified):

[DllImport(@"mydll.dll", CallingConvention=CallingConvention.???)]
private static extern int getUserProfileData(out UserProfileData userProfile);

Then convert the returned pointers to strings like this:

string firstName = Marshal.PtrToStringAnsi(userProfile.firstName);

and so on for the other fields.

Presumably the unmanaged code also exposes a function that deallocates the memory returned in the struct. Call that once you are done with the struct.

like image 150
David Heffernan Avatar answered Nov 10 '22 04:11

David Heffernan