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.

Was it helpful?

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top