Thursday, February 17, 2011

string to char* marshaling

Hi,

a strange problem ....

I wrote a managed c++ class that has the followig function:

void EndPointsMappingWrapper::GetLastError(char* strErrorMessage) { strErrorMessage = (char*) Marshal::StringToHGlobalAnsi(_managedObject->GetLastError()).ToPointer(); }

As you can see, this is simple methode to copy the managed string of the last error to the unmanaged world (char*)

from my unmanaged class I call this method like that:

char err[1000]; ofer->GetLastError(err);

The Problem:

putting a breakpoint at the managed c++ methode shows that the string is successfully translated into the char*

however, onece I return to the unmanaged class, the content of err[1000] is lost and its empty again...

Any suggesting will be wellcomed...

Ofer

From stackoverflow
  • 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

  • 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';
    }
    

    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.

0 comments:

Post a Comment