devolver la cadena de la función c ++ a VB .Net

I am trying to call C++ function from VB.Net code which returns string using P/Invoke, but it is returning only single character.

C function Declaration

  extern "C" __declspec(dllexport) LPSTR Get_GetDescription(HANDLE)

C function Definition

  LPSTR Get_GetDescription(HANDLE resultBreakDown){
    return LPSTR(((CalcBreakDown*)resultBreakDown)->GetDescription().c_str()); 
}

VB.Net Code

    <DllImport("FeeEngineDll.dll", CallingConvention:=CallingConvention.Cdecl)> _
    Public Shared Function Get_GetDescription(ByVal resultBreakDown As IntPtr, ByVal indexSubs As Integer, ByVal indexLine As Integer) As <MarshalAsAttribute(LPStr)> String
    End Function

Is there any problem with return type or Marshalling?

preguntado el 08 de noviembre de 11 a las 11:11

Both. The C++ code is fundamentally broken, it returns a dangling pointer. Furthermore, this is hard crash in Vista and Win7, string buffers returned as a function return must be allocated with CoTaskMemAlloc(). -

1 Respuestas

extern "C" __declspec(dllexport) LPSTR Get_GetDescription(HANDLE)

Returning a pointer like this is rather dangerous, as it is not clear who owns the memory and thus who should take responsibility for freeing it.

It would be safer to create the buffer in your VB code and pass it into the DLL where the value can be memcpy'ed in. So we could rewrite the C++ side like:

extern "C" __declspec(dllexport) void Get_GetDescription(HANDLE, LPSTR)

void Get_GetDescription(HANDLE resultBreakDown, LPSTR buffer){
    memcpy(buffer,
           ((CalcBreakDown*)resultBreakDown)->GetDescription().c_str(),
           ((CalcBreakDown*)resultBreakDown)->GetDescription().length()+1); 
}

And then redo the VB code as follows:

<DllImport("FeeEngineDll.dll", CharSet:=CharSet.Ansi, CallingConvention:=CallingConvention.Cdecl)> _
Public Shared Sub Get_GetDescription(ByVal resultBreakDown As IntPtr, <MarshalAs(UnmanagedType.LPStr)> ByVal szFilename As StringBuilder)
End Sub

Yo he añadido CharSet:=CharSet.Ansi to the DllImport. Your C++ code is not using unicode characters, whereas VB probably will be, so best specify that, you probably don't need to put it in but I like to make these things explicit.

Tenga en cuenta el uso de StringBuilder en lugar de String as strings are immutable in VB. Finally, you will need to be careful to allocate enough space in your string builder for the description:

Dim buffer As StringBuilder = New StringBuilder(512)

You can either do this by using a large number in your VB code, as I have just done. This will however, cause problems if your C++ code ever copies more characters than you allocated.

Other better options would either be to pass in the buffer size to the C++ code so that it knows how much it is allowed to write, or to have a get size function in the C++ code that can be used to determine how much space should be allocated for the buffer.

respondido 09 nov., 11:19

..Thanks a lot for detailed insight to a question...I can also write a code in unmanaged c++ to free the memory for eg.Void FreeUmanagedString(void* p){delete[]p;}.....and call this function from my VB.Net code....this approach is better or the approach you have mentioned in your answer is better. - sachin

I would still very much favour the approach I outlined. As Hans pointed out in his comment on the question there are other potential pit falls with allocating memory in a DLL and using it else where. - Charles Keepax

...i have used your approach ....but one problem is coming...uneven characters are coming in the extra space of buffer....when i am showing the text returned from the unmanaged code - sachin

Oops apologies the code I posted doesn't copy the NULL terminating byte for the string, fixed now. Just add 1 to the length to copy the extra byte. - Charles Keepax

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.