string to char* marshaling
-
06-07-2019 - |
Question
I wrote a managed C++ class that has the following function:
void EndPointsMappingWrapper::GetLastError(char* strErrorMessage)
{
strErrorMessage = (char*) Marshal::StringToHGlobalAnsi(_managedObject->GetLastError()).ToPointer();
}
As you can see, this is a simple method to copy the managed string of the last error to the unmanaged world (char*
).
From my unmanaged class I call the method like this:
char err[1000];
ofer->GetLastError(err);
Putting a breakpoint at the managed C++ method shows that the string is successfully translated into the char*
. However, once I return to the unmanaged class, the content of err[1000]
is lost and it's empty again.
Solution
You are assigning the value of the passed parameter (strErrorMessage) instead of copying to that address the content of the buffer returned by Marshal::StringToHGlobalAnsi.
A correct implementation should be:
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));
}
The length is the size of the buffer passed.
strncpy()
will copy at the most len bytes. If there is no null byte among the first n bytes of the str, the destination string won't be null terminated. For that reason we force the '\0' in the last byte of the buffer.
OTHER TIPS
We use the following C++ Class to do the conversions for us and it works fine. You should be able to modify your method to use it.
H File
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;
};
CPP File
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 );
}
The problem is that StringToHGlobalAnsi creates a new unmanged memory and does not copy into the memory you intended to use which you assigned into strErrorMessage.
To resolve this you should do some thing like:
void EndPointsMappingWrapper::GetLastError(char** strErrorMessage)
{
*strErrorMessage = (char*) Marshal::StringToHGlobalAnsi(_managedObject->GetLastError()).ToPointer();
}
And the usage should look like:
char* err;
GetLastError(&err);
//and here you need to free the error string memory
for more information check out this msdn article