string to char * marshaling
-
06-07-2019 - |
Pregunta
Escribí una clase administrada de C ++ que tiene la siguiente función:
void EndPointsMappingWrapper::GetLastError(char* strErrorMessage)
{
strErrorMessage = (char*) Marshal::StringToHGlobalAnsi(_managedObject->GetLastError()).ToPointer();
}
Como puede ver, este es un método simple para copiar la cadena administrada del último error al mundo no administrado ( char *
).
Desde mi clase no administrada, llamo al método de esta manera:
char err[1000];
ofer->GetLastError(err);
Poner un punto de interrupción en el método C ++ administrado muestra que la cadena se tradujo con éxito al char *
. Sin embargo, una vez que regreso a la clase no administrada, el contenido de err [1000]
se pierde y vuelve a estar vacío.
Solución
Está asignando el valor del parámetro pasado (strErrorMessage) en lugar de copiar a esa dirección el contenido del búfer devuelto por Marshal :: StringToHGlobalAnsi.
Una implementación correcta debería ser:
void EndPointsMappingWrapper::GetLastError(char* strErrorMessage, int len)
{ char *str = (char*) Marshal::StringToHGlobalAnsi(_managedObject->GetLastError()).ToPointer();
strncpy(strErrorMessage,str,len);
strErrorMessage[len-1] = '\0';
Marshal::FreeHGlobal(IntPtr(str));
}
La longitud es el tamaño del búfer pasado.
strncpy ()
copiará como máximo len bytes. Si no hay un byte nulo entre los primeros n bytes de la cadena , la cadena de destino no terminará en nulo. Por esa razón, forzamos el '\ 0' en el último byte del búfer.
Otros consejos
Utilizamos la siguiente clase C ++ para hacer las conversiones por nosotros y funciona bien. Debería poder modificar su método para usarlo.
Archivo H
public ref class ManagedStringConverter
{
public:
ManagedStringConverter( System::String^ pString );
~ManagedStringConverter();
property char* PrimitiveString
{
char* get() { return m_pString; }
}
/// <summary>
/// Converts a System::String to a char * string. You must release this with FreeString.
/// </summary>
static const char* StringToChar( System::String^ str );
/// <summary>
/// Converts a System::String to a __wchar_t * string. You must release this with FreeString.
/// </summary>
static const __wchar_t * StringToWChar( System::String^ str );
/// <summary>
/// Frees memory allocated in StringToChar()
/// </summary>
static void FreeString( const char * pszStr );
private:
char* m_pString;
};
Archivo CPP
ManagedStringConverter::ManagedStringConverter( System::String^ pString )
{
m_pString = const_cast<char*>( ManagedStringConverter::StringToChar( pString ) );
}
ManagedStringConverter::~ManagedStringConverter()
{
ManagedStringConverter::FreeString( m_pString );
}
// static
const char * ManagedStringConverter::StringToChar( System::String^ str )
{
IntPtr^ ip = Marshal::StringToHGlobalAnsi( str );
if ( ip != IntPtr::Zero )
{
return reinterpret_cast<const char *>( ip->ToPointer() );
}
else
{
return nullptr;
}
}
// static
const __wchar_t * ManagedStringConverter::StringToWChar( System::String^ str )
{
IntPtr^ ip = Marshal::StringToHGlobalUni( str );
if ( ip != IntPtr::Zero )
{
return reinterpret_cast<const __wchar_t *>( ip->ToPointer() );
}
else
{
return nullptr;
}
}
// static
void ManagedStringConverter::FreeString( const char * pszStr )
{
IntPtr ip = IntPtr( (void *)pszStr );
Marshal::FreeHGlobal( ip );
}
El problema es que StringToHGlobalAnsi crea una nueva memoria no administrada y no copia en la memoria que pretendía usar que asignó a strErrorMessage.
Para resolver esto, debe hacer algo como:
void EndPointsMappingWrapper::GetLastError(char** strErrorMessage)
{
*strErrorMessage = (char*) Marshal::StringToHGlobalAnsi(_managedObject->GetLastError()).ToPointer();
}
Y el uso debería verse así:
char* err;
GetLastError(&err);
//and here you need to free the error string memory
para obtener más información, consulte este msdn artículo