Search code examples
c#pinvokemarshallingmixed-mode

Marshalling native .dll in C# with multiple pointers


Having the following code in C++:

  • nConId is Connection Identifier
  • pParName the parameter name
  • pSubName the subParameter Name (if any)
  • pValue_out a pointer to a char array of lenght FCL_PAR_VALUE_LENGH
  • nValueSize the real size of pValue_out vector (at least FCL_PAR_VALUE_LENGH)
extern "C" MY_API int ReadParameter(const ConnectionId_T nConId, const char* pParName,
    const char *pSubName, char *pValue_out, const int nValueSize );

My try is:

[DllImport("mydll.dll", CharSet = CharSet.Ansi,CallingConvention=CallingConvention.Cdecl)]
public static extern int ReadParameter(ConnectionId_T pConId, IntPtr pParName,
    ref IntPtr pSubName, ref IntPtr[] pValue_out, int nValueSize);

I'm using the following code to call that function:

# nConId is returned from another function and the his value is 0

public const int FCL_PAR_VALUE_LENGH = 128; 

string param_string = "AUXF";
IntPtr pParName = (IntPtr)Marshal.StringToHGlobalAnsi(param_string);

string subparam_string = "T";
IntPtr pSubName = (IntPtr)Marshal.StringToHGlobalAnsi(subparam_string);

IntPtr[] aParValue = new IntPtr[FCL_PAR_VALUE_LENGH]; 

int returnedValue = ReadParameter(nConId, pParName, ref pSubName,
    ref aParValue, FCL_PAR_VALUE_LENGH);

When I run the code I get an AccessViolationException, so I guess there's something wrong in my call.

Do I've my marshall wrong? What do I've to change in the code in order to get the good reponse?

PS: I also know that the call returns also something to aParValue.


Solution

  • You're working too hard with those char*s. It's perfectly legal (and encouraged) to marshal a System.String for input, and a StringBuilder for output.

    [DllImport("mydll.dll", CharSet = CharSet.Ansi,CallingConvention=CallingConvention.Cdecl)]
    public static extern int ReadParameter(
        ConnectionId_T pConId, 
        string pParName, 
        string pSubName,
        StringBuilder pValue_out,
        int nValueSize);
    

    usage

    const int sbLength = 256; //use a domain relevant value
    StringBuilder sb = new StringBuilder(sbLength + 1); //for null character, hard to say if you need it without seeing the C++ code, but easier to just add it than find out.
    int result = ReadParameter(conId, "paramname", "paramsubname", sb, sbLength);
    

    You haven't given any indication about the underlying type of ConnectionId_T so I'm assuming you've ruled that out as an issue.

    MSDN Reference