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 by ref oras an out, whichever is correct (I parameter. Or possibly a ref parameter. I can't tell from here) which it should be.
Your DllImportDllImport will look like this (with the correct calling convention specified):
[DllImport(@"mydll.dll", CallingConvention=CallingConvention.???)] private static extern int getUserProfileData(refout 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.