Search code examples
c#visual-c++pinvokemarshallingunmarshalling

Marshal structure which contains strings - incorrect string received in unmanaged C function


[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct sName_d
{
[MarshalAs(UnmanagedType.LPStr)]
public string szCountry;
[MarshalAs(UnmanagedType.LPStr)]
public string szCommonName;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct sCertData_d
{
public Int32 dwVersion;

public sName_d sIssuer;

public sName_d sSubject;
}

public void GenerateCert()
{
sCertData_d cert = new sCertData_d();
sName_d caIssuer = new sName_d();
caIssuer.szCountry = "US";
caIssuer.szCommonName = "John";
sName_d caSubject = new sName_d();
caSubject.szCountry = "UK";
caSubject.szCommonName = "Johann";
cert.sIssuer = caIssuer;
cert.sSubject= caSubject;
NativeMethods.GenerateCert(ref cert);
}

In the above code, NativeMethods.GenerateCert is an unmanaged function of C. When Call reaches inside of this function, I am not getting the string values "John", "UK", "Johann" and "US".

[DllImport("AuthLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int GenerateCert(ref sCertData_d cert);

Unmanaged function prototype is like this -

typedef struct sName_d
{
char szCountry[0x35];

char szCommonName[0x35];
}sName_d;

typedef struct sCertData_d
{
int version;
sName_d sIssuer;
sName_d sSubject;
}sCertData_d;
int GenerateCert(const sCertData_d *psCert);

Solution

  • Your translation of sName_d is wrong. The unmanaged structure is:

    typedef struct sName_d
    {
        char szCountry[0x35];
        char szCommonName[0x35];
    } sName_d;
    

    These are inline character arrays. You marshaled these as UnmanagedType.LPStr. That's a pointer to null-terminated string. You need to use UnmanagedType.ByValTStr.

    Used for in-line, fixed-length character arrays that appear within a structure. The character type used with ByValTStr is determined by the System.Runtime.InteropServices.CharSet argument of the System.Runtime.InteropServices.StructLayoutAttribute attribute applied to the containing structure. Always use the MarshalAsAttribute.SizeConst field to indicate the size of the array. .NET Framework ByValTStr types behave like C-style, fixed-size strings inside a structure (for example, char s[5]).

    Your translation should be:

    [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
    public struct sName_d
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x35)]
        public string szCountry;
    
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x35)]
        public string szCommonName;
    }